Skip to content

Commit 96aa087

Browse files
authored
feat(menu)!: Add MenuOption and convert to MenuItem to Stencil (#2969)
Adds the `Menu.Option` component for actionability (`Menu.Item`) vs selection (`Menu.Option`) and converts `Menu.Item` to use a Stencil. [category:Components] ### BREAKING CHANGES We've removed the `MenuItemProps` export from `@workday/canvas-kit-react/menu`. Use `ExtractProps<typeof Menu.Item, never>` instead. We don't mean to export prop interfaces of polymorphic components. The `never` means "don't add element props". The second parameter is used to pass the interface that the `as` prop is pointing to.
1 parent 3646c0a commit 96aa087

File tree

26 files changed

+552
-274
lines changed

26 files changed

+552
-274
lines changed

cypress/component/Autocomplete.spec.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,8 +96,8 @@ describe('Autocomplete', () => {
9696
cy.findAllByRole('option').eq(0).should('not.have.class', 'focus');
9797
});
9898

99-
it('should not set aria-selected to the first option', () => {
100-
cy.findAllByRole('option').eq(0).should('have.not.attr', 'aria-selected');
99+
it('should set aria-selected=false to the first option', () => {
100+
cy.findAllByRole('option').eq(0).should('have.attr', 'aria-selected', 'false');
101101
});
102102
});
103103

cypress/component/Select.spec.tsx

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,10 @@ describe('Select', () => {
117117

118118
context('the menu', () => {
119119
it('should scroll so that the "San Francisco (United States)" option is fully visible', () => {
120-
cy.findByText('San Francisco (United States)').should('have.class', 'focus');
120+
cy.findByRole('option', {name: 'San Francisco (United States)'}).should(
121+
'have.class',
122+
'focus'
123+
);
121124
});
122125
});
123126
});
@@ -130,7 +133,10 @@ describe('Select', () => {
130133

131134
context('the menu', () => {
132135
it('should scroll so that the "San Mateo (United States)" option is fully visible', () => {
133-
cy.findByText('San Mateo (United States)').should('have.class', 'focus');
136+
cy.findByRole('option', {name: 'San Mateo (United States)'}).should(
137+
'have.class',
138+
'focus'
139+
);
134140
});
135141
});
136142
});
@@ -143,7 +149,10 @@ describe('Select', () => {
143149

144150
context('the menu', () => {
145151
it('should scroll so that the "Dallas (United States)" option is fully visible', () => {
146-
cy.findByText('Dallas (United States)').should('have.class', 'focus');
152+
cy.findByRole('option', {name: 'Dallas (United States)'}).should(
153+
'have.class',
154+
'focus'
155+
);
147156
});
148157
});
149158
});
@@ -160,7 +169,10 @@ describe('Select', () => {
160169

161170
context('the menu', () => {
162171
it('should set assistive focus to the "San Francisco (United States)" option', () => {
163-
cy.findByText('San Francisco (United States)').should('have.class', 'focus');
172+
cy.findByRole('option', {name: 'San Francisco (United States)'}).should(
173+
'have.class',
174+
'focus'
175+
);
164176
});
165177
});
166178
});
@@ -172,7 +184,10 @@ describe('Select', () => {
172184

173185
context('the select input', () => {
174186
it('should set assistive focus to the "San Francisco (United States)" option', () => {
175-
cy.findByText('San Francisco (United States)').should('have.class', 'focus');
187+
cy.findByRole('option', {name: 'San Francisco (United States)'}).should(
188+
'have.class',
189+
'focus'
190+
);
176191
});
177192
});
178193
});
@@ -185,7 +200,10 @@ describe('Select', () => {
185200

186201
context('the select input', () => {
187202
it('should set assistive focus to the "San Mateo (United States)" option', () => {
188-
cy.findByText('San Mateo (United States)').should('have.class', 'focus');
203+
cy.findByRole('option', {name: 'San Mateo (United States)'}).should(
204+
'have.class',
205+
'focus'
206+
);
189207
});
190208
});
191209
});
@@ -203,7 +221,10 @@ describe('Select', () => {
203221

204222
context('the menu', () => {
205223
it('should scroll so that the "Dallas (United States)" option is centered in view', () => {
206-
cy.findByText('Dallas (United States)').should('have.class', 'focus');
224+
cy.findByRole('option', {name: 'Dallas (United States)'}).should(
225+
'have.class',
226+
'focus'
227+
);
207228
});
208229
});
209230
});
@@ -423,8 +444,8 @@ describe('Select', () => {
423444
});
424445
context('when Boulder is reached via the arrow key', () => {
425446
it('should show Boulder (United States)', () => {
426-
cy.findByText('Boulder (United States)').should('have.class', 'focus');
427-
cy.findByText('Boulder (United States)').should('be.visible');
447+
cy.findByRole('option', {name: 'Boulder (United States)'}).should('have.class', 'focus');
448+
cy.findByRole('option', {name: 'Boulder (United States)'}).should('be.visible');
428449
});
429450
});
430451
});
@@ -483,7 +504,7 @@ describe('Select', () => {
483504
context(`when the "Phone" option (with the value "phone") is clicked`, () => {
484505
beforeEach(() => {
485506
cy.findByLabelText('Contact');
486-
cy.findByText('Phone').click();
507+
cy.findByRole('option', {name: 'Phone'}).click();
487508
});
488509

489510
context('the select input', () => {
@@ -509,7 +530,7 @@ describe('Select', () => {
509530

510531
context('the menu', () => {
511532
it('should set assistive focus to the "Phone" option', () => {
512-
cy.findByText('Phone').should('have.class', 'focus');
533+
cy.findByRole('option', {name: 'Phone'}).should('have.class', 'focus');
513534
});
514535
});
515536
});

modules/docs/mdx/12.0-UPGRADE-GUIDE.mdx

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ A note to the reader:
4646
- [Form Field](#form-field)
4747
- [Form Field Group](#form-field-group)
4848
- [Form Field Field](#form-field-field)
49+
- [Menu Item](#menu-item)
4950
- [Search Form](#search-form)
5051
- [Select](#select)
5152
- [Text Area](#text-area)
@@ -375,6 +376,26 @@ this sub-component when using `FormField`. This component also exists on `FormFi
375376
</FormField>
376377
```
377378

379+
### Menu Item
380+
381+
**PR: ** [2969](https://github.com/Workday/canvas-kit/pull/2969)
382+
383+
`Menu.Item` was converted to use Stencils for styling and uses SystemIcon stencil variables to
384+
change icon color instead of deeply nested selectors. We also added `Menu.Option` component for
385+
menus that have a selected visual state. `Menu.Option` will need more accessibility affordances that
386+
depend on the nature of your use of the `Menu` component. For example, `<Combobox>` and `<Select>`
387+
use `Menu.Option` and add keyboard events and `aria-*` attributes to function according to w3c
388+
specifications.
389+
390+
We've deprecated the `isDisabled` prop. It didn't do anything in v10 or v11. It was part of the
391+
preview Menu deprecation, but was never hooked up. We mapped it to `aria-disabled` and added a
392+
deprecation comment to use `aria-disabled` instead.
393+
394+
We've removed the `MenuItemProps` export from `@workday/canvas-kit-react/menu`. Use
395+
`ExtractProps<typeof Menu.Item, never>` instead. We don't mean to export prop interfaces of
396+
polymorphic components. The `never` means "don't add element props". The second parameter is used to
397+
pass the interface that the `as` prop is pointing to.
398+
378399
### Search Form (Labs)
379400

380401
**PRs:** [#2934](https://github.com/Workday/canvas-kit/pull/2934),

modules/labs-react/combobox/lib/Combobox.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,7 @@ export const Combobox = ({
282282
event: React.SyntheticEvent<Element, Event>,
283283
menuItemProps: any
284284
): void => {
285-
if (menuItemProps.isDisabled) {
285+
if (menuItemProps.isDisabled || menuItemProps['aria-disabled']) {
286286
return;
287287
}
288288
setShowingAutocomplete(false);
@@ -390,7 +390,7 @@ export const Combobox = ({
390390
if (selectedAutocompleteIndex != null) {
391391
const item = interactiveAutocompleteItems[selectedAutocompleteIndex];
392392
handleAutocompleteClick(event, item.props);
393-
if (item.props.isDisabled) {
393+
if (item.props.isDisabled || item.props['aria-disabled']) {
394394
nextIndex = selectedAutocompleteIndex;
395395
}
396396
event.stopPropagation();

modules/labs-react/combobox/spec/Combobox.spec.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ describe('Combobox', () => {
152152
const menuText = 'menuText';
153153
const id = 'my-id';
154154
const autocompleteItems = [
155-
<StyledMenuItem isDisabled={true} onClick={cb}>
155+
<StyledMenuItem aria-disabled={true} onClick={cb}>
156156
{menuText}
157157
</StyledMenuItem>,
158158
];

modules/labs-react/combobox/stories/examples/Basic.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
11
import React, {ReactNode, ReactElement, FC, ChangeEvent} from 'react';
2+
import {ExtractProps} from '@workday/canvas-kit-react/common';
23
import {
34
Combobox,
45
ComboboxProps,
56
ComboBoxMenuItemGroup,
67
} from '@workday/canvas-kit-labs-react/combobox';
78
import {FormField} from '@workday/canvas-kit-react/form-field';
8-
import {StyledMenuItem, MenuItemProps} from '@workday/canvas-kit-react/menu';
9+
import {StyledMenuItem} from '@workday/canvas-kit-react/menu';
910
import {TextInput} from '@workday/canvas-kit-react/text-input';
1011

1112
const autocompleteResult = (
1213
textModifier: number,
1314
disabled: boolean
14-
): ReactElement<MenuItemProps> => (
15-
<StyledMenuItem isDisabled={disabled}>
15+
): ReactElement<ExtractProps<typeof StyledMenuItem>> => (
16+
<StyledMenuItem aria-disabled={disabled}>
1617
Result
1718
<span>
1819
num<span>ber</span>

modules/labs-react/combobox/stories/examples/DisabledItem.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
11
import React, {ReactNode, ReactElement, FC, ChangeEvent} from 'react';
2+
import {ExtractProps} from '@workday/canvas-kit-react/common';
23
import {
34
Combobox,
45
ComboboxProps,
56
ComboBoxMenuItemGroup,
67
} from '@workday/canvas-kit-labs-react/combobox';
78
import {FormField} from '@workday/canvas-kit-react/form-field';
8-
import {StyledMenuItem, MenuItemProps} from '@workday/canvas-kit-react/menu';
9+
import {StyledMenuItem} from '@workday/canvas-kit-react/menu';
910
import {TextInput} from '@workday/canvas-kit-react/text-input';
1011

1112
const autocompleteResult = (
1213
textModifier: number,
1314
disabled: boolean
14-
): ReactElement<MenuItemProps> => (
15-
<StyledMenuItem isDisabled={disabled}>
15+
): ReactElement<ExtractProps<typeof StyledMenuItem>> => (
16+
<StyledMenuItem aria-disabled={disabled}>
1617
Result
1718
<span>
1819
num<span>ber</span>

modules/labs-react/combobox/stories/examples/GroupOfResult.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
11
import React, {ReactNode, ReactElement, FC, ChangeEvent} from 'react';
2+
import {ExtractProps} from '@workday/canvas-kit-react/common';
23
import {
34
Combobox,
45
ComboboxProps,
56
ComboBoxMenuItemGroup,
67
} from '@workday/canvas-kit-labs-react/combobox';
78
import {FormField} from '@workday/canvas-kit-react/form-field';
8-
import {StyledMenuItem, MenuItemProps} from '@workday/canvas-kit-react/menu';
9+
import {StyledMenuItem} from '@workday/canvas-kit-react/menu';
910
import {TextInput} from '@workday/canvas-kit-react/text-input';
1011

1112
const autocompleteResult = (
1213
textModifier: number,
1314
disabled: boolean
14-
): ReactElement<MenuItemProps> => (
15-
<StyledMenuItem isDisabled={disabled}>
15+
): ReactElement<ExtractProps<typeof StyledMenuItem>> => (
16+
<StyledMenuItem aria-disabled={disabled}>
1617
Result{' '}
1718
<span>
1819
num<span>ber</span>

modules/labs-react/combobox/stories/examples/Grow.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
11
import React, {ReactNode, ReactElement, FC, ChangeEvent} from 'react';
2+
import {ExtractProps} from '@workday/canvas-kit-react/common';
23
import {
34
Combobox,
45
ComboboxProps,
56
ComboBoxMenuItemGroup,
67
} from '@workday/canvas-kit-labs-react/combobox';
78
import {FormField} from '@workday/canvas-kit-react/form-field';
8-
import {StyledMenuItem, MenuItemProps} from '@workday/canvas-kit-react/menu';
9+
import {StyledMenuItem} from '@workday/canvas-kit-react/menu';
910
import {TextInput} from '@workday/canvas-kit-react/text-input';
1011

1112
const autocompleteResult = (
1213
textModifier: number,
1314
disabled: boolean
14-
): ReactElement<MenuItemProps> => (
15-
<StyledMenuItem isDisabled={disabled}>
15+
): ReactElement<ExtractProps<typeof StyledMenuItem>> => (
16+
<StyledMenuItem aria-disabled={disabled}>
1617
Result
1718
<span>
1819
num<span>ber</span>

modules/labs-react/combobox/stories/examples/NoClearButton.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
11
import React, {ReactNode, ReactElement, FC, ChangeEvent} from 'react';
2+
import {ExtractProps} from '@workday/canvas-kit-react/common';
23
import {
34
Combobox,
45
ComboboxProps,
56
ComboBoxMenuItemGroup,
67
} from '@workday/canvas-kit-labs-react/combobox';
78
import {FormField} from '@workday/canvas-kit-react/form-field';
8-
import {StyledMenuItem, MenuItemProps} from '@workday/canvas-kit-react/menu';
9+
import {StyledMenuItem} from '@workday/canvas-kit-react/menu';
910
import {TextInput} from '@workday/canvas-kit-react/text-input';
1011

1112
const autocompleteResult = (
1213
textModifier: number,
1314
disabled: boolean
14-
): ReactElement<MenuItemProps> => (
15-
<StyledMenuItem isDisabled={disabled}>
15+
): ReactElement<ExtractProps<typeof StyledMenuItem>> => (
16+
<StyledMenuItem aria-disabled={disabled}>
1617
Result
1718
<span>
1819
num<span>ber</span>

0 commit comments

Comments
 (0)