From fb819170f66933b1c7953680af34b6005c00f570 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Mon, 12 Aug 2024 18:17:22 +0200 Subject: [PATCH 1/4] Export useCompositeStore from package --- packages/components/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/components/src/index.ts b/packages/components/src/index.ts index 4c724a461e677..cd6d2a77db9cb 100644 --- a/packages/components/src/index.ts +++ b/packages/components/src/index.ts @@ -62,7 +62,7 @@ export { CompositeItem as __unstableCompositeItem, useCompositeState as __unstableUseCompositeState, } from './composite/legacy'; -export { Composite } from './composite'; +export { Composite, useCompositeStore } from './composite'; export { ConfirmDialog as __experimentalConfirmDialog } from './confirm-dialog'; export { default as CustomSelectControl } from './custom-select-control'; export { default as Dashicon } from './dashicon'; From 0a8060047fd4256c56241ad9ff1a31f04d8d1e35 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Mon, 12 Aug 2024 18:18:19 +0200 Subject: [PATCH 2/4] Add `accessibleWhenDisabled` prop to `Composite.Item` --- packages/components/src/composite/README.md | 18 +++++++++++++ .../src/composite/stories/index.story.tsx | 27 ++++++++++++++++++- packages/components/src/composite/types.ts | 16 +++++++++++ 3 files changed, 60 insertions(+), 1 deletion(-) diff --git a/packages/components/src/composite/README.md b/packages/components/src/composite/README.md index 7bd12d0cabfa0..877c329aa366a 100644 --- a/packages/components/src/composite/README.md +++ b/packages/components/src/composite/README.md @@ -189,6 +189,24 @@ The contents of the component. Renders a composite item. +##### `accessibleWhenDisabled`: `boolean` + +Indicates whether the element should be focusable even when it is +`disabled`. + +This is important when discoverability is a concern. For example: + +> A toolbar in an editor contains a set of special smart paste functions +> that are disabled when the clipboard is empty or when the function is not +> applicable to the current content of the clipboard. It could be helpful to +> keep the disabled buttons focusable if the ability to discover their +> functionality is primarily via their presence on the toolbar. + +Learn more on [Focusability of disabled +controls](https://www.w3.org/WAI/ARIA/apg/practices/keyboard-interface/#focusabilityofdisabledcontrols). + +- Required: no + ##### `render`: `RenderProp & { ref?: React.Ref | undefined; }> | React.ReactElement>` Allows the component to be rendered as a different HTML element or React component. The value can be a React element or a function that takes in the original component props and gives back a React element with the props merged. diff --git a/packages/components/src/composite/stories/index.story.tsx b/packages/components/src/composite/stories/index.story.tsx index f1be53445f79a..6cdcae0682bd2 100644 --- a/packages/components/src/composite/stories/index.story.tsx +++ b/packages/components/src/composite/stories/index.story.tsx @@ -74,6 +74,28 @@ const meta: Meta< typeof UseCompositeStorePlaceholder > = { table: { type: { summary: 'React.ReactNode' } }, }, }; + const accessibleWhenDisabled = { + name: 'accessibleWhenDisabled', + description: `Indicates whether the element should be focusable even when it is +\`disabled\`. + +This is important when discoverability is a concern. For example: + +> A toolbar in an editor contains a set of special smart paste functions +> that are disabled when the clipboard is empty or when the function is not +> applicable to the current content of the clipboard. It could be helpful to +> keep the disabled buttons focusable if the ability to discover their +> functionality is primarily via their presence on the toolbar. + +Learn more on [Focusability of disabled +controls](https://www.w3.org/WAI/ARIA/apg/practices/keyboard-interface/#focusabilityofdisabledcontrols).`, + table: { + type: { + summary: 'boolean', + }, + }, + }; + const argTypes = { useCompositeStore: { activeId: { @@ -230,7 +252,10 @@ This only affects the composite widget behavior. You still need to set \`dir="rt 'Composite.Group': commonArgTypes, 'Composite.GroupLabel': commonArgTypes, 'Composite.Row': commonArgTypes, - 'Composite.Item': commonArgTypes, + 'Composite.Item': { + ...commonArgTypes, + accessibleWhenDisabled, + }, 'Composite.Hover': commonArgTypes, 'Composite.Typeahead': commonArgTypes, }; diff --git a/packages/components/src/composite/types.ts b/packages/components/src/composite/types.ts index 8bd4b447a83ae..a6eee2d55f399 100644 --- a/packages/components/src/composite/types.ts +++ b/packages/components/src/composite/types.ts @@ -177,6 +177,22 @@ export type CompositeItemProps = { * The contents of the component. */ children?: Ariakit.CompositeItemProps[ 'children' ]; + /** + * Indicates whether the element should be focusable even when it is + * `disabled`. + * + * This is important when discoverability is a concern. For example: + * + * > A toolbar in an editor contains a set of special smart paste functions + * that are disabled when the clipboard is empty or when the function is not + * applicable to the current content of the clipboard. It could be helpful to + * keep the disabled buttons focusable if the ability to discover their + * functionality is primarily via their presence on the toolbar. + * + * Learn more on [Focusability of disabled + * controls](https://www.w3.org/WAI/ARIA/apg/practices/keyboard-interface/#focusabilityofdisabledcontrols). + */ + accessibleWhenDisabled?: Ariakit.CompositeItemProps[ 'accessibleWhenDisabled' ]; }; export type CompositeRowProps = { From 0271849037cd25f559aec70cfe8454d2226b411f Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Mon, 12 Aug 2024 18:19:10 +0200 Subject: [PATCH 3/4] Add `focusable`, `disabled`, `accessibleWhenDisabled`, and `onFocusVisible` props to `Composite` --- packages/components/src/composite/README.md | 49 ++++++++++++++++++ packages/components/src/composite/index.tsx | 6 ++- .../src/composite/stories/index.story.tsx | 44 ++++++++++++++++ packages/components/src/composite/types.ts | 51 +++++++++++++++++++ 4 files changed, 148 insertions(+), 2 deletions(-) diff --git a/packages/components/src/composite/README.md b/packages/components/src/composite/README.md index 877c329aa366a..3670e31b01e9d 100644 --- a/packages/components/src/composite/README.md +++ b/packages/components/src/composite/README.md @@ -147,6 +147,55 @@ Allows the component to be rendered as a different HTML element or React compone - Required: no +##### `focusable`: `boolean` + +Makes the component a focusable element. When this element gains keyboard focus, it gets a `data-focus-visible` attribute and triggers the `onFocusVisible` prop. + +The component supports the `disabled` prop even for those elements not supporting the native `disabled` attribute. Disabled elements may be still accessible via keyboard by using the the `accessibleWhenDisabled` prop. + +Non-native focusable elements will lose their focusability entirely. However, native focusable elements will retain their inherent focusability. + +- Required: no + +##### `disabled`: `boolean` + +Determines if the element is disabled. This sets the `aria-disabled` attribute accordingly, enabling support for all elements, including those that don't support the native `disabled` attribute. + +This feature can be combined with the `accessibleWhenDisabled` prop to +make disabled elements still accessible via keyboard. + +**Note**: For this prop to work, the `focusable` prop must be set to +`true`, if it's not set by default. + +- Required: no +- Default: `false` + +##### `accessibleWhenDisabled`: `boolean` + +Indicates whether the element should be focusable even when it is +`disabled`. + +This is important when discoverability is a concern. For example: + +> A toolbar in an editor contains a set of special smart paste functions +> that are disabled when the clipboard is empty or when the function is not +> applicable to the current content of the clipboard. It could be helpful to +> keep the disabled buttons focusable if the ability to discover their +> functionality is primarily via their presence on the toolbar. + +Learn more on [Focusability of disabled +controls](https://www.w3.org/WAI/ARIA/apg/practices/keyboard-interface/#focusabilityofdisabledcontrols). + +- Required: no + +##### `onFocusVisible`: `(event: SyntheticEvent) => void` + +Custom event handler invoked when the element gains focus through keyboard interaction or a key press occurs while the element is in focus. This is the programmatic equivalent of the `data-focus-visible` attribute. + +**Note**: For this prop to work, the `focusable` prop must be set to `true` if it's not set by default. + +- Required: no + ##### `children`: `React.ReactNode` The contents of the component. diff --git a/packages/components/src/composite/index.tsx b/packages/components/src/composite/index.tsx index 4e87b9a55fa5b..f5d92330cada3 100644 --- a/packages/components/src/composite/index.tsx +++ b/packages/components/src/composite/index.tsx @@ -136,8 +136,10 @@ export const Composite = Object.assign( forwardRef< HTMLDivElement, WordPressComponentProps< CompositeProps, 'div', false > - >( function CompositeRow( props, ref ) { - return ; + >( function Composite( { disabled = false, ...props }, ref ) { + return ( + + ); } ), { displayName: 'Composite', diff --git a/packages/components/src/composite/stories/index.story.tsx b/packages/components/src/composite/stories/index.story.tsx index 6cdcae0682bd2..405962b92a761 100644 --- a/packages/components/src/composite/stories/index.story.tsx +++ b/packages/components/src/composite/stories/index.story.tsx @@ -248,6 +248,50 @@ This only affects the composite widget behavior. You still need to set \`dir="rt }, type: { required: true }, }, + focusable: { + name: 'focusable', + description: `Makes the component a focusable element. When this element gains keyboard focus, it gets a \`data-focus-visible\` attribute and triggers the \`onFocusVisible\` prop. + +The component supports the \`disabled\` prop even for those elements not supporting the native \`disabled\` attribute. Disabled elements may be still accessible via keyboard by using the the \`accessibleWhenDisabled\` prop. + +Non-native focusable elements will lose their focusability entirely. However, native focusable elements will retain their inherent focusability.`, + table: { + type: { + summary: 'boolean', + }, + }, + }, + disabled: { + name: 'disabled', + description: `Determines if the element is disabled. This sets the \`aria-disabled\` attribute accordingly, enabling support for all elements, including those that don't support the native \`disabled\` attribute. + +This feature can be combined with the \`accessibleWhenDisabled\` prop to +make disabled elements still accessible via keyboard. + +**Note**: For this prop to work, the \`focusable\` prop must be set to +\`true\`, if it's not set by default.`, + table: { + defaultValue: { + summary: 'false', + }, + type: { + summary: 'boolean', + }, + }, + }, + accessibleWhenDisabled, + onFocusVisible: { + name: 'onFocusVisible', + description: `Custom event handler invoked when the element gains focus through keyboard interaction or a key press occurs while the element is in focus. This is the programmatic equivalent of the \`data-focus-visible\` attribute. + +**Note**: For this prop to work, the \`focusable\` prop must be set to \`true\` if it's not set by default.`, + table: { + type: { + summary: + '(event: SyntheticEvent) => void', + }, + }, + }, }, 'Composite.Group': commonArgTypes, 'Composite.GroupLabel': commonArgTypes, diff --git a/packages/components/src/composite/types.ts b/packages/components/src/composite/types.ts index a6eee2d55f399..5afe410f7582b 100644 --- a/packages/components/src/composite/types.ts +++ b/packages/components/src/composite/types.ts @@ -131,6 +131,57 @@ export type CompositeProps = { * merged. */ render?: Ariakit.CompositeProps[ 'render' ]; + /** + * Makes the component a focusable element. When this element gains keyboard + * focus, it gets a `data-focus-visible` attribute and triggers the + * `onFocusVisible` prop. + * The component supports the `disabled` prop even for those elements not + * supporting the native `disabled` attribute. Disabled elements may be + * still accessible via keyboard by using the the `accessibleWhenDisabled` + * prop. + * Non-native focusable elements will lose their focusability entirely. + * However, native focusable elements will retain their inherent focusability. + */ + focusable?: Ariakit.CompositeProps[ 'focusable' ]; + /** + * Determines if the element is disabled. This sets the `aria-disabled` + * attribute accordingly, enabling support for all elements, including those + * that don't support the native `disabled` attribute. + * + * This feature can be combined with the `accessibleWhenDisabled` prop to + * make disabled elements still accessible via keyboard. + * + * **Note**: For this prop to work, the `focusable` prop must be set to + * `true`, if it's not set by default. + * + * @default false + */ + disabled?: Ariakit.CompositeProps[ 'disabled' ]; + /** + * Indicates whether the element should be focusable even when it is + * `disabled`. + * + * This is important when discoverability is a concern. For example: + * + * > A toolbar in an editor contains a set of special smart paste functions + * that are disabled when the clipboard is empty or when the function is not + * applicable to the current content of the clipboard. It could be helpful to + * keep the disabled buttons focusable if the ability to discover their + * functionality is primarily via their presence on the toolbar. + * + * Learn more on [Focusability of disabled + * controls](https://www.w3.org/WAI/ARIA/apg/practices/keyboard-interface/#focusabilityofdisabledcontrols). + */ + accessibleWhenDisabled?: Ariakit.CompositeProps[ 'accessibleWhenDisabled' ]; + /** + * Custom event handler invoked when the element gains focus through keyboard + * interaction or a key press occurs while the element is in focus. This is + * the programmatic equivalent of the `data-focus-visible` attribute. + * + * **Note**: For this prop to work, the `focusable` prop must be set to `true` + * if it's not set by default. + */ + onFocusVisible?: Ariakit.CompositeProps[ 'onFocusVisible' ]; /** * The contents of the component. */ From eaf87f0558f6a4c09b3f81ca68601e58e6ba5c15 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Mon, 12 Aug 2024 18:25:08 +0200 Subject: [PATCH 4/4] CHANGELOG --- packages/components/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index 57ba69197cfa9..761e6604a127a 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -6,6 +6,7 @@ - `Composite`: add stable version of the component ([#63564](https://github.com/WordPress/gutenberg/pull/63564)). - `Composite`: add `Hover` and `Typeahead` subcomponents ([#64399](https://github.com/WordPress/gutenberg/pull/64399)). +- `Composite`: export `useCompositeStore, add focus-related props to `Composite`and`Composite.Item` subcomponents ([#64450](https://github.com/WordPress/gutenberg/pull/64450)). ### Enhancements