From b6a3cd65bb21d7a86debd40db7341c75874fabeb Mon Sep 17 00:00:00 2001 From: Austin Sullivan Date: Fri, 18 Mar 2022 17:52:50 -0400 Subject: [PATCH 1/6] fix(Card): indicate card selectivity and status if using a screen reader --- packages/react-core/src/components/Card/Card.tsx | 6 ++++++ packages/react-core/src/demos/Card/Card.md | 15 +++++++++++++-- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/packages/react-core/src/components/Card/Card.tsx b/packages/react-core/src/components/Card/Card.tsx index ac22e2c151b..9cc64d1e58d 100644 --- a/packages/react-core/src/components/Card/Card.tsx +++ b/packages/react-core/src/components/Card/Card.tsx @@ -36,6 +36,8 @@ export interface CardProps extends React.HTMLProps, OUIAProps { isPlain?: boolean; /** Flag indicating if a card is expanded. Modifies the card to be expandable. */ isExpanded?: boolean; + /** Flag indicating that the card is an option in a listbox */ + isOption?: boolean; } interface CardContextProps { @@ -65,6 +67,7 @@ export const Card: React.FunctionComponent = ({ isLarge = false, isFullHeight = false, isPlain = false, + isOption = false, ouiaId, ouiaSafe = true, ...props @@ -77,6 +80,8 @@ export const Card: React.FunctionComponent = ({ isLarge = false; } + const ariaProps = isOption ? { role: 'option', 'aria-selected': isSelected } : {}; + const getSelectableModifiers = () => { if (isDisabledRaised) { return css(styles.modifiers.nonSelectableRaised); @@ -112,6 +117,7 @@ export const Card: React.FunctionComponent = ({ className )} tabIndex={isSelectable || isSelectableRaised ? '0' : undefined} + {...ariaProps} {...props} {...ouiaProps} > diff --git a/packages/react-core/src/demos/Card/Card.md b/packages/react-core/src/demos/Card/Card.md index eab5c78d57b..c0c302345f4 100644 --- a/packages/react-core/src/demos/Card/Card.md +++ b/packages/react-core/src/demos/Card/Card.md @@ -103,7 +103,8 @@ class CardViewBasic extends React.Component { splitButtonDropdownIsOpen: false, page: 1, perPage: 10, - totalItemCount: 10 + totalItemCount: 10, + focusedItemId: '', }; this.onToolbarDropdownToggle = isLowerToolbarDropdownOpen => { @@ -230,6 +231,10 @@ class CardViewBasic extends React.Component { }; }); }; + + this.onFocus = id => { + this.setState({focusedItemId: id}) + } } selectedItems(e) { @@ -559,11 +564,14 @@ class CardViewBasic extends React.Component { - + this.onFocus("add-card")} > @@ -579,10 +587,13 @@ class CardViewBasic extends React.Component { {filtered.map((product, key) => ( this.onFocus(product.name)} + id={product.name} onKeyDown={(e) => this.onKeyDown(e, product.id)} onClick={() => this.onClick(product.id)} isSelected={selectedItems.includes(product.id)} From dd082de34adcfccb73b0ad9ac8b6fc2091b5a74c Mon Sep 17 00:00:00 2001 From: Austin Sullivan Date: Wed, 23 Mar 2022 15:20:35 -0400 Subject: [PATCH 2/6] correctly describe card adding card --- packages/react-core/src/demos/Card/Card.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/react-core/src/demos/Card/Card.md b/packages/react-core/src/demos/Card/Card.md index c0c302345f4..8a010054e23 100644 --- a/packages/react-core/src/demos/Card/Card.md +++ b/packages/react-core/src/demos/Card/Card.md @@ -565,18 +565,18 @@ class CardViewBasic extends React.Component { - this.onFocus("add-card")} + aria-describedby="#add-card-title" > - + <Title headingLevel="h2" size="md" id="add-card-title"> Add a new card to your page From 0ce9109ba9c737480b7ca49efbbe9f821557b910 Mon Sep 17 00:00:00 2001 From: Austin Sullivan Date: Wed, 23 Mar 2022 16:45:10 -0400 Subject: [PATCH 3/6] update other examples to have selectable cards as listbox options --- .../src/components/Card/examples/CardLegacySelectable.tsx | 6 ++++-- .../src/components/Card/examples/CardSelectable.tsx | 8 +++++--- packages/react-core/src/demos/PrimaryDetail.md | 4 +++- packages/react-core/src/demos/examples/Tabs/ModalTabs.tsx | 3 ++- 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/packages/react-core/src/components/Card/examples/CardLegacySelectable.tsx b/packages/react-core/src/components/Card/examples/CardLegacySelectable.tsx index b8af08215e0..fd7b5da5771 100644 --- a/packages/react-core/src/components/Card/examples/CardLegacySelectable.tsx +++ b/packages/react-core/src/components/Card/examples/CardLegacySelectable.tsx @@ -60,11 +60,12 @@ export const CardLegacySelectable: React.FunctionComponent = () => { ]; return ( - <> +
@@ -88,12 +89,13 @@ export const CardLegacySelectable: React.FunctionComponent = () => { id="legacy-second-card" onKeyDown={onKeyDown} onClick={onClick} + isOption isSelectable isSelected={selected === 'legacy-second-card'} > Second card This is a selectable card. Click me to select me. Click again to deselect me. - +
); }; diff --git a/packages/react-core/src/components/Card/examples/CardSelectable.tsx b/packages/react-core/src/components/Card/examples/CardSelectable.tsx index b8708882d51..bb56bd103f8 100644 --- a/packages/react-core/src/components/Card/examples/CardSelectable.tsx +++ b/packages/react-core/src/components/Card/examples/CardSelectable.tsx @@ -63,11 +63,12 @@ export const CardSelectable: React.FunctionComponent = () => { ]; return ( - +
@@ -91,6 +92,7 @@ export const CardSelectable: React.FunctionComponent = () => { id="selectable-second-card" onKeyDown={onKeyDown} onClick={onClick} + isOption isSelectableRaised isSelected={selected === 'selectable-second-card'} > @@ -98,10 +100,10 @@ export const CardSelectable: React.FunctionComponent = () => { This is a selectable card. Click me to select me. Click again to deselect me.
- + Third card This is a raised but disabled card. - +
); }; diff --git a/packages/react-core/src/demos/PrimaryDetail.md b/packages/react-core/src/demos/PrimaryDetail.md index 293a448c06a..e1b7cf797d2 100644 --- a/packages/react-core/src/demos/PrimaryDetail.md +++ b/packages/react-core/src/demos/PrimaryDetail.md @@ -1100,6 +1100,7 @@ class PrimaryDetailCardView extends React.Component { return; } if ([13, 32].includes(event.keyCode)) { + event.preventDefault(); const newSelected = event.currentTarget.id; this.setState({ activeCard: newSelected, @@ -1262,7 +1263,7 @@ class PrimaryDetailCardView extends React.Component { }; const drawerContent = ( - + {filtered.map((product, key) => ( diff --git a/packages/react-core/src/demos/examples/Tabs/ModalTabs.tsx b/packages/react-core/src/demos/examples/Tabs/ModalTabs.tsx index 3a0222f73aa..98d11ea400d 100644 --- a/packages/react-core/src/demos/examples/Tabs/ModalTabs.tsx +++ b/packages/react-core/src/demos/examples/Tabs/ModalTabs.tsx @@ -89,9 +89,10 @@ export const ModalTabs: React.FunctionComponent = () => {
- + {products.map(product => ( Date: Thu, 24 Mar 2022 15:57:26 -0400 Subject: [PATCH 4/6] give the "add card" card the option role --- packages/react-core/src/demos/Card/Card.md | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/react-core/src/demos/Card/Card.md b/packages/react-core/src/demos/Card/Card.md index 8a010054e23..32b1bf5fbf1 100644 --- a/packages/react-core/src/demos/Card/Card.md +++ b/packages/react-core/src/demos/Card/Card.md @@ -566,6 +566,7 @@ class CardViewBasic extends React.Component { Date: Thu, 24 Mar 2022 15:58:49 -0400 Subject: [PATCH 5/6] add documentation explaining listbox usage --- packages/react-core/src/components/Card/examples/Card.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/react-core/src/components/Card/examples/Card.md b/packages/react-core/src/components/Card/examples/Card.md index 6239b8a7913..2b4100da40b 100644 --- a/packages/react-core/src/components/Card/examples/Card.md +++ b/packages/react-core/src/components/Card/examples/Card.md @@ -68,6 +68,10 @@ import pfLogoSmall from './pf-logo-small.svg'; ### Selectable +Note: The listbox role on the containing div of this examples is needed because the cards have the option role via their `isOption` prop, and options are required to be inside a listbox. See the [option role documentation](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/option_role) from MDN for more information. + +The cards need to be options in order to effectively communicate that they're selectable (and what their current selection state is) for those using screen readers. + ```ts file='./CardSelectable.tsx' ``` From 5ac102ff12b074d4963e61e3fda4d112658ee47d Mon Sep 17 00:00:00 2001 From: Austin Sullivan Date: Thu, 24 Mar 2022 16:46:40 -0400 Subject: [PATCH 6/6] update option role description to be more inclusive --- packages/react-core/src/components/Card/examples/Card.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-core/src/components/Card/examples/Card.md b/packages/react-core/src/components/Card/examples/Card.md index 2b4100da40b..b8058d02a69 100644 --- a/packages/react-core/src/components/Card/examples/Card.md +++ b/packages/react-core/src/components/Card/examples/Card.md @@ -70,7 +70,7 @@ import pfLogoSmall from './pf-logo-small.svg'; Note: The listbox role on the containing div of this examples is needed because the cards have the option role via their `isOption` prop, and options are required to be inside a listbox. See the [option role documentation](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/option_role) from MDN for more information. -The cards need to be options in order to effectively communicate that they're selectable (and what their current selection state is) for those using screen readers. +The cards need to be options in order to effectively communicate that they're selectable (and what their current selection state is) for those using assistive technologies (such as screen readers). ```ts file='./CardSelectable.tsx' ```