@wordpress/ui 0.9.1-next.v.202603161435.0 → 0.10.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 +38 -1
- package/CONTRIBUTING.md +31 -0
- package/build/alert-dialog/context.cjs +34 -0
- package/build/alert-dialog/context.cjs.map +7 -0
- package/build/alert-dialog/index.cjs +37 -0
- package/build/alert-dialog/index.cjs.map +7 -0
- package/build/alert-dialog/popup.cjs +93 -0
- package/build/alert-dialog/popup.cjs.map +7 -0
- package/build/alert-dialog/root.cjs +52 -0
- package/build/alert-dialog/root.cjs.map +7 -0
- package/build/alert-dialog/trigger.cjs +48 -0
- package/build/alert-dialog/trigger.cjs.map +7 -0
- package/build/alert-dialog/types.cjs +19 -0
- package/build/alert-dialog/types.cjs.map +7 -0
- package/build/badge/badge.cjs +2 -2
- package/build/badge/badge.cjs.map +1 -1
- package/build/button/button.cjs +7 -7
- package/build/button/button.cjs.map +2 -2
- package/build/card/content.cjs +3 -3
- package/build/card/content.cjs.map +2 -2
- package/build/card/full-bleed.cjs +3 -3
- package/build/card/full-bleed.cjs.map +2 -2
- package/build/card/header.cjs +3 -3
- package/build/card/header.cjs.map +2 -2
- package/build/card/root.cjs +5 -5
- package/build/card/root.cjs.map +2 -2
- package/build/card/title.cjs +26 -13
- package/build/card/title.cjs.map +3 -3
- package/build/collapsible-card/content.cjs +24 -3
- package/build/collapsible-card/content.cjs.map +4 -4
- package/build/collapsible-card/context.cjs +35 -0
- package/build/collapsible-card/context.cjs.map +7 -0
- package/build/collapsible-card/header-description.cjs +52 -0
- package/build/collapsible-card/header-description.cjs.map +7 -0
- package/build/collapsible-card/header.cjs +38 -17
- package/build/collapsible-card/header.cjs.map +2 -2
- package/build/collapsible-card/index.cjs +3 -0
- package/build/collapsible-card/index.cjs.map +2 -2
- package/build/collapsible-card/types.cjs.map +1 -1
- package/build/dialog/action.cjs +4 -2
- package/build/dialog/action.cjs.map +2 -2
- package/build/dialog/close-icon.cjs +2 -1
- package/build/dialog/close-icon.cjs.map +2 -2
- package/build/dialog/footer.cjs +3 -3
- package/build/dialog/footer.cjs.map +2 -2
- package/build/dialog/header.cjs +3 -3
- package/build/dialog/header.cjs.map +2 -2
- package/build/dialog/popup.cjs +22 -5
- package/build/dialog/popup.cjs.map +2 -2
- package/build/dialog/title.cjs +3 -3
- package/build/dialog/title.cjs.map +2 -2
- package/build/dialog/types.cjs.map +1 -1
- package/build/empty-state/actions.cjs +66 -0
- package/build/empty-state/actions.cjs.map +7 -0
- package/build/empty-state/description.cjs +66 -0
- package/build/empty-state/description.cjs.map +7 -0
- package/build/empty-state/icon.cjs +69 -0
- package/build/empty-state/icon.cjs.map +7 -0
- package/build/empty-state/index.cjs +46 -0
- package/build/empty-state/index.cjs.map +7 -0
- package/build/empty-state/root.cjs +66 -0
- package/build/empty-state/root.cjs.map +7 -0
- package/build/empty-state/title.cjs +68 -0
- package/build/empty-state/title.cjs.map +7 -0
- package/build/empty-state/types.cjs +19 -0
- package/build/empty-state/types.cjs.map +7 -0
- package/build/empty-state/visual.cjs +66 -0
- package/build/empty-state/visual.cjs.map +7 -0
- package/build/form/index.cjs +27 -0
- package/build/form/index.cjs.map +7 -0
- package/build/form/input-control/index.cjs +31 -0
- package/build/form/input-control/index.cjs.map +7 -0
- package/build/form/input-control/input-control.cjs +50 -0
- package/build/form/input-control/input-control.cjs.map +7 -0
- package/build/form/input-control/types.cjs +19 -0
- package/build/form/input-control/types.cjs.map +7 -0
- package/build/form/primitives/field/description.cjs +2 -2
- package/build/form/primitives/field/description.cjs.map +1 -1
- package/build/form/primitives/field/details.cjs +2 -2
- package/build/form/primitives/field/details.cjs.map +1 -1
- package/build/form/primitives/field/label.cjs +2 -2
- package/build/form/primitives/field/label.cjs.map +1 -1
- package/build/form/primitives/field/root.cjs +4 -4
- package/build/form/primitives/field/root.cjs.map +2 -2
- package/build/form/primitives/fieldset/description.cjs +2 -2
- package/build/form/primitives/fieldset/description.cjs.map +1 -1
- package/build/form/primitives/fieldset/details.cjs +2 -2
- package/build/form/primitives/fieldset/details.cjs.map +1 -1
- package/build/form/primitives/fieldset/legend.cjs +2 -2
- package/build/form/primitives/fieldset/legend.cjs.map +1 -1
- package/build/form/primitives/fieldset/root.cjs +2 -2
- package/build/form/primitives/fieldset/root.cjs.map +1 -1
- package/build/form/primitives/input/input.cjs +4 -4
- package/build/form/primitives/input/input.cjs.map +1 -1
- package/build/form/primitives/input-layout/input-layout.cjs +4 -4
- package/build/form/primitives/input-layout/input-layout.cjs.map +1 -1
- package/build/form/primitives/input-layout/slot.cjs +5 -4
- package/build/form/primitives/input-layout/slot.cjs.map +2 -2
- package/build/form/primitives/select/item.cjs +5 -5
- package/build/form/primitives/select/item.cjs.map +2 -2
- package/build/form/primitives/select/popup.cjs +7 -7
- package/build/form/primitives/select/popup.cjs.map +2 -2
- package/build/form/primitives/select/trigger.cjs +5 -5
- package/build/form/primitives/select/trigger.cjs.map +2 -2
- package/build/form/primitives/textarea/textarea.cjs +3 -3
- package/build/form/primitives/textarea/textarea.cjs.map +2 -2
- package/build/form/types.cjs +19 -0
- package/build/form/types.cjs.map +7 -0
- package/build/icon-button/icon-button.cjs +2 -2
- package/build/icon-button/icon-button.cjs.map +1 -1
- package/build/index.cjs +8 -2
- package/build/index.cjs.map +2 -2
- package/build/link/link.cjs +6 -6
- package/build/link/link.cjs.map +1 -1
- package/build/notice/action-button.cjs +2 -2
- package/build/notice/action-button.cjs.map +1 -1
- package/build/notice/action-link.cjs +2 -2
- package/build/notice/action-link.cjs.map +1 -1
- package/build/notice/actions.cjs +2 -2
- package/build/notice/actions.cjs.map +1 -1
- package/build/notice/close-icon.cjs +2 -2
- package/build/notice/close-icon.cjs.map +1 -1
- package/build/notice/description.cjs +2 -2
- package/build/notice/description.cjs.map +1 -1
- package/build/notice/index.cjs.map +1 -1
- package/build/notice/root.cjs +4 -4
- package/build/notice/root.cjs.map +1 -1
- package/build/notice/title.cjs +2 -2
- package/build/notice/title.cjs.map +1 -1
- package/build/stack/stack.cjs +2 -2
- package/build/stack/stack.cjs.map +1 -1
- package/build/tabs/context.cjs +121 -0
- package/build/tabs/context.cjs.map +7 -0
- package/build/tabs/list.cjs +3 -3
- package/build/tabs/list.cjs.map +2 -2
- package/build/tabs/panel.cjs +5 -3
- package/build/tabs/panel.cjs.map +2 -2
- package/build/tabs/root.cjs +2 -1
- package/build/tabs/root.cjs.map +2 -2
- package/build/tabs/tab.cjs +5 -3
- package/build/tabs/tab.cjs.map +2 -2
- package/build/text/text.cjs +2 -2
- package/build/text/text.cjs.map +1 -1
- package/build/tooltip/popup.cjs +4 -4
- package/build/tooltip/popup.cjs.map +1 -1
- package/build/tooltip/root.cjs.map +2 -2
- package/build/utils/use-deprioritized-initial-focus.cjs +64 -0
- package/build/utils/use-deprioritized-initial-focus.cjs.map +7 -0
- package/build/visually-hidden/visually-hidden.cjs +2 -2
- package/build/visually-hidden/visually-hidden.cjs.map +1 -1
- package/build-module/alert-dialog/context.mjs +9 -0
- package/build-module/alert-dialog/context.mjs.map +7 -0
- package/build-module/alert-dialog/index.mjs +10 -0
- package/build-module/alert-dialog/index.mjs.map +7 -0
- package/build-module/alert-dialog/popup.mjs +58 -0
- package/build-module/alert-dialog/popup.mjs.map +7 -0
- package/build-module/alert-dialog/root.mjs +27 -0
- package/build-module/alert-dialog/root.mjs.map +7 -0
- package/build-module/alert-dialog/trigger.mjs +13 -0
- package/build-module/alert-dialog/trigger.mjs.map +7 -0
- package/build-module/alert-dialog/types.mjs +1 -0
- package/build-module/alert-dialog/types.mjs.map +7 -0
- package/build-module/badge/badge.mjs +2 -2
- package/build-module/badge/badge.mjs.map +1 -1
- package/build-module/button/button.mjs +7 -7
- package/build-module/button/button.mjs.map +2 -2
- package/build-module/card/content.mjs +3 -3
- package/build-module/card/content.mjs.map +2 -2
- package/build-module/card/full-bleed.mjs +3 -3
- package/build-module/card/full-bleed.mjs.map +2 -2
- package/build-module/card/header.mjs +3 -3
- package/build-module/card/header.mjs.map +2 -2
- package/build-module/card/root.mjs +5 -5
- package/build-module/card/root.mjs.map +2 -2
- package/build-module/card/title.mjs +16 -13
- package/build-module/card/title.mjs.map +2 -2
- package/build-module/collapsible-card/content.mjs +24 -3
- package/build-module/collapsible-card/content.mjs.map +3 -3
- package/build-module/collapsible-card/context.mjs +10 -0
- package/build-module/collapsible-card/context.mjs.map +7 -0
- package/build-module/collapsible-card/header-description.mjs +27 -0
- package/build-module/collapsible-card/header-description.mjs.map +7 -0
- package/build-module/collapsible-card/header.mjs +39 -18
- package/build-module/collapsible-card/header.mjs.map +2 -2
- package/build-module/collapsible-card/index.mjs +2 -0
- package/build-module/collapsible-card/index.mjs.map +2 -2
- package/build-module/dialog/action.mjs +4 -2
- package/build-module/dialog/action.mjs.map +2 -2
- package/build-module/dialog/close-icon.mjs +2 -1
- package/build-module/dialog/close-icon.mjs.map +2 -2
- package/build-module/dialog/footer.mjs +3 -3
- package/build-module/dialog/footer.mjs.map +2 -2
- package/build-module/dialog/header.mjs +3 -3
- package/build-module/dialog/header.mjs.map +2 -2
- package/build-module/dialog/popup.mjs +22 -5
- package/build-module/dialog/popup.mjs.map +2 -2
- package/build-module/dialog/title.mjs +3 -3
- package/build-module/dialog/title.mjs.map +2 -2
- package/build-module/empty-state/actions.mjs +31 -0
- package/build-module/empty-state/actions.mjs.map +7 -0
- package/build-module/empty-state/description.mjs +31 -0
- package/build-module/empty-state/description.mjs.map +7 -0
- package/build-module/empty-state/icon.mjs +34 -0
- package/build-module/empty-state/icon.mjs.map +7 -0
- package/build-module/empty-state/index.mjs +16 -0
- package/build-module/empty-state/index.mjs.map +7 -0
- package/build-module/empty-state/root.mjs +31 -0
- package/build-module/empty-state/root.mjs.map +7 -0
- package/build-module/empty-state/title.mjs +33 -0
- package/build-module/empty-state/title.mjs.map +7 -0
- package/build-module/empty-state/types.mjs +1 -0
- package/build-module/empty-state/types.mjs.map +7 -0
- package/build-module/empty-state/visual.mjs +31 -0
- package/build-module/empty-state/visual.mjs.map +7 -0
- package/build-module/form/index.mjs +4 -0
- package/build-module/form/index.mjs.map +7 -0
- package/build-module/form/input-control/index.mjs +6 -0
- package/build-module/form/input-control/index.mjs.map +7 -0
- package/build-module/form/input-control/input-control.mjs +25 -0
- package/build-module/form/input-control/input-control.mjs.map +7 -0
- package/build-module/form/input-control/types.mjs +1 -0
- package/build-module/form/input-control/types.mjs.map +7 -0
- package/build-module/form/primitives/field/description.mjs +2 -2
- package/build-module/form/primitives/field/description.mjs.map +1 -1
- package/build-module/form/primitives/field/details.mjs +2 -2
- package/build-module/form/primitives/field/details.mjs.map +1 -1
- package/build-module/form/primitives/field/label.mjs +2 -2
- package/build-module/form/primitives/field/label.mjs.map +1 -1
- package/build-module/form/primitives/field/root.mjs +4 -4
- package/build-module/form/primitives/field/root.mjs.map +2 -2
- package/build-module/form/primitives/fieldset/description.mjs +2 -2
- package/build-module/form/primitives/fieldset/description.mjs.map +1 -1
- package/build-module/form/primitives/fieldset/details.mjs +2 -2
- package/build-module/form/primitives/fieldset/details.mjs.map +1 -1
- package/build-module/form/primitives/fieldset/legend.mjs +2 -2
- package/build-module/form/primitives/fieldset/legend.mjs.map +1 -1
- package/build-module/form/primitives/fieldset/root.mjs +2 -2
- package/build-module/form/primitives/fieldset/root.mjs.map +1 -1
- package/build-module/form/primitives/input/input.mjs +4 -4
- package/build-module/form/primitives/input/input.mjs.map +1 -1
- package/build-module/form/primitives/input-layout/input-layout.mjs +4 -4
- package/build-module/form/primitives/input-layout/input-layout.mjs.map +1 -1
- package/build-module/form/primitives/input-layout/slot.mjs +5 -4
- package/build-module/form/primitives/input-layout/slot.mjs.map +2 -2
- package/build-module/form/primitives/select/item.mjs +5 -5
- package/build-module/form/primitives/select/item.mjs.map +2 -2
- package/build-module/form/primitives/select/popup.mjs +7 -7
- package/build-module/form/primitives/select/popup.mjs.map +2 -2
- package/build-module/form/primitives/select/trigger.mjs +5 -5
- package/build-module/form/primitives/select/trigger.mjs.map +2 -2
- package/build-module/form/primitives/textarea/textarea.mjs +3 -3
- package/build-module/form/primitives/textarea/textarea.mjs.map +2 -2
- package/build-module/form/types.mjs +1 -0
- package/build-module/form/types.mjs.map +7 -0
- package/build-module/icon-button/icon-button.mjs +2 -2
- package/build-module/icon-button/icon-button.mjs.map +1 -1
- package/build-module/index.mjs +5 -1
- package/build-module/index.mjs.map +2 -2
- package/build-module/link/link.mjs +6 -6
- package/build-module/link/link.mjs.map +1 -1
- package/build-module/notice/action-button.mjs +2 -2
- package/build-module/notice/action-button.mjs.map +1 -1
- package/build-module/notice/action-link.mjs +2 -2
- package/build-module/notice/action-link.mjs.map +1 -1
- package/build-module/notice/actions.mjs +2 -2
- package/build-module/notice/actions.mjs.map +1 -1
- package/build-module/notice/close-icon.mjs +2 -2
- package/build-module/notice/close-icon.mjs.map +1 -1
- package/build-module/notice/description.mjs +2 -2
- package/build-module/notice/description.mjs.map +1 -1
- package/build-module/notice/index.mjs.map +1 -1
- package/build-module/notice/root.mjs +4 -4
- package/build-module/notice/root.mjs.map +1 -1
- package/build-module/notice/title.mjs +2 -2
- package/build-module/notice/title.mjs.map +1 -1
- package/build-module/stack/stack.mjs +2 -2
- package/build-module/stack/stack.mjs.map +1 -1
- package/build-module/tabs/context.mjs +101 -0
- package/build-module/tabs/context.mjs.map +7 -0
- package/build-module/tabs/list.mjs +3 -3
- package/build-module/tabs/list.mjs.map +2 -2
- package/build-module/tabs/panel.mjs +5 -3
- package/build-module/tabs/panel.mjs.map +2 -2
- package/build-module/tabs/root.mjs +2 -1
- package/build-module/tabs/root.mjs.map +2 -2
- package/build-module/tabs/tab.mjs +5 -3
- package/build-module/tabs/tab.mjs.map +2 -2
- package/build-module/text/text.mjs +2 -2
- package/build-module/text/text.mjs.map +1 -1
- package/build-module/tooltip/popup.mjs +4 -4
- package/build-module/tooltip/popup.mjs.map +1 -1
- package/build-module/tooltip/root.mjs.map +2 -2
- package/build-module/utils/use-deprioritized-initial-focus.mjs +39 -0
- package/build-module/utils/use-deprioritized-initial-focus.mjs.map +7 -0
- package/build-module/visually-hidden/visually-hidden.mjs +2 -2
- package/build-module/visually-hidden/visually-hidden.mjs.map +1 -1
- package/build-types/alert-dialog/context.d.ts +8 -0
- package/build-types/alert-dialog/context.d.ts.map +1 -0
- package/build-types/alert-dialog/index.d.ts +4 -0
- package/build-types/alert-dialog/index.d.ts.map +1 -0
- package/build-types/alert-dialog/popup.d.ts +4 -0
- package/build-types/alert-dialog/popup.d.ts.map +1 -0
- package/build-types/alert-dialog/root.d.ts +24 -0
- package/build-types/alert-dialog/root.d.ts.map +1 -0
- package/build-types/alert-dialog/stories/index.story.d.ts +44 -0
- package/build-types/alert-dialog/stories/index.story.d.ts.map +1 -0
- package/build-types/alert-dialog/test/index.test.d.ts +2 -0
- package/build-types/alert-dialog/test/index.test.d.ts.map +1 -0
- package/build-types/alert-dialog/trigger.d.ts +6 -0
- package/build-types/alert-dialog/trigger.d.ts.map +1 -0
- package/build-types/alert-dialog/types.d.ts +70 -0
- package/build-types/alert-dialog/types.d.ts.map +1 -0
- package/build-types/card/title.d.ts.map +1 -1
- package/build-types/collapsible-card/content.d.ts.map +1 -1
- package/build-types/collapsible-card/context.d.ts +4 -0
- package/build-types/collapsible-card/context.d.ts.map +1 -0
- package/build-types/collapsible-card/header-description.d.ts +15 -0
- package/build-types/collapsible-card/header-description.d.ts.map +1 -0
- package/build-types/collapsible-card/header.d.ts.map +1 -1
- package/build-types/collapsible-card/index.d.ts +2 -1
- package/build-types/collapsible-card/index.d.ts.map +1 -1
- package/build-types/collapsible-card/stories/index.story.d.ts +10 -0
- package/build-types/collapsible-card/stories/index.story.d.ts.map +1 -1
- package/build-types/collapsible-card/types.d.ts +21 -0
- package/build-types/collapsible-card/types.d.ts.map +1 -1
- package/build-types/dialog/action.d.ts.map +1 -1
- package/build-types/dialog/close-icon.d.ts.map +1 -1
- package/build-types/dialog/popup.d.ts.map +1 -1
- package/build-types/dialog/stories/index.story.d.ts +0 -6
- package/build-types/dialog/stories/index.story.d.ts.map +1 -1
- package/build-types/dialog/types.d.ts +5 -5
- package/build-types/dialog/types.d.ts.map +1 -1
- package/build-types/empty-state/actions.d.ts +7 -0
- package/build-types/empty-state/actions.d.ts.map +1 -0
- package/build-types/empty-state/description.d.ts +7 -0
- package/build-types/empty-state/description.d.ts.map +1 -0
- package/build-types/empty-state/icon.d.ts +7 -0
- package/build-types/empty-state/icon.d.ts.map +1 -0
- package/build-types/empty-state/index.d.ts +8 -0
- package/build-types/empty-state/index.d.ts.map +1 -0
- package/build-types/empty-state/root.d.ts +6 -0
- package/build-types/empty-state/root.d.ts.map +1 -0
- package/build-types/empty-state/stories/index.story.d.ts +8 -0
- package/build-types/empty-state/stories/index.story.d.ts.map +1 -0
- package/build-types/empty-state/test/actions.test.d.ts +2 -0
- package/build-types/empty-state/test/actions.test.d.ts.map +1 -0
- package/build-types/empty-state/test/description.test.d.ts +2 -0
- package/build-types/empty-state/test/description.test.d.ts.map +1 -0
- package/build-types/empty-state/test/icon.test.d.ts +2 -0
- package/build-types/empty-state/test/icon.test.d.ts.map +1 -0
- package/build-types/empty-state/test/root.test.d.ts +2 -0
- package/build-types/empty-state/test/root.test.d.ts.map +1 -0
- package/build-types/empty-state/test/title.test.d.ts +2 -0
- package/build-types/empty-state/test/title.test.d.ts.map +1 -0
- package/build-types/empty-state/test/visual.test.d.ts +2 -0
- package/build-types/empty-state/test/visual.test.d.ts.map +1 -0
- package/build-types/empty-state/title.d.ts +6 -0
- package/build-types/empty-state/title.d.ts.map +1 -0
- package/build-types/empty-state/types.d.ts +40 -0
- package/build-types/empty-state/types.d.ts.map +1 -0
- package/build-types/empty-state/visual.d.ts +7 -0
- package/build-types/empty-state/visual.d.ts.map +1 -0
- package/build-types/form/index.d.ts +3 -0
- package/build-types/form/index.d.ts.map +1 -0
- package/build-types/form/input-control/index.d.ts +2 -0
- package/build-types/form/input-control/index.d.ts.map +1 -0
- package/build-types/form/input-control/input-control.d.ts +6 -0
- package/build-types/form/input-control/input-control.d.ts.map +1 -0
- package/build-types/form/input-control/stories/index.story.d.ts +16 -0
- package/build-types/form/input-control/stories/index.story.d.ts.map +1 -0
- package/build-types/form/input-control/test/index.test.d.ts +2 -0
- package/build-types/form/input-control/test/index.test.d.ts.map +1 -0
- package/build-types/form/input-control/types.d.ts +4 -0
- package/build-types/form/input-control/types.d.ts.map +1 -0
- package/build-types/form/primitives/field/stories/index.story.d.ts.map +1 -1
- package/build-types/form/primitives/fieldset/stories/index.story.d.ts.map +1 -1
- package/build-types/form/primitives/input/stories/index.story.d.ts +2 -0
- package/build-types/form/primitives/input/stories/index.story.d.ts.map +1 -1
- package/build-types/form/primitives/input-layout/slot.d.ts.map +1 -1
- package/build-types/form/primitives/input-layout/stories/index.story.d.ts +5 -0
- package/build-types/form/primitives/input-layout/stories/index.story.d.ts.map +1 -1
- package/build-types/form/stories/shared.d.ts +3 -0
- package/build-types/form/stories/shared.d.ts.map +1 -0
- package/build-types/form/types.d.ts +30 -0
- package/build-types/form/types.d.ts.map +1 -0
- package/build-types/index.d.ts +3 -1
- package/build-types/index.d.ts.map +1 -1
- package/build-types/notice/index.d.ts +0 -1
- package/build-types/notice/index.d.ts.map +1 -1
- package/build-types/tabs/context.d.ts +26 -0
- package/build-types/tabs/context.d.ts.map +1 -0
- package/build-types/tabs/panel.d.ts.map +1 -1
- package/build-types/tabs/root.d.ts.map +1 -1
- package/build-types/tabs/tab.d.ts.map +1 -1
- package/build-types/tooltip/root.d.ts +12 -0
- package/build-types/tooltip/root.d.ts.map +1 -1
- package/build-types/tooltip/stories/index.story.d.ts.map +1 -1
- package/build-types/utils/test/use-deprioritized-initial-focus.test.d.ts +2 -0
- package/build-types/utils/test/use-deprioritized-initial-focus.test.d.ts.map +1 -0
- package/build-types/utils/use-deprioritized-initial-focus.d.ts +35 -0
- package/build-types/utils/use-deprioritized-initial-focus.d.ts.map +1 -0
- package/package.json +17 -16
- package/src/alert-dialog/context.tsx +14 -0
- package/src/alert-dialog/index.ts +3 -0
- package/src/alert-dialog/popup.tsx +58 -0
- package/src/alert-dialog/root.tsx +48 -0
- package/src/alert-dialog/stories/index.story.tsx +254 -0
- package/src/alert-dialog/style.module.css +10 -0
- package/src/alert-dialog/test/index.test.tsx +537 -0
- package/src/alert-dialog/trigger.tsx +15 -0
- package/src/alert-dialog/types.ts +83 -0
- package/src/button/style.module.css +2 -0
- package/src/card/stories/index.story.tsx +1 -1
- package/src/card/style.module.css +3 -5
- package/src/card/title.tsx +12 -11
- package/src/collapsible-card/content.tsx +16 -3
- package/src/collapsible-card/context.ts +7 -0
- package/src/collapsible-card/header-description.tsx +43 -0
- package/src/collapsible-card/header.tsx +47 -24
- package/src/collapsible-card/index.ts +2 -1
- package/src/collapsible-card/stories/index.story.tsx +99 -1
- package/src/collapsible-card/style.module.css +34 -2
- package/src/collapsible-card/test/index.test.tsx +96 -9
- package/src/collapsible-card/types.ts +22 -0
- package/src/dialog/action.tsx +8 -2
- package/src/dialog/close-icon.tsx +1 -0
- package/src/dialog/popup.tsx +21 -2
- package/src/dialog/stories/index.story.tsx +0 -28
- package/src/dialog/style.module.css +5 -5
- package/src/dialog/test/index.test.tsx +117 -0
- package/src/dialog/types.ts +11 -5
- package/src/empty-state/actions.tsx +24 -0
- package/src/empty-state/description.tsx +27 -0
- package/src/empty-state/icon.tsx +24 -0
- package/src/empty-state/index.ts +8 -0
- package/src/empty-state/root.tsx +23 -0
- package/src/empty-state/stories/index.story.tsx +64 -0
- package/src/empty-state/style.module.css +53 -0
- package/src/empty-state/test/actions.test.tsx +18 -0
- package/src/empty-state/test/description.test.tsx +13 -0
- package/src/empty-state/test/icon.test.tsx +13 -0
- package/src/empty-state/test/root.test.tsx +13 -0
- package/src/empty-state/test/title.test.tsx +13 -0
- package/src/empty-state/test/visual.test.tsx +17 -0
- package/src/empty-state/title.tsx +23 -0
- package/src/empty-state/types.ts +45 -0
- package/src/empty-state/visual.tsx +24 -0
- package/src/form/index.ts +3 -0
- package/src/form/input-control/index.ts +1 -0
- package/src/form/input-control/input-control.tsx +33 -0
- package/src/form/input-control/stories/index.story.tsx +163 -0
- package/src/form/input-control/test/index.test.tsx +53 -0
- package/src/form/input-control/types.ts +5 -0
- package/src/form/primitives/field/root.tsx +2 -2
- package/src/form/primitives/field/stories/index.story.tsx +2 -7
- package/src/form/primitives/fieldset/stories/index.story.tsx +2 -7
- package/src/form/primitives/input/stories/index.story.tsx +7 -0
- package/src/form/primitives/input-layout/slot.tsx +6 -2
- package/src/form/primitives/input-layout/stories/index.story.tsx +22 -1
- package/src/form/primitives/stories/overview.mdx +15 -0
- package/src/form/primitives/textarea/textarea.tsx +1 -1
- package/src/form/stories/shared.tsx +19 -0
- package/src/form/types.ts +34 -0
- package/src/index.ts +3 -1
- package/src/notice/index.ts +0 -2
- package/src/notice/style.module.css +1 -1
- package/src/tabs/context.tsx +170 -0
- package/src/tabs/panel.tsx +3 -0
- package/src/tabs/root.tsx +6 -1
- package/src/tabs/style.module.css +1 -1
- package/src/tabs/tab.tsx +3 -0
- package/src/tabs/test/index.test.tsx +162 -0
- package/src/tooltip/root.tsx +12 -0
- package/src/tooltip/stories/index.story.tsx +20 -15
- package/src/utils/css/item-popup.module.css +1 -0
- package/src/utils/css/select-trigger.module.css +1 -0
- package/src/utils/test/use-deprioritized-initial-focus.test.tsx +230 -0
- package/src/utils/use-deprioritized-initial-focus.ts +83 -0
package/src/tabs/panel.tsx
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { forwardRef } from '@wordpress/element';
|
|
2
2
|
import clsx from 'clsx';
|
|
3
3
|
import { Tabs as _Tabs } from '@base-ui/react/tabs';
|
|
4
|
+
import { useRegisterPanel } from './context';
|
|
4
5
|
import styles from './style.module.css';
|
|
5
6
|
import type { TabPanelProps } from './types';
|
|
6
7
|
|
|
@@ -12,6 +13,8 @@ import type { TabPanelProps } from './types';
|
|
|
12
13
|
*/
|
|
13
14
|
export const Panel = forwardRef< HTMLDivElement, TabPanelProps >(
|
|
14
15
|
function TabPanel( { className, ...otherProps }, forwardedRef ) {
|
|
16
|
+
useRegisterPanel();
|
|
17
|
+
|
|
15
18
|
return (
|
|
16
19
|
<_Tabs.Panel
|
|
17
20
|
ref={ forwardedRef }
|
package/src/tabs/root.tsx
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { forwardRef } from '@wordpress/element';
|
|
2
2
|
import { Tabs as _Tabs } from '@base-ui/react/tabs';
|
|
3
|
+
import { TabsValidationProvider } from './context';
|
|
3
4
|
import type { TabRootProps } from './types';
|
|
4
5
|
|
|
5
6
|
/**
|
|
@@ -10,6 +11,10 @@ import type { TabRootProps } from './types';
|
|
|
10
11
|
*/
|
|
11
12
|
export const Root = forwardRef< HTMLDivElement, TabRootProps >(
|
|
12
13
|
function TabsRoot( { ...otherProps }, forwardedRef ) {
|
|
13
|
-
return
|
|
14
|
+
return (
|
|
15
|
+
<TabsValidationProvider>
|
|
16
|
+
<_Tabs.Root ref={ forwardedRef } { ...otherProps } />
|
|
17
|
+
</TabsValidationProvider>
|
|
18
|
+
);
|
|
14
19
|
}
|
|
15
20
|
);
|
package/src/tabs/tab.tsx
CHANGED
|
@@ -3,6 +3,7 @@ import clsx from 'clsx';
|
|
|
3
3
|
import { Tabs as _Tabs } from '@base-ui/react/tabs';
|
|
4
4
|
import { chevronRight } from '@wordpress/icons';
|
|
5
5
|
import { Icon } from '../icon';
|
|
6
|
+
import { useRegisterTab } from './context';
|
|
6
7
|
import styles from './style.module.css';
|
|
7
8
|
import type { TabProps } from './types';
|
|
8
9
|
|
|
@@ -16,6 +17,8 @@ export const Tab = forwardRef< HTMLButtonElement, TabProps >( function Tab(
|
|
|
16
17
|
{ className, children, ...otherProps },
|
|
17
18
|
forwardedRef
|
|
18
19
|
) {
|
|
20
|
+
useRegisterTab();
|
|
21
|
+
|
|
19
22
|
return (
|
|
20
23
|
<_Tabs.Tab
|
|
21
24
|
ref={ forwardedRef }
|
|
@@ -2256,5 +2256,167 @@ describe( 'Tabs', () => {
|
|
|
2256
2256
|
);
|
|
2257
2257
|
} );
|
|
2258
2258
|
} );
|
|
2259
|
+
|
|
2260
|
+
describe( 'Development mode validation', () => {
|
|
2261
|
+
function collectUncaughtErrors() {
|
|
2262
|
+
const errors: Error[] = [];
|
|
2263
|
+
const handler = ( event: ErrorEvent ) => {
|
|
2264
|
+
event.preventDefault();
|
|
2265
|
+
errors.push( event.error );
|
|
2266
|
+
};
|
|
2267
|
+
window.addEventListener( 'error', handler );
|
|
2268
|
+
return {
|
|
2269
|
+
errors,
|
|
2270
|
+
cleanup: () => window.removeEventListener( 'error', handler ),
|
|
2271
|
+
};
|
|
2272
|
+
}
|
|
2273
|
+
|
|
2274
|
+
it( 'should throw when there are more Tabs than Panels', async () => {
|
|
2275
|
+
const { errors, cleanup } = collectUncaughtErrors();
|
|
2276
|
+
|
|
2277
|
+
render(
|
|
2278
|
+
<Tabs.Root defaultValue="one">
|
|
2279
|
+
<Tabs.List>
|
|
2280
|
+
<Tabs.Tab value="one">One</Tabs.Tab>
|
|
2281
|
+
<Tabs.Tab value="two">Two</Tabs.Tab>
|
|
2282
|
+
<Tabs.Tab value="three">Three</Tabs.Tab>
|
|
2283
|
+
</Tabs.List>
|
|
2284
|
+
<Tabs.Panel value="one">First panel</Tabs.Panel>
|
|
2285
|
+
<Tabs.Panel value="two">Second panel</Tabs.Panel>
|
|
2286
|
+
</Tabs.Root>
|
|
2287
|
+
);
|
|
2288
|
+
|
|
2289
|
+
await waitForComponentToBeInitializedWithSelectedTab( 'One' );
|
|
2290
|
+
|
|
2291
|
+
await waitFor( () => {
|
|
2292
|
+
expect( errors.length ).toBeGreaterThan( 0 );
|
|
2293
|
+
} );
|
|
2294
|
+
|
|
2295
|
+
expect( errors[ 0 ].message ).toBe(
|
|
2296
|
+
'Tabs: Tab/Panel count mismatch (3 Tabs, 2 Panels). Each Tab must be associated with exactly one Panel. Mismatched or missing associations can break screen reader navigation and violate WAI-ARIA Tabs pattern requirements.'
|
|
2297
|
+
);
|
|
2298
|
+
|
|
2299
|
+
cleanup();
|
|
2300
|
+
} );
|
|
2301
|
+
|
|
2302
|
+
it( 'should throw when there are more Panels than Tabs', async () => {
|
|
2303
|
+
const { errors, cleanup } = collectUncaughtErrors();
|
|
2304
|
+
|
|
2305
|
+
render(
|
|
2306
|
+
<Tabs.Root defaultValue="one">
|
|
2307
|
+
<Tabs.List>
|
|
2308
|
+
<Tabs.Tab value="one">One</Tabs.Tab>
|
|
2309
|
+
<Tabs.Tab value="two">Two</Tabs.Tab>
|
|
2310
|
+
</Tabs.List>
|
|
2311
|
+
<Tabs.Panel value="one">First panel</Tabs.Panel>
|
|
2312
|
+
<Tabs.Panel value="two">Second panel</Tabs.Panel>
|
|
2313
|
+
<Tabs.Panel value="three">Third panel</Tabs.Panel>
|
|
2314
|
+
</Tabs.Root>
|
|
2315
|
+
);
|
|
2316
|
+
|
|
2317
|
+
await waitForComponentToBeInitializedWithSelectedTab( 'One' );
|
|
2318
|
+
|
|
2319
|
+
await waitFor( () => {
|
|
2320
|
+
expect( errors.length ).toBeGreaterThan( 0 );
|
|
2321
|
+
} );
|
|
2322
|
+
|
|
2323
|
+
expect( errors[ 0 ].message ).toBe(
|
|
2324
|
+
'Tabs: Tab/Panel count mismatch (2 Tabs, 3 Panels). Each Tab must be associated with exactly one Panel. Mismatched or missing associations can break screen reader navigation and violate WAI-ARIA Tabs pattern requirements.'
|
|
2325
|
+
);
|
|
2326
|
+
|
|
2327
|
+
cleanup();
|
|
2328
|
+
} );
|
|
2329
|
+
|
|
2330
|
+
it( 'should not throw when Tab and Panel counts match', async () => {
|
|
2331
|
+
const { errors, cleanup } = collectUncaughtErrors();
|
|
2332
|
+
|
|
2333
|
+
render(
|
|
2334
|
+
<Tabs.Root defaultValue="one">
|
|
2335
|
+
<Tabs.List>
|
|
2336
|
+
<Tabs.Tab value="one">One</Tabs.Tab>
|
|
2337
|
+
<Tabs.Tab value="two">Two</Tabs.Tab>
|
|
2338
|
+
</Tabs.List>
|
|
2339
|
+
<Tabs.Panel value="one">First panel</Tabs.Panel>
|
|
2340
|
+
<Tabs.Panel value="two">Second panel</Tabs.Panel>
|
|
2341
|
+
</Tabs.Root>
|
|
2342
|
+
);
|
|
2343
|
+
|
|
2344
|
+
await waitForComponentToBeInitializedWithSelectedTab( 'One' );
|
|
2345
|
+
|
|
2346
|
+
// Wait a bit to ensure validation has run
|
|
2347
|
+
await new Promise( ( resolve ) => setTimeout( resolve, 50 ) );
|
|
2348
|
+
|
|
2349
|
+
expect( errors ).toHaveLength( 0 );
|
|
2350
|
+
|
|
2351
|
+
cleanup();
|
|
2352
|
+
} );
|
|
2353
|
+
|
|
2354
|
+
it( 'should throw when tabs are used without any panels', async () => {
|
|
2355
|
+
const { errors, cleanup } = collectUncaughtErrors();
|
|
2356
|
+
|
|
2357
|
+
render(
|
|
2358
|
+
<Tabs.Root>
|
|
2359
|
+
<Tabs.List>
|
|
2360
|
+
<Tabs.Tab value="one">One</Tabs.Tab>
|
|
2361
|
+
<Tabs.Tab value="two">Two</Tabs.Tab>
|
|
2362
|
+
</Tabs.List>
|
|
2363
|
+
</Tabs.Root>
|
|
2364
|
+
);
|
|
2365
|
+
|
|
2366
|
+
await waitFor( () => {
|
|
2367
|
+
expect( errors.length ).toBeGreaterThan( 0 );
|
|
2368
|
+
} );
|
|
2369
|
+
|
|
2370
|
+
expect( errors[ 0 ].message ).toBe(
|
|
2371
|
+
'Tabs: Tab/Panel count mismatch (2 Tabs, 0 Panels). Each Tab must be associated with exactly one Panel. Mismatched or missing associations can break screen reader navigation and violate WAI-ARIA Tabs pattern requirements.'
|
|
2372
|
+
);
|
|
2373
|
+
|
|
2374
|
+
cleanup();
|
|
2375
|
+
} );
|
|
2376
|
+
|
|
2377
|
+
it( 'should detect count mismatch after dynamic changes', async () => {
|
|
2378
|
+
const { errors, cleanup } = collectUncaughtErrors();
|
|
2379
|
+
|
|
2380
|
+
const { rerender } = render(
|
|
2381
|
+
<Tabs.Root defaultValue="one">
|
|
2382
|
+
<Tabs.List>
|
|
2383
|
+
<Tabs.Tab value="one">One</Tabs.Tab>
|
|
2384
|
+
<Tabs.Tab value="two">Two</Tabs.Tab>
|
|
2385
|
+
</Tabs.List>
|
|
2386
|
+
<Tabs.Panel value="one">First panel</Tabs.Panel>
|
|
2387
|
+
<Tabs.Panel value="two">Second panel</Tabs.Panel>
|
|
2388
|
+
</Tabs.Root>
|
|
2389
|
+
);
|
|
2390
|
+
|
|
2391
|
+
await waitForComponentToBeInitializedWithSelectedTab( 'One' );
|
|
2392
|
+
|
|
2393
|
+
// Wait for validation
|
|
2394
|
+
await new Promise( ( resolve ) => setTimeout( resolve, 50 ) );
|
|
2395
|
+
|
|
2396
|
+
// No errors since counts match
|
|
2397
|
+
expect( errors ).toHaveLength( 0 );
|
|
2398
|
+
|
|
2399
|
+
// Remove a panel to create a mismatch
|
|
2400
|
+
rerender(
|
|
2401
|
+
<Tabs.Root defaultValue="one">
|
|
2402
|
+
<Tabs.List>
|
|
2403
|
+
<Tabs.Tab value="one">One</Tabs.Tab>
|
|
2404
|
+
<Tabs.Tab value="two">Two</Tabs.Tab>
|
|
2405
|
+
</Tabs.List>
|
|
2406
|
+
<Tabs.Panel value="one">First panel</Tabs.Panel>
|
|
2407
|
+
</Tabs.Root>
|
|
2408
|
+
);
|
|
2409
|
+
|
|
2410
|
+
await waitFor( () => {
|
|
2411
|
+
expect( errors.length ).toBeGreaterThan( 0 );
|
|
2412
|
+
} );
|
|
2413
|
+
|
|
2414
|
+
expect( errors[ 0 ].message ).toBe(
|
|
2415
|
+
'Tabs: Tab/Panel count mismatch (2 Tabs, 1 Panels). Each Tab must be associated with exactly one Panel. Mismatched or missing associations can break screen reader navigation and violate WAI-ARIA Tabs pattern requirements.'
|
|
2416
|
+
);
|
|
2417
|
+
|
|
2418
|
+
cleanup();
|
|
2419
|
+
} );
|
|
2420
|
+
} );
|
|
2259
2421
|
} );
|
|
2260
2422
|
/* eslint-enable jest/no-conditional-expect */
|
package/src/tooltip/root.tsx
CHANGED
|
@@ -1,6 +1,18 @@
|
|
|
1
1
|
import { Tooltip } from '@base-ui/react/tooltip';
|
|
2
2
|
import type { RootProps } from './types';
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* `Tooltip` is used to visually show the label of an icon button, or other such interactive controls
|
|
6
|
+
* that don't have a visual text label.
|
|
7
|
+
*
|
|
8
|
+
* Tooltips are not available on touch devices, and thus should not be used for infotips,
|
|
9
|
+
* descriptions, or dynamic status messages.
|
|
10
|
+
*
|
|
11
|
+
* The tooltip itself does not provide any accessible labeling, so when using the
|
|
12
|
+
* `Tooltip` primitive you must ensure that the trigger is accessibly labeled (e.g. with an `aria-label`).
|
|
13
|
+
*
|
|
14
|
+
* See also: [IconButton](https://wordpress.github.io/gutenberg/?path=/docs/design-system-components-iconbutton--docs)
|
|
15
|
+
*/
|
|
4
16
|
function Root( props: RootProps ) {
|
|
5
17
|
return <Tooltip.Root { ...props } />;
|
|
6
18
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
2
|
-
import {
|
|
2
|
+
import { formatBold, formatItalic } from '@wordpress/icons';
|
|
3
|
+
import { Icon, Tooltip } from '../..';
|
|
3
4
|
|
|
4
5
|
const meta: Meta< typeof Tooltip.Root > = {
|
|
5
6
|
title: 'Design System/Components/Tooltip',
|
|
@@ -16,8 +17,8 @@ export const Default: StoryObj< typeof Tooltip.Root > = {
|
|
|
16
17
|
args: {
|
|
17
18
|
children: (
|
|
18
19
|
<>
|
|
19
|
-
<Tooltip.Trigger
|
|
20
|
-
<Tooltip.Popup>
|
|
20
|
+
<Tooltip.Trigger aria-label="Save">💾</Tooltip.Trigger>
|
|
21
|
+
<Tooltip.Popup>Save</Tooltip.Popup>
|
|
21
22
|
</>
|
|
22
23
|
),
|
|
23
24
|
},
|
|
@@ -51,23 +52,23 @@ export const Positioning: StoryObj< typeof Tooltip.Root > = {
|
|
|
51
52
|
} }
|
|
52
53
|
>
|
|
53
54
|
<Tooltip.Root>
|
|
54
|
-
<Tooltip.Trigger
|
|
55
|
-
<Tooltip.Popup side="top">
|
|
55
|
+
<Tooltip.Trigger aria-label="Up">⬆️</Tooltip.Trigger>
|
|
56
|
+
<Tooltip.Popup side="top">Up</Tooltip.Popup>
|
|
56
57
|
</Tooltip.Root>
|
|
57
58
|
|
|
58
59
|
<Tooltip.Root>
|
|
59
|
-
<Tooltip.Trigger
|
|
60
|
-
<Tooltip.Popup side="right">
|
|
60
|
+
<Tooltip.Trigger aria-label="Forward">➡️</Tooltip.Trigger>
|
|
61
|
+
<Tooltip.Popup side="right">Forward</Tooltip.Popup>
|
|
61
62
|
</Tooltip.Root>
|
|
62
63
|
|
|
63
64
|
<Tooltip.Root>
|
|
64
|
-
<Tooltip.Trigger
|
|
65
|
-
<Tooltip.Popup side="bottom">
|
|
65
|
+
<Tooltip.Trigger aria-label="Down">⬇️</Tooltip.Trigger>
|
|
66
|
+
<Tooltip.Popup side="bottom">Down</Tooltip.Popup>
|
|
66
67
|
</Tooltip.Root>
|
|
67
68
|
|
|
68
69
|
<Tooltip.Root>
|
|
69
|
-
<Tooltip.Trigger
|
|
70
|
-
<Tooltip.Popup side="left">
|
|
70
|
+
<Tooltip.Trigger aria-label="Back">⬅️</Tooltip.Trigger>
|
|
71
|
+
<Tooltip.Popup side="left">Back</Tooltip.Popup>
|
|
71
72
|
</Tooltip.Root>
|
|
72
73
|
</div>
|
|
73
74
|
),
|
|
@@ -83,13 +84,17 @@ export const WithProvider: StoryObj< typeof Tooltip.Root > = {
|
|
|
83
84
|
<Tooltip.Provider delay={ 0 }>
|
|
84
85
|
<div style={ { display: 'flex', gap: '1rem' } }>
|
|
85
86
|
<Tooltip.Root>
|
|
86
|
-
<Tooltip.Trigger>
|
|
87
|
-
|
|
87
|
+
<Tooltip.Trigger aria-label="Bold">
|
|
88
|
+
<Icon icon={ formatBold } />
|
|
89
|
+
</Tooltip.Trigger>
|
|
90
|
+
<Tooltip.Popup>Bold</Tooltip.Popup>
|
|
88
91
|
</Tooltip.Root>
|
|
89
92
|
|
|
90
93
|
<Tooltip.Root>
|
|
91
|
-
<Tooltip.Trigger>
|
|
92
|
-
|
|
94
|
+
<Tooltip.Trigger aria-label="Italic">
|
|
95
|
+
<Icon icon={ formatItalic } />
|
|
96
|
+
</Tooltip.Trigger>
|
|
97
|
+
<Tooltip.Popup>Italic</Tooltip.Popup>
|
|
93
98
|
</Tooltip.Root>
|
|
94
99
|
</div>
|
|
95
100
|
</Tooltip.Provider>
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
import { render, screen } from '@testing-library/react';
|
|
2
|
+
import { createRef, useEffect } from '@wordpress/element';
|
|
3
|
+
import { useDeprioritizedInitialFocus } from '../use-deprioritized-initial-focus';
|
|
4
|
+
|
|
5
|
+
const ATTR = 'data-test-deprioritized';
|
|
6
|
+
|
|
7
|
+
function TestHarness( {
|
|
8
|
+
initialFocus,
|
|
9
|
+
onResolved,
|
|
10
|
+
}: {
|
|
11
|
+
initialFocus?: Parameters<
|
|
12
|
+
typeof useDeprioritizedInitialFocus
|
|
13
|
+
>[ 0 ][ 'initialFocus' ];
|
|
14
|
+
onResolved: (
|
|
15
|
+
result: ReturnType< typeof useDeprioritizedInitialFocus >
|
|
16
|
+
) => void;
|
|
17
|
+
} ) {
|
|
18
|
+
const result = useDeprioritizedInitialFocus( {
|
|
19
|
+
initialFocus,
|
|
20
|
+
deprioritizedAttribute: ATTR,
|
|
21
|
+
} );
|
|
22
|
+
|
|
23
|
+
useEffect( () => {
|
|
24
|
+
onResolved( result );
|
|
25
|
+
} );
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<div ref={ result.popupRef } data-testid="popup">
|
|
29
|
+
<button { ...{ [ ATTR ]: '' } }>Close</button>
|
|
30
|
+
<button>Action</button>
|
|
31
|
+
<input type="text" />
|
|
32
|
+
</div>
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
describe( 'useDeprioritizedInitialFocus', () => {
|
|
37
|
+
describe( 'passthrough', () => {
|
|
38
|
+
it( 'passes through false unchanged', () => {
|
|
39
|
+
let resolved: ReturnType< typeof useDeprioritizedInitialFocus >;
|
|
40
|
+
|
|
41
|
+
render(
|
|
42
|
+
<TestHarness
|
|
43
|
+
initialFocus={ false }
|
|
44
|
+
onResolved={ ( r ) => {
|
|
45
|
+
resolved = r;
|
|
46
|
+
} }
|
|
47
|
+
/>
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
expect( resolved!.resolvedInitialFocus ).toBe( false );
|
|
51
|
+
} );
|
|
52
|
+
|
|
53
|
+
it( 'passes through a ref unchanged', () => {
|
|
54
|
+
const ref = createRef< HTMLElement >();
|
|
55
|
+
let resolved: ReturnType< typeof useDeprioritizedInitialFocus >;
|
|
56
|
+
|
|
57
|
+
render(
|
|
58
|
+
<TestHarness
|
|
59
|
+
initialFocus={ ref }
|
|
60
|
+
onResolved={ ( r ) => {
|
|
61
|
+
resolved = r;
|
|
62
|
+
} }
|
|
63
|
+
/>
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
expect( resolved!.resolvedInitialFocus ).toBe( ref );
|
|
67
|
+
} );
|
|
68
|
+
|
|
69
|
+
it( 'passes through a custom callback unchanged', () => {
|
|
70
|
+
const cb = () => true as const;
|
|
71
|
+
let resolved: ReturnType< typeof useDeprioritizedInitialFocus >;
|
|
72
|
+
|
|
73
|
+
render(
|
|
74
|
+
<TestHarness
|
|
75
|
+
initialFocus={ cb }
|
|
76
|
+
onResolved={ ( r ) => {
|
|
77
|
+
resolved = r;
|
|
78
|
+
} }
|
|
79
|
+
/>
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
expect( resolved!.resolvedInitialFocus ).toBe( cb );
|
|
83
|
+
} );
|
|
84
|
+
} );
|
|
85
|
+
|
|
86
|
+
describe( 'default behavior (initialFocus undefined or true)', () => {
|
|
87
|
+
it.each( [ undefined, true ] )(
|
|
88
|
+
'returns a callback when initialFocus is %s',
|
|
89
|
+
( value ) => {
|
|
90
|
+
let resolved: ReturnType< typeof useDeprioritizedInitialFocus >;
|
|
91
|
+
|
|
92
|
+
render(
|
|
93
|
+
<TestHarness
|
|
94
|
+
initialFocus={ value }
|
|
95
|
+
onResolved={ ( r ) => {
|
|
96
|
+
resolved = r;
|
|
97
|
+
} }
|
|
98
|
+
/>
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
expect( typeof resolved!.resolvedInitialFocus ).toBe(
|
|
102
|
+
'function'
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
it( 'returns the popup element on touch interactions', () => {
|
|
108
|
+
let resolved: ReturnType< typeof useDeprioritizedInitialFocus >;
|
|
109
|
+
|
|
110
|
+
render(
|
|
111
|
+
<TestHarness
|
|
112
|
+
onResolved={ ( r ) => {
|
|
113
|
+
resolved = r;
|
|
114
|
+
} }
|
|
115
|
+
/>
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
const callback = resolved!.resolvedInitialFocus as (
|
|
119
|
+
type: string
|
|
120
|
+
) => HTMLElement | boolean | null;
|
|
121
|
+
const result = callback( 'touch' );
|
|
122
|
+
|
|
123
|
+
expect( result ).toBe( screen.getByTestId( 'popup' ) );
|
|
124
|
+
} );
|
|
125
|
+
|
|
126
|
+
it( 'skips the deprioritized element on non-touch interactions', () => {
|
|
127
|
+
let resolved: ReturnType< typeof useDeprioritizedInitialFocus >;
|
|
128
|
+
|
|
129
|
+
render(
|
|
130
|
+
<TestHarness
|
|
131
|
+
onResolved={ ( r ) => {
|
|
132
|
+
resolved = r;
|
|
133
|
+
} }
|
|
134
|
+
/>
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
const callback = resolved!.resolvedInitialFocus as (
|
|
138
|
+
type: string
|
|
139
|
+
) => HTMLElement | boolean | null;
|
|
140
|
+
const result = callback( 'mouse' );
|
|
141
|
+
|
|
142
|
+
// Should return the Action button, skipping Close
|
|
143
|
+
expect( result ).toBeInstanceOf( HTMLButtonElement );
|
|
144
|
+
expect( result as HTMLButtonElement ).toHaveTextContent( 'Action' );
|
|
145
|
+
} );
|
|
146
|
+
|
|
147
|
+
it( 'falls back to default when only deprioritized elements exist', () => {
|
|
148
|
+
let resolved: ReturnType< typeof useDeprioritizedInitialFocus >;
|
|
149
|
+
|
|
150
|
+
function OnlyDeprioritized( {
|
|
151
|
+
onResolved: onResolvedProp,
|
|
152
|
+
}: {
|
|
153
|
+
onResolved: (
|
|
154
|
+
r: ReturnType< typeof useDeprioritizedInitialFocus >
|
|
155
|
+
) => void;
|
|
156
|
+
} ) {
|
|
157
|
+
const result = useDeprioritizedInitialFocus( {
|
|
158
|
+
initialFocus: undefined,
|
|
159
|
+
deprioritizedAttribute: ATTR,
|
|
160
|
+
} );
|
|
161
|
+
|
|
162
|
+
useEffect( () => {
|
|
163
|
+
onResolvedProp( result );
|
|
164
|
+
} );
|
|
165
|
+
|
|
166
|
+
return (
|
|
167
|
+
<div ref={ result.popupRef } data-testid="popup">
|
|
168
|
+
<button { ...{ [ ATTR ]: '' } }>Close</button>
|
|
169
|
+
<p>No other tabbable elements</p>
|
|
170
|
+
</div>
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
render(
|
|
175
|
+
<OnlyDeprioritized
|
|
176
|
+
onResolved={ ( r ) => {
|
|
177
|
+
resolved = r;
|
|
178
|
+
} }
|
|
179
|
+
/>
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
const callback = resolved!.resolvedInitialFocus as (
|
|
183
|
+
type: string
|
|
184
|
+
) => HTMLElement | boolean | null;
|
|
185
|
+
const result = callback( 'keyboard' );
|
|
186
|
+
|
|
187
|
+
// Falls back to Base UI's default
|
|
188
|
+
expect( result ).toBe( true );
|
|
189
|
+
} );
|
|
190
|
+
|
|
191
|
+
it( 'returns true when popupRef is not attached', () => {
|
|
192
|
+
let resolved: ReturnType< typeof useDeprioritizedInitialFocus >;
|
|
193
|
+
|
|
194
|
+
function NoRef( {
|
|
195
|
+
onResolved: onResolvedProp,
|
|
196
|
+
}: {
|
|
197
|
+
onResolved: (
|
|
198
|
+
r: ReturnType< typeof useDeprioritizedInitialFocus >
|
|
199
|
+
) => void;
|
|
200
|
+
} ) {
|
|
201
|
+
const result = useDeprioritizedInitialFocus( {
|
|
202
|
+
initialFocus: undefined,
|
|
203
|
+
deprioritizedAttribute: ATTR,
|
|
204
|
+
} );
|
|
205
|
+
|
|
206
|
+
useEffect( () => {
|
|
207
|
+
onResolvedProp( result );
|
|
208
|
+
} );
|
|
209
|
+
|
|
210
|
+
// Intentionally not attaching popupRef to any element
|
|
211
|
+
return <div>Nothing</div>;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
render(
|
|
215
|
+
<NoRef
|
|
216
|
+
onResolved={ ( r ) => {
|
|
217
|
+
resolved = r;
|
|
218
|
+
} }
|
|
219
|
+
/>
|
|
220
|
+
);
|
|
221
|
+
|
|
222
|
+
const callback = resolved!.resolvedInitialFocus as (
|
|
223
|
+
type: string
|
|
224
|
+
) => HTMLElement | boolean | null;
|
|
225
|
+
|
|
226
|
+
expect( callback( 'touch' ) ).toBe( true );
|
|
227
|
+
expect( callback( 'mouse' ) ).toBe( true );
|
|
228
|
+
} );
|
|
229
|
+
} );
|
|
230
|
+
} );
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import type { Dialog as _Dialog } from '@base-ui/react/dialog';
|
|
2
|
+
import { useMemo, useRef } from '@wordpress/element';
|
|
3
|
+
import { tabbable } from 'tabbable';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Derived from Base UI's `Dialog.Popup.Props['initialFocus']`.
|
|
7
|
+
* The same type is shared by all Base UI overlay popups (Dialog, Popover, etc.).
|
|
8
|
+
*/
|
|
9
|
+
type InitialFocus = _Dialog.Popup.Props[ 'initialFocus' ];
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Options matching Base UI's internal tabbable configuration.
|
|
13
|
+
* @see https://github.com/floating-ui/floating-ui/blob/master/packages/react/src/utils/tabbable.ts
|
|
14
|
+
*/
|
|
15
|
+
const getTabbableOptions = () => ( {
|
|
16
|
+
getShadowRoot: true,
|
|
17
|
+
displayCheck:
|
|
18
|
+
typeof ResizeObserver === 'function' &&
|
|
19
|
+
ResizeObserver.toString().includes( '[native code]' )
|
|
20
|
+
? ( 'full' as const )
|
|
21
|
+
: ( 'none' as const ),
|
|
22
|
+
} );
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Returns a resolved `initialFocus` value that deprioritizes elements
|
|
26
|
+
* marked with a given data attribute (e.g. a close icon), and an internal
|
|
27
|
+
* ref that must be merged onto the popup element.
|
|
28
|
+
*
|
|
29
|
+
* When `initialFocus` is `undefined` or `true` (the default behavior),
|
|
30
|
+
* the hook replaces it with a callback that:
|
|
31
|
+
* 1. On touch interactions — focuses the popup element itself (preventing
|
|
32
|
+
* the virtual keyboard on Android), matching Base UI's default.
|
|
33
|
+
* 2. On other interactions — returns the first tabbable element that does
|
|
34
|
+
* *not* carry `deprioritizedAttribute`. Falls back to Base UI's default
|
|
35
|
+
* when the deprioritized element is the only tabbable element.
|
|
36
|
+
*
|
|
37
|
+
* All other `initialFocus` values (`false`, `RefObject`, callback) pass
|
|
38
|
+
* through unchanged.
|
|
39
|
+
*
|
|
40
|
+
* @param props
|
|
41
|
+
* @param props.initialFocus The consumer-provided `initialFocus` value.
|
|
42
|
+
* @param props.deprioritizedAttribute The data attribute whose elements should be deprioritized.
|
|
43
|
+
*/
|
|
44
|
+
export function useDeprioritizedInitialFocus( {
|
|
45
|
+
initialFocus,
|
|
46
|
+
deprioritizedAttribute,
|
|
47
|
+
}: {
|
|
48
|
+
initialFocus: InitialFocus;
|
|
49
|
+
deprioritizedAttribute: string;
|
|
50
|
+
} ) {
|
|
51
|
+
const popupRef = useRef< HTMLDivElement >( null );
|
|
52
|
+
|
|
53
|
+
const resolvedInitialFocus = useMemo( (): InitialFocus => {
|
|
54
|
+
if ( initialFocus !== undefined && initialFocus !== true ) {
|
|
55
|
+
return initialFocus;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return ( interactionType ): HTMLElement | boolean | null => {
|
|
59
|
+
if ( interactionType === 'touch' ) {
|
|
60
|
+
return popupRef.current ?? true;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const popup = popupRef.current;
|
|
64
|
+
if ( ! popup ) {
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const tabbables = tabbable( popup, getTabbableOptions() );
|
|
69
|
+
for ( const el of tabbables ) {
|
|
70
|
+
if (
|
|
71
|
+
el instanceof HTMLElement &&
|
|
72
|
+
! el.hasAttribute( deprioritizedAttribute )
|
|
73
|
+
) {
|
|
74
|
+
return el;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return true;
|
|
79
|
+
};
|
|
80
|
+
}, [ initialFocus, deprioritizedAttribute ] );
|
|
81
|
+
|
|
82
|
+
return { resolvedInitialFocus, popupRef };
|
|
83
|
+
}
|