@primer/components 31.2.0-rc.c7f73427 → 31.2.1-rc.0e01900c
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/.github/workflows/ci.yml +5 -2
- package/.github/workflows/release.yml +1 -0
- package/.github/workflows/release_canary.yml +1 -0
- package/CHANGELOG.md +18 -0
- package/dist/browser.esm.js +656 -645
- package/dist/browser.esm.js.map +1 -1
- package/dist/browser.umd.js +211 -200
- package/dist/browser.umd.js.map +1 -1
- package/docs/content/ActionList2.mdx +358 -0
- package/docs/content/StateLabel.md +5 -4
- package/docs/content/getting-started.md +1 -1
- package/docs/src/@primer/gatsby-theme-doctocat/live-code-scope.js +17 -0
- package/lib/ActionList/Divider.jsx +29 -0
- package/lib/ActionList/Group.jsx +23 -0
- package/lib/ActionList/Header.jsx +66 -0
- package/lib/ActionList/Item.js +28 -19
- package/lib/ActionList/Item.jsx +311 -0
- package/lib/ActionList/List.jsx +138 -0
- package/lib/ActionList/index.js +12 -23
- package/lib/ActionList2/Description.d.ts +12 -0
- package/lib/ActionList2/Description.js +57 -0
- package/lib/ActionList2/Description.jsx +29 -0
- package/lib/ActionList2/Divider.d.ts +5 -0
- package/lib/ActionList2/Divider.js +35 -0
- package/lib/ActionList2/Divider.jsx +22 -0
- package/lib/ActionList2/Group.d.ts +11 -0
- package/lib/ActionList2/Group.js +57 -0
- package/lib/ActionList2/Group.jsx +25 -0
- package/lib/ActionList2/Header.d.ts +26 -0
- package/lib/ActionList2/Header.js +55 -0
- package/lib/ActionList2/Header.jsx +36 -0
- package/lib/ActionList2/Item.d.ts +63 -0
- package/lib/ActionList2/Item.js +242 -0
- package/lib/ActionList2/Item.jsx +174 -0
- package/lib/ActionList2/LinkItem.d.ts +17 -0
- package/lib/ActionList2/LinkItem.js +57 -0
- package/lib/ActionList2/LinkItem.jsx +28 -0
- package/lib/ActionList2/List.d.ts +26 -0
- package/lib/ActionList2/List.js +59 -0
- package/lib/ActionList2/List.jsx +41 -0
- package/lib/ActionList2/Selection.d.ts +5 -0
- package/lib/ActionList2/Selection.js +84 -0
- package/lib/ActionList2/Selection.jsx +50 -0
- package/lib/ActionList2/Visuals.d.ts +9 -0
- package/lib/ActionList2/Visuals.js +90 -0
- package/lib/ActionList2/Visuals.jsx +48 -0
- package/lib/ActionList2/index.d.ts +36 -0
- package/lib/ActionList2/index.js +29 -0
- package/lib/ActionMenu.jsx +73 -0
- package/lib/AnchoredOverlay/AnchoredOverlay.jsx +100 -0
- package/lib/AnchoredOverlay/index.js +4 -12
- package/lib/Autocomplete/Autocomplete.d.ts +28 -28
- package/lib/Autocomplete/Autocomplete.jsx +100 -0
- package/lib/Autocomplete/AutocompleteContext.jsx +5 -0
- package/lib/Autocomplete/AutocompleteInput.d.ts +28 -28
- package/lib/Autocomplete/AutocompleteInput.jsx +113 -0
- package/lib/Autocomplete/AutocompleteMenu.jsx +190 -0
- package/lib/Autocomplete/AutocompleteOverlay.jsx +55 -0
- package/lib/Autocomplete/index.js +7 -14
- package/lib/Avatar.jsx +34 -0
- package/lib/AvatarPair.jsx +29 -0
- package/lib/AvatarStack.jsx +151 -0
- package/lib/BaseStyles.jsx +65 -0
- package/lib/BorderBox.jsx +18 -0
- package/lib/Box.jsx +10 -0
- package/lib/BranchName.jsx +20 -0
- package/lib/Breadcrumbs.jsx +74 -0
- package/lib/Button/Button.d.ts +25 -25
- package/lib/Button/Button.jsx +60 -0
- package/lib/Button/ButtonBase.jsx +36 -0
- package/lib/Button/ButtonClose.d.ts +45 -45
- package/lib/Button/ButtonClose.jsx +55 -0
- package/lib/Button/ButtonDanger.d.ts +25 -25
- package/lib/Button/ButtonDanger.jsx +63 -0
- package/lib/Button/ButtonGroup.jsx +55 -0
- package/lib/Button/ButtonInvisible.d.ts +25 -25
- package/lib/Button/ButtonInvisible.jsx +52 -0
- package/lib/Button/ButtonOutline.d.ts +25 -25
- package/lib/Button/ButtonOutline.jsx +63 -0
- package/lib/Button/ButtonPrimary.d.ts +25 -25
- package/lib/Button/ButtonPrimary.jsx +62 -0
- package/lib/Button/ButtonStyles.jsx +37 -0
- package/lib/Button/ButtonTableList.jsx +49 -0
- package/lib/Button/index.js +21 -70
- package/lib/Caret.jsx +93 -0
- package/lib/CircleBadge.jsx +43 -0
- package/lib/CircleOcticon.d.ts +42 -42
- package/lib/CircleOcticon.jsx +21 -0
- package/lib/CounterLabel.jsx +44 -0
- package/lib/Details.jsx +21 -0
- package/lib/Dialog/ConfirmationDialog.jsx +146 -0
- package/lib/Dialog/Dialog.js +1 -0
- package/lib/Dialog/Dialog.jsx +273 -0
- package/lib/Dialog.d.ts +45 -45
- package/lib/Dialog.jsx +131 -0
- package/lib/Dropdown.d.ts +176 -176
- package/lib/Dropdown.jsx +134 -0
- package/lib/DropdownMenu/DropdownButton.d.ts +46 -46
- package/lib/DropdownMenu/DropdownButton.jsx +14 -0
- package/lib/DropdownMenu/DropdownMenu.jsx +70 -0
- package/lib/DropdownMenu/index.js +6 -20
- package/lib/DropdownStyles.js +18 -26
- package/lib/FilterList.d.ts +42 -42
- package/lib/FilterList.jsx +63 -0
- package/lib/FilteredActionList/FilteredActionList.jsx +100 -0
- package/lib/FilteredActionList/index.js +4 -12
- package/lib/FilteredSearch.jsx +29 -0
- package/lib/Flash.jsx +70 -0
- package/lib/Flex.jsx +15 -0
- package/lib/FormGroup.jsx +25 -0
- package/lib/Grid.jsx +15 -0
- package/lib/Header.jsx +90 -0
- package/lib/Heading.jsx +21 -0
- package/lib/Label.jsx +84 -0
- package/lib/LabelGroup.jsx +19 -0
- package/lib/Link.jsx +38 -0
- package/lib/NewButton/button-counter.d.ts +6 -0
- package/lib/NewButton/button-counter.js +31 -0
- package/lib/NewButton/button-counter.jsx +14 -0
- package/lib/NewButton/button.d.ts +13 -0
- package/lib/NewButton/button.js +316 -0
- package/lib/NewButton/button.jsx +278 -0
- package/lib/NewButton/index.d.ts +14 -0
- package/lib/NewButton/index.js +8 -0
- package/lib/NewButton/types.d.ts +32 -0
- package/lib/NewButton/types.js +2 -0
- package/lib/Overlay.jsx +156 -0
- package/lib/Pagehead.jsx +18 -0
- package/lib/Pagination/Pagination.jsx +163 -0
- package/lib/Pagination/index.js +6 -12
- package/lib/Pagination/model.jsx +174 -0
- package/lib/PointerBox.jsx +25 -0
- package/lib/Popover.jsx +210 -0
- package/lib/Portal/Portal.jsx +79 -0
- package/lib/Portal/index.js +5 -16
- package/lib/Position.d.ts +4 -4
- package/lib/Position.jsx +46 -0
- package/lib/ProgressBar.jsx +39 -0
- package/lib/SelectMenu/SelectMenu.d.ts +246 -246
- package/lib/SelectMenu/SelectMenu.jsx +114 -0
- package/lib/SelectMenu/SelectMenuContext.jsx +5 -0
- package/lib/SelectMenu/SelectMenuDivider.jsx +43 -0
- package/lib/SelectMenu/SelectMenuFilter.jsx +59 -0
- package/lib/SelectMenu/SelectMenuFooter.jsx +46 -0
- package/lib/SelectMenu/SelectMenuHeader.jsx +44 -0
- package/lib/SelectMenu/SelectMenuItem.d.ts +1 -1
- package/lib/SelectMenu/SelectMenuItem.jsx +143 -0
- package/lib/SelectMenu/SelectMenuList.jsx +60 -0
- package/lib/SelectMenu/SelectMenuLoadingAnimation.jsx +21 -0
- package/lib/SelectMenu/SelectMenuModal.d.ts +1 -1
- package/lib/SelectMenu/SelectMenuModal.jsx +119 -0
- package/lib/SelectMenu/SelectMenuTab.jsx +93 -0
- package/lib/SelectMenu/SelectMenuTabPanel.jsx +43 -0
- package/lib/SelectMenu/SelectMenuTabs.jsx +58 -0
- package/lib/SelectMenu/hooks/useKeyboardNav.js +80 -96
- package/lib/SelectMenu/index.js +7 -14
- package/lib/SelectPanel/SelectPanel.jsx +105 -0
- package/lib/SelectPanel/index.js +4 -12
- package/lib/SideNav.jsx +177 -0
- package/lib/Spinner.jsx +35 -0
- package/lib/StateLabel.d.ts +1 -1
- package/lib/StateLabel.js +6 -1
- package/lib/StateLabel.jsx +94 -0
- package/lib/StyledOcticon.jsx +20 -0
- package/lib/SubNav.jsx +104 -0
- package/lib/TabNav.jsx +60 -0
- package/lib/Text.jsx +14 -0
- package/lib/TextInput.jsx +23 -0
- package/lib/TextInputWithTokens.d.ts +28 -28
- package/lib/TextInputWithTokens.jsx +218 -0
- package/lib/ThemeProvider.jsx +130 -0
- package/lib/Timeline.d.ts +43 -43
- package/lib/Timeline.jsx +124 -0
- package/lib/Token/AvatarToken.d.ts +1 -1
- package/lib/Token/AvatarToken.jsx +54 -0
- package/lib/Token/IssueLabelToken.d.ts +1 -1
- package/lib/Token/IssueLabelToken.jsx +125 -0
- package/lib/Token/Token.d.ts +1 -1
- package/lib/Token/Token.jsx +103 -0
- package/lib/Token/TokenBase.jsx +88 -0
- package/lib/Token/_RemoveTokenButton.jsx +108 -0
- package/lib/Token/_TokenTextContainer.jsx +49 -0
- package/lib/Token/index.js +11 -30
- package/lib/Tooltip.jsx +246 -0
- package/lib/Truncate.jsx +27 -0
- package/lib/UnderlineNav.jsx +90 -0
- package/lib/_TextInputWrapper.jsx +120 -0
- package/lib/_UnstyledTextInput.jsx +22 -0
- package/lib/__tests__/ActionList.test.jsx +49 -0
- package/lib/__tests__/ActionList.types.test.jsx +45 -0
- package/lib/__tests__/ActionList2.test.d.ts +1 -0
- package/lib/__tests__/ActionList2.test.js +53 -0
- package/lib/__tests__/ActionList2.test.jsx +46 -0
- package/lib/__tests__/ActionMenu.test.jsx +124 -0
- package/lib/__tests__/AnchoredOverlay.test.jsx +121 -0
- package/lib/__tests__/Autocomplete.test.jsx +299 -0
- package/lib/__tests__/Avatar.test.jsx +42 -0
- package/lib/__tests__/AvatarStack.test.jsx +43 -0
- package/lib/__tests__/BorderBox.test.jsx +36 -0
- package/lib/__tests__/Box.test.jsx +41 -0
- package/lib/__tests__/BranchName.test.jsx +27 -0
- package/lib/__tests__/Breadcrumbs.test.jsx +28 -0
- package/lib/__tests__/BreadcrumbsItem.test.jsx +31 -0
- package/lib/__tests__/Button.test.jsx +100 -0
- package/lib/__tests__/Caret.test.jsx +37 -0
- package/lib/__tests__/CircleBadge.test.jsx +55 -0
- package/lib/__tests__/CircleOcticon.test.jsx +45 -0
- package/lib/__tests__/ConfirmationDialog.test.jsx +119 -0
- package/lib/__tests__/CounterLabel.test.jsx +36 -0
- package/lib/__tests__/Details.test.jsx +85 -0
- package/lib/__tests__/Dialog.test.jsx +139 -0
- package/lib/__tests__/Dropdown.test.jsx +49 -0
- package/lib/__tests__/DropdownMenu.test.jsx +119 -0
- package/lib/__tests__/FilterList.test.jsx +27 -0
- package/lib/__tests__/FilterListItem.test.jsx +31 -0
- package/lib/__tests__/FilteredSearch.test.jsx +27 -0
- package/lib/__tests__/Flash.test.jsx +36 -0
- package/lib/__tests__/Flex.test.jsx +51 -0
- package/lib/__tests__/FormGroup.test.jsx +36 -0
- package/lib/__tests__/Grid.test.jsx +69 -0
- package/lib/__tests__/Header.test.jsx +45 -0
- package/lib/__tests__/Heading.test.jsx +71 -0
- package/lib/__tests__/KeyPaths.types.test.js +5 -8
- package/lib/__tests__/Label.test.jsx +33 -0
- package/lib/__tests__/LabelGroup.test.jsx +29 -0
- package/lib/__tests__/Link.test.jsx +43 -0
- package/lib/__tests__/Merge.types.test.js +13 -19
- package/lib/__tests__/NewButton.test.d.ts +1 -0
- package/lib/__tests__/NewButton.test.js +95 -0
- package/lib/__tests__/NewButton.test.jsx +61 -0
- package/lib/__tests__/Overlay.test.jsx +105 -0
- package/lib/__tests__/Pagehead.test.jsx +25 -0
- package/lib/__tests__/Pagination/Pagination.test.jsx +32 -0
- package/lib/__tests__/Pagination/PaginationModel.test.jsx +118 -0
- package/lib/__tests__/PointerBox.test.jsx +33 -0
- package/lib/__tests__/Popover.test.jsx +58 -0
- package/lib/__tests__/Portal.test.jsx +102 -0
- package/lib/__tests__/Position.test.jsx +96 -0
- package/lib/__tests__/ProgressBar.test.jsx +38 -0
- package/lib/__tests__/SelectMenu.test.jsx +120 -0
- package/lib/__tests__/SelectPanel.test.jsx +48 -0
- package/lib/__tests__/SideNav.test.jsx +55 -0
- package/lib/__tests__/Spinner.test.jsx +41 -0
- package/lib/__tests__/StateLabel.test.jsx +46 -0
- package/lib/__tests__/StyledOcticon.test.jsx +28 -0
- package/lib/__tests__/SubNav.test.jsx +47 -0
- package/lib/__tests__/SubNavLink.test.jsx +31 -0
- package/lib/__tests__/TabNav.test.jsx +32 -0
- package/lib/__tests__/Text.test.jsx +71 -0
- package/lib/__tests__/TextInput.test.jsx +45 -0
- package/lib/__tests__/TextInputWithTokens.test.jsx +302 -0
- package/lib/__tests__/ThemeProvider.test.jsx +314 -0
- package/lib/__tests__/Timeline.test.jsx +51 -0
- package/lib/__tests__/Token.test.jsx +93 -0
- package/lib/__tests__/Tooltip.test.jsx +46 -0
- package/lib/__tests__/Truncate.test.jsx +41 -0
- package/lib/__tests__/UnderlineNav.test.jsx +53 -0
- package/lib/__tests__/UnderlineNavLink.test.jsx +31 -0
- package/lib/__tests__/behaviors/anchoredPosition.test.js +229 -376
- package/lib/__tests__/behaviors/focusTrap.test.jsx +184 -0
- package/lib/__tests__/behaviors/focusZone.test.jsx +406 -0
- package/lib/__tests__/behaviors/iterateFocusableElements.test.jsx +58 -0
- package/lib/__tests__/behaviors/scrollIntoViewingArea.test.js +145 -216
- package/lib/__tests__/filterObject.test.js +48 -27
- package/lib/__tests__/hooks/useAnchoredPosition.test.jsx +29 -0
- package/lib/__tests__/hooks/useOnEscapePress.test.jsx +19 -0
- package/lib/__tests__/hooks/useOnOutsideClick.test.jsx +63 -0
- package/lib/__tests__/hooks/useOpenAndCloseFocus.test.jsx +61 -0
- package/lib/__tests__/hooks/useProvidedStateOrCreate.test.jsx +56 -0
- package/lib/__tests__/theme.test.js +33 -34
- package/lib/__tests__/themeGet.test.js +12 -23
- package/lib/__tests__/useSafeTimeout.test.jsx +36 -0
- package/lib/__tests__/utils/createSlots.test.d.ts +1 -0
- package/lib/__tests__/utils/createSlots.test.js +75 -0
- package/lib/__tests__/utils/createSlots.test.jsx +57 -0
- package/lib/behaviors/anchoredPosition.js +205 -234
- package/lib/behaviors/focusTrap.js +121 -157
- package/lib/behaviors/focusZone.js +434 -509
- package/lib/behaviors/scrollIntoViewingArea.js +18 -35
- package/lib/constants.js +39 -43
- package/lib/drafts.d.ts +8 -0
- package/lib/drafts.js +21 -0
- package/lib/hooks/index.js +16 -60
- package/lib/hooks/useAnchoredPosition.js +32 -40
- package/lib/hooks/useCombinedRefs.js +32 -36
- package/lib/hooks/useDetails.jsx +39 -0
- package/lib/hooks/useDialog.js +72 -96
- package/lib/hooks/useFocusTrap.js +43 -60
- package/lib/hooks/useFocusZone.js +54 -50
- package/lib/hooks/useOnEscapePress.js +25 -36
- package/lib/hooks/useOnOutsideClick.jsx +61 -0
- package/lib/hooks/useOpenAndCloseFocus.js +22 -34
- package/lib/hooks/useOverlay.jsx +15 -0
- package/lib/hooks/useProvidedRefOrCreate.js +10 -14
- package/lib/hooks/useProvidedStateOrCreate.js +13 -16
- package/lib/hooks/useRenderForcingRef.js +13 -17
- package/lib/hooks/useResizeObserver.js +15 -18
- package/lib/hooks/useSafeTimeout.js +22 -30
- package/lib/hooks/useScrollFlash.js +16 -23
- package/lib/index.d.ts +2 -0
- package/lib/index.js +163 -636
- package/lib/polyfills/eventListenerSignal.js +37 -45
- package/lib/stories/ActionList2.stories.js +908 -0
- package/lib/stories/NewButton.stories.js +230 -0
- package/lib/sx.d.ts +2 -0
- package/lib/sx.js +10 -14
- package/lib/theme-preval.js +65 -2945
- package/lib/theme.js +3 -12
- package/lib/utils/create-slots.d.ts +17 -0
- package/lib/utils/create-slots.js +105 -0
- package/lib/utils/create-slots.jsx +65 -0
- package/lib/utils/deprecate.jsx +59 -0
- package/lib/utils/isNumeric.jsx +7 -0
- package/lib/utils/iterateFocusableElements.js +63 -85
- package/lib/utils/ssr.jsx +6 -0
- package/lib/utils/test-deprecations.jsx +20 -0
- package/lib/utils/test-helpers.jsx +8 -0
- package/lib/utils/test-matchers.jsx +100 -0
- package/lib/utils/testing.d.ts +14 -1
- package/lib/utils/testing.jsx +206 -0
- package/lib/utils/theme.js +33 -47
- package/lib/utils/types/AriaRole.js +2 -1
- package/lib/utils/types/ComponentProps.js +2 -1
- package/lib/utils/types/Flatten.js +2 -1
- package/lib/utils/types/KeyPaths.js +2 -1
- package/lib/utils/types/MandateProps.js +16 -1
- package/lib/utils/types/Merge.js +2 -1
- package/lib/utils/types/index.js +16 -69
- package/lib/utils/uniqueId.js +5 -8
- package/lib/utils/use-force-update.d.ts +1 -0
- package/lib/utils/use-force-update.js +13 -0
- package/lib/utils/useIsomorphicLayoutEffect.js +8 -11
- package/lib/utils/userAgent.js +8 -12
- package/lib-esm/ActionList/Item.js +28 -19
- package/lib-esm/ActionList2/Description.d.ts +12 -0
- package/lib-esm/ActionList2/Description.js +41 -0
- package/lib-esm/ActionList2/Divider.d.ts +5 -0
- package/lib-esm/ActionList2/Divider.js +23 -0
- package/lib-esm/ActionList2/Group.d.ts +11 -0
- package/lib-esm/ActionList2/Group.js +40 -0
- package/lib-esm/ActionList2/Header.d.ts +26 -0
- package/lib-esm/ActionList2/Header.js +44 -0
- package/lib-esm/ActionList2/Item.d.ts +63 -0
- package/lib-esm/ActionList2/Item.js +208 -0
- package/lib-esm/ActionList2/LinkItem.d.ts +17 -0
- package/lib-esm/ActionList2/LinkItem.js +43 -0
- package/lib-esm/ActionList2/List.d.ts +26 -0
- package/lib-esm/ActionList2/List.js +37 -0
- package/lib-esm/ActionList2/Selection.d.ts +5 -0
- package/lib-esm/ActionList2/Selection.js +66 -0
- package/lib-esm/ActionList2/Visuals.d.ts +9 -0
- package/lib-esm/ActionList2/Visuals.js +68 -0
- package/lib-esm/ActionList2/index.d.ts +36 -0
- package/lib-esm/ActionList2/index.js +33 -0
- package/lib-esm/Autocomplete/Autocomplete.d.ts +28 -28
- package/lib-esm/Autocomplete/AutocompleteInput.d.ts +28 -28
- package/lib-esm/Button/Button.d.ts +25 -25
- package/lib-esm/Button/ButtonClose.d.ts +45 -45
- package/lib-esm/Button/ButtonDanger.d.ts +25 -25
- package/lib-esm/Button/ButtonInvisible.d.ts +25 -25
- package/lib-esm/Button/ButtonOutline.d.ts +25 -25
- package/lib-esm/Button/ButtonPrimary.d.ts +25 -25
- package/lib-esm/CircleOcticon.d.ts +42 -42
- package/lib-esm/Dialog/Dialog.js +1 -0
- package/lib-esm/Dialog.d.ts +45 -45
- package/lib-esm/Dropdown.d.ts +176 -176
- package/lib-esm/DropdownMenu/DropdownButton.d.ts +46 -46
- package/lib-esm/FilterList.d.ts +42 -42
- package/lib-esm/NewButton/button-counter.d.ts +6 -0
- package/lib-esm/NewButton/button-counter.js +18 -0
- package/lib-esm/NewButton/button.d.ts +13 -0
- package/lib-esm/NewButton/button.js +298 -0
- package/lib-esm/NewButton/index.d.ts +14 -0
- package/lib-esm/NewButton/index.js +5 -0
- package/lib-esm/NewButton/types.d.ts +32 -0
- package/lib-esm/NewButton/types.js +1 -0
- package/lib-esm/Position.d.ts +4 -4
- package/lib-esm/SelectMenu/SelectMenu.d.ts +246 -246
- package/lib-esm/SelectMenu/SelectMenuItem.d.ts +1 -1
- package/lib-esm/SelectMenu/SelectMenuModal.d.ts +1 -1
- package/lib-esm/StateLabel.d.ts +1 -1
- package/lib-esm/StateLabel.js +7 -2
- package/lib-esm/TextInputWithTokens.d.ts +28 -28
- package/lib-esm/Timeline.d.ts +43 -43
- package/lib-esm/Token/AvatarToken.d.ts +1 -1
- package/lib-esm/Token/IssueLabelToken.d.ts +1 -1
- package/lib-esm/Token/Token.d.ts +1 -1
- package/lib-esm/__tests__/ActionList2.test.d.ts +1 -0
- package/lib-esm/__tests__/ActionList2.test.js +41 -0
- package/lib-esm/__tests__/NewButton.test.d.ts +1 -0
- package/lib-esm/__tests__/NewButton.test.js +84 -0
- package/lib-esm/__tests__/utils/createSlots.test.d.ts +1 -0
- package/lib-esm/__tests__/utils/createSlots.test.js +67 -0
- package/lib-esm/drafts.d.ts +8 -0
- package/lib-esm/drafts.js +9 -0
- package/lib-esm/index.d.ts +2 -0
- package/lib-esm/index.js +1 -0
- package/lib-esm/stories/ActionList2.stories.js +796 -0
- package/lib-esm/stories/NewButton.stories.js +178 -0
- package/lib-esm/sx.d.ts +2 -0
- package/lib-esm/sx.js +3 -1
- package/lib-esm/theme-preval.js +81 -2
- package/lib-esm/utils/create-slots.d.ts +17 -0
- package/lib-esm/utils/create-slots.js +84 -0
- package/lib-esm/utils/testing.d.ts +14 -1
- package/lib-esm/utils/use-force-update.d.ts +1 -0
- package/lib-esm/utils/use-force-update.js +6 -0
- package/package-lock.json +153 -14
- package/package.json +7 -4
- package/script/build +1 -1
- package/src/ActionList/Item.tsx +32 -19
- package/src/ActionList2/Description.tsx +52 -0
- package/src/ActionList2/Divider.tsx +24 -0
- package/src/ActionList2/Group.tsx +34 -0
- package/src/ActionList2/Header.tsx +58 -0
- package/src/ActionList2/Item.tsx +246 -0
- package/src/ActionList2/LinkItem.tsx +49 -0
- package/src/ActionList2/List.tsx +55 -0
- package/src/ActionList2/Selection.tsx +60 -0
- package/src/ActionList2/Visuals.tsx +76 -0
- package/src/ActionList2/index.ts +39 -0
- package/src/Dialog/Dialog.tsx +1 -0
- package/src/NewButton/button-counter.tsx +15 -0
- package/src/NewButton/button.tsx +279 -0
- package/src/NewButton/index.ts +10 -0
- package/src/NewButton/types.ts +36 -0
- package/src/StateLabel.tsx +14 -2
- package/src/__tests__/ActionList2.test.tsx +47 -0
- package/src/__tests__/NewButton.test.tsx +70 -0
- package/src/__tests__/__snapshots__/ActionList2.test.tsx.snap +14 -0
- package/src/__tests__/__snapshots__/Autocomplete.test.tsx.snap +722 -255
- package/src/__tests__/__snapshots__/CircleBadge.test.tsx.snap +1 -0
- package/src/__tests__/__snapshots__/CircleOcticon.test.tsx.snap +1 -0
- package/src/__tests__/__snapshots__/Dialog.test.tsx.snap +1 -0
- package/src/__tests__/__snapshots__/DropdownMenu.test.tsx.snap +1 -0
- package/src/__tests__/__snapshots__/NewButton.test.tsx.snap +300 -0
- package/src/__tests__/__snapshots__/SelectMenu.test.tsx.snap +4 -0
- package/src/__tests__/__snapshots__/SelectPanel.test.tsx.snap +1 -0
- package/src/__tests__/__snapshots__/StateLabel.test.tsx.snap +13 -6
- package/src/__tests__/__snapshots__/StyledOcticon.test.tsx.snap +1 -0
- package/src/__tests__/__snapshots__/TextInputWithTokens.test.tsx.snap +66 -0
- package/src/__tests__/__snapshots__/Token.test.tsx.snap +17 -0
- package/src/__tests__/utils/__snapshots__/createSlots.test.tsx.snap +55 -0
- package/src/__tests__/utils/createSlots.test.tsx +74 -0
- package/src/drafts.ts +10 -0
- package/src/index.ts +2 -0
- package/src/stories/ActionList2.stories.tsx +1291 -0
- package/src/stories/NewButton.stories.tsx +201 -0
- package/src/sx.ts +3 -0
- package/src/theme-preval.js +1 -0
- package/src/utils/create-slots.tsx +96 -0
- package/src/utils/use-force-update.ts +7 -0
- package/stats.html +1 -1
- package/tsconfig.base.json +20 -0
- package/tsconfig.build.json +2 -2
- package/tsconfig.json +4 -17
@@ -0,0 +1,184 @@
|
|
1
|
+
"use strict";
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
4
|
+
};
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
6
|
+
const react_1 = __importDefault(require("react"));
|
7
|
+
const react_2 = require("@testing-library/react");
|
8
|
+
const focusTrap_1 = require("../../behaviors/focusTrap");
|
9
|
+
// Since we use strict `isTabbable` checks within focus trap, we need to mock these
|
10
|
+
// properties that Jest does not populate.
|
11
|
+
beforeAll(() => {
|
12
|
+
try {
|
13
|
+
Object.defineProperties(HTMLElement.prototype, {
|
14
|
+
offsetHeight: {
|
15
|
+
get: () => 42
|
16
|
+
},
|
17
|
+
offsetWidth: {
|
18
|
+
get: () => 42
|
19
|
+
},
|
20
|
+
getClientRects: {
|
21
|
+
get: () => () => [42]
|
22
|
+
}
|
23
|
+
});
|
24
|
+
}
|
25
|
+
catch {
|
26
|
+
// ignore
|
27
|
+
}
|
28
|
+
});
|
29
|
+
it('Should initially focus the container when activated', () => {
|
30
|
+
const { container } = react_2.render(<div>
|
31
|
+
<button tabIndex={0}>Bad Apple</button>
|
32
|
+
<div id="trapContainer">
|
33
|
+
<button tabIndex={0}>Apple</button>
|
34
|
+
<button tabIndex={0}>Banana</button>
|
35
|
+
<button tabIndex={0}>Cantaloupe</button>
|
36
|
+
</div>
|
37
|
+
</div>);
|
38
|
+
const trapContainer = container.querySelector('#trapContainer');
|
39
|
+
const controller = focusTrap_1.focusTrap(trapContainer);
|
40
|
+
expect(document.activeElement).toEqual(trapContainer);
|
41
|
+
controller.abort();
|
42
|
+
});
|
43
|
+
it('Should initially focus the initialFocus element when specified', () => {
|
44
|
+
const { container } = react_2.render(<div id="trapContainer">
|
45
|
+
<button tabIndex={0}>Apple</button>
|
46
|
+
<button tabIndex={0}>Banana</button>
|
47
|
+
<button tabIndex={0}>Cantaloupe</button>
|
48
|
+
</div>);
|
49
|
+
const trapContainer = container.querySelector('#trapContainer');
|
50
|
+
const secondButton = trapContainer.querySelectorAll('button')[1];
|
51
|
+
const controller = focusTrap_1.focusTrap(trapContainer, secondButton);
|
52
|
+
expect(document.activeElement).toEqual(secondButton);
|
53
|
+
controller.abort();
|
54
|
+
});
|
55
|
+
it('Should prevent focus from exiting the trap, returns focus to previously-focused element', async () => {
|
56
|
+
const { container } = react_2.render(<div>
|
57
|
+
<div id="trapContainer">
|
58
|
+
<button tabIndex={0}>Apple</button>
|
59
|
+
<button tabIndex={0}>Banana</button>
|
60
|
+
<button tabIndex={0}>Cantaloupe</button>
|
61
|
+
</div>
|
62
|
+
<button id="durian" tabIndex={0}>
|
63
|
+
Durian
|
64
|
+
</button>
|
65
|
+
</div>);
|
66
|
+
const trapContainer = container.querySelector('#trapContainer');
|
67
|
+
const secondButton = trapContainer.querySelectorAll('button')[1];
|
68
|
+
const durianButton = container.querySelector('#durian');
|
69
|
+
const controller = focusTrap_1.focusTrap(trapContainer);
|
70
|
+
focus(durianButton);
|
71
|
+
expect(document.activeElement).toEqual(trapContainer);
|
72
|
+
focus(secondButton);
|
73
|
+
expect(document.activeElement).toEqual(secondButton);
|
74
|
+
focus(durianButton);
|
75
|
+
expect(document.activeElement).toEqual(secondButton);
|
76
|
+
controller.abort();
|
77
|
+
});
|
78
|
+
it('Should prevent focus from exiting the trap if there are no focusable children', async () => {
|
79
|
+
const { container } = react_2.render(<div>
|
80
|
+
<div id="trapContainer"></div>
|
81
|
+
<button id="durian" tabIndex={0}>
|
82
|
+
Durian
|
83
|
+
</button>
|
84
|
+
</div>);
|
85
|
+
const trapContainer = container.querySelector('#trapContainer');
|
86
|
+
const durianButton = container.querySelector('#durian');
|
87
|
+
const controller = focusTrap_1.focusTrap(trapContainer);
|
88
|
+
focus(durianButton);
|
89
|
+
expect(document.activeElement === durianButton).toEqual(false);
|
90
|
+
controller.abort();
|
91
|
+
});
|
92
|
+
it('Should cycle focus from last element to first element and vice-versa', async () => {
|
93
|
+
const { container } = react_2.render(<div>
|
94
|
+
<div id="trapContainer">
|
95
|
+
<button tabIndex={0}>Apple</button>
|
96
|
+
<button tabIndex={0}>Banana</button>
|
97
|
+
<button tabIndex={0}>Cantaloupe</button>
|
98
|
+
</div>
|
99
|
+
<button id="durian" tabIndex={0}>
|
100
|
+
Durian
|
101
|
+
</button>
|
102
|
+
</div>);
|
103
|
+
const trapContainer = container.querySelector('#trapContainer');
|
104
|
+
const firstButton = trapContainer.querySelector('button');
|
105
|
+
const lastButton = trapContainer.querySelectorAll('button')[2];
|
106
|
+
const controller = focusTrap_1.focusTrap(trapContainer);
|
107
|
+
lastButton.focus();
|
108
|
+
react_2.fireEvent(lastButton, new KeyboardEvent('keydown', { bubbles: true, key: 'Tab' }));
|
109
|
+
expect(document.activeElement).toEqual(firstButton);
|
110
|
+
react_2.fireEvent(firstButton, new KeyboardEvent('keydown', { bubbles: true, key: 'Tab', shiftKey: true }));
|
111
|
+
expect(document.activeElement).toEqual(lastButton);
|
112
|
+
controller.abort();
|
113
|
+
});
|
114
|
+
it('Should should release the trap when the signal is aborted', async () => {
|
115
|
+
const { container } = react_2.render(<div>
|
116
|
+
<div id="trapContainer">
|
117
|
+
<button tabIndex={0}>Apple</button>
|
118
|
+
<button tabIndex={0}>Banana</button>
|
119
|
+
<button tabIndex={0}>Cantaloupe</button>
|
120
|
+
</div>
|
121
|
+
<button id="durian" tabIndex={0}>
|
122
|
+
Durian
|
123
|
+
</button>
|
124
|
+
</div>);
|
125
|
+
const trapContainer = container.querySelector('#trapContainer');
|
126
|
+
const durianButton = container.querySelector('#durian');
|
127
|
+
const controller = focusTrap_1.focusTrap(trapContainer);
|
128
|
+
focus(durianButton);
|
129
|
+
expect(document.activeElement).toEqual(trapContainer);
|
130
|
+
controller.abort();
|
131
|
+
focus(durianButton);
|
132
|
+
expect(document.activeElement).toEqual(durianButton);
|
133
|
+
});
|
134
|
+
it('Should should release the trap when the container is removed from the DOM', async () => {
|
135
|
+
const { container } = react_2.render(<div>
|
136
|
+
<div id="trapContainer">
|
137
|
+
<button tabIndex={0}>Apple</button>
|
138
|
+
</div>
|
139
|
+
<button id="durian" tabIndex={0}>
|
140
|
+
Durian
|
141
|
+
</button>
|
142
|
+
</div>);
|
143
|
+
const trapContainer = container.querySelector('#trapContainer');
|
144
|
+
const durianButton = container.querySelector('#durian');
|
145
|
+
const firstButton = trapContainer.querySelector('button');
|
146
|
+
focusTrap_1.focusTrap(trapContainer);
|
147
|
+
focus(durianButton);
|
148
|
+
expect(document.activeElement).toEqual(trapContainer);
|
149
|
+
// empty trap and remove it from the DOM
|
150
|
+
trapContainer.removeChild(firstButton);
|
151
|
+
trapContainer.parentElement?.removeChild(trapContainer);
|
152
|
+
focus(durianButton);
|
153
|
+
expect(document.activeElement).toEqual(durianButton);
|
154
|
+
});
|
155
|
+
it('Should handle dynamic content', async () => {
|
156
|
+
const { container } = react_2.render(<div>
|
157
|
+
<div id="trapContainer">
|
158
|
+
<button tabIndex={0}>Apple</button>
|
159
|
+
<button tabIndex={0}>Banana</button>
|
160
|
+
<button tabIndex={0}>Cantaloupe</button>
|
161
|
+
</div>
|
162
|
+
<button id="durian" tabIndex={0}>
|
163
|
+
Durian
|
164
|
+
</button>
|
165
|
+
</div>);
|
166
|
+
const trapContainer = container.querySelector('#trapContainer');
|
167
|
+
const [firstButton, secondButton, thirdButton] = trapContainer.querySelectorAll('button');
|
168
|
+
const controller = focusTrap_1.focusTrap(trapContainer);
|
169
|
+
secondButton.focus();
|
170
|
+
trapContainer.removeChild(thirdButton);
|
171
|
+
react_2.fireEvent(secondButton, new KeyboardEvent('keydown', { bubbles: true, key: 'Tab' }));
|
172
|
+
expect(document.activeElement).toEqual(firstButton);
|
173
|
+
react_2.fireEvent(firstButton, new KeyboardEvent('keydown', { bubbles: true, key: 'Tab', shiftKey: true }));
|
174
|
+
expect(document.activeElement).toEqual(secondButton);
|
175
|
+
controller.abort();
|
176
|
+
});
|
177
|
+
/**
|
178
|
+
* Helper to handle firing the focusin event, which jest/JSDOM does not do for us.
|
179
|
+
* @param element
|
180
|
+
*/
|
181
|
+
function focus(element) {
|
182
|
+
element.focus();
|
183
|
+
react_2.fireEvent(element, new FocusEvent('focusin', { bubbles: true }));
|
184
|
+
}
|
@@ -0,0 +1,406 @@
|
|
1
|
+
"use strict";
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
4
|
+
};
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
6
|
+
const react_1 = __importDefault(require("react"));
|
7
|
+
const react_2 = require("@testing-library/react");
|
8
|
+
const user_event_1 = __importDefault(require("@testing-library/user-event"));
|
9
|
+
const focusZone_1 = require("../../behaviors/focusZone");
|
10
|
+
async function nextTick() {
|
11
|
+
return new Promise(resolve => setTimeout(resolve, 0));
|
12
|
+
}
|
13
|
+
const moveDown = () => user_event_1.default.type(document.activeElement, '{arrowdown}');
|
14
|
+
const moveUp = () => user_event_1.default.type(document.activeElement, '{arrowup}');
|
15
|
+
// Since we use strict `isTabbable` checks within focus trap, we need to mock these
|
16
|
+
// properties that Jest does not populate.
|
17
|
+
beforeAll(() => {
|
18
|
+
try {
|
19
|
+
Object.defineProperties(HTMLElement.prototype, {
|
20
|
+
offsetHeight: {
|
21
|
+
get: () => 42
|
22
|
+
},
|
23
|
+
offsetWidth: {
|
24
|
+
get: () => 42
|
25
|
+
},
|
26
|
+
getClientRects: {
|
27
|
+
get: () => () => [42]
|
28
|
+
}
|
29
|
+
});
|
30
|
+
}
|
31
|
+
catch {
|
32
|
+
// ignore
|
33
|
+
}
|
34
|
+
});
|
35
|
+
it('Should allow arrow keys to move focus', () => {
|
36
|
+
const { container } = react_2.render(<div>
|
37
|
+
<button tabIndex={0}>Bad Apple</button>
|
38
|
+
<div id="focusZone">
|
39
|
+
<button tabIndex={0}>Apple</button>
|
40
|
+
<button tabIndex={0}>Banana</button>
|
41
|
+
<button tabIndex={0}>Cantaloupe</button>
|
42
|
+
</div>
|
43
|
+
</div>);
|
44
|
+
const focusZoneContainer = container.querySelector('#focusZone');
|
45
|
+
const [firstButton, secondButton] = focusZoneContainer.querySelectorAll('button');
|
46
|
+
const controller = focusZone_1.focusZone(focusZoneContainer);
|
47
|
+
firstButton.focus();
|
48
|
+
expect(document.activeElement).toEqual(firstButton);
|
49
|
+
user_event_1.default.type(firstButton, '{arrowdown}');
|
50
|
+
expect(document.activeElement).toEqual(secondButton);
|
51
|
+
controller.abort();
|
52
|
+
});
|
53
|
+
it('Should have one tab-stop inside the focus zone when enabled', () => {
|
54
|
+
const { container } = react_2.render(<div>
|
55
|
+
<button tabIndex={0}>Bad Apple</button>
|
56
|
+
<div id="focusZone">
|
57
|
+
<button tabIndex={0}>Apple</button>
|
58
|
+
<button tabIndex={0}>Banana</button>
|
59
|
+
<button tabIndex={0}>Cantaloupe</button>
|
60
|
+
</div>
|
61
|
+
<button tabIndex={0}>Next Apple</button>
|
62
|
+
</div>);
|
63
|
+
const focusZoneContainer = container.querySelector('#focusZone');
|
64
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
65
|
+
const [one, two, three, four, five] = container.querySelectorAll('button');
|
66
|
+
const controller = focusZone_1.focusZone(focusZoneContainer);
|
67
|
+
one.focus();
|
68
|
+
user_event_1.default.tab();
|
69
|
+
user_event_1.default.tab();
|
70
|
+
expect(document.activeElement).toEqual(five);
|
71
|
+
controller.abort();
|
72
|
+
one.focus();
|
73
|
+
user_event_1.default.tab();
|
74
|
+
user_event_1.default.tab();
|
75
|
+
expect(document.activeElement).toEqual(three);
|
76
|
+
controller.abort();
|
77
|
+
});
|
78
|
+
it('Should prevent moving focus outside the zone', () => {
|
79
|
+
const { container } = react_2.render(<div>
|
80
|
+
<button tabIndex={0}>Bad Apple</button>
|
81
|
+
<div id="focusZone">
|
82
|
+
<button tabIndex={0}>Apple</button>
|
83
|
+
<button tabIndex={0}>Banana</button>
|
84
|
+
<button tabIndex={0}>Cantaloupe</button>
|
85
|
+
</div>
|
86
|
+
</div>);
|
87
|
+
const focusZoneContainer = container.querySelector('#focusZone');
|
88
|
+
const [firstButton, secondButton, thirdButton] = focusZoneContainer.querySelectorAll('button');
|
89
|
+
const controller = focusZone_1.focusZone(focusZoneContainer);
|
90
|
+
firstButton.focus();
|
91
|
+
expect(document.activeElement).toEqual(firstButton);
|
92
|
+
user_event_1.default.type(firstButton, '{arrowup}');
|
93
|
+
expect(document.activeElement).toEqual(firstButton);
|
94
|
+
user_event_1.default.type(firstButton, '{arrowdown}');
|
95
|
+
expect(document.activeElement).toEqual(secondButton);
|
96
|
+
user_event_1.default.type(secondButton, '{arrowdown}');
|
97
|
+
expect(document.activeElement).toEqual(thirdButton);
|
98
|
+
user_event_1.default.type(thirdButton, '{arrowdown}');
|
99
|
+
expect(document.activeElement).toEqual(thirdButton);
|
100
|
+
controller.abort();
|
101
|
+
});
|
102
|
+
it('Should do focus wrapping correctly', () => {
|
103
|
+
const { container } = react_2.render(<div>
|
104
|
+
<button tabIndex={0}>Bad Apple</button>
|
105
|
+
<div id="focusZone">
|
106
|
+
<button tabIndex={0}>Apple</button>
|
107
|
+
<button tabIndex={0}>Banana</button>
|
108
|
+
<button tabIndex={0}>Cantaloupe</button>
|
109
|
+
</div>
|
110
|
+
</div>);
|
111
|
+
const focusZoneContainer = container.querySelector('#focusZone');
|
112
|
+
const [firstButton, secondButton, thirdButton] = focusZoneContainer.querySelectorAll('button');
|
113
|
+
const controller = focusZone_1.focusZone(focusZoneContainer, { focusOutBehavior: 'wrap' });
|
114
|
+
firstButton.focus();
|
115
|
+
expect(document.activeElement).toEqual(firstButton);
|
116
|
+
user_event_1.default.type(firstButton, '{arrowup}');
|
117
|
+
expect(document.activeElement).toEqual(thirdButton);
|
118
|
+
user_event_1.default.type(thirdButton, '{arrowup}');
|
119
|
+
expect(document.activeElement).toEqual(secondButton);
|
120
|
+
user_event_1.default.type(secondButton, '{arrowdown}');
|
121
|
+
expect(document.activeElement).toEqual(thirdButton);
|
122
|
+
user_event_1.default.type(thirdButton, '{arrowdown}');
|
123
|
+
expect(document.activeElement).toEqual(firstButton);
|
124
|
+
controller.abort();
|
125
|
+
});
|
126
|
+
it('Should call custom getNextFocusable callback', () => {
|
127
|
+
const { container } = react_2.render(<div>
|
128
|
+
<button tabIndex={0}>Bad Apple</button>
|
129
|
+
<div id="focusZone">
|
130
|
+
<button tabIndex={0}>Apple</button>
|
131
|
+
<button tabIndex={0}>Banana</button>
|
132
|
+
<button tabIndex={0}>Cantaloupe</button>
|
133
|
+
</div>
|
134
|
+
</div>);
|
135
|
+
const focusZoneContainer = container.querySelector('#focusZone');
|
136
|
+
const [firstButton, secondButton] = focusZoneContainer.querySelectorAll('button');
|
137
|
+
const getNextFocusableCallback = jest.fn();
|
138
|
+
const controller = focusZone_1.focusZone(focusZoneContainer, { getNextFocusable: getNextFocusableCallback });
|
139
|
+
firstButton.focus();
|
140
|
+
expect(document.activeElement).toEqual(firstButton);
|
141
|
+
user_event_1.default.type(firstButton, '{arrowdown}');
|
142
|
+
expect(getNextFocusableCallback).toHaveBeenCalledWith('next', firstButton, expect.anything());
|
143
|
+
user_event_1.default.type(secondButton, '{home}');
|
144
|
+
expect(getNextFocusableCallback).toHaveBeenCalledWith('start', secondButton, expect.anything());
|
145
|
+
controller.abort();
|
146
|
+
});
|
147
|
+
it('Should focus-in to the most recently-focused element', () => {
|
148
|
+
const { container } = react_2.render(<div>
|
149
|
+
<button tabIndex={0} id="outside">
|
150
|
+
Bad Apple
|
151
|
+
</button>
|
152
|
+
<div id="focusZone">
|
153
|
+
<button tabIndex={0}>Apple</button>
|
154
|
+
<button tabIndex={0}>Banana</button>
|
155
|
+
<button tabIndex={0}>Cantaloupe</button>
|
156
|
+
</div>
|
157
|
+
</div>);
|
158
|
+
const focusZoneContainer = container.querySelector('#focusZone');
|
159
|
+
const outsideButton = container.querySelector('#outside');
|
160
|
+
const [firstButton, secondButton] = focusZoneContainer.querySelectorAll('button');
|
161
|
+
const controller = focusZone_1.focusZone(focusZoneContainer);
|
162
|
+
firstButton.focus();
|
163
|
+
expect(document.activeElement).toEqual(firstButton);
|
164
|
+
user_event_1.default.type(firstButton, '{arrowdown}');
|
165
|
+
expect(document.activeElement).toEqual(secondButton);
|
166
|
+
outsideButton.focus();
|
167
|
+
user_event_1.default.tab();
|
168
|
+
expect(document.activeElement).toEqual(secondButton);
|
169
|
+
controller.abort();
|
170
|
+
});
|
171
|
+
it('Should focus-in to the first element when focusInStrategy is "first"', () => {
|
172
|
+
const { container } = react_2.render(<div>
|
173
|
+
<button tabIndex={0} id="outside">
|
174
|
+
Bad Apple
|
175
|
+
</button>
|
176
|
+
<div id="focusZone">
|
177
|
+
<button tabIndex={0}>Apple</button>
|
178
|
+
<button tabIndex={0}>Banana</button>
|
179
|
+
<button tabIndex={0}>Cantaloupe</button>
|
180
|
+
</div>
|
181
|
+
</div>);
|
182
|
+
const focusZoneContainer = container.querySelector('#focusZone');
|
183
|
+
const outsideButton = container.querySelector('#outside');
|
184
|
+
const [firstButton, secondButton] = focusZoneContainer.querySelectorAll('button');
|
185
|
+
const controller = focusZone_1.focusZone(focusZoneContainer, { focusInStrategy: 'first' });
|
186
|
+
firstButton.focus();
|
187
|
+
expect(document.activeElement).toEqual(firstButton);
|
188
|
+
user_event_1.default.type(firstButton, '{arrowdown}');
|
189
|
+
expect(document.activeElement).toEqual(secondButton);
|
190
|
+
outsideButton.focus();
|
191
|
+
user_event_1.default.tab();
|
192
|
+
expect(document.activeElement).toEqual(firstButton);
|
193
|
+
controller.abort();
|
194
|
+
});
|
195
|
+
it('Should focus-in to the closest element when focusInStrategy is "closest"', () => {
|
196
|
+
const { container } = react_2.render(<div>
|
197
|
+
<button tabIndex={0} id="outsideBefore">
|
198
|
+
Bad Apple
|
199
|
+
</button>
|
200
|
+
<div id="focusZone">
|
201
|
+
<button id="apple" tabIndex={0}>
|
202
|
+
Apple
|
203
|
+
</button>
|
204
|
+
<button id="banana" tabIndex={0}>
|
205
|
+
Banana
|
206
|
+
</button>
|
207
|
+
<button id="cantaloupe" tabIndex={0}>
|
208
|
+
Cantaloupe
|
209
|
+
</button>
|
210
|
+
</div>
|
211
|
+
<button tabIndex={0} id="outsideAfter">
|
212
|
+
Good Apple
|
213
|
+
</button>
|
214
|
+
</div>);
|
215
|
+
const focusZoneContainer = container.querySelector('#focusZone');
|
216
|
+
const outsideBefore = container.querySelector('#outsideBefore');
|
217
|
+
const outsideAfter = container.querySelector('#outsideAfter');
|
218
|
+
const [firstButton, secondButton, thirdButton] = focusZoneContainer.querySelectorAll('button');
|
219
|
+
const controller = focusZone_1.focusZone(focusZoneContainer, { focusInStrategy: 'closest' });
|
220
|
+
firstButton.focus();
|
221
|
+
expect(document.activeElement).toEqual(firstButton);
|
222
|
+
user_event_1.default.type(firstButton, '{arrowdown}');
|
223
|
+
expect(document.activeElement).toEqual(secondButton);
|
224
|
+
outsideBefore.focus();
|
225
|
+
user_event_1.default.tab();
|
226
|
+
expect(document.activeElement).toEqual(firstButton);
|
227
|
+
outsideAfter.focus();
|
228
|
+
user_event_1.default.tab({ shift: true });
|
229
|
+
expect(document.activeElement).toEqual(thirdButton);
|
230
|
+
controller.abort();
|
231
|
+
});
|
232
|
+
it('Should call the custom focusInStrategy callback', () => {
|
233
|
+
const { container } = react_2.render(<div>
|
234
|
+
<button tabIndex={0} id="outside">
|
235
|
+
Bad Apple
|
236
|
+
</button>
|
237
|
+
<div id="focusZone">
|
238
|
+
<button tabIndex={0}>Apple</button>
|
239
|
+
<button tabIndex={0}>Banana</button>
|
240
|
+
<button tabIndex={0}>Cantaloupe</button>
|
241
|
+
</div>
|
242
|
+
</div>);
|
243
|
+
const focusZoneContainer = container.querySelector('#focusZone');
|
244
|
+
const outsideButton = container.querySelector('#outside');
|
245
|
+
const [, secondButton] = focusZoneContainer.querySelectorAll('button');
|
246
|
+
const focusInCallback = jest.fn().mockReturnValue(secondButton);
|
247
|
+
const controller = focusZone_1.focusZone(focusZoneContainer, { focusInStrategy: focusInCallback });
|
248
|
+
outsideButton.focus();
|
249
|
+
user_event_1.default.tab();
|
250
|
+
expect(focusInCallback).toHaveBeenCalledWith(outsideButton);
|
251
|
+
expect(document.activeElement).toEqual(secondButton);
|
252
|
+
controller.abort();
|
253
|
+
});
|
254
|
+
it('Should respect inputs by not moving focus if key would have some other effect', () => {
|
255
|
+
const { container } = react_2.render(<div>
|
256
|
+
<button tabIndex={0} id="outside">
|
257
|
+
Bad Apple
|
258
|
+
</button>
|
259
|
+
<div id="focusZone">
|
260
|
+
<button tabIndex={0}>Apple</button>
|
261
|
+
<input type="text" defaultValue="Banana" tabIndex={0}/>
|
262
|
+
<button tabIndex={0}>Cantaloupe</button>
|
263
|
+
</div>
|
264
|
+
</div>);
|
265
|
+
const focusZoneContainer = container.querySelector('#focusZone');
|
266
|
+
const [firstButton, secondButton] = focusZoneContainer.querySelectorAll('button');
|
267
|
+
const input = focusZoneContainer.querySelector('input');
|
268
|
+
const controller = focusZone_1.focusZone(focusZoneContainer, { bindKeys: focusZone_1.FocusKeys.ArrowHorizontal | focusZone_1.FocusKeys.HomeAndEnd });
|
269
|
+
firstButton.focus();
|
270
|
+
user_event_1.default.type(firstButton, '{arrowright}');
|
271
|
+
expect(document.activeElement).toEqual(input);
|
272
|
+
user_event_1.default.type(input, '{arrowleft}');
|
273
|
+
expect(document.activeElement).toEqual(input);
|
274
|
+
user_event_1.default.type(input, '{arrowright}');
|
275
|
+
expect(document.activeElement).toEqual(input);
|
276
|
+
user_event_1.default.type(input, '{arrowright}');
|
277
|
+
expect(document.activeElement).toEqual(secondButton);
|
278
|
+
controller.abort();
|
279
|
+
});
|
280
|
+
it('Should focus-in to the first element if the last-focused element is removed', async () => {
|
281
|
+
const { container } = react_2.render(<div>
|
282
|
+
<button tabIndex={0} id="outside">
|
283
|
+
Bad Apple
|
284
|
+
</button>
|
285
|
+
<div id="focusZone">
|
286
|
+
<button tabIndex={0}>Apple</button>
|
287
|
+
<button tabIndex={0}>Banana</button>
|
288
|
+
<button tabIndex={0}>Cantaloupe</button>
|
289
|
+
</div>
|
290
|
+
</div>);
|
291
|
+
const focusZoneContainer = container.querySelector('#focusZone');
|
292
|
+
const [firstButton, secondButton, thirdButton] = focusZoneContainer.querySelectorAll('button');
|
293
|
+
const outsideButton = container.querySelector('#outside');
|
294
|
+
const controller = focusZone_1.focusZone(focusZoneContainer);
|
295
|
+
firstButton.focus();
|
296
|
+
user_event_1.default.type(firstButton, '{arrowdown}');
|
297
|
+
expect(document.activeElement).toEqual(secondButton);
|
298
|
+
outsideButton.focus();
|
299
|
+
focusZoneContainer.removeChild(secondButton);
|
300
|
+
// The mutation observer fires asynchronously
|
301
|
+
await nextTick();
|
302
|
+
user_event_1.default.tab();
|
303
|
+
expect(document.activeElement).toEqual(firstButton);
|
304
|
+
user_event_1.default.type(firstButton, '{arrowdown}');
|
305
|
+
expect(document.activeElement).toEqual(thirdButton);
|
306
|
+
controller.abort();
|
307
|
+
});
|
308
|
+
it('Should call onActiveDescendantChanged properly', () => {
|
309
|
+
const { container } = react_2.render(<div>
|
310
|
+
<button tabIndex={0} id="outside">
|
311
|
+
Bad Apple
|
312
|
+
</button>
|
313
|
+
<input id="control" defaultValue="control input" tabIndex={0}/>
|
314
|
+
<div id="focusZone">
|
315
|
+
<button tabIndex={0}>Apple</button>
|
316
|
+
<button tabIndex={0}>Banana</button>
|
317
|
+
<button tabIndex={0}>Cantaloupe</button>
|
318
|
+
</div>
|
319
|
+
</div>);
|
320
|
+
const focusZoneContainer = container.querySelector('#focusZone');
|
321
|
+
const [firstButton, secondButton] = focusZoneContainer.querySelectorAll('button');
|
322
|
+
const control = container.querySelector('#control');
|
323
|
+
const activeDescendantChangedCallback = jest.fn();
|
324
|
+
const controller = focusZone_1.focusZone(focusZoneContainer, {
|
325
|
+
activeDescendantControl: control,
|
326
|
+
onActiveDescendantChanged: activeDescendantChangedCallback
|
327
|
+
});
|
328
|
+
control.focus();
|
329
|
+
expect(activeDescendantChangedCallback).toHaveBeenLastCalledWith(firstButton, undefined, false);
|
330
|
+
user_event_1.default.type(control, '{arrowdown}');
|
331
|
+
expect(activeDescendantChangedCallback).toHaveBeenLastCalledWith(secondButton, firstButton, true);
|
332
|
+
user_event_1.default.type(control, '{arrowup}');
|
333
|
+
expect(activeDescendantChangedCallback).toHaveBeenLastCalledWith(firstButton, secondButton, true);
|
334
|
+
react_2.fireEvent.mouseMove(secondButton);
|
335
|
+
expect(activeDescendantChangedCallback).toHaveBeenLastCalledWith(secondButton, firstButton, false);
|
336
|
+
user_event_1.default.type(control, '{arrowup}');
|
337
|
+
expect(activeDescendantChangedCallback).toHaveBeenLastCalledWith(firstButton, secondButton, true);
|
338
|
+
user_event_1.default.type(control, '{arrowUp}');
|
339
|
+
expect(activeDescendantChangedCallback).toHaveBeenLastCalledWith(firstButton, firstButton, true);
|
340
|
+
activeDescendantChangedCallback.mockReset();
|
341
|
+
react_2.fireEvent.mouseMove(firstButton);
|
342
|
+
expect(activeDescendantChangedCallback).not.toBeCalled();
|
343
|
+
controller.abort();
|
344
|
+
});
|
345
|
+
it('Should set aria-activedescendant correctly', () => {
|
346
|
+
const { container } = react_2.render(<div>
|
347
|
+
<button tabIndex={0} id="outside">
|
348
|
+
Bad Apple
|
349
|
+
</button>
|
350
|
+
<input id="control" defaultValue="control input" tabIndex={0}/>
|
351
|
+
<div id="focusZone">
|
352
|
+
<button tabIndex={0}>Apple</button>
|
353
|
+
<button tabIndex={0}>Banana</button>
|
354
|
+
<button tabIndex={0}>Cantaloupe</button>
|
355
|
+
</div>
|
356
|
+
</div>);
|
357
|
+
const focusZoneContainer = container.querySelector('#focusZone');
|
358
|
+
const [firstButton, secondButton] = focusZoneContainer.querySelectorAll('button');
|
359
|
+
const outsideButton = container.querySelector('#outside');
|
360
|
+
const control = container.querySelector('#control');
|
361
|
+
const controller = focusZone_1.focusZone(focusZoneContainer, { activeDescendantControl: control });
|
362
|
+
control.focus();
|
363
|
+
expect(control.getAttribute('aria-activedescendant')).toEqual(firstButton.id);
|
364
|
+
user_event_1.default.type(control, '{arrowdown}');
|
365
|
+
expect(control.getAttribute('aria-activedescendant')).toEqual(secondButton.id);
|
366
|
+
user_event_1.default.type(control, '{arrowup}');
|
367
|
+
expect(control.getAttribute('aria-activedescendant')).toEqual(firstButton.id);
|
368
|
+
expect(document.activeElement).toEqual(control);
|
369
|
+
user_event_1.default.type(control, '{arrowup}');
|
370
|
+
expect(control.getAttribute('aria-activedescendant')).toEqual(firstButton.id);
|
371
|
+
outsideButton.focus();
|
372
|
+
expect(control.hasAttribute('aria-activedescendant')).toBeFalsy();
|
373
|
+
controller.abort();
|
374
|
+
});
|
375
|
+
it('Should handle elements being reordered', async () => {
|
376
|
+
const { container } = react_2.render(<div>
|
377
|
+
<div id="focusZone">
|
378
|
+
<button tabIndex={0}>Apple</button>
|
379
|
+
<button tabIndex={0}>Banana</button>
|
380
|
+
<button tabIndex={0}>Cantaloupe</button>
|
381
|
+
<button tabIndex={0}>Durian</button>
|
382
|
+
</div>
|
383
|
+
</div>);
|
384
|
+
const focusZoneContainer = container.querySelector('#focusZone');
|
385
|
+
const [firstButton, secondButton, thirdButton, fourthButton] = focusZoneContainer.querySelectorAll('button');
|
386
|
+
const controller = focusZone_1.focusZone(focusZoneContainer);
|
387
|
+
firstButton.focus();
|
388
|
+
expect(document.activeElement).toEqual(firstButton);
|
389
|
+
moveDown();
|
390
|
+
expect(document.activeElement).toEqual(secondButton);
|
391
|
+
moveUp();
|
392
|
+
expect(document.activeElement).toEqual(firstButton);
|
393
|
+
// move secondButton and thirdButton to the end of the zone, in reverse order
|
394
|
+
focusZoneContainer.appendChild(thirdButton);
|
395
|
+
focusZoneContainer.appendChild(secondButton);
|
396
|
+
// The mutation observer fires asynchronously
|
397
|
+
await nextTick();
|
398
|
+
expect(document.activeElement).toEqual(firstButton);
|
399
|
+
moveDown();
|
400
|
+
expect(document.activeElement).toEqual(fourthButton);
|
401
|
+
moveDown();
|
402
|
+
expect(document.activeElement).toEqual(thirdButton);
|
403
|
+
moveDown();
|
404
|
+
expect(document.activeElement).toEqual(secondButton);
|
405
|
+
controller.abort();
|
406
|
+
});
|
@@ -0,0 +1,58 @@
|
|
1
|
+
"use strict";
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
4
|
+
};
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
6
|
+
const react_1 = __importDefault(require("react"));
|
7
|
+
const iterateFocusableElements_1 = require("../../utils/iterateFocusableElements");
|
8
|
+
const react_2 = require("@testing-library/react");
|
9
|
+
it('Should iterate through focusable elements only', () => {
|
10
|
+
const { container } = react_2.render(<div>
|
11
|
+
<div>
|
12
|
+
<textarea></textarea>
|
13
|
+
</div>
|
14
|
+
<input />
|
15
|
+
<button>Hello</button>
|
16
|
+
<p>Not focusable</p>
|
17
|
+
{/* eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex */}
|
18
|
+
<div tabIndex={0}>
|
19
|
+
<a tabIndex={-1} href="#boo">
|
20
|
+
Not focusable
|
21
|
+
</a>
|
22
|
+
<a href="#yah">Focusable</a>
|
23
|
+
</div>
|
24
|
+
</div>);
|
25
|
+
const focusable = Array.from(iterateFocusableElements_1.iterateFocusableElements(container, { onlyTabbable: true }));
|
26
|
+
expect(focusable.length).toEqual(5);
|
27
|
+
expect(focusable[0].tagName.toLowerCase()).toEqual('textarea');
|
28
|
+
expect(focusable[1].tagName.toLowerCase()).toEqual('input');
|
29
|
+
expect(focusable[2].tagName.toLowerCase()).toEqual('button');
|
30
|
+
expect(focusable[3].tagName.toLowerCase()).toEqual('div');
|
31
|
+
expect(focusable[4].tagName.toLowerCase()).toEqual('a');
|
32
|
+
expect(focusable[4].getAttribute('href')).toEqual('#yah');
|
33
|
+
});
|
34
|
+
it('Should iterate through focusable elements in reverse', () => {
|
35
|
+
const { container } = react_2.render(<div>
|
36
|
+
<div>
|
37
|
+
<textarea></textarea>
|
38
|
+
</div>
|
39
|
+
<input />
|
40
|
+
<button>Hello</button>
|
41
|
+
<p>Not focusable</p>
|
42
|
+
{/* eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex */}
|
43
|
+
<div tabIndex={0}>
|
44
|
+
<a tabIndex={-1} href="#boo">
|
45
|
+
Not focusable
|
46
|
+
</a>
|
47
|
+
<a href="#yah">Focusable</a>
|
48
|
+
</div>
|
49
|
+
</div>);
|
50
|
+
const focusable = Array.from(iterateFocusableElements_1.iterateFocusableElements(container, { reverse: true, onlyTabbable: true }));
|
51
|
+
expect(focusable.length).toEqual(5);
|
52
|
+
expect(focusable[0].tagName.toLowerCase()).toEqual('a');
|
53
|
+
expect(focusable[0].getAttribute('href')).toEqual('#yah');
|
54
|
+
expect(focusable[1].tagName.toLowerCase()).toEqual('div');
|
55
|
+
expect(focusable[2].tagName.toLowerCase()).toEqual('button');
|
56
|
+
expect(focusable[3].tagName.toLowerCase()).toEqual('input');
|
57
|
+
expect(focusable[4].tagName.toLowerCase()).toEqual('textarea');
|
58
|
+
});
|