Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ executors:
parameters:
current_golden_images_hash:
type: string
default: 583d8dd3401eba050520e50eebc1c9fd8a217f32
default: b42bc845d071194c838dcad064ac2b18f6072b88
commands:
downstream:
steps:
Expand Down
19 changes: 19 additions & 0 deletions packages/action-menu/src/ActionMenu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ import {
PropertyValues,
html,
ifDefined,
property,
} from '@spectrum-web-components/base';
import '@spectrum-web-components/menu/sp-menu.js';
import '@spectrum-web-components/popover/sp-popover.js';
import { PickerBase } from '@spectrum-web-components/picker';
import '@spectrum-web-components/action-button/sp-action-button.js';
import { ObserveSlotText } from '@spectrum-web-components/shared/src/observe-slot-text.js';
Expand All @@ -33,6 +36,9 @@ export class ActionMenu extends ObserveSlotText(PickerBase, 'label') {
return [actionMenuStyles];
}

@property({ type: String })
public selects: undefined | 'single' = undefined;

protected listRole: 'listbox' | 'menu' = 'menu';
protected itemRole = 'menuitem';
private get hasLabel(): boolean {
Expand Down Expand Up @@ -72,6 +78,19 @@ export class ActionMenu extends ObserveSlotText(PickerBase, 'label') {
`;
}

protected get renderPopover(): TemplateResult {
return html`
<sp-popover id="popover" @sp-overlay-closed=${this.onOverlayClosed}>
<sp-menu
id="menu"
role="${this.listRole}"
@change=${this.handleChange}
.selects=${this.selects}
></sp-menu>
</sp-popover>
`;
}

protected updated(changedProperties: PropertyValues): void {
super.updated(changedProperties);
if (changedProperties.has('invalid')) {
Expand Down
12 changes: 12 additions & 0 deletions packages/action-menu/stories/action-menu.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,13 +67,25 @@ interface StoryArgs {
disabled?: boolean;
open?: boolean;
customIcon?: string | TemplateResult;
selects?: 'single';
selected?: boolean;
}

const Template = (args: StoryArgs = {}): TemplateResult =>
ActionMenuMarkup(args);

export const Default = (args: StoryArgs = {}): TemplateResult => Template(args);

export const selects = (args: StoryArgs = {}): TemplateResult =>
Template({
...args,
selects: 'single',
selected: true,
});
selects.args = {
open: true,
};

export const iconOnly = (args: StoryArgs = {}): TemplateResult =>
Template(args);
iconOnly.args = {
Expand Down
8 changes: 6 additions & 2 deletions packages/action-menu/stories/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ export const ActionMenuMarkup = ({
open = false,
visibleLabel = '',
customIcon = '' as string | TemplateResult,
size = 'm',
size = 'm' as 'm' | 's' | 'l' | 'xl' | 'xxl',
selects = '' as 'single',
selected = false,
} = {}): TemplateResult => {
return html`
<sp-action-menu
Expand All @@ -32,6 +34,8 @@ export const ActionMenuMarkup = ({
?open=${open}
size=${size}
@change="${changeHandler}"
.selects=${selects ? selects : undefined}
value=${selected ? 'Select Inverse' : ''}
>
${customIcon ? customIcon : html``}
${visibleLabel
Expand All @@ -40,7 +44,7 @@ export const ActionMenuMarkup = ({
`
: html``}
<sp-menu-item>Deselect</sp-menu-item>
<sp-menu-item>Select Inverse</sp-menu-item>
<sp-menu-item ?selected=${selected}>Select Inverse</sp-menu-item>
<sp-menu-item>Feather...</sp-menu-item>
<sp-menu-item>Select and Mask...</sp-menu-item>
<sp-menu-divider></sp-menu-divider>
Expand Down
90 changes: 88 additions & 2 deletions packages/menu/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ import {
</sp-menu>
```

Often an `<sp-menu>` element will be delivered inside of an `<sp-popover>` element when displaying it above other content.

```html
<sp-popover open style="position: relative">
<sp-menu>
Expand All @@ -71,6 +73,90 @@ import {
</sp-popover>
```

## Accessibility
## Managing a selection

The `<sp-menu>` element can be instructed to maintain a selection via the `selects` attribute. Depending on the chosen algorithm, the `<sp-menu>` element will hold a `value` property and manage the `selected` state of its `<sp-menu-item>` descendants.

### selects="single"

When `selects` is set to `single`, the `<sp-menu>` element will maintain one selected item after an initial selection is made.

```html
<p>
The value of the `&lt;sp-menu&gt;` element is:
<span id="single-value"></span>
</p>
<sp-menu
label="Choose a shape"
selects="single"
onchange="this.previousElementSibling.querySelector('#single-value').textContent=this.value"
>
<sp-menu-item value="item-1">Square</sp-menu-item>
<sp-menu-item value="item-2">Triangle</sp-menu-item>
<sp-menu-item value="item-3">Parallelogram</sp-menu-item>
<sp-menu-item value="item-4">Star</sp-menu-item>
<sp-menu-item value="item-5">Hexagon</sp-menu-item>
<sp-menu-item value="item-6" disabled>Circle</sp-menu-item>
</sp-menu>
```

### selects="multiple"

When `selects` is set to `multiple`, the `<sp-menu>` element will maintain zero or more selected items.

```html
<p>
The value of the `&lt;sp-menu&gt;` element is:
<span id="multiple-value">item-3,item-4</span>
</p>
<sp-menu
label="Choose some fruit"
selects="multiple"
onchange="this.previousElementSibling.querySelector('#multiple-value').textContent=this.value"
>
<sp-menu-item value="item-1">Apple</sp-menu-item>
<sp-menu-item value="item-2">Banana</sp-menu-item>
<sp-menu-item value="item-3" selected>Goji berry</sp-menu-item>
<sp-menu-item value="item-4" selected>Grapes</sp-menu-item>
<sp-menu-item value="item-5" disabled>Kumquat</sp-menu-item>
<sp-menu-item value="item-6">Orange</sp-menu-item>
</sp-menu>
```

### selects="inherit"

When `selects` is set to `inherit`, the `<sp-menu>` element will allow its `<sp-menu-item>` children to participate in the selection of its nearest `<sp-menu>` ancestor.

```html
<p>
The value of the `&lt;sp-menu&gt;` element is:
<span id="inherit-value">item-3 || item-4 || item-8 || item-11</span>
</p>
<sp-menu
label="Choose some groceries"
selects="multiple"
value-separator=" || "
onchange="this.previousElementSibling.querySelector('#inherit-value').textContent=this.value"
>
<sp-menu label="Fruit" selects="inherit">
<sp-menu-item value="item-1">Apple</sp-menu-item>
<sp-menu-item value="item-2">Banana</sp-menu-item>
<sp-menu-item value="item-3" selected>Goji berry</sp-menu-item>
<sp-menu-item value="item-4" selected>Grapes</sp-menu-item>
<sp-menu-item value="item-5" disabled>Kumquat</sp-menu-item>
<sp-menu-item value="item-6">Orange</sp-menu-item>
</sp-menu>
<sp-menu label="Vegetables" selects="inherit">
<sp-menu-item value="item-7">Carrot</sp-menu-item>
<sp-menu-item value="item-8" selected>Garlic</sp-menu-item>
<sp-menu-item value="item-9" disabled>Lettuce</sp-menu-item>
<sp-menu-item value="item-10">Onion</sp-menu-item>
<sp-menu-item value="item-11" selected>Potato</sp-menu-item>
<sp-menu-item value="item-12">Tomato</sp-menu-item>
</sp-menu>
</sp-menu>
```

## "change" event

`<sp-menu>`, `<sp-menu-group>`, and `<sp-menu-item>` each deliver a different part of the wai-aria "menu" pattern and support the `menu`, `group`, and `menuitem` roles respectively. To support ease of keyboard navigation, only the first active _or_ first selected `<sp-menu-item>` can be accessed in the tab order. Once the focus has entered the menu the up and down arrow keys can be used to access the rest of the menu.
Whether `<sp-menu>` carries a selection or not, when one of the `<sp-menu-item>` children that it manages is activated the `<sp-menu>` element will dispatch a `change` event. At dispatch time, even when a selection is not held due to the absence of the `selects` attribute, the `value` of the `<sp-menu>` will correspond to the `<sp-menu-item>` that was activated. When the `selects` attribute is present, this `value` will be persisted beyond the lifecycle of the `change` event. When `selects="multiple"` the values of multiple items will be comma separated unless otherwise set via the `value-separator` attribute.
31 changes: 27 additions & 4 deletions packages/menu/menu-group.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
## Overview

An `<sp-menu-group>` will gather a collection of `<sp-menu-item>` elements into a group as part of the content delivered in an `<sp-menu>` element.
An `<sp-menu-group>` will gather a collection of `<sp-menu-item>` elements into a group as part of the content delivered in an `<sp-menu>` element. Supplying content to the `header` slot will allow it label the group both visually and for screen readers. Like `<sp-menu>`, an `<sp-menu-group>` element can maintain a selection as outlined by the value or absence of its `selects` attribute.

### Usage

Expand Down Expand Up @@ -30,22 +30,45 @@ An `<sp-menu-group>` can be used to organize `<sp-menu-item>`s in an `<sp-memu>`

<!-- prettier-ignore -->
```html
<p>
Your favorite park in New York is: <span id="group-1-value"></span>
<br><br>
Your favorite park in San Fransisco is: <span id="group-2-value"></span>
</p>
<sp-popover open style="position: relative">
<sp-menu>
<sp-menu-group>
<sp-menu
label="What are your favorite parks?"
style="width: 200px"
onchange="this.parentElement
.previousElementSibling
.querySelector(`#${arguments[0].target.id}-value`)
.textContent = arguments[0].target.value">
<sp-menu-group
id="group-1"
selects="single"
>
<span slot="header">New York</span>
<sp-menu-item>
Central Park
</sp-menu-item>
<sp-menu-item>
Flushing Meadows Corona Park
</sp-menu-item>
<sp-menu-item>
Prospect Park
</sp-menu-item>
</sp-menu-group>
<sp-menu-group>
<sp-menu-group
id="group-2"
selects="single"
>
<span slot="header">San Fransisco</span>
<sp-menu-item>
Golden Gate Park
</sp-menu-item>
<sp-menu-item>
John McLaren Park
</sp-menu-item>
<sp-menu-item>
Lake Merced Park
</sp-menu-item>
Expand Down
Loading