@wordpress/components 23.8.0 → 24.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +71 -0
- package/CONTRIBUTING.md +65 -1
- package/README.md +1 -3
- package/build/autocomplete/autocompleter-ui.js +0 -2
- package/build/autocomplete/autocompleter-ui.js.map +1 -1
- package/build/autocomplete/index.js +2 -0
- package/build/autocomplete/index.js.map +1 -1
- package/build/button/index.js +2 -0
- package/build/button/index.js.map +1 -1
- package/build/card/card-media/component.js +2 -1
- package/build/card/card-media/component.js.map +1 -1
- package/build/checkbox-control/index.js +2 -2
- package/build/checkbox-control/index.js.map +1 -1
- package/build/color-palette/index.native.js +12 -0
- package/build/color-palette/index.native.js.map +1 -1
- package/build/combobox-control/index.js +7 -5
- package/build/combobox-control/index.js.map +1 -1
- package/build/combobox-control/styles.js +3 -3
- package/build/combobox-control/styles.js.map +1 -1
- package/build/custom-gradient-picker/index.native.js +3 -1
- package/build/custom-gradient-picker/index.native.js.map +1 -1
- package/build/dimension-control/index.js +1 -1
- package/build/dimension-control/index.js.map +1 -1
- package/build/draggable/index.js +8 -8
- package/build/draggable/index.js.map +1 -1
- package/build/form-token-field/index.js +5 -3
- package/build/form-token-field/index.js.map +1 -1
- package/build/form-token-field/styles.js +3 -3
- package/build/form-token-field/styles.js.map +1 -1
- package/build/index.js.map +1 -1
- package/build/mobile/bottom-sheet/cell.native.js +6 -6
- package/build/mobile/bottom-sheet/cell.native.js.map +1 -1
- package/build/mobile/color-settings/palette.screen.native.js +0 -8
- package/build/mobile/color-settings/palette.screen.native.js.map +1 -1
- package/build/mobile/global-styles-context/index.native.js +13 -1
- package/build/mobile/global-styles-context/index.native.js.map +1 -1
- package/build/mobile/global-styles-context/utils.native.js +21 -4
- package/build/mobile/global-styles-context/utils.native.js.map +1 -1
- package/build/mobile/link-picker/link-picker-results.native.js +3 -1
- package/build/mobile/link-picker/link-picker-results.native.js.map +1 -1
- package/build/mobile/segmented-control/index.native.js +4 -2
- package/build/mobile/segmented-control/index.native.js.map +1 -1
- package/build/modal/index.js +2 -1
- package/build/modal/index.js.map +1 -1
- package/build/navigable-container/container.js +39 -19
- package/build/navigable-container/container.js.map +1 -1
- package/build/navigable-container/index.js.map +1 -1
- package/build/navigable-container/menu.js +37 -5
- package/build/navigable-container/menu.js.map +1 -1
- package/build/navigable-container/tabbable.js +45 -4
- package/build/navigable-container/tabbable.js.map +1 -1
- package/build/navigable-container/types.js +6 -0
- package/build/navigable-container/types.js.map +1 -0
- package/build/navigator/navigator-screen/component.js +1 -1
- package/build/navigator/navigator-screen/component.js.map +1 -1
- package/build/palette-edit/index.js +34 -12
- package/build/palette-edit/index.js.map +1 -1
- package/build/popover/index.js +1 -8
- package/build/popover/index.js.map +1 -1
- package/build/private-apis.js +4 -1
- package/build/private-apis.js.map +1 -1
- package/build/sandbox/index.native.js +10 -3
- package/build/sandbox/index.native.js.map +1 -1
- package/build/slot-fill/bubbles-virtually/fill.js +2 -1
- package/build/slot-fill/bubbles-virtually/fill.js.map +1 -1
- package/build/slot-fill/bubbles-virtually/slot-fill-provider.js +45 -35
- package/build/slot-fill/bubbles-virtually/slot-fill-provider.js.map +1 -1
- package/build/slot-fill/bubbles-virtually/use-slot.js +11 -26
- package/build/slot-fill/bubbles-virtually/use-slot.js.map +1 -1
- package/build/slot-fill/fill.js +7 -31
- package/build/slot-fill/fill.js.map +1 -1
- package/build/slot-fill/index.js +20 -7
- package/build/slot-fill/index.js.map +1 -1
- package/build/slot-fill/provider.js +0 -6
- package/build/slot-fill/provider.js.map +1 -1
- package/build/slot-fill/slot.js +0 -5
- package/build/slot-fill/slot.js.map +1 -1
- package/build/spinner/styles.js +4 -4
- package/build/spinner/styles.js.map +1 -1
- package/build/tab-panel/index.js.map +1 -1
- package/build/theme/color-algorithms.js +1 -1
- package/build/theme/color-algorithms.js.map +1 -1
- package/build/toolbar/toolbar-button/index.js +1 -2
- package/build/toolbar/toolbar-button/index.js.map +1 -1
- package/build/toolbar/toolbar-item/index.js +4 -2
- package/build/toolbar/toolbar-item/index.js.map +1 -1
- package/build/tree-grid/index.js +3 -3
- package/build/tree-grid/index.js.map +1 -1
- package/build/utils/colors-values.js +3 -3
- package/build/utils/colors-values.js.map +1 -1
- package/build/utils/use-deprecated-props.js +35 -0
- package/build/utils/use-deprecated-props.js.map +1 -0
- package/build/view/component.js +1 -2
- package/build/view/component.js.map +1 -1
- package/build-module/autocomplete/autocompleter-ui.js +1 -3
- package/build-module/autocomplete/autocompleter-ui.js.map +1 -1
- package/build-module/autocomplete/index.js +3 -3
- package/build-module/autocomplete/index.js.map +1 -1
- package/build-module/button/index.js +2 -0
- package/build-module/button/index.js.map +1 -1
- package/build-module/card/card-media/component.js +2 -1
- package/build-module/card/card-media/component.js.map +1 -1
- package/build-module/checkbox-control/index.js +2 -2
- package/build-module/checkbox-control/index.js.map +1 -1
- package/build-module/color-palette/index.native.js +13 -1
- package/build-module/color-palette/index.native.js.map +1 -1
- package/build-module/combobox-control/index.js +6 -5
- package/build-module/combobox-control/index.js.map +1 -1
- package/build-module/combobox-control/styles.js +3 -3
- package/build-module/combobox-control/styles.js.map +1 -1
- package/build-module/custom-gradient-picker/index.native.js +3 -1
- package/build-module/custom-gradient-picker/index.native.js.map +1 -1
- package/build-module/dimension-control/index.js +1 -1
- package/build-module/dimension-control/index.js.map +1 -1
- package/build-module/draggable/index.js +8 -8
- package/build-module/draggable/index.js.map +1 -1
- package/build-module/form-token-field/index.js +4 -3
- package/build-module/form-token-field/index.js.map +1 -1
- package/build-module/form-token-field/styles.js +3 -3
- package/build-module/form-token-field/styles.js.map +1 -1
- package/build-module/index.js.map +1 -1
- package/build-module/mobile/bottom-sheet/cell.native.js +6 -5
- package/build-module/mobile/bottom-sheet/cell.native.js.map +1 -1
- package/build-module/mobile/color-settings/palette.screen.native.js +0 -8
- package/build-module/mobile/color-settings/palette.screen.native.js.map +1 -1
- package/build-module/mobile/global-styles-context/index.native.js +13 -1
- package/build-module/mobile/global-styles-context/index.native.js.map +1 -1
- package/build-module/mobile/global-styles-context/utils.native.js +21 -3
- package/build-module/mobile/global-styles-context/utils.native.js.map +1 -1
- package/build-module/mobile/link-picker/link-picker-results.native.js +3 -1
- package/build-module/mobile/link-picker/link-picker-results.native.js.map +1 -1
- package/build-module/mobile/segmented-control/index.native.js +4 -2
- package/build-module/mobile/segmented-control/index.native.js.map +1 -1
- package/build-module/modal/index.js +2 -1
- package/build-module/modal/index.js.map +1 -1
- package/build-module/navigable-container/container.js +43 -19
- package/build-module/navigable-container/container.js.map +1 -1
- package/build-module/navigable-container/index.js +0 -2
- package/build-module/navigable-container/index.js.map +1 -1
- package/build-module/navigable-container/menu.js +36 -4
- package/build-module/navigable-container/menu.js.map +1 -1
- package/build-module/navigable-container/tabbable.js +44 -3
- package/build-module/navigable-container/tabbable.js.map +1 -1
- package/build-module/navigable-container/types.js +2 -0
- package/build-module/navigable-container/types.js.map +1 -0
- package/build-module/navigator/navigator-screen/component.js +1 -1
- package/build-module/navigator/navigator-screen/component.js.map +1 -1
- package/build-module/palette-edit/index.js +34 -13
- package/build-module/palette-edit/index.js.map +1 -1
- package/build-module/popover/index.js +1 -8
- package/build-module/popover/index.js.map +1 -1
- package/build-module/private-apis.js +3 -1
- package/build-module/private-apis.js.map +1 -1
- package/build-module/sandbox/index.native.js +10 -3
- package/build-module/sandbox/index.native.js.map +1 -1
- package/build-module/slot-fill/bubbles-virtually/fill.js +2 -1
- package/build-module/slot-fill/bubbles-virtually/fill.js.map +1 -1
- package/build-module/slot-fill/bubbles-virtually/slot-fill-provider.js +46 -36
- package/build-module/slot-fill/bubbles-virtually/slot-fill-provider.js.map +1 -1
- package/build-module/slot-fill/bubbles-virtually/use-slot.js +12 -27
- package/build-module/slot-fill/bubbles-virtually/use-slot.js.map +1 -1
- package/build-module/slot-fill/fill.js +7 -31
- package/build-module/slot-fill/fill.js.map +1 -1
- package/build-module/slot-fill/index.js +17 -8
- package/build-module/slot-fill/index.js.map +1 -1
- package/build-module/slot-fill/provider.js +0 -6
- package/build-module/slot-fill/provider.js.map +1 -1
- package/build-module/slot-fill/slot.js +0 -5
- package/build-module/slot-fill/slot.js.map +1 -1
- package/build-module/spinner/styles.js +4 -4
- package/build-module/spinner/styles.js.map +1 -1
- package/build-module/tab-panel/index.js.map +1 -1
- package/build-module/theme/color-algorithms.js +1 -1
- package/build-module/theme/color-algorithms.js.map +1 -1
- package/build-module/toolbar/toolbar-button/index.js +1 -2
- package/build-module/toolbar/toolbar-button/index.js.map +1 -1
- package/build-module/toolbar/toolbar-item/index.js +5 -2
- package/build-module/toolbar/toolbar-item/index.js.map +1 -1
- package/build-module/tree-grid/index.js +3 -3
- package/build-module/tree-grid/index.js.map +1 -1
- package/build-module/utils/colors-values.js +3 -3
- package/build-module/utils/colors-values.js.map +1 -1
- package/build-module/utils/use-deprecated-props.js +25 -0
- package/build-module/utils/use-deprecated-props.js.map +1 -0
- package/build-module/view/component.js +1 -2
- package/build-module/view/component.js.map +1 -1
- package/build-style/style-rtl.css +83 -70
- package/build-style/style.css +83 -70
- package/build-types/autocomplete/autocompleter-ui.d.ts.map +1 -1
- package/build-types/autocomplete/index.d.ts.map +1 -1
- package/build-types/autocomplete/types.d.ts +2 -18
- package/build-types/autocomplete/types.d.ts.map +1 -1
- package/build-types/box-control/styles/box-control-styles.d.ts +1 -1
- package/build-types/button/deprecated.d.ts +12 -6
- package/build-types/button/deprecated.d.ts.map +1 -1
- package/build-types/button/index.d.ts.map +1 -1
- package/build-types/button/types.d.ts +7 -0
- package/build-types/button/types.d.ts.map +1 -1
- package/build-types/card/card-media/component.d.ts +2 -1
- package/build-types/card/card-media/component.d.ts.map +1 -1
- package/build-types/card/stories/index.d.ts +21 -1
- package/build-types/card/stories/index.d.ts.map +1 -1
- package/build-types/checkbox-control/index.d.ts.map +1 -1
- package/build-types/color-palette/styles.d.ts +1 -1
- package/build-types/color-picker/styles.d.ts +3 -3
- package/build-types/combobox-control/index.d.ts +1 -1
- package/build-types/combobox-control/index.d.ts.map +1 -1
- package/build-types/combobox-control/stories/index.d.ts.map +1 -1
- package/build-types/combobox-control/styles.d.ts +1 -1
- package/build-types/combobox-control/types.d.ts +8 -1
- package/build-types/combobox-control/types.d.ts.map +1 -1
- package/build-types/date-time/date/styles.d.ts +1 -1
- package/build-types/date-time/time/styles.d.ts +4 -4
- package/build-types/dimension-control/index.d.ts +1 -1
- package/build-types/draggable/index.d.ts +1 -1
- package/build-types/draggable/index.d.ts.map +1 -1
- package/build-types/draggable/stories/index.d.ts +8 -0
- package/build-types/draggable/stories/index.d.ts.map +1 -1
- package/build-types/draggable/types.d.ts +7 -0
- package/build-types/draggable/types.d.ts.map +1 -1
- package/build-types/focal-point-picker/styles/focal-point-picker-style.d.ts +1 -1
- package/build-types/form-token-field/index.d.ts.map +1 -1
- package/build-types/form-token-field/styles.d.ts +1 -1
- package/build-types/form-token-field/types.d.ts +8 -1
- package/build-types/form-token-field/types.d.ts.map +1 -1
- package/build-types/index.d.ts +1 -0
- package/build-types/index.d.ts.map +1 -1
- package/build-types/modal/index.d.ts.map +1 -1
- package/build-types/navigable-container/container.d.ts +20 -1
- package/build-types/navigable-container/container.d.ts.map +1 -1
- package/build-types/navigable-container/index.d.ts +5 -2
- package/build-types/navigable-container/index.d.ts.map +1 -1
- package/build-types/navigable-container/menu.d.ts +45 -11
- package/build-types/navigable-container/menu.d.ts.map +1 -1
- package/build-types/navigable-container/stories/navigable-menu.d.ts +12 -0
- package/build-types/navigable-container/stories/navigable-menu.d.ts.map +1 -0
- package/build-types/navigable-container/stories/tabbable-container.d.ts +12 -0
- package/build-types/navigable-container/stories/tabbable-container.d.ts.map +1 -0
- package/build-types/navigable-container/tabbable.d.ts +52 -9
- package/build-types/navigable-container/tabbable.d.ts.map +1 -1
- package/build-types/navigable-container/test/navigable-menu.d.ts +2 -0
- package/build-types/navigable-container/test/navigable-menu.d.ts.map +1 -0
- package/build-types/navigable-container/test/tababble-container.d.ts +2 -0
- package/build-types/navigable-container/test/tababble-container.d.ts.map +1 -0
- package/build-types/navigable-container/types.d.ts +61 -0
- package/build-types/navigable-container/types.d.ts.map +1 -0
- package/build-types/navigation/styles/navigation-styles.d.ts +1 -1
- package/build-types/navigator/navigator-back-button/component.d.ts +2 -1
- package/build-types/navigator/navigator-back-button/component.d.ts.map +1 -1
- package/build-types/navigator/navigator-back-button/hook.d.ts +2 -1
- package/build-types/navigator/navigator-back-button/hook.d.ts.map +1 -1
- package/build-types/navigator/navigator-button/component.d.ts +2 -1
- package/build-types/navigator/navigator-button/component.d.ts.map +1 -1
- package/build-types/navigator/navigator-button/hook.d.ts +2 -1
- package/build-types/navigator/navigator-button/hook.d.ts.map +1 -1
- package/build-types/navigator/navigator-screen/component.d.ts +1 -1
- package/build-types/navigator/navigator-screen/component.d.ts.map +1 -1
- package/build-types/navigator/navigator-to-parent-button/component.d.ts +2 -1
- package/build-types/navigator/navigator-to-parent-button/component.d.ts.map +1 -1
- package/build-types/number-control/index.d.ts +1 -1
- package/build-types/number-control/stories/index.d.ts +1 -1
- package/build-types/palette-edit/index.d.ts +1 -1
- package/build-types/palette-edit/index.d.ts.map +1 -1
- package/build-types/palette-edit/stories/index.d.ts.map +1 -1
- package/build-types/palette-edit/styles.d.ts +16 -10
- package/build-types/palette-edit/styles.d.ts.map +1 -1
- package/build-types/palette-edit/types.d.ts +8 -0
- package/build-types/palette-edit/types.d.ts.map +1 -1
- package/build-types/popover/index.d.ts +1 -1
- package/build-types/popover/index.d.ts.map +1 -1
- package/build-types/popover/stories/e2e/index.d.ts +1 -1
- package/build-types/private-apis.d.ts.map +1 -1
- package/build-types/range-control/styles/range-control-styles.d.ts +1 -1
- package/build-types/slot-fill/bubbles-virtually/fill.d.ts.map +1 -1
- package/build-types/slot-fill/bubbles-virtually/slot-fill-provider.d.ts.map +1 -1
- package/build-types/slot-fill/bubbles-virtually/use-slot.d.ts.map +1 -1
- package/build-types/slot-fill/fill.d.ts +4 -2
- package/build-types/slot-fill/fill.d.ts.map +1 -1
- package/build-types/slot-fill/index.d.ts +14 -3
- package/build-types/slot-fill/index.d.ts.map +1 -1
- package/build-types/slot-fill/provider.d.ts +0 -2
- package/build-types/slot-fill/provider.d.ts.map +1 -1
- package/build-types/slot-fill/slot.d.ts.map +1 -1
- package/build-types/spinner/styles.d.ts.map +1 -1
- package/build-types/toolbar/stories/index.d.ts.map +1 -1
- package/build-types/toolbar/toolbar-button/index.d.ts +12 -6
- package/build-types/toolbar/toolbar-button/index.d.ts.map +1 -1
- package/build-types/toolbar/toolbar-item/index.d.ts +6 -4
- package/build-types/toolbar/toolbar-item/index.d.ts.map +1 -1
- package/build-types/tree-grid/index.d.ts.map +1 -1
- package/build-types/tree-grid/types.d.ts +7 -0
- package/build-types/tree-grid/types.d.ts.map +1 -1
- package/build-types/ui/context/wordpress-component.d.ts +1 -1
- package/build-types/ui/context/wordpress-component.d.ts.map +1 -1
- package/build-types/unit-control/index.d.ts +1 -1
- package/build-types/unit-control/styles/unit-control-styles.d.ts +1 -1
- package/build-types/utils/use-deprecated-props.d.ts +9 -0
- package/build-types/utils/use-deprecated-props.d.ts.map +1 -0
- package/build-types/view/component.d.ts +1 -1
- package/build-types/view/component.d.ts.map +1 -1
- package/package.json +21 -22
- package/src/autocomplete/README.md +82 -54
- package/src/autocomplete/autocompleter-ui.tsx +0 -2
- package/src/autocomplete/index.tsx +1 -2
- package/src/autocomplete/types.ts +3 -19
- package/src/button/index.tsx +2 -0
- package/src/button/style.scss +13 -6
- package/src/button/types.ts +7 -0
- package/src/card/card-media/README.md +1 -1
- package/src/card/card-media/component.tsx +2 -1
- package/src/card/stories/index.tsx +47 -26
- package/src/checkbox-control/index.tsx +6 -2
- package/src/checkbox-control/style.scss +1 -4
- package/src/color-palette/index.native.js +20 -1
- package/src/color-picker/test/index.tsx +99 -99
- package/src/combobox-control/index.tsx +24 -18
- package/src/combobox-control/stories/index.tsx +0 -1
- package/src/combobox-control/styles.ts +4 -4
- package/src/combobox-control/types.ts +8 -1
- package/src/custom-gradient-picker/index.native.js +1 -1
- package/src/custom-gradient-picker/style.scss +2 -2
- package/src/dimension-control/README.md +1 -1
- package/src/dimension-control/index.tsx +1 -1
- package/src/draggable/README.md +8 -1
- package/src/draggable/index.tsx +7 -10
- package/src/draggable/stories/index.tsx +69 -33
- package/src/draggable/types.ts +7 -0
- package/src/form-toggle/style.scss +1 -5
- package/src/form-token-field/index.tsx +7 -3
- package/src/form-token-field/styles.ts +4 -4
- package/src/form-token-field/types.ts +8 -1
- package/src/index.ts +1 -0
- package/src/mobile/bottom-sheet/cell.native.js +4 -5
- package/src/mobile/color-settings/palette.screen.native.js +0 -7
- package/src/mobile/global-styles-context/index.native.js +12 -1
- package/src/mobile/global-styles-context/utils.native.js +18 -3
- package/src/mobile/link-picker/link-picker-results.native.js +3 -0
- package/src/mobile/segmented-control/index.native.js +2 -2
- package/src/modal/index.tsx +6 -1
- package/src/modal/style.scss +21 -13
- package/src/navigable-container/README.md +24 -13
- package/src/navigable-container/{container.js → container.tsx} +57 -27
- package/src/navigable-container/{index.js → index.tsx} +0 -1
- package/src/navigable-container/menu.tsx +100 -0
- package/src/navigable-container/stories/{navigable-menu.js → navigable-menu.tsx} +15 -10
- package/src/navigable-container/stories/{tabbable-container.js → tabbable-container.tsx} +15 -6
- package/src/navigable-container/tabbable.tsx +92 -0
- package/src/navigable-container/test/{navigable-menu.js → navigable-menu.tsx} +3 -1
- package/src/navigable-container/test/{tababble-container.js → tababble-container.tsx} +53 -24
- package/src/navigable-container/types.ts +76 -0
- package/src/navigator/navigator-screen/component.tsx +1 -1
- package/src/palette-edit/index.tsx +45 -7
- package/src/palette-edit/stories/index.tsx +4 -0
- package/src/palette-edit/types.ts +11 -0
- package/src/popover/index.tsx +2 -15
- package/src/private-apis.ts +2 -0
- package/src/sandbox/index.native.js +12 -1
- package/src/slot-fill/bubbles-virtually/fill.js +2 -1
- package/src/slot-fill/bubbles-virtually/slot-fill-provider.js +46 -60
- package/src/slot-fill/bubbles-virtually/use-slot.js +14 -41
- package/src/slot-fill/fill.js +4 -26
- package/src/slot-fill/index.js +14 -8
- package/src/slot-fill/provider.js +0 -6
- package/src/slot-fill/slot.js +0 -5
- package/src/snackbar/style.scss +2 -1
- package/src/spinner/styles.ts +2 -0
- package/src/style.scss +6 -0
- package/src/tab-panel/index.tsx +1 -1
- package/src/theme/color-algorithms.ts +1 -1
- package/src/theme/stories/index.tsx +1 -1
- package/src/theme/test/color-algorithms.ts +2 -2
- package/src/toggle-group-control/test/__snapshots__/index.tsx.snap +4 -4
- package/src/toolbar/stories/index.tsx +26 -24
- package/src/toolbar/toolbar-button/index.tsx +10 -13
- package/src/toolbar/toolbar-item/{index.js → index.tsx} +12 -3
- package/src/tree-grid/README.md +18 -0
- package/src/tree-grid/index.tsx +7 -2
- package/src/tree-grid/types.ts +7 -0
- package/src/ui/context/wordpress-component.ts +1 -1
- package/src/utils/colors-values.js +3 -3
- package/src/utils/theme-variables.scss +4 -4
- package/src/utils/use-deprecated-props.ts +29 -0
- package/src/view/component.tsx +2 -2
- package/tsconfig.json +3 -2
- package/tsconfig.tsbuildinfo +1 -1
- package/src/CONTRIBUTING.md +0 -78
- package/src/README.md +0 -20
- package/src/navigable-container/menu.js +0 -62
- package/src/navigable-container/tabbable.js +0 -46
|
@@ -1,14 +1,23 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* External dependencies
|
|
3
|
+
*/
|
|
4
|
+
import type { ForwardedRef } from 'react';
|
|
5
|
+
|
|
2
6
|
/**
|
|
3
7
|
* WordPress dependencies
|
|
4
8
|
*/
|
|
5
9
|
import { Component, forwardRef } from '@wordpress/element';
|
|
6
10
|
import { focus } from '@wordpress/dom';
|
|
7
11
|
|
|
12
|
+
/**
|
|
13
|
+
* Internal dependencies
|
|
14
|
+
*/
|
|
15
|
+
import type { NavigableContainerProps } from './types';
|
|
16
|
+
|
|
8
17
|
const noop = () => {};
|
|
9
18
|
const MENU_ITEM_ROLES = [ 'menuitem', 'menuitemradio', 'menuitemcheckbox' ];
|
|
10
19
|
|
|
11
|
-
function cycleValue( value, total, offset ) {
|
|
20
|
+
function cycleValue( value: number, total: number, offset: number ) {
|
|
12
21
|
const nextValue = value + offset;
|
|
13
22
|
if ( nextValue < 0 ) {
|
|
14
23
|
return total + nextValue;
|
|
@@ -19,9 +28,11 @@ function cycleValue( value, total, offset ) {
|
|
|
19
28
|
return nextValue;
|
|
20
29
|
}
|
|
21
30
|
|
|
22
|
-
class NavigableContainer extends Component {
|
|
23
|
-
|
|
24
|
-
|
|
31
|
+
class NavigableContainer extends Component< NavigableContainerProps > {
|
|
32
|
+
container?: HTMLDivElement;
|
|
33
|
+
|
|
34
|
+
constructor( args: NavigableContainerProps ) {
|
|
35
|
+
super( args );
|
|
25
36
|
this.onKeyDown = this.onKeyDown.bind( this );
|
|
26
37
|
this.bindContainer = this.bindContainer.bind( this );
|
|
27
38
|
|
|
@@ -30,21 +41,27 @@ class NavigableContainer extends Component {
|
|
|
30
41
|
}
|
|
31
42
|
|
|
32
43
|
componentDidMount() {
|
|
44
|
+
if ( ! this.container ) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
|
|
33
48
|
// We use DOM event listeners instead of React event listeners
|
|
34
49
|
// because we want to catch events from the underlying DOM tree
|
|
35
50
|
// The React Tree can be different from the DOM tree when using
|
|
36
51
|
// portals. Block Toolbars for instance are rendered in a separate
|
|
37
52
|
// React Trees.
|
|
38
53
|
this.container.addEventListener( 'keydown', this.onKeyDown );
|
|
39
|
-
this.container.addEventListener( 'focus', this.onFocus );
|
|
40
54
|
}
|
|
41
55
|
|
|
42
56
|
componentWillUnmount() {
|
|
57
|
+
if ( ! this.container ) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
43
61
|
this.container.removeEventListener( 'keydown', this.onKeyDown );
|
|
44
|
-
this.container.removeEventListener( 'focus', this.onFocus );
|
|
45
62
|
}
|
|
46
63
|
|
|
47
|
-
bindContainer( ref ) {
|
|
64
|
+
bindContainer( ref: HTMLDivElement ) {
|
|
48
65
|
const { forwardedRef } = this.props;
|
|
49
66
|
this.container = ref;
|
|
50
67
|
|
|
@@ -55,10 +72,14 @@ class NavigableContainer extends Component {
|
|
|
55
72
|
}
|
|
56
73
|
}
|
|
57
74
|
|
|
58
|
-
getFocusableContext( target ) {
|
|
75
|
+
getFocusableContext( target: Element ) {
|
|
76
|
+
if ( ! this.container ) {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
|
|
59
80
|
const { onlyBrowserTabstops } = this.props;
|
|
60
81
|
const finder = onlyBrowserTabstops ? focus.tabbable : focus.focusable;
|
|
61
|
-
const focusables = finder.find( this.container );
|
|
82
|
+
const focusables = finder.find( this.container ) as HTMLElement[];
|
|
62
83
|
|
|
63
84
|
const index = this.getFocusableIndex( focusables, target );
|
|
64
85
|
if ( index > -1 && target ) {
|
|
@@ -67,14 +88,11 @@ class NavigableContainer extends Component {
|
|
|
67
88
|
return null;
|
|
68
89
|
}
|
|
69
90
|
|
|
70
|
-
getFocusableIndex( focusables, target ) {
|
|
71
|
-
|
|
72
|
-
if ( directIndex !== -1 ) {
|
|
73
|
-
return directIndex;
|
|
74
|
-
}
|
|
91
|
+
getFocusableIndex( focusables: Element[], target: Element ) {
|
|
92
|
+
return focusables.indexOf( target );
|
|
75
93
|
}
|
|
76
94
|
|
|
77
|
-
onKeyDown( event ) {
|
|
95
|
+
onKeyDown( event: KeyboardEvent ) {
|
|
78
96
|
if ( this.props.onKeyDown ) {
|
|
79
97
|
this.props.onKeyDown( event );
|
|
80
98
|
}
|
|
@@ -98,15 +116,13 @@ class NavigableContainer extends Component {
|
|
|
98
116
|
// from scrolling. The preventDefault also prevents Voiceover from
|
|
99
117
|
// 'handling' the event, as voiceover will try to use arrow keys
|
|
100
118
|
// for highlighting text.
|
|
101
|
-
const targetRole =
|
|
119
|
+
const targetRole = (
|
|
120
|
+
event.target as HTMLDivElement | null
|
|
121
|
+
)?.getAttribute( 'role' );
|
|
102
122
|
const targetHasMenuItemRole =
|
|
103
|
-
MENU_ITEM_ROLES.includes( targetRole );
|
|
104
|
-
|
|
105
|
-
// `preventDefault()` on tab to avoid having the browser move the focus
|
|
106
|
-
// after this component has already moved it.
|
|
107
|
-
const isTab = event.code === 'Tab';
|
|
123
|
+
!! targetRole && MENU_ITEM_ROLES.includes( targetRole );
|
|
108
124
|
|
|
109
|
-
if ( targetHasMenuItemRole
|
|
125
|
+
if ( targetHasMenuItemRole ) {
|
|
110
126
|
event.preventDefault();
|
|
111
127
|
}
|
|
112
128
|
}
|
|
@@ -115,9 +131,13 @@ class NavigableContainer extends Component {
|
|
|
115
131
|
return;
|
|
116
132
|
}
|
|
117
133
|
|
|
118
|
-
const
|
|
119
|
-
|
|
120
|
-
)
|
|
134
|
+
const activeElement = ( event.target as HTMLElement | null )
|
|
135
|
+
?.ownerDocument?.activeElement;
|
|
136
|
+
if ( ! activeElement ) {
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const context = getFocusableContext( activeElement );
|
|
121
141
|
if ( ! context ) {
|
|
122
142
|
return;
|
|
123
143
|
}
|
|
@@ -126,9 +146,16 @@ class NavigableContainer extends Component {
|
|
|
126
146
|
const nextIndex = cycle
|
|
127
147
|
? cycleValue( index, focusables.length, offset )
|
|
128
148
|
: index + offset;
|
|
149
|
+
|
|
129
150
|
if ( nextIndex >= 0 && nextIndex < focusables.length ) {
|
|
130
151
|
focusables[ nextIndex ].focus();
|
|
131
152
|
onNavigate( nextIndex, focusables[ nextIndex ] );
|
|
153
|
+
|
|
154
|
+
// `preventDefault()` on tab to avoid having the browser move the focus
|
|
155
|
+
// after this component has already moved it.
|
|
156
|
+
if ( event.code === 'Tab' ) {
|
|
157
|
+
event.preventDefault();
|
|
158
|
+
}
|
|
132
159
|
}
|
|
133
160
|
}
|
|
134
161
|
|
|
@@ -152,7 +179,10 @@ class NavigableContainer extends Component {
|
|
|
152
179
|
}
|
|
153
180
|
}
|
|
154
181
|
|
|
155
|
-
const forwardedNavigableContainer = (
|
|
182
|
+
const forwardedNavigableContainer = (
|
|
183
|
+
props: NavigableContainerProps,
|
|
184
|
+
ref: ForwardedRef< HTMLDivElement >
|
|
185
|
+
) => {
|
|
156
186
|
return <NavigableContainer { ...props } forwardedRef={ ref } />;
|
|
157
187
|
};
|
|
158
188
|
forwardedNavigableContainer.displayName = 'NavigableContainer';
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External dependencies
|
|
3
|
+
*/
|
|
4
|
+
import type { ForwardedRef } from 'react';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* WordPress dependencies
|
|
8
|
+
*/
|
|
9
|
+
import { forwardRef } from '@wordpress/element';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Internal dependencies
|
|
13
|
+
*/
|
|
14
|
+
import NavigableContainer from './container';
|
|
15
|
+
import type { NavigableMenuProps } from './types';
|
|
16
|
+
|
|
17
|
+
export function UnforwardedNavigableMenu(
|
|
18
|
+
{ role = 'menu', orientation = 'vertical', ...rest }: NavigableMenuProps,
|
|
19
|
+
ref: ForwardedRef< any >
|
|
20
|
+
) {
|
|
21
|
+
const eventToOffset = ( evt: KeyboardEvent ) => {
|
|
22
|
+
const { code } = evt;
|
|
23
|
+
|
|
24
|
+
let next = [ 'ArrowDown' ];
|
|
25
|
+
let previous = [ 'ArrowUp' ];
|
|
26
|
+
|
|
27
|
+
if ( orientation === 'horizontal' ) {
|
|
28
|
+
next = [ 'ArrowRight' ];
|
|
29
|
+
previous = [ 'ArrowLeft' ];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if ( orientation === 'both' ) {
|
|
33
|
+
next = [ 'ArrowRight', 'ArrowDown' ];
|
|
34
|
+
previous = [ 'ArrowLeft', 'ArrowUp' ];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if ( next.includes( code ) ) {
|
|
38
|
+
return 1;
|
|
39
|
+
} else if ( previous.includes( code ) ) {
|
|
40
|
+
return -1;
|
|
41
|
+
} else if (
|
|
42
|
+
[ 'ArrowDown', 'ArrowUp', 'ArrowLeft', 'ArrowRight' ].includes(
|
|
43
|
+
code
|
|
44
|
+
)
|
|
45
|
+
) {
|
|
46
|
+
// Key press should be handled, e.g. have event propagation and
|
|
47
|
+
// default behavior handled by NavigableContainer but not result
|
|
48
|
+
// in an offset.
|
|
49
|
+
return 0;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return undefined;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
return (
|
|
56
|
+
<NavigableContainer
|
|
57
|
+
ref={ ref }
|
|
58
|
+
stopNavigationEvents
|
|
59
|
+
onlyBrowserTabstops={ false }
|
|
60
|
+
role={ role }
|
|
61
|
+
aria-orientation={
|
|
62
|
+
role !== 'presentation' &&
|
|
63
|
+
( orientation === 'vertical' || orientation === 'horizontal' )
|
|
64
|
+
? orientation
|
|
65
|
+
: undefined
|
|
66
|
+
}
|
|
67
|
+
eventToOffset={ eventToOffset }
|
|
68
|
+
{ ...rest }
|
|
69
|
+
/>
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* A container for a navigable menu.
|
|
75
|
+
*
|
|
76
|
+
* ```jsx
|
|
77
|
+
* import {
|
|
78
|
+
* NavigableMenu,
|
|
79
|
+
* Button,
|
|
80
|
+
* } from '@wordpress/components';
|
|
81
|
+
*
|
|
82
|
+
* function onNavigate( index, target ) {
|
|
83
|
+
* console.log( `Navigates to ${ index }`, target );
|
|
84
|
+
* }
|
|
85
|
+
*
|
|
86
|
+
* const MyNavigableContainer = () => (
|
|
87
|
+
* <div>
|
|
88
|
+
* <span>Navigable Menu:</span>
|
|
89
|
+
* <NavigableMenu onNavigate={ onNavigate } orientation="horizontal">
|
|
90
|
+
* <Button variant="secondary">Item 1</Button>
|
|
91
|
+
* <Button variant="secondary">Item 2</Button>
|
|
92
|
+
* <Button variant="secondary">Item 3</Button>
|
|
93
|
+
* </NavigableMenu>
|
|
94
|
+
* </div>
|
|
95
|
+
* );
|
|
96
|
+
* ```
|
|
97
|
+
*/
|
|
98
|
+
export const NavigableMenu = forwardRef( UnforwardedNavigableMenu );
|
|
99
|
+
|
|
100
|
+
export default NavigableMenu;
|
|
@@ -1,25 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External dependencies
|
|
3
|
+
*/
|
|
4
|
+
import type { ComponentMeta, ComponentStory } from '@storybook/react';
|
|
5
|
+
|
|
1
6
|
/**
|
|
2
7
|
* Internal dependencies
|
|
3
8
|
*/
|
|
4
9
|
import { NavigableMenu } from '..';
|
|
5
10
|
|
|
6
|
-
|
|
11
|
+
const meta: ComponentMeta< typeof NavigableMenu > = {
|
|
7
12
|
title: 'Components/NavigableMenu',
|
|
8
13
|
component: NavigableMenu,
|
|
9
14
|
argTypes: {
|
|
10
|
-
children: { type: null },
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
},
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
options: [ 'horizontal', 'vertical' ],
|
|
17
|
-
control: { type: 'radio' },
|
|
15
|
+
children: { control: { type: null } },
|
|
16
|
+
},
|
|
17
|
+
parameters: {
|
|
18
|
+
actions: { argTypesRegex: '^on.*' },
|
|
19
|
+
controls: {
|
|
20
|
+
expanded: true,
|
|
18
21
|
},
|
|
22
|
+
docs: { source: { state: 'open' } },
|
|
19
23
|
},
|
|
20
24
|
};
|
|
25
|
+
export default meta;
|
|
21
26
|
|
|
22
|
-
export const Default = ( args ) => {
|
|
27
|
+
export const Default: ComponentStory< typeof NavigableMenu > = ( args ) => {
|
|
23
28
|
return (
|
|
24
29
|
<>
|
|
25
30
|
<button>Before navigable menu</button>
|
|
@@ -1,21 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External dependencies
|
|
3
|
+
*/
|
|
4
|
+
import type { ComponentMeta, ComponentStory } from '@storybook/react';
|
|
5
|
+
|
|
1
6
|
/**
|
|
2
7
|
* Internal dependencies
|
|
3
8
|
*/
|
|
4
9
|
import { TabbableContainer } from '..';
|
|
5
10
|
|
|
6
|
-
|
|
11
|
+
const meta: ComponentMeta< typeof TabbableContainer > = {
|
|
7
12
|
title: 'Components/TabbableContainer',
|
|
8
13
|
component: TabbableContainer,
|
|
9
14
|
argTypes: {
|
|
10
|
-
children: { type: null },
|
|
11
|
-
|
|
12
|
-
|
|
15
|
+
children: { control: { type: null } },
|
|
16
|
+
},
|
|
17
|
+
parameters: {
|
|
18
|
+
actions: { argTypesRegex: '^on.*' },
|
|
19
|
+
controls: {
|
|
20
|
+
expanded: true,
|
|
13
21
|
},
|
|
14
|
-
|
|
22
|
+
docs: { source: { state: 'open' } },
|
|
15
23
|
},
|
|
16
24
|
};
|
|
25
|
+
export default meta;
|
|
17
26
|
|
|
18
|
-
export const Default = ( args ) => {
|
|
27
|
+
export const Default: ComponentStory< typeof TabbableContainer > = ( args ) => {
|
|
19
28
|
return (
|
|
20
29
|
<>
|
|
21
30
|
<button>Before tabbable container</button>
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External dependencies
|
|
3
|
+
*/
|
|
4
|
+
import type { ForwardedRef } from 'react';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* WordPress dependencies
|
|
8
|
+
*/
|
|
9
|
+
import { forwardRef } from '@wordpress/element';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Internal dependencies
|
|
13
|
+
*/
|
|
14
|
+
import NavigableContainer from './container';
|
|
15
|
+
import type { TabbableContainerProps } from './types';
|
|
16
|
+
|
|
17
|
+
export function UnforwardedTabbableContainer(
|
|
18
|
+
{ eventToOffset, ...props }: TabbableContainerProps,
|
|
19
|
+
ref: ForwardedRef< any >
|
|
20
|
+
) {
|
|
21
|
+
const innerEventToOffset = ( evt: KeyboardEvent ) => {
|
|
22
|
+
const { code, shiftKey } = evt;
|
|
23
|
+
if ( 'Tab' === code ) {
|
|
24
|
+
return shiftKey ? -1 : 1;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Allow custom handling of keys besides Tab.
|
|
28
|
+
//
|
|
29
|
+
// By default, TabbableContainer will move focus forward on Tab and
|
|
30
|
+
// backward on Shift+Tab. The handler below will be used for all other
|
|
31
|
+
// events. The semantics for `eventToOffset`'s return
|
|
32
|
+
// values are the following:
|
|
33
|
+
//
|
|
34
|
+
// - +1: move focus forward
|
|
35
|
+
// - -1: move focus backward
|
|
36
|
+
// - 0: don't move focus, but acknowledge event and thus stop it
|
|
37
|
+
// - undefined: do nothing, let the event propagate.
|
|
38
|
+
if ( eventToOffset ) {
|
|
39
|
+
return eventToOffset( evt );
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return undefined;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<NavigableContainer
|
|
47
|
+
ref={ ref }
|
|
48
|
+
stopNavigationEvents
|
|
49
|
+
onlyBrowserTabstops
|
|
50
|
+
eventToOffset={ innerEventToOffset }
|
|
51
|
+
{ ...props }
|
|
52
|
+
/>
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* A container for tabbable elements.
|
|
58
|
+
*
|
|
59
|
+
* ```jsx
|
|
60
|
+
* import {
|
|
61
|
+
* TabbableContainer,
|
|
62
|
+
* Button,
|
|
63
|
+
* } from '@wordpress/components';
|
|
64
|
+
*
|
|
65
|
+
* function onNavigate( index, target ) {
|
|
66
|
+
* console.log( `Navigates to ${ index }`, target );
|
|
67
|
+
* }
|
|
68
|
+
*
|
|
69
|
+
* const MyTabbableContainer = () => (
|
|
70
|
+
* <div>
|
|
71
|
+
* <span>Tabbable Container:</span>
|
|
72
|
+
* <TabbableContainer onNavigate={ onNavigate }>
|
|
73
|
+
* <Button variant="secondary" tabIndex="0">
|
|
74
|
+
* Section 1
|
|
75
|
+
* </Button>
|
|
76
|
+
* <Button variant="secondary" tabIndex="0">
|
|
77
|
+
* Section 2
|
|
78
|
+
* </Button>
|
|
79
|
+
* <Button variant="secondary" tabIndex="0">
|
|
80
|
+
* Section 3
|
|
81
|
+
* </Button>
|
|
82
|
+
* <Button variant="secondary" tabIndex="0">
|
|
83
|
+
* Section 4
|
|
84
|
+
* </Button>
|
|
85
|
+
* </TabbableContainer>
|
|
86
|
+
* </div>
|
|
87
|
+
* );
|
|
88
|
+
* ```
|
|
89
|
+
*/
|
|
90
|
+
export const TabbableContainer = forwardRef( UnforwardedTabbableContainer );
|
|
91
|
+
|
|
92
|
+
export default TabbableContainer;
|
|
@@ -8,8 +8,9 @@ import userEvent from '@testing-library/user-event';
|
|
|
8
8
|
* Internal dependencies
|
|
9
9
|
*/
|
|
10
10
|
import { NavigableMenu } from '../menu';
|
|
11
|
+
import type { NavigableMenuProps } from '../types';
|
|
11
12
|
|
|
12
|
-
const NavigableMenuTestCase = ( props ) => (
|
|
13
|
+
const NavigableMenuTestCase = ( props: NavigableMenuProps ) => (
|
|
13
14
|
<NavigableMenu { ...props }>
|
|
14
15
|
<button>Item 1</button>
|
|
15
16
|
<span>
|
|
@@ -34,6 +35,7 @@ describe( 'NavigableMenu', () => {
|
|
|
34
35
|
// Mocking `getClientRects()` is necessary to pass a check performed by
|
|
35
36
|
// the `focus.tabbable.find()` and by the `focus.focusable.find()` functions
|
|
36
37
|
// from the `@wordpress/dom` package.
|
|
38
|
+
// @ts-expect-error We're not trying to comply to the DOM spec, only mocking
|
|
37
39
|
window.HTMLElement.prototype.getClientRects = function () {
|
|
38
40
|
return [ 'trick-jsdom-into-having-size-for-element-rect' ];
|
|
39
41
|
};
|
|
@@ -8,20 +8,25 @@ import userEvent from '@testing-library/user-event';
|
|
|
8
8
|
* Internal dependencies
|
|
9
9
|
*/
|
|
10
10
|
import { TabbableContainer } from '../tabbable';
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
<
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
11
|
+
import type { TabbableContainerProps } from '../types';
|
|
12
|
+
|
|
13
|
+
const TabbableContainerTestCase = ( props: TabbableContainerProps ) => (
|
|
14
|
+
<>
|
|
15
|
+
<button>Before container</button>
|
|
16
|
+
<TabbableContainer { ...props }>
|
|
17
|
+
<button>Item 1</button>
|
|
18
|
+
<span>
|
|
19
|
+
<span tabIndex={ -1 }>Item 2 (not tabbable)</span>
|
|
20
|
+
</span>
|
|
21
|
+
<span>
|
|
22
|
+
<span tabIndex={ 0 }>Item 3</span>
|
|
23
|
+
</span>
|
|
24
|
+
<p>I can not be tabbed</p>
|
|
25
|
+
<input type="text" disabled name="disabled-input" />
|
|
26
|
+
<a href="https://example.com">Item 4</a>
|
|
27
|
+
</TabbableContainer>
|
|
28
|
+
<button>After container</button>
|
|
29
|
+
</>
|
|
25
30
|
);
|
|
26
31
|
|
|
27
32
|
const getTabbableContainerTabbables = () => [
|
|
@@ -37,6 +42,7 @@ describe( 'TabbableContainer', () => {
|
|
|
37
42
|
// Mocking `getClientRects()` is necessary to pass a check performed by
|
|
38
43
|
// the `focus.tabbable.find()` and by the `focus.focusable.find()` functions
|
|
39
44
|
// from the `@wordpress/dom` package.
|
|
45
|
+
// @ts-expect-error We're not trying to comply to the DOM spec, only mocking
|
|
40
46
|
window.HTMLElement.prototype.getClientRects = function () {
|
|
41
47
|
return [ 'trick-jsdom-into-having-size-for-element-rect' ];
|
|
42
48
|
};
|
|
@@ -55,7 +61,11 @@ describe( 'TabbableContainer', () => {
|
|
|
55
61
|
|
|
56
62
|
const tabbables = getTabbableContainerTabbables();
|
|
57
63
|
|
|
58
|
-
|
|
64
|
+
await user.tab();
|
|
65
|
+
expect(
|
|
66
|
+
screen.getByRole( 'button', { name: 'Before container' } )
|
|
67
|
+
).toHaveFocus();
|
|
68
|
+
|
|
59
69
|
await user.tab();
|
|
60
70
|
expect( tabbables[ 0 ] ).toHaveFocus();
|
|
61
71
|
|
|
@@ -89,7 +99,11 @@ describe( 'TabbableContainer', () => {
|
|
|
89
99
|
const lastTabbableIndex = tabbables.length - 1;
|
|
90
100
|
const lastTabbable = tabbables[ lastTabbableIndex ];
|
|
91
101
|
|
|
92
|
-
|
|
102
|
+
await user.tab();
|
|
103
|
+
expect(
|
|
104
|
+
screen.getByRole( 'button', { name: 'Before container' } )
|
|
105
|
+
).toHaveFocus();
|
|
106
|
+
|
|
93
107
|
await user.tab();
|
|
94
108
|
expect( firstTabbable ).toHaveFocus();
|
|
95
109
|
|
|
@@ -114,12 +128,17 @@ describe( 'TabbableContainer', () => {
|
|
|
114
128
|
/>
|
|
115
129
|
);
|
|
116
130
|
|
|
117
|
-
// With the `cycle` prop set to `false`, cycling is not allowed.
|
|
118
131
|
// By default, cycling from first to last and from last to first is allowed.
|
|
132
|
+
// With the `cycle` prop set to `false`, cycling is not allowed.
|
|
133
|
+
// Therefore, focus will escape the `TabbableContainer` and continue its
|
|
134
|
+
// natural path in the page.
|
|
119
135
|
await user.tab( { shift: true } );
|
|
120
|
-
expect(
|
|
136
|
+
expect(
|
|
137
|
+
screen.getByRole( 'button', { name: 'Before container' } )
|
|
138
|
+
).toHaveFocus();
|
|
121
139
|
expect( onNavigateSpy ).toHaveBeenCalledTimes( 2 );
|
|
122
140
|
|
|
141
|
+
await user.tab();
|
|
123
142
|
await user.tab();
|
|
124
143
|
await user.tab();
|
|
125
144
|
expect( lastTabbable ).toHaveFocus();
|
|
@@ -129,8 +148,12 @@ describe( 'TabbableContainer', () => {
|
|
|
129
148
|
lastTabbable
|
|
130
149
|
);
|
|
131
150
|
|
|
151
|
+
// Focus will move to the next natively focusable elements after
|
|
152
|
+
// `TabbableContainer`
|
|
132
153
|
await user.tab();
|
|
133
|
-
expect(
|
|
154
|
+
expect(
|
|
155
|
+
screen.getByRole( 'button', { name: 'After container' } )
|
|
156
|
+
).toHaveFocus();
|
|
134
157
|
expect( onNavigateSpy ).toHaveBeenCalledTimes( 4 );
|
|
135
158
|
} );
|
|
136
159
|
|
|
@@ -149,21 +172,27 @@ describe( 'TabbableContainer', () => {
|
|
|
149
172
|
|
|
150
173
|
const tabbables = getTabbableContainerTabbables();
|
|
151
174
|
|
|
152
|
-
|
|
175
|
+
await user.tab();
|
|
176
|
+
expect(
|
|
177
|
+
screen.getByRole( 'button', { name: 'Before container' } )
|
|
178
|
+
).toHaveFocus();
|
|
179
|
+
expect( externalWrapperOnKeyDownSpy ).toHaveBeenCalledTimes( 0 );
|
|
180
|
+
|
|
153
181
|
await user.tab();
|
|
154
182
|
expect( tabbables[ 0 ] ).toHaveFocus();
|
|
183
|
+
expect( externalWrapperOnKeyDownSpy ).toHaveBeenCalledTimes( 1 );
|
|
155
184
|
|
|
156
185
|
await user.keyboard( '[Space]' );
|
|
157
|
-
expect( externalWrapperOnKeyDownSpy ).toHaveBeenCalledTimes(
|
|
186
|
+
expect( externalWrapperOnKeyDownSpy ).toHaveBeenCalledTimes( 2 );
|
|
158
187
|
|
|
159
188
|
await user.tab();
|
|
160
|
-
expect( externalWrapperOnKeyDownSpy ).toHaveBeenCalledTimes(
|
|
189
|
+
expect( externalWrapperOnKeyDownSpy ).toHaveBeenCalledTimes( 2 );
|
|
161
190
|
await user.tab( { shift: true } );
|
|
162
191
|
// This extra call is caused by the "shift" key being pressed
|
|
163
192
|
// on its own before "tab"
|
|
164
|
-
expect( externalWrapperOnKeyDownSpy ).toHaveBeenCalledTimes(
|
|
193
|
+
expect( externalWrapperOnKeyDownSpy ).toHaveBeenCalledTimes( 3 );
|
|
165
194
|
|
|
166
195
|
await user.keyboard( '[Escape]' );
|
|
167
|
-
expect( externalWrapperOnKeyDownSpy ).toHaveBeenCalledTimes(
|
|
196
|
+
expect( externalWrapperOnKeyDownSpy ).toHaveBeenCalledTimes( 4 );
|
|
168
197
|
} );
|
|
169
198
|
} );
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External dependencies
|
|
3
|
+
*/
|
|
4
|
+
import type { ForwardedRef, ReactNode } from 'react';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Internal dependencies
|
|
8
|
+
*/
|
|
9
|
+
import type { WordPressComponentProps } from '../ui/context';
|
|
10
|
+
|
|
11
|
+
type BaseProps = {
|
|
12
|
+
/**
|
|
13
|
+
* The component children.
|
|
14
|
+
*/
|
|
15
|
+
children?: ReactNode;
|
|
16
|
+
/**
|
|
17
|
+
* A boolean which tells the component whether or not to cycle from the end back to the beginning and vice versa.
|
|
18
|
+
*
|
|
19
|
+
* @default true
|
|
20
|
+
*/
|
|
21
|
+
cycle?: boolean;
|
|
22
|
+
/**
|
|
23
|
+
* A callback invoked on the keydown event.
|
|
24
|
+
*/
|
|
25
|
+
onKeyDown?: ( event: KeyboardEvent ) => void;
|
|
26
|
+
/**
|
|
27
|
+
* A callback invoked when the menu navigates to one of its children passing the index and child as an argument
|
|
28
|
+
*/
|
|
29
|
+
onNavigate?: ( index: number, focusable: HTMLElement ) => void;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export type NavigableContainerProps = WordPressComponentProps<
|
|
33
|
+
BaseProps & {
|
|
34
|
+
/**
|
|
35
|
+
* Gets an offset, given an event.
|
|
36
|
+
*/
|
|
37
|
+
eventToOffset: ( event: KeyboardEvent ) => -1 | 0 | 1 | undefined;
|
|
38
|
+
/**
|
|
39
|
+
* The forwarded ref.
|
|
40
|
+
*/
|
|
41
|
+
forwardedRef?: ForwardedRef< any >;
|
|
42
|
+
/**
|
|
43
|
+
* Whether to only consider browser tab stops.
|
|
44
|
+
*
|
|
45
|
+
* @default false
|
|
46
|
+
*/
|
|
47
|
+
onlyBrowserTabstops: boolean;
|
|
48
|
+
/**
|
|
49
|
+
* Whether to stop navigation events.
|
|
50
|
+
*
|
|
51
|
+
* @default false
|
|
52
|
+
*/
|
|
53
|
+
stopNavigationEvents: boolean;
|
|
54
|
+
},
|
|
55
|
+
'div',
|
|
56
|
+
false
|
|
57
|
+
>;
|
|
58
|
+
|
|
59
|
+
export type NavigableMenuProps = WordPressComponentProps<
|
|
60
|
+
BaseProps & {
|
|
61
|
+
/**
|
|
62
|
+
* The orientation of the menu.
|
|
63
|
+
*
|
|
64
|
+
* @default 'vertical'
|
|
65
|
+
*/
|
|
66
|
+
orientation?: 'vertical' | 'horizontal' | 'both';
|
|
67
|
+
},
|
|
68
|
+
'div',
|
|
69
|
+
false
|
|
70
|
+
>;
|
|
71
|
+
|
|
72
|
+
export type TabbableContainerProps = WordPressComponentProps<
|
|
73
|
+
BaseProps & Partial< Pick< NavigableContainerProps, 'eventToOffset' > >,
|
|
74
|
+
'div',
|
|
75
|
+
false
|
|
76
|
+
>;
|
|
@@ -43,7 +43,7 @@ const animationExitDelay = 0;
|
|
|
43
43
|
// as some of them would overlap with HTML props (e.g. `onAnimationStart`, ...)
|
|
44
44
|
type Props = Omit<
|
|
45
45
|
WordPressComponentProps< NavigatorScreenProps, 'div', false >,
|
|
46
|
-
Exclude< keyof MotionProps, 'style' >
|
|
46
|
+
Exclude< keyof MotionProps, 'style' | 'children' >
|
|
47
47
|
>;
|
|
48
48
|
|
|
49
49
|
function UnconnectedNavigatorScreen(
|