@astryxdesign/core 0.7.6-canary.f73623d → 0.7.6-canary.f77aaf2
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/dist/AppShell/AppShell.d.ts.map +1 -1
- package/dist/AppShell/AppShell.js +0 -6
- package/dist/Badge/Badge.d.ts.map +1 -1
- package/dist/Badge/Badge.js +2 -1
- package/dist/BottomSheet/BottomSheet.d.ts.map +1 -1
- package/dist/BottomSheet/BottomSheet.js +6 -2
- package/dist/BottomSheet/BottomSheetPanel.d.ts.map +1 -1
- package/dist/BottomSheet/BottomSheetPanel.js +36 -9
- package/dist/BottomSheet/BottomSheetSwitcher.d.ts +1 -1
- package/dist/BottomSheet/BottomSheetSwitcher.js +1 -1
- package/dist/BottomSheet/useMobileKeyboard.d.ts.map +1 -1
- package/dist/BottomSheet/useMobileKeyboard.js +44 -8
- package/dist/BottomSheet/useSheetGestures.d.ts +36 -6
- package/dist/BottomSheet/useSheetGestures.d.ts.map +1 -1
- package/dist/BottomSheet/useSheetGestures.js +67 -10
- package/dist/Breadcrumbs/BreadcrumbItem.js +4 -2
- package/dist/Center/Center.d.ts +1 -1
- package/dist/Center/Center.js +1 -1
- package/dist/Chat/ChatComposer.d.ts.map +1 -1
- package/dist/Chat/ChatComposer.js +10 -5
- package/dist/Chat/ChatComposerDrawer.d.ts +1 -1
- package/dist/Chat/ChatComposerDrawer.js +1 -1
- package/dist/Chat/ChatComposerInput.d.ts +1 -1
- package/dist/Chat/ChatComposerInput.js +1 -1
- package/dist/Chat/ChatLayout.d.ts.map +1 -1
- package/dist/Chat/ChatLayout.js +1 -1
- package/dist/Chat/ChatMessageList.d.ts.map +1 -1
- package/dist/Chat/ChatMessageList.js +3 -1
- package/dist/Chat/ChatPastedTextToken.d.ts.map +1 -1
- package/dist/Chat/ChatPastedTextToken.js +15 -4
- package/dist/Chat/ChatSystemMessage.d.ts.map +1 -1
- package/dist/Chat/ChatSystemMessage.js +2 -1
- package/dist/Chat/ChatToolCalls.d.ts.map +1 -1
- package/dist/CheckboxList/CheckboxList.d.ts.map +1 -1
- package/dist/CheckboxList/CheckboxList.js +5 -2
- package/dist/CheckboxList/CheckboxListContext.d.ts +8 -0
- package/dist/CheckboxList/CheckboxListContext.d.ts.map +1 -1
- package/dist/CheckboxList/CheckboxListItem.d.ts.map +1 -1
- package/dist/CheckboxList/CheckboxListItem.js +5 -2
- package/dist/Code/Code.d.ts +1 -1
- package/dist/Code/Code.js +1 -1
- package/dist/CommandPalette/CommandPaletteInput.d.ts.map +1 -1
- package/dist/CommandPalette/CommandPaletteInput.js +8 -5
- package/dist/ComplexSelector/ComplexSelector.d.ts +2 -2
- package/dist/ComplexSelector/ComplexSelector.d.ts.map +1 -1
- package/dist/ComplexSelector/ComplexSelector.js +7 -2
- package/dist/DateInput/DateInput.d.ts +3 -2
- package/dist/DateInput/DateInput.d.ts.map +1 -1
- package/dist/DateInput/MonthScroller.d.ts.map +1 -1
- package/dist/DateInput/MonthScroller.js +17 -18
- package/dist/DateRangeInput/DateRangeInput.d.ts +3 -2
- package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
- package/dist/DateRangeInput/DateRangeInput.js +8 -4
- package/dist/Dialog/Dialog.d.ts.map +1 -1
- package/dist/Dialog/Dialog.js +10 -15
- package/dist/Drawer/Drawer.d.ts +1 -1
- package/dist/Drawer/Drawer.js +1 -1
- package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenu.js +19 -8
- package/dist/DropdownMenu/DropdownMenuGroup.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenuGroup.js +2 -1
- package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenuSubMenu.js +1 -1
- package/dist/DropdownMenu/MenuBottomSheetActionList.d.ts.map +1 -1
- package/dist/DropdownMenu/MenuBottomSheetActionList.js +9 -3
- package/dist/Field/Field.d.ts +2 -2
- package/dist/Field/Field.d.ts.map +1 -1
- package/dist/Field/FieldLabel.d.ts.map +1 -1
- package/dist/Field/FieldLabel.js +19 -2
- package/dist/Field/PanelSearchInput.d.ts.map +1 -1
- package/dist/Field/PanelSearchInput.js +10 -5
- package/dist/Field/fieldStatusPlacement.d.ts +15 -0
- package/dist/Field/fieldStatusPlacement.d.ts.map +1 -0
- package/dist/Field/fieldStatusPlacement.js +1 -0
- package/dist/FieldStatus/FieldStatus.d.ts +16 -28
- package/dist/FieldStatus/FieldStatus.d.ts.map +1 -1
- package/dist/FieldStatus/FieldStatus.js +25 -3
- package/dist/FieldStatus/index.d.ts +7 -0
- package/dist/FieldStatus/index.d.ts.map +1 -1
- package/dist/FileInput/FileInput.d.ts +10 -2
- package/dist/FileInput/FileInput.d.ts.map +1 -1
- package/dist/FileInput/FileInput.js +86 -35
- package/dist/Item/Item.js +5 -5
- package/dist/Item/ItemSwipeLayer.d.ts.map +1 -1
- package/dist/Item/ItemSwipeLayer.js +8 -2
- package/dist/Layer/useTouchTrigger.d.ts +35 -3
- package/dist/Layer/useTouchTrigger.d.ts.map +1 -1
- package/dist/Layer/useTouchTrigger.js +80 -11
- package/dist/Link/Link.d.ts.map +1 -1
- package/dist/Link/Link.js +10 -1
- package/dist/Markdown/Markdown.d.ts.map +1 -1
- package/dist/Markdown/Markdown.js +1 -0
- package/dist/MetadataList/MetadataList.d.ts.map +1 -1
- package/dist/MetadataList/MetadataList.js +2 -1
- package/dist/MetadataList/MetadataListItem.d.ts.map +1 -1
- package/dist/MetadataList/MetadataListItem.js +2 -1
- package/dist/MultiSelector/MultiSelector.d.ts +2 -2
- package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
- package/dist/MultiSelector/MultiSelector.js +35 -25
- package/dist/MultiSelector/hooks.d.ts.map +1 -1
- package/dist/MultiSelector/hooks.js +4 -2
- package/dist/NumberInput/NumberInput.d.ts +3 -2
- package/dist/NumberInput/NumberInput.d.ts.map +1 -1
- package/dist/NumberInput/NumberInput.js +11 -15
- package/dist/OverflowList/OverflowList.d.ts.map +1 -1
- package/dist/OverflowList/OverflowList.js +2 -1
- package/dist/Pagination/Pagination.d.ts.map +1 -1
- package/dist/Popover/Popover.js +2 -2
- package/dist/Popover/usePopover.d.ts +7 -1
- package/dist/Popover/usePopover.d.ts.map +1 -1
- package/dist/Popover/usePopover.js +8 -2
- package/dist/PowerSearch/PowerSearch.d.ts +3 -2
- package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
- package/dist/PowerSearch/PowerSearch.js +4 -2
- package/dist/RadioList/RadioList.d.ts.map +1 -1
- package/dist/RadioList/RadioList.js +7 -2
- package/dist/RadioList/RadioListItem.d.ts.map +1 -1
- package/dist/RadioList/RadioListItem.js +8 -2
- package/dist/RadioList/RadioListReasonContext.d.ts +2 -0
- package/dist/RadioList/RadioListReasonContext.d.ts.map +1 -0
- package/dist/RadioList/RadioListReasonContext.js +22 -0
- package/dist/Resizable/useResizable.d.ts.map +1 -1
- package/dist/Resizable/useResizable.js +53 -10
- package/dist/SegmentedControl/SegmentedControl.d.ts.map +1 -1
- package/dist/SegmentedControl/SegmentedControl.js +22 -5
- package/dist/SegmentedControl/SegmentedControlContext.d.ts +6 -0
- package/dist/SegmentedControl/SegmentedControlContext.d.ts.map +1 -1
- package/dist/SegmentedControl/SegmentedControlItem.d.ts +1 -1
- package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
- package/dist/SegmentedControl/SegmentedControlItem.js +16 -3
- package/dist/Selector/Selector.d.ts +3 -3
- package/dist/Selector/Selector.d.ts.map +1 -1
- package/dist/Selector/Selector.js +14 -8
- package/dist/Selector/hooks.js +2 -2
- package/dist/Selector/useSelectorPresentation.d.ts +7 -1
- package/dist/Selector/useSelectorPresentation.d.ts.map +1 -1
- package/dist/Selector/useSelectorPresentation.js +2 -1
- package/dist/SideNav/railMotion.d.ts +5 -5
- package/dist/SideNav/railMotion.d.ts.map +1 -1
- package/dist/SideNav/railMotion.js +69 -177
- package/dist/Slider/Slider.d.ts.map +1 -1
- package/dist/Slider/Slider.js +100 -17
- package/dist/Table/BaseTable.d.ts.map +1 -1
- package/dist/Table/BaseTable.js +7 -6
- package/dist/Table/glidingDivider.d.ts +8 -1
- package/dist/Table/glidingDivider.d.ts.map +1 -1
- package/dist/Table/glidingDivider.js +8 -3
- package/dist/Table/plugins/sortable/useTableSortable.d.ts.map +1 -1
- package/dist/Table/plugins/stickyColumns/useTableStickyColumns.d.ts.map +1 -1
- package/dist/Table/plugins/stickyColumns/useTableStickyColumns.js +1 -2
- package/dist/TextArea/TextArea.d.ts +2 -2
- package/dist/TextArea/TextArea.d.ts.map +1 -1
- package/dist/TextInput/TextInput.d.ts +3 -2
- package/dist/TextInput/TextInput.d.ts.map +1 -1
- package/dist/TimeInput/TimeInput.d.ts +3 -2
- package/dist/TimeInput/TimeInput.d.ts.map +1 -1
- package/dist/Timer/Timer.js +1 -1
- package/dist/Toast/useToast.d.ts.map +1 -1
- package/dist/Toast/useToast.js +8 -2
- package/dist/ToggleButton/ToggleButton.d.ts.map +1 -1
- package/dist/ToggleButton/ToggleButton.js +11 -3
- package/dist/Token/Token.d.ts +1 -1
- package/dist/Token/Token.d.ts.map +1 -1
- package/dist/Token/Token.js +15 -0
- package/dist/Tokenizer/Tokenizer.d.ts +3 -2
- package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
- package/dist/Tokenizer/Tokenizer.js +73 -50
- package/dist/Tokenizer/TokenizerBottomSheet.d.ts +1 -1
- package/dist/Tokenizer/TokenizerBottomSheet.d.ts.map +1 -1
- package/dist/Tokenizer/TokenizerBottomSheet.js +9 -15
- package/dist/TopNav/TopNavMegaMenu.d.ts.map +1 -1
- package/dist/TopNav/TopNavMegaMenu.js +4 -3
- package/dist/TreeList/TreeList.d.ts.map +1 -1
- package/dist/TreeList/TreeList.js +6 -3
- package/dist/TreeList/TreeListItem.d.ts.map +1 -1
- package/dist/TreeList/TreeListItem.js +4 -3
- package/dist/Typeahead/BaseTypeahead.d.ts +4 -1
- package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
- package/dist/Typeahead/BaseTypeahead.js +262 -43
- package/dist/Typeahead/Typeahead.d.ts +3 -2
- package/dist/Typeahead/Typeahead.d.ts.map +1 -1
- package/dist/Typeahead/Typeahead.js +36 -10
- package/dist/Typeahead/index.d.ts +1 -1
- package/dist/Typeahead/index.d.ts.map +1 -1
- package/dist/Typeahead/types.d.ts +26 -3
- package/dist/Typeahead/types.d.ts.map +1 -1
- package/dist/astryx.css +1 -2
- package/dist/gestures/haptics.d.ts.map +1 -1
- package/dist/gestures/haptics.js +1 -5
- package/dist/hooks/menuPressMarker.d.ts +13 -0
- package/dist/hooks/menuPressMarker.d.ts.map +1 -0
- package/dist/hooks/menuPressMarker.js +15 -0
- package/dist/hooks/useClickableContainer.d.ts +24 -4
- package/dist/hooks/useClickableContainer.d.ts.map +1 -1
- package/dist/hooks/useClickableContainer.js +41 -14
- package/dist/hooks/useFocusReturnVisibility.d.ts +8 -1
- package/dist/hooks/useFocusReturnVisibility.d.ts.map +1 -1
- package/dist/hooks/useFocusReturnVisibility.js +14 -8
- package/dist/hooks/useInputStatusIcon.d.ts +2 -2
- package/dist/hooks/useInputStatusIcon.d.ts.map +1 -1
- package/dist/hooks/useInputStatusIcon.js +1 -2
- package/dist/hooks/useListFocus.d.ts +19 -0
- package/dist/hooks/useListFocus.d.ts.map +1 -1
- package/dist/hooks/useListFocus.js +26 -1
- package/dist/hooks/useMenuPress.d.ts +2 -2
- package/dist/hooks/useMenuPress.d.ts.map +1 -1
- package/dist/hooks/useMenuPress.js +5 -6
- package/dist/hooks/useTreeFocus.d.ts.map +1 -1
- package/dist/hooks/useTreeFocus.js +13 -5
- package/dist/hooks/useTypeahead.d.ts +3 -1
- package/dist/hooks/useTypeahead.d.ts.map +1 -1
- package/dist/hooks/useTypeahead.js +21 -15
- package/dist/i18n/generated-locales/en.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/en.generated.js +6 -0
- package/dist/i18n/generated-locales/pseudo.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/pseudo.generated.js +6 -0
- package/dist/i18n/useListFormatter.d.ts +9 -0
- package/dist/i18n/useListFormatter.d.ts.map +1 -0
- package/dist/i18n/useListFormatter.js +41 -0
- package/dist/motion/animatedLayout.d.ts +130 -5
- package/dist/motion/animatedLayout.d.ts.map +1 -1
- package/dist/motion/animatedLayout.js +771 -44
- package/dist/motion/animator.d.ts.map +1 -1
- package/dist/motion/animator.js +38 -2
- package/dist/privateAttributes/segmentDisabled.stylex.d.ts +5 -0
- package/dist/privateAttributes/segmentDisabled.stylex.d.ts.map +1 -0
- package/dist/privateAttributes/segmentDisabled.stylex.js +28 -0
- package/dist/theme/Theme.d.ts +10 -0
- package/dist/theme/Theme.d.ts.map +1 -1
- package/dist/theme/Theme.js +8 -4
- package/dist/theme/useTheme.d.ts.map +1 -1
- package/dist/theme/useTheme.js +7 -1
- package/dist/utils/focusReturn.d.ts +5 -3
- package/dist/utils/focusReturn.d.ts.map +1 -1
- package/dist/utils/focusReturn.js +6 -4
- package/dist/utils/getNodeText.d.ts.map +1 -1
- package/dist/utils/getNodeText.js +2 -1
- package/dist/utils/interactionModality.d.ts +78 -7
- package/dist/utils/interactionModality.d.ts.map +1 -1
- package/dist/utils/interactionModality.js +125 -42
- package/dist/utils/pressDisabled.d.ts +21 -0
- package/dist/utils/pressDisabled.d.ts.map +1 -0
- package/dist/utils/pressDisabled.js +32 -0
- package/dist/utils/pressFeedback.d.ts.map +1 -1
- package/dist/utils/pressFeedback.js +5 -3
- package/dist/utils/typeaheadMatch.d.ts +24 -0
- package/dist/utils/typeaheadMatch.d.ts.map +1 -0
- package/dist/utils/typeaheadMatch.js +112 -0
- package/locales/en.json +25 -1
- package/locales/pseudo.json +18 -0
- package/package.json +19 -5
- package/src/AppShell/AppShell.tsx +0 -9
- package/src/Badge/Badge.tsx +2 -1
- package/src/BottomSheet/BottomSheet.tsx +4 -2
- package/src/BottomSheet/BottomSheetPanel.tsx +39 -8
- package/src/BottomSheet/BottomSheetSwitcher.tsx +1 -1
- package/src/BottomSheet/useMobileKeyboard.ts +51 -10
- package/src/BottomSheet/useSheetGestures.ts +92 -15
- package/src/Breadcrumbs/BreadcrumbItem.tsx +2 -1
- package/src/Center/Center.tsx +1 -1
- package/src/Chat/ChatComposer.tsx +7 -10
- package/src/Chat/ChatComposerDrawer.tsx +1 -1
- package/src/Chat/ChatComposerInput.tsx +1 -1
- package/src/Chat/ChatLayout.tsx +1 -6
- package/src/Chat/ChatMessageList.tsx +1 -0
- package/src/Chat/ChatPastedTextToken.tsx +14 -4
- package/src/Chat/ChatSystemMessage.tsx +4 -1
- package/src/Chat/ChatToolCalls.tsx +5 -0
- package/src/CheckboxList/CheckboxList.tsx +7 -1
- package/src/CheckboxList/CheckboxListContext.tsx +8 -0
- package/src/CheckboxList/CheckboxListItem.tsx +8 -2
- package/src/Code/Code.tsx +1 -1
- package/src/CommandPalette/CommandPaletteInput.tsx +5 -8
- package/src/ComplexSelector/ComplexSelector.tsx +22 -4
- package/src/DateInput/DateInput.tsx +2 -2
- package/src/DateInput/MonthScroller.tsx +15 -17
- package/src/DateRangeInput/DateRangeInput.tsx +20 -5
- package/src/Dialog/Dialog.tsx +9 -25
- package/src/Drawer/Drawer.tsx +1 -1
- package/src/DropdownMenu/DropdownMenu.tsx +24 -9
- package/src/DropdownMenu/DropdownMenuGroup.tsx +2 -1
- package/src/DropdownMenu/DropdownMenuSubMenu.tsx +3 -1
- package/src/DropdownMenu/MenuBottomSheetActionList.tsx +6 -3
- package/src/Field/Field.doc.mjs +2 -1
- package/src/Field/Field.tsx +2 -2
- package/src/Field/FieldLabel.doc.mjs +4 -2
- package/src/Field/FieldLabel.tsx +18 -2
- package/src/Field/PanelSearchInput.tsx +7 -8
- package/src/Field/fieldStatusPlacement.ts +18 -0
- package/src/FieldStatus/FieldStatus.tsx +33 -3
- package/src/FieldStatus/index.ts +7 -0
- package/src/FileInput/FileInput.doc.mjs +3 -3
- package/src/FileInput/FileInput.tsx +103 -36
- package/src/HoverCard/HoverCard.doc.mjs +3 -3
- package/src/HoverCard/useHoverCard.doc.mjs +1 -1
- package/src/Item/Item.tsx +5 -5
- package/src/Item/ItemSwipeLayer.tsx +8 -1
- package/src/Layer/useTouchTrigger.ts +107 -18
- package/src/Link/Link.tsx +9 -2
- package/src/Markdown/Markdown.tsx +1 -0
- package/src/MetadataList/MetadataList.tsx +4 -2
- package/src/MetadataList/MetadataListItem.tsx +2 -1
- package/src/MultiSelector/MultiSelector.tsx +57 -36
- package/src/MultiSelector/hooks.ts +7 -5
- package/src/NumberInput/NumberInput.tsx +13 -20
- package/src/OverflowList/OverflowList.tsx +2 -1
- package/src/Pagination/Pagination.tsx +1 -0
- package/src/Popover/Popover.tsx +2 -2
- package/src/Popover/usePopover.tsx +7 -2
- package/src/PowerSearch/PowerSearch.tsx +4 -2
- package/src/RadioList/RadioList.tsx +13 -2
- package/src/RadioList/RadioListItem.tsx +9 -1
- package/src/RadioList/RadioListReasonContext.ts +25 -0
- package/src/Resizable/useResizable.ts +50 -7
- package/src/SegmentedControl/SegmentedControl.tsx +43 -8
- package/src/SegmentedControl/SegmentedControlContext.ts +6 -0
- package/src/SegmentedControl/SegmentedControlItem.tsx +12 -1
- package/src/Selector/Selector.tsx +23 -9
- package/src/Selector/hooks.ts +2 -2
- package/src/Selector/useSelectorPresentation.ts +8 -1
- package/src/SideNav/railMotion.ts +73 -179
- package/src/Slider/Slider.tsx +116 -19
- package/src/Table/BaseTable.tsx +10 -6
- package/src/Table/glidingDivider.ts +8 -3
- package/src/Table/plugins/sortable/useTableSortable.tsx +2 -0
- package/src/Table/plugins/stickyColumns/useTableStickyColumns.tsx +1 -2
- package/src/TextArea/TextArea.tsx +3 -2
- package/src/TextInput/TextInput.tsx +2 -2
- package/src/TimeInput/TimeInput.tsx +2 -2
- package/src/Timer/Timer.tsx +1 -1
- package/src/Toast/useToast.tsx +8 -3
- package/src/ToggleButton/ToggleButton.tsx +10 -3
- package/src/Token/Token.tsx +15 -0
- package/src/Tokenizer/Tokenizer.doc.mjs +12 -8
- package/src/Tokenizer/Tokenizer.tsx +81 -53
- package/src/Tokenizer/TokenizerBottomSheet.tsx +9 -15
- package/src/Tooltip/Tooltip.doc.mjs +3 -3
- package/src/Tooltip/useTooltip.doc.mjs +1 -1
- package/src/TopNav/TopNavMegaMenu.tsx +4 -3
- package/src/TreeList/TreeList.tsx +6 -3
- package/src/TreeList/TreeListItem.tsx +5 -3
- package/src/Typeahead/BaseTypeahead.doc.mjs +7 -4
- package/src/Typeahead/BaseTypeahead.tsx +314 -40
- package/src/Typeahead/Typeahead.doc.mjs +21 -4
- package/src/Typeahead/Typeahead.tsx +42 -11
- package/src/Typeahead/TypeaheadItem.doc.mjs +6 -3
- package/src/Typeahead/index.ts +1 -1
- package/src/Typeahead/types.ts +27 -3
- package/src/gestures/haptics.ts +1 -8
- package/src/hooks/menuPressMarker.ts +15 -0
- package/src/hooks/useClickableContainer.ts +48 -17
- package/src/hooks/useFocusReturnVisibility.ts +15 -12
- package/src/hooks/useInputStatusIcon.tsx +2 -2
- package/src/hooks/useListFocus.ts +38 -1
- package/src/hooks/useMenuPress.ts +5 -5
- package/src/hooks/useTreeFocus.doc.mjs +2 -2
- package/src/hooks/useTreeFocus.ts +16 -10
- package/src/hooks/useTypeahead.doc.mjs +2 -2
- package/src/hooks/useTypeahead.ts +21 -20
- package/src/i18n/generated-locales/en.generated.ts +8 -0
- package/src/i18n/generated-locales/pseudo.generated.ts +8 -0
- package/src/i18n/useListFormatter.ts +42 -0
- package/src/motion/animatedLayout.ts +977 -45
- package/src/motion/animator.ts +55 -2
- package/src/privateAttributes/segmentDisabled.stylex.ts +28 -0
- package/src/theme/Theme.tsx +8 -4
- package/src/theme/useTheme.ts +6 -1
- package/src/utils/focusReturn.ts +6 -4
- package/src/utils/getNodeText.ts +2 -1
- package/src/utils/interactionModality.ts +165 -44
- package/src/utils/pressDisabled.ts +32 -0
- package/src/utils/pressFeedback.ts +5 -3
- package/src/utils/typeaheadMatch.ts +141 -0
- package/dist/Item/ItemMarkerColumns.a11y.chromium.spec.d.ts +0 -2
- package/dist/Item/ItemMarkerColumns.a11y.chromium.spec.js +0 -132
- package/dist/Lightbox/LightboxKeyboard.a11y.browser.spec.d.ts +0 -2
- package/dist/Lightbox/LightboxKeyboard.a11y.browser.spec.js +0 -78
- package/dist/Lightbox/LightboxViewport.a11y.browser.spec.d.ts +0 -2
- package/dist/Lightbox/LightboxViewport.a11y.browser.spec.js +0 -66
- package/dist/List/ListMarkers.a11y.chromium.spec.d.ts +0 -2
- package/dist/List/ListMarkers.a11y.chromium.spec.js +0 -677
- package/dist/Markdown/Markdown.fr23.bench.d.ts +0 -2
- package/dist/Markdown/Markdown.fr23.bench.d.ts.map +0 -1
- package/dist/Markdown/Markdown.fr23.bench.js +0 -152
- package/dist/Markdown/MarkdownTable.a11y.chromium.spec.d.ts +0 -2
- package/dist/Markdown/MarkdownTable.a11y.chromium.spec.js +0 -590
- package/dist/Markdown/plugins/headingLinks.a11y.chromium.spec.d.ts +0 -2
- package/dist/Markdown/plugins/headingLinks.a11y.chromium.spec.js +0 -657
- package/dist/MobileNav/MobileNavPinchZoom.a11y.chromium.spec.d.ts +0 -2
- package/dist/MobileNav/MobileNavPinchZoom.a11y.chromium.spec.js +0 -153
- package/dist/MultiSelector/CreateRow.a11y.chromium.spec.d.ts +0 -2
- package/dist/MultiSelector/CreateRow.a11y.chromium.spec.js +0 -117
- package/dist/MultiSelector/MultiSelector.disabledFocus.a11y.chromium.spec.d.ts +0 -2
- package/dist/MultiSelector/MultiSelector.disabledFocus.a11y.chromium.spec.js +0 -49
- package/dist/MultiSelector/OptionActions.a11y.chromium.spec.d.ts +0 -2
- package/dist/MultiSelector/OptionActions.a11y.chromium.spec.js +0 -273
- package/dist/Outline/OutlineFocus.a11y.chromium.spec.d.ts +0 -2
- package/dist/Outline/OutlineFocus.a11y.chromium.spec.js +0 -421
- package/dist/Typeahead/BaseTypeahead.emptyState.a11y.chromium.spec.d.ts +0 -2
- package/dist/Typeahead/BaseTypeahead.emptyState.a11y.chromium.spec.js +0 -118
- package/dist/reset.a11y.chromium.spec.d.ts +0 -2
- package/dist/reset.a11y.chromium.spec.js +0 -231
- package/dist/reset.until-found.a11y.browser.spec.d.ts +0 -2
- package/dist/reset.until-found.a11y.browser.spec.js +0 -72
- package/src/AlertDialog/AlertDialog.spec.md +0 -259
- package/src/AlertDialog/AlertDialog.test.tsx +0 -519
- package/src/AlertDialog/__tests__/AlertDialog.a11y.chromium.spec.ts +0 -128
- package/src/AlertDialog/__tests__/AlertDialog.a11y.renders.tsx +0 -52
- package/src/AlertDialog/__tests__/AlertDialog.a11y.states.ts +0 -68
- package/src/AlertDialog/__tests__/AlertDialog.a11y.test.tsx +0 -144
- package/src/AlertDialog/useImperativeAlertDialog.test.tsx +0 -163
- package/src/AppShell/AppShell.spec.md +0 -331
- package/src/AppShell/AppShell.test.tsx +0 -1310
- package/src/AppShell/__tests__/AppShellMainKeyboard.a11y.browser.spec.ts +0 -534
- package/src/AppShell/observeMainOverflow.test.ts +0 -173
- package/src/AspectRatio/AspectRatio.spec.md +0 -179
- package/src/AspectRatio/AspectRatio.test.tsx +0 -398
- package/src/AspectRatio/__tests__/AspectRatioFitLayers.a11y.browser.spec.ts +0 -158
- package/src/Avatar/Avatar.spec.md +0 -253
- package/src/Avatar/Avatar.test.tsx +0 -867
- package/src/Avatar/AvatarStatusDot.spec.md +0 -208
- package/src/Avatar/AvatarStatusDot.test.tsx +0 -306
- package/src/Avatar/__tests__/AvatarForcedColors.a11y.chromium.spec.ts +0 -126
- package/src/AvatarGroup/AvatarGroup.spec.md +0 -205
- package/src/AvatarGroup/AvatarGroup.test.tsx +0 -313
- package/src/AvatarGroup/AvatarGroupOverflow.spec.md +0 -210
- package/src/AvatarGroup/AvatarGroupOverflow.test.tsx +0 -263
- package/src/Badge/Badge.spec.md +0 -207
- package/src/Badge/Badge.test.tsx +0 -125
- package/src/Badge/__tests__/BadgeLargerText.a11y.chromium.spec.ts +0 -260
- package/src/Banner/Banner.spec.md +0 -282
- package/src/Banner/Banner.test.tsx +0 -879
- package/src/Blockquote/Blockquote.spec.md +0 -171
- package/src/Blockquote/Blockquote.test.tsx +0 -121
- package/src/BottomSheet/BottomSheet.spec.md +0 -457
- package/src/BottomSheet/BottomSheet.test.tsx +0 -3154
- package/src/BottomSheet/BottomSheetEdgeTint.test.tsx +0 -248
- package/src/BottomSheet/BottomSheetKeyboard.test.tsx +0 -195
- package/src/BottomSheet/BottomSheetPanel.test.tsx +0 -549
- package/src/BottomSheet/BottomSheetReplace.test.tsx +0 -1638
- package/src/BottomSheet/BottomSheetStacking.announce.test.tsx +0 -246
- package/src/BottomSheet/BottomSheetStacking.test.tsx +0 -3184
- package/src/BottomSheet/BottomSheetSwitcher.spec.md +0 -367
- package/src/BottomSheet/BottomSheetSwitcher.test.tsx +0 -2010
- package/src/BottomSheet/__tests__/BottomSheetDrag.a11y.chromium.spec.ts +0 -129
- package/src/BottomSheet/__tests__/BottomSheetFocusReturn.a11y.browser.spec.ts +0 -97
- package/src/BottomSheet/__tests__/BottomSheetKeyboard.a11y.browser.spec.ts +0 -415
- package/src/BottomSheet/__tests__/BottomSheetNativeModal.a11y.browser.spec.ts +0 -462
- package/src/BottomSheet/__tests__/BottomSheetPinchZoom.a11y.chromium.spec.ts +0 -247
- package/src/BottomSheet/__tests__/BottomSheetToastOutlet.a11y.chromium.spec.ts +0 -83
- package/src/BottomSheet/snapOffsets.test.ts +0 -254
- package/src/BottomSheet/useSheetGestures.test.ts +0 -1690
- package/src/Breadcrumbs/BreadcrumbItem.spec.md +0 -269
- package/src/Breadcrumbs/Breadcrumbs.spec.md +0 -165
- package/src/Breadcrumbs/Breadcrumbs.test.tsx +0 -921
- package/src/Breadcrumbs/__tests__/Breadcrumbs.a11y.chromium.spec.ts +0 -132
- package/src/Breadcrumbs/__tests__/Breadcrumbs.a11y.renders.tsx +0 -45
- package/src/Breadcrumbs/__tests__/Breadcrumbs.a11y.states.ts +0 -81
- package/src/Breadcrumbs/__tests__/Breadcrumbs.a11y.test.tsx +0 -125
- package/src/Button/Button.spec.md +0 -322
- package/src/Button/Button.test.tsx +0 -864
- package/src/Button/__tests__/Button.a11y.chromium.spec.ts +0 -355
- package/src/Button/__tests__/Button.a11y.harnesses.tsx +0 -47
- package/src/Button/__tests__/Button.a11y.known-failures.ts +0 -25
- package/src/Button/__tests__/Button.a11y.renders.tsx +0 -139
- package/src/Button/__tests__/Button.a11y.states.ts +0 -317
- package/src/Button/__tests__/Button.a11y.test.tsx +0 -187
- package/src/ButtonGroup/ButtonGroup.spec.md +0 -220
- package/src/ButtonGroup/ButtonGroup.test.tsx +0 -1025
- package/src/Calendar/Calendar.spec.md +0 -248
- package/src/Calendar/Calendar.test.tsx +0 -2186
- package/src/Calendar/__tests__/Calendar.a11y.chromium.spec.ts +0 -141
- package/src/Calendar/__tests__/Calendar.a11y.known-failures.ts +0 -12
- package/src/Calendar/__tests__/Calendar.a11y.renders.tsx +0 -57
- package/src/Calendar/__tests__/Calendar.a11y.states.ts +0 -139
- package/src/Calendar/__tests__/Calendar.a11y.test.tsx +0 -137
- package/src/Calendar/dayCellUtils.test.ts +0 -520
- package/src/Calendar/getInitialFocusDate.test.ts +0 -94
- package/src/Calendar/getStandaloneShortWeekdayNames.test.ts +0 -54
- package/src/Calendar/hooks/useCalendarConstraints.test.ts +0 -153
- package/src/Calendar/hooks/useCalendarDays.test.ts +0 -265
- package/src/Calendar/hooks/useCalendarNavigation.test.ts +0 -252
- package/src/Card/Card.spec.md +0 -205
- package/src/Card/Card.test.tsx +0 -202
- package/src/Carousel/Carousel.spec.md +0 -146
- package/src/Carousel/Carousel.test.tsx +0 -1036
- package/src/Carousel/__tests__/Carousel.a11y.chromium.spec.ts +0 -207
- package/src/Carousel/__tests__/Carousel.a11y.known-failures.ts +0 -12
- package/src/Carousel/__tests__/Carousel.a11y.renders.tsx +0 -39
- package/src/Carousel/__tests__/Carousel.a11y.states.ts +0 -139
- package/src/Carousel/__tests__/Carousel.a11y.test.tsx +0 -127
- package/src/Center/Center.spec.md +0 -205
- package/src/Center/Center.test.tsx +0 -406
- package/src/Chat/ChatComposer.spec.md +0 -246
- package/src/Chat/ChatComposer.test.tsx +0 -364
- package/src/Chat/ChatComposerDrawer.spec.md +0 -267
- package/src/Chat/ChatComposerDrawer.test.tsx +0 -130
- package/src/Chat/ChatComposerInput.spec.md +0 -224
- package/src/Chat/ChatComposerInput.test.tsx +0 -2136
- package/src/Chat/ChatComposerTokenElement.spec.md +0 -164
- package/src/Chat/ChatComposerTokenElement.test.tsx +0 -85
- package/src/Chat/ChatDictationButton.spec.md +0 -205
- package/src/Chat/ChatDictationButton.test.tsx +0 -108
- package/src/Chat/ChatLayout.spec.md +0 -276
- package/src/Chat/ChatLayout.test.tsx +0 -311
- package/src/Chat/ChatLayoutScrollButton.spec.md +0 -322
- package/src/Chat/ChatLayoutScrollButton.test.tsx +0 -99
- package/src/Chat/ChatMessage.spec.md +0 -265
- package/src/Chat/ChatMessage.test.tsx +0 -344
- package/src/Chat/ChatMessageBubble.spec.md +0 -263
- package/src/Chat/ChatMessageBubble.test.tsx +0 -294
- package/src/Chat/ChatMessageList.spec.md +0 -222
- package/src/Chat/ChatMessageList.test.tsx +0 -198
- package/src/Chat/ChatMessageMetadata.spec.md +0 -220
- package/src/Chat/ChatMessageMetadata.test.tsx +0 -66
- package/src/Chat/ChatPastedTextToken.test.tsx +0 -71
- package/src/Chat/ChatSendButton.spec.md +0 -238
- package/src/Chat/ChatSendButton.test.tsx +0 -309
- package/src/Chat/ChatSystemMessage.spec.md +0 -224
- package/src/Chat/ChatSystemMessage.test.tsx +0 -189
- package/src/Chat/ChatTokenizedText.spec.md +0 -190
- package/src/Chat/ChatTokenizedText.test.tsx +0 -163
- package/src/Chat/ChatToolCalls.spec.md +0 -194
- package/src/Chat/ChatToolCalls.test.tsx +0 -413
- package/src/Chat/__tests__/ChatLayoutScrollButton.a11y.chromium.spec.ts +0 -577
- package/src/Chat/__tests__/ChatLayoutScrollButtonSurface.a11y.chromium.spec.ts +0 -740
- package/src/Chat/__tests__/ChatMessageBubble.a11y.chromium.spec.ts +0 -457
- package/src/Chat/__tests__/ChatMessageBubble.auditProvenance.test.ts +0 -91
- package/src/Chat/__tests__/ChatMessageBubble.auditProvenance.ts +0 -91
- package/src/Chat/__tests__/ChatMessageList.a11y.chromium.spec.ts +0 -465
- package/src/Chat/__tests__/ChatMessageMetadata.a11y.chromium.spec.ts +0 -416
- package/src/Chat/__tests__/ChatSendButton.a11y.chromium.spec.ts +0 -1523
- package/src/Chat/__tests__/ChatSystemMessage.a11y.chromium.spec.ts +0 -1004
- package/src/Chat/__tests__/ChatTokenizedText.a11y.chromium.spec.ts +0 -574
- package/src/Chat/__tests__/useChatStreamScroll.a11y.chromium.spec.ts +0 -263
- package/src/Chat/__tests__/useChatStreamScroll.fixture.tsx +0 -61
- package/src/Chat/useChatDictation.test.tsx +0 -369
- package/src/Chat/useChatNewMessages.test.tsx +0 -209
- package/src/Chat/useChatPasteAsToken.test.ts +0 -191
- package/src/Chat/useChatStreamScroll.test.tsx +0 -381
- package/src/Chat/useTriggerMenu.test.tsx +0 -994
- package/src/CheckboxInput/CheckboxInput.spec.md +0 -218
- package/src/CheckboxInput/CheckboxInput.test.tsx +0 -926
- package/src/CheckboxInput/__tests__/Checkbox.a11y.chromium.spec.ts +0 -354
- package/src/CheckboxInput/__tests__/Checkbox.a11y.known-failures.ts +0 -54
- package/src/CheckboxInput/__tests__/Checkbox.a11y.renders.tsx +0 -310
- package/src/CheckboxInput/__tests__/Checkbox.a11y.states.ts +0 -404
- package/src/CheckboxInput/__tests__/Checkbox.a11y.test.tsx +0 -128
- package/src/CheckboxList/CheckboxList.fr10.test.tsx +0 -51
- package/src/CheckboxList/CheckboxList.spec.md +0 -332
- package/src/CheckboxList/CheckboxList.test.tsx +0 -1159
- package/src/CheckboxList/CheckboxListItem.spec.md +0 -302
- package/src/CheckboxList/__tests__/CheckboxList.a11y.chromium.spec.ts +0 -1998
- package/src/Citation/Citation.spec.md +0 -179
- package/src/Citation/Citation.test.tsx +0 -361
- package/src/ClickableCard/ClickableCard.spec.md +0 -241
- package/src/ClickableCard/ClickableCard.test.tsx +0 -382
- package/src/Code/Code.spec.md +0 -170
- package/src/Code/Code.test.tsx +0 -75
- package/src/CodeBlock/CodeBlock.highlightStyles.test.tsx +0 -87
- package/src/CodeBlock/CodeBlock.spec.md +0 -243
- package/src/CodeBlock/CodeBlock.test.tsx +0 -752
- package/src/CodeBlock/CodeLanguage.test.tsx +0 -173
- package/src/CodeBlock/__tests__/CodeBlockCopyClearance.a11y.chromium.spec.ts +0 -325
- package/src/CodeBlock/__tests__/CodeBlockLanguages.a11y.chromium.spec.ts +0 -64
- package/src/CodeBlock/__tests__/CodeBlockOverflowStop.a11y.chromium.spec.ts +0 -126
- package/src/CodeBlock/__tests__/DiffRowPaint.a11y.chromium.spec.ts +0 -94
- package/src/CodeBlock/highlightRanges.test.ts +0 -253
- package/src/CodeBlock/languages.test.ts +0 -402
- package/src/CodeBlock/tokenizer.test.ts +0 -280
- package/src/CodeBlock/tokenizerBudget.test.ts +0 -112
- package/src/Collapsible/Collapsible.spec.md +0 -247
- package/src/Collapsible/Collapsible.test.tsx +0 -620
- package/src/Collapsible/CollapsibleGroup.spec.md +0 -229
- package/src/Collapsible/CollapsibleGroup.test.tsx +0 -656
- package/src/Collapsible/__tests__/Collapsible.a11y.chromium.spec.ts +0 -194
- package/src/Collapsible/__tests__/Collapsible.a11y.states.ts +0 -117
- package/src/Collapsible/__tests__/Collapsible.a11y.test.tsx +0 -140
- package/src/Collapsible/__tests__/CollapsibleGroup.a11y.chromium.spec.ts +0 -559
- package/src/Collapsible/__tests__/CollapsibleGroup.a11y.renders.tsx +0 -45
- package/src/Collapsible/__tests__/CollapsibleGroup.a11y.states.ts +0 -139
- package/src/Collapsible/__tests__/CollapsibleGroup.a11y.test.tsx +0 -135
- package/src/Collapsible/__tests__/CollapsibleGroupAccordion.a11y.chromium.spec.ts +0 -141
- package/src/Collapsible/useCollapsible.test.tsx +0 -236
- package/src/CommandPalette/CommandPalette.perf.test.ts +0 -100
- package/src/CommandPalette/CommandPalette.spec.md +0 -278
- package/src/CommandPalette/CommandPalette.test.tsx +0 -750
- package/src/CommandPalette/CommandPaletteEmpty.spec.md +0 -190
- package/src/CommandPalette/CommandPaletteEmpty.test.tsx +0 -101
- package/src/CommandPalette/CommandPaletteFooter.test.tsx +0 -122
- package/src/CommandPalette/CommandPaletteGroup.test.tsx +0 -129
- package/src/CommandPalette/CommandPaletteInput.spec.md +0 -215
- package/src/CommandPalette/CommandPaletteInput.test.tsx +0 -539
- package/src/CommandPalette/CommandPaletteItem.spec.md +0 -223
- package/src/CommandPalette/CommandPaletteItem.test.tsx +0 -417
- package/src/CommandPalette/CommandPaletteList.spec.md +0 -194
- package/src/CommandPalette/CommandPaletteList.test.tsx +0 -184
- package/src/CommandPalette/__tests__/CommandPalette.a11y.chromium.spec.ts +0 -600
- package/src/CommandPalette/__tests__/CommandPaletteEmptyAudit.a11y.chromium.spec.ts +0 -715
- package/src/CommandPalette/__tests__/CommandPaletteFooter.a11y.chromium.spec.ts +0 -582
- package/src/CommandPalette/__tests__/CommandPaletteInputAudit.a11y.chromium.spec.ts +0 -336
- package/src/CommandPalette/__tests__/CommandPaletteItemAudit.a11y.chromium.spec.ts +0 -523
- package/src/ComplexSelector/ComplexSelector.focusableDisabled.test.tsx +0 -76
- package/src/ComplexSelector/ComplexSelector.fr10.test.tsx +0 -262
- package/src/ComplexSelector/ComplexSelector.spec.md +0 -266
- package/src/ComplexSelector/ComplexSelector.test.tsx +0 -1171
- package/src/ComplexSelector/__tests__/ComplexSelector.a11y.chromium.spec.ts +0 -965
- package/src/ContextMenu/ContextMenu.spec.md +0 -279
- package/src/ContextMenu/ContextMenu.test.tsx +0 -1617
- package/src/ContextMenu/ContextMenu.triggerProps.test.tsx +0 -54
- package/src/ContextMenu/ContextMenuItem.spec.md +0 -132
- package/src/ContextMenu/__tests__/ContextMenuAudit.a11y.chromium.spec.ts +0 -439
- package/src/ContextMenu/__tests__/ContextMenuCursorAnchor.a11y.browser.spec.ts +0 -154
- package/src/ContextMenu/__tests__/ContextMenuLongPress.a11y.chromium.spec.ts +0 -117
- package/src/DateInput/DateInput.fr10.test.tsx +0 -283
- package/src/DateInput/DateInput.spec.md +0 -319
- package/src/DateInput/DateInput.test.tsx +0 -1571
- package/src/DateInput/DateInputTouch.test.tsx +0 -2948
- package/src/DateInput/MonthScrollerSettle.test.tsx +0 -165
- package/src/DateInput/NativeDateField.test.tsx +0 -741
- package/src/DateInput/Presentation.test.tsx +0 -121
- package/src/DateInput/__tests__/DateInputAudit.a11y.chromium.spec.ts +0 -577
- package/src/DateInput/__tests__/MonthScrollerSteps.interaction.browser.spec.ts +0 -312
- package/src/DateInput/__tests__/WheelPinchZoom.a11y.chromium.spec.ts +0 -171
- package/src/DateRangeInput/DateRangeInput.fr10.test.tsx +0 -165
- package/src/DateRangeInput/DateRangeInput.spec.md +0 -203
- package/src/DateRangeInput/DateRangeInput.test.tsx +0 -1151
- package/src/DateTimeInput/DateTimeInput.fr10.test.tsx +0 -319
- package/src/DateTimeInput/DateTimeInput.spec.md +0 -270
- package/src/DateTimeInput/DateTimeInput.test.tsx +0 -2656
- package/src/DateTimeInput/DateTimeInputTouch.test.tsx +0 -1071
- package/src/DateTimeInput/NativePickerSegments.test.tsx +0 -657
- package/src/DateTimeInput/Presentation.test.tsx +0 -89
- package/src/Dialog/Dialog.source-build.test.mjs +0 -43
- package/src/Dialog/Dialog.spec.md +0 -189
- package/src/Dialog/Dialog.test.tsx +0 -1599
- package/src/Dialog/DialogHeader.test.tsx +0 -477
- package/src/Dialog/__tests__/Dialog.a11y.browser.spec.ts +0 -111
- package/src/Dialog/__tests__/Dialog.a11y.chromium.spec.ts +0 -218
- package/src/Dialog/__tests__/Dialog.a11y.known-failures.ts +0 -28
- package/src/Dialog/__tests__/Dialog.a11y.renders.tsx +0 -107
- package/src/Dialog/__tests__/Dialog.a11y.states.ts +0 -104
- package/src/Dialog/__tests__/Dialog.a11y.test.tsx +0 -102
- package/src/Dialog/__tests__/Dialog.reduced-motion.a11y.browser.spec.ts +0 -252
- package/src/Dialog/__tests__/DialogSecondDocument.a11y.browser.spec.ts +0 -202
- package/src/Dialog/modules/DialogHeader.spec.md +0 -211
- package/src/Dialog/useImperativeDialog.test.tsx +0 -78
- package/src/Divider/Divider.spec.md +0 -252
- package/src/Divider/Divider.test.tsx +0 -155
- package/src/Drawer/Drawer.spec.md +0 -388
- package/src/Drawer/Drawer.test.tsx +0 -1335
- package/src/Drawer/DrawerDirection.test.tsx +0 -127
- package/src/Drawer/DrawerHeader.spec.md +0 -181
- package/src/Drawer/DrawerHeader.test.tsx +0 -102
- package/src/Drawer/__tests__/DrawerAudit.a11y.chromium.spec.ts +0 -402
- package/src/Drawer/__tests__/DrawerHeaderAudit.a11y.chromium.spec.ts +0 -298
- package/src/Drawer/__tests__/DrawerNonModalPageScroll.a11y.chromium.spec.ts +0 -267
- package/src/DropdownMenu/DropdownMenu.spec.md +0 -562
- package/src/DropdownMenu/DropdownMenu.ssr.test.tsx +0 -93
- package/src/DropdownMenu/DropdownMenu.test.tsx +0 -4498
- package/src/DropdownMenu/DropdownMenuCheckboxItem.spec.md +0 -192
- package/src/DropdownMenu/DropdownMenuContext.public.test.ts +0 -22
- package/src/DropdownMenu/DropdownMenuDivider.spec.md +0 -186
- package/src/DropdownMenu/DropdownMenuGroup.spec.md +0 -145
- package/src/DropdownMenu/DropdownMenuItem.spec.md +0 -215
- package/src/DropdownMenu/DropdownMenuLazySheet.test.tsx +0 -126
- package/src/DropdownMenu/DropdownMenuRadioGroup.spec.md +0 -187
- package/src/DropdownMenu/DropdownMenuRadioItem.spec.md +0 -227
- package/src/DropdownMenu/DropdownMenuSelectable.test.tsx +0 -635
- package/src/DropdownMenu/DropdownMenuSheetFirstOpen.test.tsx +0 -72
- package/src/DropdownMenu/DropdownMenuSubMenu.spec.md +0 -231
- package/src/DropdownMenu/DropdownMenuSubMenu.test.tsx +0 -2442
- package/src/DropdownMenu/__tests__/DropdownMenuDividerAudit.a11y.chromium.spec.ts +0 -281
- package/src/DropdownMenu/__tests__/DropdownMenuDrillIn.a11y.chromium.spec.ts +0 -175
- package/src/DropdownMenu/__tests__/DropdownMenuFocusReturn.a11y.browser.spec.ts +0 -74
- package/src/DropdownMenu/__tests__/DropdownMenuGroupAudit.a11y.chromium.spec.ts +0 -335
- package/src/DropdownMenu/__tests__/DropdownMenuItemAudit.a11y.chromium.spec.ts +0 -367
- package/src/DropdownMenu/__tests__/DropdownMenuRadioGroupAudit.a11y.chromium.spec.ts +0 -328
- package/src/DropdownMenu/__tests__/DropdownMenuRadioItemAudit.a11y.chromium.spec.ts +0 -540
- package/src/DropdownMenu/__tests__/DropdownMenuSubMenuAudit.a11y.chromium.spec.ts +0 -671
- package/src/DropdownMenu/__tests__/Menu.a11y.chromium.spec.ts +0 -158
- package/src/DropdownMenu/__tests__/Menu.a11y.known-failures.ts +0 -26
- package/src/DropdownMenu/__tests__/Menu.a11y.renders.tsx +0 -65
- package/src/DropdownMenu/__tests__/Menu.a11y.states.ts +0 -138
- package/src/DropdownMenu/__tests__/Menu.a11y.test.tsx +0 -136
- package/src/DropdownMenu/__tests__/MenuButton.a11y.chromium.spec.ts +0 -149
- package/src/DropdownMenu/__tests__/MenuButton.a11y.known-failures.ts +0 -12
- package/src/DropdownMenu/__tests__/MenuButton.a11y.renders.tsx +0 -67
- package/src/DropdownMenu/__tests__/MenuButton.a11y.states.ts +0 -141
- package/src/DropdownMenu/__tests__/MenuButton.a11y.test.tsx +0 -138
- package/src/DropdownMenu/__tests__/MenuHeaderFooter.a11y.chromium.spec.ts +0 -231
- package/src/DropdownMenu/__tests__/MenuHoverFocus.a11y.chromium.spec.ts +0 -238
- package/src/DropdownMenu/__tests__/MenuPinchZoom.a11y.chromium.spec.ts +0 -173
- package/src/DropdownMenu/__tests__/MenuPress.a11y.chromium.spec.ts +0 -503
- package/src/DropdownMenu/__tests__/SubMenuHeaderFooter.a11y.chromium.spec.ts +0 -268
- package/src/DropdownMenu/__tests__/warmMenuBottomSheet.tsx +0 -43
- package/src/DropdownMenu/lazyMenuBottomSheet.test.ts +0 -60
- package/src/DropdownMenu/modules/useMenuHover.spec.md +0 -283
- package/src/DropdownMenu/modules/useMenuPress.spec.md +0 -348
- package/src/EmptyState/EmptyState.announce.test.tsx +0 -227
- package/src/EmptyState/EmptyState.spec.md +0 -203
- package/src/EmptyState/EmptyState.test.tsx +0 -284
- package/src/Field/Field.spec.md +0 -226
- package/src/Field/Field.test.tsx +0 -800
- package/src/Field/FieldLabel.spec.md +0 -183
- package/src/Field/FieldLabel.test.tsx +0 -244
- package/src/Field/InputClearButton.test.tsx +0 -395
- package/src/Field/__tests__/FieldLabelAudit.a11y.chromium.spec.ts +0 -297
- package/src/Field/fieldInputFamily.test.tsx +0 -160
- package/src/FieldStatus/FieldStatus.spec.md +0 -224
- package/src/FieldStatus/FieldStatus.test.tsx +0 -482
- package/src/FieldStatus/__tests__/StatusMessage.a11y.chromium.spec.ts +0 -206
- package/src/FieldStatus/__tests__/StatusMessage.a11y.known-failures.ts +0 -13
- package/src/FieldStatus/__tests__/StatusMessage.a11y.renders.tsx +0 -287
- package/src/FieldStatus/__tests__/StatusMessage.a11y.states.ts +0 -302
- package/src/FieldStatus/__tests__/StatusMessage.a11y.test.tsx +0 -154
- package/src/FileInput/FileInput.fr10.test.tsx +0 -21
- package/src/FileInput/FileInput.spec.md +0 -199
- package/src/FileInput/FileInput.test.tsx +0 -1291
- package/src/FormLayout/FormLayout.spec.md +0 -209
- package/src/FormLayout/FormLayout.test.tsx +0 -444
- package/src/FormLayout/__snapshots__/FormLayout.test.tsx.snap +0 -46
- package/src/Grid/Grid.spec.md +0 -193
- package/src/Grid/Grid.test.tsx +0 -509
- package/src/Grid/GridSpan.spec.md +0 -158
- package/src/HStack/HStack.test.tsx +0 -129
- package/src/Heading/Heading.spec.md +0 -198
- package/src/Heading/Heading.test.tsx +0 -328
- package/src/HoverCard/HoverCard.spec.md +0 -244
- package/src/HoverCard/HoverCard.test.tsx +0 -1339
- package/src/Icon/Icon.spec.md +0 -244
- package/src/Icon/Icon.test.tsx +0 -990
- package/src/Icon/globalIconRegistry.test.tsx +0 -613
- package/src/IconButton/IconButton.spec.md +0 -197
- package/src/IconButton/IconButton.test.tsx +0 -156
- package/src/Indicator/CheckIndicator.spec.md +0 -164
- package/src/Indicator/CheckboxIndicator.spec.md +0 -164
- package/src/Indicator/Indicator.spec.md +0 -188
- package/src/Indicator/Indicator.test.tsx +0 -389
- package/src/Indicator/indicatorRegistry.test.tsx +0 -106
- package/src/InputGroup/InputGroup.spec.md +0 -199
- package/src/InputGroup/InputGroup.test.tsx +0 -502
- package/src/InputGroup/InputGroupText.spec.md +0 -139
- package/src/InteractiveRoleContext/InteractiveRoleContext.test.tsx +0 -185
- package/src/Item/Item.source-build.test.mjs +0 -88
- package/src/Item/Item.spec.md +0 -226
- package/src/Item/Item.swipeLayer.test.tsx +0 -74
- package/src/Item/Item.test.tsx +0 -1515
- package/src/Item/ItemMarkerColumns.a11y.chromium.spec.ts +0 -211
- package/src/Item/__tests__/ItemSwipeActions.a11y.chromium.spec.ts +0 -337
- package/src/Item/__tests__/ItemSwipePinchZoom.a11y.chromium.spec.ts +0 -186
- package/src/Kbd/Kbd.spec.md +0 -185
- package/src/Kbd/Kbd.test.tsx +0 -299
- package/src/Layer/LayerProvider.spec.md +0 -149
- package/src/Layer/LayerProvider.test.tsx +0 -143
- package/src/Layer/__tests__/LayerBrowserHide.a11y.chromium.spec.ts +0 -126
- package/src/Layer/__tests__/LayerReducedMotion.a11y.chromium.spec.ts +0 -176
- package/src/Layer/__tests__/TouchTrigger.a11y.chromium.spec.ts +0 -200
- package/src/Layer/anchorName.test.ts +0 -146
- package/src/Layer/layerDismissalFamilies.test.tsx +0 -487
- package/src/Layer/layerDismissalInvariants.test.tsx +0 -501
- package/src/Layer/layerHost.test.ts +0 -117
- package/src/Layer/layerScopedContext.test.tsx +0 -499
- package/src/Layer/layerViewportInset.source-build.test.mjs +0 -90
- package/src/Layer/layerViewportInset.test.ts +0 -120
- package/src/Layer/popoverOperations.test.ts +0 -205
- package/src/Layer/useLayer.test.tsx +0 -2845
- package/src/Layer/useLayerDismissal.test.tsx +0 -580
- package/src/Layer/useTouchTrigger.test.ts +0 -70
- package/src/Layout/Layout.spec.md +0 -243
- package/src/Layout/Layout.test.tsx +0 -375
- package/src/Layout/LayoutContent.spec.md +0 -205
- package/src/Layout/LayoutFooter.spec.md +0 -190
- package/src/Layout/LayoutHeader.spec.md +0 -193
- package/src/Layout/LayoutPanel.spec.md +0 -194
- package/src/Layout/LayoutSlots.test.tsx +0 -376
- package/src/Layout/__tests__/Layout.a11y.chromium.spec.ts +0 -134
- package/src/Layout/__tests__/Layout.a11y.renders.tsx +0 -110
- package/src/Layout/__tests__/Layout.a11y.states.ts +0 -93
- package/src/Layout/__tests__/Layout.a11y.test.tsx +0 -142
- package/src/Layout/__tests__/childrenAsContent.test.tsx +0 -59
- package/src/Layout/__tests__/contentWidth.test.tsx +0 -303
- package/src/Layout/__tests__/edgeCompensation.test.tsx +0 -191
- package/src/Layout/__tests__/nestedPadding.test.tsx +0 -264
- package/src/Layout/overlayPaddingReset.test.tsx +0 -224
- package/src/Lightbox/Lightbox.spec.md +0 -199
- package/src/Lightbox/Lightbox.test.tsx +0 -837
- package/src/Lightbox/LightboxKeyboard.a11y.browser.spec.ts +0 -78
- package/src/Lightbox/LightboxViewport.a11y.browser.spec.ts +0 -74
- package/src/Lightbox/useLightbox.test.tsx +0 -263
- package/src/Link/Link.spec.md +0 -230
- package/src/Link/Link.test.tsx +0 -457
- package/src/Link/LinkProvider.spec.md +0 -167
- package/src/Link/__tests__/Link.a11y.chromium.spec.ts +0 -153
- package/src/Link/__tests__/Link.a11y.renders.tsx +0 -97
- package/src/Link/__tests__/Link.a11y.states.ts +0 -104
- package/src/Link/__tests__/Link.a11y.test.tsx +0 -127
- package/src/Link/__tests__/Link.navigation.a11y.chromium.spec.ts +0 -262
- package/src/Link/__tests__/Link.truncation.a11y.chromium.spec.ts +0 -58
- package/src/Link/useLinkComponent.test.tsx +0 -572
- package/src/Link/useLinkify.test.tsx +0 -146
- package/src/List/List.spec.md +0 -336
- package/src/List/List.test.tsx +0 -1052
- package/src/List/ListItem.source-build.test.mjs +0 -41
- package/src/List/ListItem.spec.md +0 -205
- package/src/List/ListMarkers.a11y.chromium.spec.ts +0 -904
- package/src/Markdown/Markdown.codeLanguages.test.tsx +0 -137
- package/src/Markdown/Markdown.fr23.bench.ts +0 -176
- package/src/Markdown/Markdown.fr23.perf.test.ts +0 -484
- package/src/Markdown/Markdown.helpers.perf.test.ts +0 -495
- package/src/Markdown/Markdown.public.test.ts +0 -309
- package/src/Markdown/Markdown.renderBoundary.test.tsx +0 -72
- package/src/Markdown/Markdown.rerender.test.tsx +0 -143
- package/src/Markdown/Markdown.spec.md +0 -595
- package/src/Markdown/Markdown.test.tsx +0 -1383
- package/src/Markdown/MarkdownTable.a11y.chromium.spec.ts +0 -727
- package/src/Markdown/__tests__/MarkdownNesting.a11y.chromium.spec.ts +0 -102
- package/src/Markdown/angleDestinations.test.tsx +0 -104
- package/src/Markdown/ast.test.ts +0 -374
- package/src/Markdown/atxHeadingIndentation.test.tsx +0 -53
- package/src/Markdown/blockNesting.test.tsx +0 -124
- package/src/Markdown/blockquoteMarkers.test.tsx +0 -69
- package/src/Markdown/characterReferences.test.ts +0 -47
- package/src/Markdown/codeSpans.test.tsx +0 -65
- package/src/Markdown/emphasis.test.tsx +0 -214
- package/src/Markdown/fenceInfo.test.tsx +0 -47
- package/src/Markdown/imageAlt.test.tsx +0 -84
- package/src/Markdown/incremental.test.ts +0 -1198
- package/src/Markdown/incrementalIndentation.test.tsx +0 -36
- package/src/Markdown/indentedFences.test.tsx +0 -374
- package/src/Markdown/indentedTables.test.tsx +0 -53
- package/src/Markdown/linkBrackets.test.tsx +0 -116
- package/src/Markdown/linkDestinationLinear.test.tsx +0 -54
- package/src/Markdown/linkDestinations.test.tsx +0 -127
- package/src/Markdown/linkTextCode.test.tsx +0 -65
- package/src/Markdown/listBullets.test.tsx +0 -55
- package/src/Markdown/listContinuation.test.tsx +0 -94
- package/src/Markdown/listThematicBreak.test.tsx +0 -49
- package/src/Markdown/longLinkLabels.test.tsx +0 -49
- package/src/Markdown/modules/headingLinks.spec.md +0 -262
- package/src/Markdown/modules/remark.spec.md +0 -269
- package/src/Markdown/modules/softBreaks.spec.md +0 -137
- package/src/Markdown/modules/sourceLines.spec.md +0 -162
- package/src/Markdown/openFenceFinalNewline.test.tsx +0 -85
- package/src/Markdown/parser.perf.test.ts +0 -398
- package/src/Markdown/parser.public.test.ts +0 -144
- package/src/Markdown/parser.test.ts +0 -1925
- package/src/Markdown/plugin-renderer/MarkdownPluginNodeRenderer.test.tsx +0 -300
- package/src/Markdown/plugins/frontmatter.test.tsx +0 -150
- package/src/Markdown/plugins/headingLinks.a11y.chromium.spec.ts +0 -856
- package/src/Markdown/plugins/headingLinks.ssr.test.tsx +0 -143
- package/src/Markdown/plugins/headingLinks.test.tsx +0 -637
- package/src/Markdown/plugins/protocol.test.tsx +0 -1083
- package/src/Markdown/plugins/semanticFence.test.tsx +0 -655
- package/src/Markdown/plugins/softBreaks.test.tsx +0 -172
- package/src/Markdown/plugins/sourceDecoration.test.tsx +0 -799
- package/src/Markdown/plugins/sourceLines.test.tsx +0 -715
- package/src/Markdown/plugins/textTransform.test.ts +0 -467
- package/src/Markdown/remark.test.tsx +0 -1877
- package/src/Markdown/streaming.test.ts +0 -148
- package/src/Markdown/streamingListShapes.test.tsx +0 -93
- package/src/Markdown/trailingSpaces.test.tsx +0 -45
- package/src/MetadataList/MetadataList.spec.md +0 -187
- package/src/MetadataList/MetadataList.test.tsx +0 -383
- package/src/MetadataList/MetadataListItem.spec.md +0 -165
- package/src/MobileNav/MobileNav.spec.md +0 -306
- package/src/MobileNav/MobileNav.test.tsx +0 -296
- package/src/MobileNav/MobileNavCloseEdgeCases.test.tsx +0 -584
- package/src/MobileNav/MobileNavCloseTiming.test.ts +0 -58
- package/src/MobileNav/MobileNavCloseVisibility.test.tsx +0 -172
- package/src/MobileNav/MobileNavEntryAnimation.test.tsx +0 -212
- package/src/MobileNav/MobileNavFocus.test.tsx +0 -553
- package/src/MobileNav/MobileNavPinchZoom.a11y.chromium.spec.ts +0 -147
- package/src/MobileNav/MobileNavReopen.test.tsx +0 -118
- package/src/MobileNav/MobileNavScrollbarGutter.test.tsx +0 -109
- package/src/MobileNav/MobileNavToggle.test.tsx +0 -179
- package/src/MobileNav/__tests__/MobileNavHeader.a11y.chromium.spec.ts +0 -89
- package/src/MobileNav/__tests__/MobileNavWidth.a11y.chromium.spec.ts +0 -103
- package/src/MoreMenu/MoreMenu.spec.md +0 -268
- package/src/MoreMenu/MoreMenu.test.tsx +0 -367
- package/src/MultiSelector/CreateRow.a11y.chromium.spec.ts +0 -148
- package/src/MultiSelector/MultiSelector.disabledFocus.a11y.chromium.spec.ts +0 -72
- package/src/MultiSelector/MultiSelector.focusableDisabled.test.tsx +0 -121
- package/src/MultiSelector/MultiSelector.fr10.test.tsx +0 -221
- package/src/MultiSelector/MultiSelector.spec.md +0 -357
- package/src/MultiSelector/MultiSelector.test.tsx +0 -4971
- package/src/MultiSelector/OptionActions.a11y.chromium.spec.ts +0 -310
- package/src/MultiSelector/__tests__/MultiSelector.a11y.chromium.spec.ts +0 -46
- package/src/MultiSelector/__tests__/MultiSelector.listbox.a11y.test.tsx +0 -135
- package/src/NavIcon/NavIcon.spec.md +0 -158
- package/src/NavIcon/NavIcon.test.tsx +0 -41
- package/src/NavMenu/NavHeadingMenu.spec.md +0 -264
- package/src/NavMenu/NavHeadingMenu.test.tsx +0 -714
- package/src/NavMenu/__tests__/NavMenusSlideSelection.a11y.chromium.spec.ts +0 -137
- package/src/NumberInput/NumberInput.fr10.test.tsx +0 -21
- package/src/NumberInput/NumberInput.spec.md +0 -270
- package/src/NumberInput/NumberInput.test.tsx +0 -2697
- package/src/NumberInput/__tests__/NumberInput.a11y.chromium.spec.ts +0 -124
- package/src/NumberInput/__tests__/NumberInput.a11y.known-failures.ts +0 -26
- package/src/NumberInput/__tests__/NumberInput.a11y.renders.tsx +0 -77
- package/src/NumberInput/__tests__/NumberInput.a11y.states.ts +0 -116
- package/src/NumberInput/__tests__/NumberInput.a11y.test.tsx +0 -154
- package/src/NumberInput/__tests__/NumberInput.coarse-pointer.a11y.chromium.spec.ts +0 -102
- package/src/NumberInput/numberInputCommit.test.ts +0 -72
- package/src/NumberInput/numberParser.docblock.test.ts +0 -234
- package/src/NumberInput/numberParser.test.ts +0 -222
- package/src/Outline/Outline.spec.md +0 -258
- package/src/Outline/Outline.test.tsx +0 -1134
- package/src/Outline/OutlineFocus.a11y.chromium.spec.ts +0 -551
- package/src/Outline/modules/parseOutlineFromMarkdown.spec.md +0 -150
- package/src/Outline/parseOutlineFromMarkdown.test.ts +0 -104
- package/src/OverflowList/OverflowList.spec.md +0 -204
- package/src/OverflowList/OverflowList.test.tsx +0 -1073
- package/src/Overlay/Overlay.spec.md +0 -304
- package/src/Overlay/Overlay.test.tsx +0 -424
- package/src/Pagination/Pagination.spec.md +0 -300
- package/src/Pagination/Pagination.test.tsx +0 -1754
- package/src/Popover/Popover.spec.md +0 -525
- package/src/Popover/Popover.test.tsx +0 -1508
- package/src/Popover/PopoverPresentation.test.tsx +0 -952
- package/src/Popover/__tests__/PopoverSecondDocument.a11y.chromium.spec.ts +0 -267
- package/src/PowerSearch/PowerSearch.spec.md +0 -661
- package/src/PowerSearch/PowerSearch.test.tsx +0 -1027
- package/src/PowerSearch/PowerSearchEditPopover.test.tsx +0 -1190
- package/src/PowerSearch/PowerSearchFilterEditor.test.tsx +0 -377
- package/src/PowerSearch/PowerSearchToken.test.tsx +0 -230
- package/src/PowerSearch/PowerSearchValueEditor.test.tsx +0 -754
- package/src/PowerSearch/formatFilterValue.test.ts +0 -339
- package/src/PowerSearch/resolveDateTimeRangePart.test.ts +0 -40
- package/src/PowerSearch/usePowerSearchConfig.test.ts +0 -832
- package/src/PowerSearch/usePowerSearchSource.test.ts +0 -621
- package/src/ProgressBar/ProgressBar.spec.md +0 -261
- package/src/ProgressBar/ProgressBar.test.tsx +0 -928
- package/src/ProgressBar/__tests__/ProgressBar.a11y.chromium.spec.ts +0 -127
- package/src/ProgressBar/__tests__/ProgressBar.a11y.known-failures.ts +0 -12
- package/src/ProgressBar/__tests__/ProgressBar.a11y.renders.tsx +0 -38
- package/src/ProgressBar/__tests__/ProgressBar.a11y.states.ts +0 -123
- package/src/ProgressBar/__tests__/ProgressBar.a11y.test.tsx +0 -107
- package/src/ProgressBar/__tests__/ProgressBarMotion.a11y.chromium.spec.ts +0 -831
- package/src/RadioList/RadioList.fr10.test.tsx +0 -67
- package/src/RadioList/RadioList.spec.md +0 -227
- package/src/RadioList/RadioList.test.tsx +0 -1051
- package/src/RadioList/RadioListItem.press.source-build.test.mjs +0 -80
- package/src/RadioList/RadioListItem.spec.md +0 -223
- package/src/RadioList/__tests__/RadioGroup.a11y.chromium.spec.ts +0 -274
- package/src/RadioList/__tests__/RadioGroup.a11y.known-failures.ts +0 -12
- package/src/RadioList/__tests__/RadioGroup.a11y.renders.tsx +0 -232
- package/src/RadioList/__tests__/RadioGroup.a11y.states.ts +0 -503
- package/src/RadioList/__tests__/RadioGroup.a11y.test.tsx +0 -217
- package/src/Resizable/Resizable.spec.md +0 -298
- package/src/Resizable/ResizeHandle.test.tsx +0 -1067
- package/src/Resizable/__tests__/ResizeHandle.a11y.chromium.spec.ts +0 -127
- package/src/Resizable/__tests__/ResizeHandle.a11y.known-failures.ts +0 -39
- package/src/Resizable/__tests__/ResizeHandle.a11y.renders.tsx +0 -80
- package/src/Resizable/__tests__/ResizeHandle.a11y.states.ts +0 -197
- package/src/Resizable/__tests__/ResizeHandle.a11y.test.tsx +0 -123
- package/src/Resizable/shippedDirections.test.ts +0 -462
- package/src/Resizable/useResizable.test.ts +0 -1725
- package/src/Resizable/utils.test.ts +0 -74
- package/src/ScrollableArea/ScrollableArea.spec.md +0 -190
- package/src/ScrollableArea/ScrollableArea.test.tsx +0 -304
- package/src/ScrollableArea/modules/useScrollableArea.spec.md +0 -149
- package/src/Section/Section.spec.md +0 -197
- package/src/Section/Section.test.tsx +0 -376
- package/src/SegmentedControl/SegmentedControl.spec.md +0 -261
- package/src/SegmentedControl/SegmentedControl.test.tsx +0 -981
- package/src/SegmentedControl/SegmentedControlItem.spec.md +0 -206
- package/src/SelectableCard/SelectableCard.spec.md +0 -230
- package/src/SelectableCard/SelectableCard.test.tsx +0 -216
- package/src/Selector/Selector.focusableDisabled.test.tsx +0 -127
- package/src/Selector/Selector.fr10.test.tsx +0 -149
- package/src/Selector/Selector.sheetActiveDescendant.test.tsx +0 -95
- package/src/Selector/Selector.source-build.test.mjs +0 -79
- package/src/Selector/Selector.spec.md +0 -379
- package/src/Selector/Selector.test.tsx +0 -5117
- package/src/Selector/__tests__/Combobox.a11y.known-failures.ts +0 -27
- package/src/Selector/__tests__/Combobox.a11y.states.ts +0 -145
- package/src/Selector/__tests__/ComboboxField.a11y.chromium.spec.ts +0 -458
- package/src/Selector/__tests__/EmptyStateAnnouncement.a11y.chromium.spec.ts +0 -91
- package/src/Selector/__tests__/Listbox.a11y.chromium.spec.ts +0 -131
- package/src/Selector/__tests__/Listbox.a11y.dom.ts +0 -38
- package/src/Selector/__tests__/Listbox.a11y.inventory.test.ts +0 -63
- package/src/Selector/__tests__/Listbox.a11y.known-failures.ts +0 -12
- package/src/Selector/__tests__/Listbox.a11y.states.ts +0 -232
- package/src/Selector/__tests__/Selector.a11y.chromium.spec.ts +0 -46
- package/src/Selector/__tests__/Selector.combobox.a11y.chromium.spec.ts +0 -109
- package/src/Selector/__tests__/Selector.combobox.a11y.test.tsx +0 -133
- package/src/Selector/__tests__/Selector.listbox.a11y.test.tsx +0 -128
- package/src/Selector/useCombobox.public.test.tsx +0 -89
- package/src/Selector/useCombobox.test.tsx +0 -444
- package/src/SideNav/SideNav.spec.md +0 -247
- package/src/SideNav/SideNav.test.tsx +0 -4275
- package/src/SideNav/SideNavCollapseButton.spec.md +0 -161
- package/src/SideNav/SideNavHeading.spec.md +0 -226
- package/src/SideNav/SideNavItem.spec.md +0 -236
- package/src/SideNav/SideNavRailMotion.test.tsx +0 -620
- package/src/SideNav/SideNavSection.spec.md +0 -153
- package/src/SideNav/__tests__/SideNavRailMotion.interaction.browser.spec.ts +0 -542
- package/src/SizeContext/SizeContext.test.tsx +0 -226
- package/src/Skeleton/Skeleton.spec.md +0 -184
- package/src/Skeleton/Skeleton.test.tsx +0 -148
- package/src/Slider/Slider.fr10.test.tsx +0 -93
- package/src/Slider/Slider.spec.md +0 -285
- package/src/Slider/Slider.test.tsx +0 -1661
- package/src/Slider/__tests__/Slider.a11y.chromium.spec.ts +0 -125
- package/src/Slider/__tests__/Slider.a11y.known-failures.ts +0 -12
- package/src/Slider/__tests__/Slider.a11y.renders.tsx +0 -42
- package/src/Slider/__tests__/Slider.a11y.states.ts +0 -106
- package/src/Slider/__tests__/Slider.a11y.test.tsx +0 -105
- package/src/Spinner/Spinner.spec.md +0 -231
- package/src/Spinner/Spinner.test.tsx +0 -484
- package/src/Stack/Stack.spec.md +0 -203
- package/src/Stack/Stack.test.tsx +0 -473
- package/src/Stack/StackItem.test.tsx +0 -105
- package/src/StatusDot/StatusDot.spec.md +0 -224
- package/src/StatusDot/StatusDot.test.tsx +0 -213
- package/src/Stepper/Step.spec.md +0 -200
- package/src/Stepper/Stepper.public.test.ts +0 -91
- package/src/Stepper/Stepper.spec.md +0 -327
- package/src/Stepper/Stepper.test.tsx +0 -2130
- package/src/Switch/Switch.focusRing.source-build.test.mjs +0 -81
- package/src/Switch/Switch.spec.md +0 -277
- package/src/Switch/Switch.test.tsx +0 -1136
- package/src/Switch/__tests__/Switch.a11y.chromium.spec.ts +0 -211
- package/src/Switch/__tests__/Switch.a11y.known-failures.ts +0 -28
- package/src/Switch/__tests__/Switch.a11y.states.ts +0 -275
- package/src/Switch/__tests__/Switch.a11y.test.tsx +0 -144
- package/src/TabList/Tab.spec.md +0 -201
- package/src/TabList/TabList.spec.md +0 -217
- package/src/TabList/TabList.test.tsx +0 -1755
- package/src/TabList/TabMenu.spec.md +0 -198
- package/src/TabList/__tests__/TabListOverflow.interaction.browser.spec.ts +0 -99
- package/src/TabList/__tests__/TabPressPointer.interaction.browser.spec.ts +0 -163
- package/src/TabList/__tests__/Tabs.a11y.chromium.spec.ts +0 -199
- package/src/TabList/__tests__/Tabs.a11y.known-failures.ts +0 -12
- package/src/TabList/__tests__/Tabs.a11y.renders.tsx +0 -92
- package/src/TabList/__tests__/Tabs.a11y.states.ts +0 -247
- package/src/TabList/__tests__/Tabs.a11y.test.tsx +0 -153
- package/src/Table/Table.perf.test.tsx +0 -569
- package/src/Table/Table.spec.md +0 -395
- package/src/Table/Table.test.tsx +0 -1883
- package/src/Table/TableHeaderCell.test.tsx +0 -291
- package/src/Table/TableSelectionToolbar.test.tsx +0 -109
- package/src/Table/__tests__/Table.a11y.chromium.spec.ts +0 -141
- package/src/Table/__tests__/Table.a11y.contracts.ts +0 -35
- package/src/Table/__tests__/Table.a11y.renders.tsx +0 -112
- package/src/Table/__tests__/Table.a11y.states.ts +0 -136
- package/src/Table/__tests__/Table.a11y.test.tsx +0 -198
- package/src/Table/__tests__/TableMinWidth.a11y.chromium.spec.ts +0 -75
- package/src/Table/__tests__/TableRowGlide.a11y.browser.spec.ts +0 -382
- package/src/Table/__tests__/TableScroll.a11y.chromium.spec.ts +0 -138
- package/src/Table/__tests__/TableStickyDividers.a11y.browser.spec.ts +0 -560
- package/src/Table/plugins/activation/useTableActivation.spec.md +0 -254
- package/src/Table/plugins/columnResize/useTableColumnResize.spec.md +0 -373
- package/src/Table/plugins/columnResize/useTableColumnResize.test.tsx +0 -941
- package/src/Table/plugins/columnSettings/useTableColumnSettings.test.tsx +0 -175
- package/src/Table/plugins/columnSettings/useTableColumnSettingsState.test.tsx +0 -296
- package/src/Table/plugins/filtering/useTableFilterState.test.tsx +0 -143
- package/src/Table/plugins/filtering/useTableFiltering.test.tsx +0 -357
- package/src/Table/plugins/groupedRows/useTableGroupedRows-perf.test.tsx +0 -112
- package/src/Table/plugins/groupedRows/useTableGroupedRows.test.tsx +0 -409
- package/src/Table/plugins/infiniteScroll/useTableInfiniteScroll.spec.md +0 -244
- package/src/Table/plugins/pagination/useTablePagination-perf.test.tsx +0 -232
- package/src/Table/plugins/pagination/useTablePagination.test.tsx +0 -666
- package/src/Table/plugins/rowExpansion/useTableRowExpansion.glide.test.tsx +0 -280
- package/src/Table/plugins/rowExpansion/useTableRowExpansion.test.tsx +0 -681
- package/src/Table/plugins/rowIndex/useTableRowIndex.test.tsx +0 -132
- package/src/Table/plugins/rowStatus/useTableRowStatus.public.test.ts +0 -92
- package/src/Table/plugins/rowStatus/useTableRowStatus.spec.md +0 -355
- package/src/Table/plugins/rowStatus/useTableRowStatus.test.tsx +0 -519
- package/src/Table/plugins/selection/useTableSelection-perf.test.tsx +0 -258
- package/src/Table/plugins/selection/useTableSelection.test.tsx +0 -424
- package/src/Table/plugins/selection/useTableSelectionState.test.tsx +0 -451
- package/src/Table/plugins/sortable/__tests__/useTableSortable.a11y.test.tsx +0 -174
- package/src/Table/plugins/sortable/__tests__/useTableSortable.a11y.ts +0 -86
- package/src/Table/plugins/sortable/useTableSortable.test.tsx +0 -786
- package/src/Table/plugins/sortable/useTableSortableState.test.tsx +0 -580
- package/src/Table/plugins/stickyColumns/useTableStickyColumns.test.tsx +0 -334
- package/src/Table/plugins/tree/useTableTreeData-perf.test.tsx +0 -163
- package/src/Table/plugins/tree/useTableTreeData.test.tsx +0 -992
- package/src/Table/plugins/tree/useTableTreeState.test.tsx +0 -740
- package/src/Table/plugins/virtualization/useTableVirtualization.spec.md +0 -266
- package/src/Table/tableContextMenu.test.tsx +0 -208
- package/src/Text/Text.spec.md +0 -193
- package/src/Text/Text.test.tsx +0 -405
- package/src/Text/useTruncation.test.tsx +0 -196
- package/src/TextArea/TextArea.fr10.test.tsx +0 -21
- package/src/TextArea/TextArea.spec.md +0 -300
- package/src/TextArea/TextArea.test.tsx +0 -1317
- package/src/TextInput/TextInput.fr10.test.tsx +0 -21
- package/src/TextInput/TextInput.spec.md +0 -260
- package/src/TextInput/TextInput.test.tsx +0 -1280
- package/src/TextInput/__tests__/TextInput.a11y.chromium.spec.ts +0 -214
- package/src/TextInput/__tests__/TextInput.a11y.known-failures.ts +0 -40
- package/src/TextInput/__tests__/TextInput.a11y.renders.tsx +0 -202
- package/src/TextInput/__tests__/TextInput.a11y.states.ts +0 -384
- package/src/TextInput/__tests__/TextInput.a11y.test.tsx +0 -130
- package/src/Thumbnail/Thumbnail.spec.md +0 -244
- package/src/Thumbnail/Thumbnail.test.tsx +0 -384
- package/src/TimeInput/NativeTimeInput.test.tsx +0 -343
- package/src/TimeInput/Presentation.test.tsx +0 -192
- package/src/TimeInput/TimeInput.fr10.test.tsx +0 -312
- package/src/TimeInput/TimeInput.spec.md +0 -270
- package/src/TimeInput/TimeInput.test.tsx +0 -907
- package/src/Timer/Timer.spec.md +0 -215
- package/src/Timer/Timer.test.tsx +0 -302
- package/src/Timestamp/Timestamp.spec.md +0 -246
- package/src/Timestamp/Timestamp.test.tsx +0 -1661
- package/src/Timestamp/tooltipEntries.test.ts +0 -566
- package/src/Toast/Toast.spec.md +0 -291
- package/src/Toast/ToastViewport.modalHost.test.tsx +0 -891
- package/src/Toast/ToastViewport.test.tsx +0 -3130
- package/src/Toast/useToast.test.tsx +0 -258
- package/src/ToggleButton/ToggleButton.spec.md +0 -256
- package/src/ToggleButton/ToggleButton.test.tsx +0 -1000
- package/src/ToggleButton/ToggleButtonGroup.spec.md +0 -195
- package/src/ToggleButton/__tests__/ToggleButton.a11y.chromium.spec.ts +0 -177
- package/src/ToggleButton/__tests__/ToggleButton.a11y.renders.tsx +0 -133
- package/src/ToggleButton/__tests__/ToggleButton.a11y.states.ts +0 -167
- package/src/ToggleButton/__tests__/ToggleButton.a11y.test.tsx +0 -103
- package/src/Token/Token.spec.md +0 -261
- package/src/Token/Token.test.tsx +0 -707
- package/src/Token/__tests__/TokenRemoveTarget.a11y.chromium.spec.ts +0 -87
- package/src/Tokenizer/Tokenizer.fr10.test.tsx +0 -28
- package/src/Tokenizer/Tokenizer.props-type.test.tsx +0 -126
- package/src/Tokenizer/Tokenizer.spec.md +0 -615
- package/src/Tokenizer/Tokenizer.ssr.test.tsx +0 -196
- package/src/Tokenizer/Tokenizer.test.tsx +0 -2216
- package/src/Tokenizer/TokenizerBottomSheet.test.tsx +0 -2738
- package/src/Tokenizer/__tests__/TokenizerEndLane.a11y.chromium.spec.ts +0 -505
- package/src/Toolbar/Toolbar.spec.md +0 -227
- package/src/Toolbar/Toolbar.test.tsx +0 -600
- package/src/Toolbar/__tests__/Toolbar.a11y.chromium.spec.ts +0 -176
- package/src/Toolbar/__tests__/Toolbar.a11y.renders.tsx +0 -63
- package/src/Toolbar/__tests__/Toolbar.a11y.states.ts +0 -142
- package/src/Toolbar/__tests__/Toolbar.a11y.test.tsx +0 -126
- package/src/Tooltip/Tooltip.spec.md +0 -267
- package/src/Tooltip/Tooltip.test.tsx +0 -819
- package/src/Tooltip/__tests__/Tooltip.a11y.chromium.spec.ts +0 -141
- package/src/Tooltip/__tests__/Tooltip.a11y.renders.tsx +0 -86
- package/src/Tooltip/__tests__/Tooltip.a11y.states.ts +0 -132
- package/src/Tooltip/__tests__/Tooltip.a11y.test.tsx +0 -126
- package/src/Tooltip/__tests__/TooltipFocusReturn.a11y.chromium.spec.ts +0 -130
- package/src/Tooltip/__tests__/TooltipHoverBridge.a11y.chromium.spec.ts +0 -75
- package/src/Tooltip/__tests__/TooltipTallContent.a11y.browser.spec.ts +0 -673
- package/src/Tooltip/__tests__/TooltipTallContentTracking.a11y.browser.spec.ts +0 -1162
- package/src/Tooltip/useTooltip.test.tsx +0 -483
- package/src/TopNav/TopNav.spec.md +0 -190
- package/src/TopNav/TopNav.test.tsx +0 -863
- package/src/TopNav/TopNavHeading.spec.md +0 -211
- package/src/TopNav/TopNavItem.spec.md +0 -189
- package/src/TopNav/TopNavMegaMenu.reduced-motion.source-build.test.mjs +0 -83
- package/src/TopNav/TopNavMegaMenu.spec.md +0 -190
- package/src/TopNav/TopNavMegaMenu.test.tsx +0 -1153
- package/src/TopNav/TopNavMegaMenuFeaturedCard.spec.md +0 -150
- package/src/TopNav/TopNavMegaMenuItem.spec.md +0 -155
- package/src/TopNav/TopNavMenu.spec.md +0 -202
- package/src/TopNav/TopNavMenu.test.tsx +0 -502
- package/src/TopNav/__tests__/TopNavMobileLongHeading.a11y.chromium.spec.ts +0 -75
- package/src/TreeList/TreeList.spec.md +0 -328
- package/src/TreeList/TreeList.test.tsx +0 -1737
- package/src/TreeList/__tests__/TreeList.a11y.chromium.spec.ts +0 -138
- package/src/TreeList/__tests__/TreeList.a11y.renders.tsx +0 -44
- package/src/TreeList/__tests__/TreeList.a11y.states.ts +0 -164
- package/src/TreeList/__tests__/TreeList.a11y.test.tsx +0 -138
- package/src/TreeList/__tests__/TreeListGuideAlignment.interaction.browser.spec.ts +0 -127
- package/src/TreeList/__tests__/TreeListRowNames.a11y.chromium.spec.ts +0 -83
- package/src/Typeahead/BaseTypeahead.emptyState.a11y.chromium.spec.ts +0 -138
- package/src/Typeahead/BaseTypeahead.spec.md +0 -270
- package/src/Typeahead/BaseTypeahead.test.tsx +0 -553
- package/src/Typeahead/Typeahead.fr10.test.tsx +0 -28
- package/src/Typeahead/Typeahead.spec.md +0 -257
- package/src/Typeahead/Typeahead.test.tsx +0 -2984
- package/src/Typeahead/TypeaheadItem.test.tsx +0 -134
- package/src/VStack/VStack.test.tsx +0 -128
- package/src/VisuallyHidden/VisuallyHidden.spec.md +0 -170
- package/src/VisuallyHidden/VisuallyHidden.test.tsx +0 -115
- package/src/__tests__/BusyOwnerAnnouncement.test.tsx +0 -853
- package/src/__tests__/CardPress.a11y.chromium.spec.ts +0 -353
- package/src/__tests__/DisabledTriggerFocus.a11y.chromium.spec.ts +0 -87
- package/src/__tests__/PressModality.a11y.chromium.spec.ts +0 -392
- package/src/__tests__/PressedState.a11y.chromium.spec.ts +0 -814
- package/src/__tests__/TestIcon.tsx +0 -26
- package/src/__tests__/babelPluginAddExtensions.test.ts +0 -190
- package/src/__tests__/fastRoleQueries.ts +0 -66
- package/src/__tests__/forcedColors.ts +0 -60
- package/src/__tests__/internalModulesNotExperimental.test.ts +0 -88
- package/src/__tests__/pressState.ts +0 -216
- package/src/__tests__/stubMatchMedia.test.ts +0 -79
- package/src/__tests__/stubMatchMedia.ts +0 -95
- package/src/__tests__/stylexDeclarations.ts +0 -51
- package/src/__tests__/unthemedLength.ts +0 -34
- package/src/docPropLiterals.test.ts +0 -964
- package/src/docPropReferences.test.ts +0 -178
- package/src/docs-script.test.mjs +0 -55
- package/src/gestures/__tests__/GestureHost.test.ts +0 -2136
- package/src/gestures/__tests__/LongPressGestureRecognizer.test.ts +0 -141
- package/src/gestures/__tests__/PanGestureRecognizer.test.ts +0 -327
- package/src/gestures/__tests__/PinchGestureRecognizer.test.ts +0 -74
- package/src/gestures/__tests__/PressTracker.test.ts +0 -275
- package/src/gestures/__tests__/ScreenEdgePanGestureRecognizer.test.ts +0 -498
- package/src/gestures/__tests__/SwipeGestureRecognizer.test.ts +0 -108
- package/src/gestures/__tests__/TapGestureRecognizer.test.ts +0 -154
- package/src/gestures/__tests__/VelocityTracker.streams.test.ts +0 -243
- package/src/gestures/__tests__/VelocityTracker.test.ts +0 -261
- package/src/gestures/__tests__/arena.test.ts +0 -864
- package/src/gestures/__tests__/arenaContinuous.test.ts +0 -207
- package/src/gestures/__tests__/arenaPinchSwipeEdge.test.ts +0 -193
- package/src/gestures/__tests__/arenaTapLongPress.test.ts +0 -211
- package/src/gestures/__tests__/browser/build-fixture.mjs +0 -46
- package/src/gestures/__tests__/browser/fixture.ts +0 -307
- package/src/gestures/__tests__/browser/gestures.interaction.browser.spec.ts +0 -1137
- package/src/gestures/__tests__/browserNegotiation.test.ts +0 -293
- package/src/gestures/__tests__/fixtures/pointer-streams/README.md +0 -82
- package/src/gestures/__tests__/fixtures/pointer-streams/android-chromium-fling.json +0 -130
- package/src/gestures/__tests__/fixtures/pointer-streams/android-chromium-slow.json +0 -860
- package/src/gestures/__tests__/fixtures/pointer-streams/android-chromium-stop-release.json +0 -357
- package/src/gestures/__tests__/fixtures/pointer-streams/android-firefox-fling.json +0 -66
- package/src/gestures/__tests__/fixtures/pointer-streams/android-firefox-slow.json +0 -268
- package/src/gestures/__tests__/fixtures/pointer-streams/android-firefox-stop-release.json +0 -128
- package/src/gestures/__tests__/fixtures/pointer-streams/capture.html +0 -123
- package/src/gestures/__tests__/fixtures/pointer-streams/desktop-chromium-mouse-fling.json +0 -64
- package/src/gestures/__tests__/fixtures/pointer-streams/desktop-chromium-mouse-slow.json +0 -268
- package/src/gestures/__tests__/fixtures/pointer-streams/desktop-chromium-mouse-stop-release.json +0 -124
- package/src/gestures/__tests__/fixtures/pointer-streams/desktop-chromium-pen-fling.json +0 -64
- package/src/gestures/__tests__/fixtures/pointer-streams/desktop-chromium-pen-slow.json +0 -268
- package/src/gestures/__tests__/fixtures/pointer-streams/desktop-chromium-pen-stop-release.json +0 -124
- package/src/gestures/__tests__/fixtures/pointer-streams/desktop-chromium-touch-fling.json +0 -84
- package/src/gestures/__tests__/fixtures/pointer-streams/desktop-chromium-touch-slow.json +0 -381
- package/src/gestures/__tests__/fixtures/pointer-streams/desktop-chromium-touch-stop-release.json +0 -171
- package/src/gestures/__tests__/fixtures/pointer-streams/ios-safari-fling.json +0 -45
- package/src/gestures/__tests__/fixtures/pointer-streams/ios-safari-slow.json +0 -101
- package/src/gestures/__tests__/fixtures/pointer-streams/ios-safari-stop-release.json +0 -130
- package/src/gestures/__tests__/haptics.test.ts +0 -187
- package/src/gestures/__tests__/harness.ts +0 -252
- package/src/gestures/__tests__/pressTrackerFactory.test.ts +0 -59
- package/src/gestures/__tests__/recognizerKind.test.ts +0 -78
- package/src/gestures/__tests__/touchActionRule.test.ts +0 -54
- package/src/gestures/__tests__/useGestureRecognizer.test.tsx +0 -570
- package/src/hooks/__tests__/MenuPressShadowRoot.a11y.chromium.spec.ts +0 -144
- package/src/hooks/__tests__/menuLinkClicks.ts +0 -71
- package/src/hooks/actionGroupTracker.exports.test.ts +0 -28
- package/src/hooks/actionGroupTracker.test.ts +0 -510
- package/src/hooks/computeOverflow.test.ts +0 -422
- package/src/hooks/deprecatedApis.public.test.ts +0 -33
- package/src/hooks/menuPressGesture.test.ts +0 -253
- package/src/hooks/scrollKeyboardDelegation.test.ts +0 -188
- package/src/hooks/scrollbarGutter.test.ts +0 -167
- package/src/hooks/useAnnounce.modal.test.tsx +0 -1291
- package/src/hooks/useAnnounce.replace.test.tsx +0 -516
- package/src/hooks/useAnnounce.test.tsx +0 -293
- package/src/hooks/useAnnounceRenderedText.adopters.test.tsx +0 -235
- package/src/hooks/useAutoMediaMode.test.ts +0 -109
- package/src/hooks/useBusyAnnouncement.adopters.test.tsx +0 -69
- package/src/hooks/useBusyAnnouncement.test.tsx +0 -112
- package/src/hooks/useClickableContainer.test.tsx +0 -236
- package/src/hooks/useClipboard.test.tsx +0 -151
- package/src/hooks/useContainerReveal.test.tsx +0 -163
- package/src/hooks/useDevWarning.test.tsx +0 -45
- package/src/hooks/useEntryAnimation.ssr.test.tsx +0 -143
- package/src/hooks/useFocusTrap.test.tsx +0 -635
- package/src/hooks/useFocusTrapEscapeShim.test.tsx +0 -166
- package/src/hooks/useGridFocus.test.tsx +0 -165
- package/src/hooks/useHotkeys.test.ts +0 -265
- package/src/hooks/useImageMode.test.ts +0 -133
- package/src/hooks/useInputContainer.test.tsx +0 -180
- package/src/hooks/useInputStatusIcon.test.tsx +0 -240
- package/src/hooks/useIsHydrating.test.tsx +0 -70
- package/src/hooks/useKeyboardHint.test.tsx +0 -92
- package/src/hooks/useListFocus.test.tsx +0 -664
- package/src/hooks/useLongPress.test.tsx +0 -152
- package/src/hooks/useMediaQuery.test.ts +0 -165
- package/src/hooks/useMenuHover.test.tsx +0 -536
- package/src/hooks/useMenuPress.test.tsx +0 -565
- package/src/hooks/useMergedRefs.test.tsx +0 -62
- package/src/hooks/useOverflow.test.ts +0 -504
- package/src/hooks/useRenamedProp.test.tsx +0 -95
- package/src/hooks/useScrollLock.test.ts +0 -248
- package/src/hooks/useScrollOverflow.test.tsx +0 -323
- package/src/hooks/useScrollableArea.test.tsx +0 -836
- package/src/hooks/useStreamingText.test.ts +0 -237
- package/src/hooks/useTreeFocus.test.tsx +0 -351
- package/src/hooks/useTypeahead.test.tsx +0 -351
- package/src/i18n/InternationalizationProvider.spec.md +0 -252
- package/src/i18n/__tests__/InternationalizationProvider.test.tsx +0 -128
- package/src/i18n/__tests__/e2e-pagination.test.tsx +0 -172
- package/src/i18n/__tests__/e2e-powersearch.test.tsx +0 -166
- package/src/i18n/__tests__/getLocaleDirection.test.ts +0 -40
- package/src/i18n/__tests__/resolve.test.ts +0 -325
- package/src/i18n/__tests__/useCollator.test.tsx +0 -115
- package/src/i18n/__tests__/useDirection.test.tsx +0 -66
- package/src/i18n/__tests__/useLocale.test.tsx +0 -52
- package/src/i18n/useTranslator.test.tsx +0 -267
- package/src/inputFontFloor.test.ts +0 -106
- package/src/internal/tokenizer-presentation.test.ts +0 -29
- package/src/internal/touch.test.ts +0 -44
- package/src/motion/__tests__/animatedLayout.interaction.browser.spec.ts +0 -1613
- package/src/motion/__tests__/animatedLayoutAdopters.interaction.browser.spec.ts +0 -217
- package/src/motion/__tests__/animatedLayoutBundle.ts +0 -17
- package/src/motion/__tests__/animator.interaction.browser.spec.ts +0 -843
- package/src/motion/__tests__/motionBundle.ts +0 -14
- package/src/motion/animatedLayout.boundary.test.ts +0 -155
- package/src/motion/animatedLayout.swap.test.ts +0 -827
- package/src/motion/animatedLayout.test.ts +0 -1154
- package/src/motion/animatedLayoutAdopters.test.tsx +0 -500
- package/src/motion/animator.test.ts +0 -3746
- package/src/motion/bezier.test.ts +0 -52
- package/src/motion/coast.test.ts +0 -99
- package/src/motion/decay.test.ts +0 -120
- package/src/motion/projection.test.ts +0 -107
- package/src/motion/reducedMotion.test.ts +0 -129
- package/src/motion/reducedMotionPlan.test.ts +0 -208
- package/src/motion/resolveMotion.test.ts +0 -123
- package/src/motion/resolveSnapPoint.test.ts +0 -189
- package/src/motion/roles.test.ts +0 -273
- package/src/motion/rubberBand.test.ts +0 -88
- package/src/motion/selectPage.test.ts +0 -31
- package/src/motion/spring.test.ts +0 -650
- package/src/motion/uikit/gestures.test.ts +0 -89
- package/src/motion/uikit/springRows.test.ts +0 -71
- package/src/motion/uikit/tableImports.test.ts +0 -293
- package/src/motion/uikit/uikit.test.ts +0 -364
- package/src/motion/uikit/values.test.ts +0 -121
- package/src/naming.test.ts +0 -44
- package/src/postinstall.test.mjs +0 -216
- package/src/reset.a11y.chromium.spec.ts +0 -238
- package/src/reset.test.ts +0 -205
- package/src/reset.until-found.a11y.browser.spec.ts +0 -76
- package/src/reset.until-found.test.ts +0 -32
- package/src/serverSafeComponents.test.ts +0 -675
- package/src/stylexValidation.source-build.test.mjs +0 -42
- package/src/tailwind-theme.test.ts +0 -153
- package/src/theme/MediaTheme.dom.test.tsx +0 -97
- package/src/theme/Theme.test.tsx +0 -337
- package/src/theme/__tests__/SpacingRootFontSize.a11y.chromium.spec.ts +0 -199
- package/src/theme/contrast.test.ts +0 -94
- package/src/theme/declarationBoundary.test.ts +0 -126
- package/src/theme/defineTheme.test.ts +0 -2028
- package/src/theme/derivedVarRegistry.test.ts +0 -592
- package/src/theme/expandColorScale.test.ts +0 -651
- package/src/theme/expandMotionScale.test.ts +0 -145
- package/src/theme/expandRadiusScale.test.ts +0 -59
- package/src/theme/expandTypeScale.test.ts +0 -351
- package/src/theme/extensibleAxes.test.ts +0 -432
- package/src/theme/generateThemeRules.test.ts +0 -1870
- package/src/theme/hct.test.ts +0 -93
- package/src/theme/onMediaTokens.test.ts +0 -431
- package/src/theme/publicThemeHelperContract.test.ts +0 -67
- package/src/theme/syntax/defineSyntaxTheme.diff.test.ts +0 -91
- package/src/theme/syntax/serverSafeSyntax.test.ts +0 -68
- package/src/theme/syntax/useSyntaxTheme.diff.test.tsx +0 -40
- package/src/theme/themeAdaptations.test.ts +0 -1795
- package/src/theme/themeMotion.test.ts +0 -620
- package/src/theme/themeRegistry.test.ts +0 -59
- package/src/theme/themingTargets.test.ts +0 -589
- package/src/theme/tokenValueCompat.test.ts +0 -204
- package/src/theme/tokens.test.ts +0 -441
- package/src/theme/useTheme.test.tsx +0 -322
- package/src/unicodePropertyEscapes.source-build.test.mjs +0 -69
- package/src/utils/chainEventHandlers.test.ts +0 -39
- package/src/utils/characters.test.ts +0 -141
- package/src/utils/color.test.ts +0 -233
- package/src/utils/composeEventHandlers.test.ts +0 -71
- package/src/utils/customPropertyArmLayering.test.ts +0 -131
- package/src/utils/dateParser.test.ts +0 -402
- package/src/utils/dateTypes.test.ts +0 -40
- package/src/utils/devWarning.test.ts +0 -56
- package/src/utils/edgeAutoscroll.test.ts +0 -192
- package/src/utils/focusReturn.test.ts +0 -119
- package/src/utils/getKey.test.ts +0 -25
- package/src/utils/getNodeText.test.ts +0 -24
- package/src/utils/hitArea.test.ts +0 -628
- package/src/utils/ime.test.ts +0 -32
- package/src/utils/inputAria.test.ts +0 -39
- package/src/utils/interactionModality.test.ts +0 -144
- package/src/utils/isApplePlatform.test.ts +0 -61
- package/src/utils/mergeProps.test.ts +0 -166
- package/src/utils/mergeRefs.test.ts +0 -48
- package/src/utils/parseStyleKey.test.ts +0 -56
- package/src/utils/plainDate.test.ts +0 -938
- package/src/utils/presentation.test.ts +0 -380
- package/src/utils/pressFeedback.test.ts +0 -1012
- package/src/utils/pressGesture.test.ts +0 -332
- package/src/utils/pressPaintPriority.test.ts +0 -80
- package/src/utils/pressPaintSource.test.ts +0 -65
- package/src/utils/pressableCoverage.test.ts +0 -192
- package/src/utils/primaryClick.test.ts +0 -38
- package/src/utils/safeUrl.test.ts +0 -101
- package/src/utils/sharedMutationObserver.test.ts +0 -106
- package/src/utils/sharedResizeObserver.test.ts +0 -283
- package/src/utils/themeProps.test.ts +0 -62
- package/src/utils/timeFormat.test.ts +0 -204
- package/src/utils/timeParser.test.ts +0 -295
- package/src/utils/touchPresentation.test.ts +0 -462
- package/src/utils/unicodeDigits.test.ts +0 -44
|
@@ -1,3154 +0,0 @@
|
|
|
1
|
-
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* @file BottomSheet.test.tsx
|
|
5
|
-
* @input Uses vitest, @testing-library/react, BottomSheet component
|
|
6
|
-
* @output Unit tests for BottomSheet behavior and observed content anatomy
|
|
7
|
-
* @position Core testing; validates BottomSheet.tsx implementation
|
|
8
|
-
*
|
|
9
|
-
* SYNC: When BottomSheet.tsx changes, update tests to match new behavior
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
import {describe, it, expect, vi, beforeEach, afterEach} from 'vitest';
|
|
13
|
-
import {act, render, screen, fireEvent} from '@testing-library/react';
|
|
14
|
-
import {createRef, useState} from 'react';
|
|
15
|
-
import {BottomSheet} from './BottomSheet';
|
|
16
|
-
import {BottomSheetEscapeContext} from './bottomSheetEscape';
|
|
17
|
-
import {resetBottomSheetStackForTests} from './bottomSheetStack';
|
|
18
|
-
import {BottomSheetSwitcher} from './BottomSheetSwitcher';
|
|
19
|
-
import {__resetInteractionModalityForTest} from '../utils/interactionModality';
|
|
20
|
-
|
|
21
|
-
// A sheet has no stops unless its host asks for them, so the tests that
|
|
22
|
-
// exercise detents pass their own. These three — a 14% peek, a half-height
|
|
23
|
-
// stop, and a 92% stop the Tall budget already covers — are the geometry the
|
|
24
|
-
// height assertions below are written against.
|
|
25
|
-
const SNAP_POINTS: ReadonlyArray<number> = [0.14, 0.5, 0.92];
|
|
26
|
-
|
|
27
|
-
// jsdom doesn't implement <dialog> open/close or pointer capture; stub them.
|
|
28
|
-
beforeEach(() => {
|
|
29
|
-
resetBottomSheetStackForTests();
|
|
30
|
-
HTMLDialogElement.prototype.showModal = vi.fn(function (
|
|
31
|
-
this: HTMLDialogElement,
|
|
32
|
-
) {
|
|
33
|
-
this.setAttribute('open', '');
|
|
34
|
-
});
|
|
35
|
-
HTMLDialogElement.prototype.show = vi.fn(function (this: HTMLDialogElement) {
|
|
36
|
-
this.setAttribute('open', '');
|
|
37
|
-
});
|
|
38
|
-
// A non-modal sheet is a manual popover; jsdom's stand-ins keep the same
|
|
39
|
-
// `open` marker the dialog paths use (the Drawer tests do the same).
|
|
40
|
-
HTMLElement.prototype.showPopover = vi.fn(function (this: HTMLElement) {
|
|
41
|
-
this.setAttribute('open', '');
|
|
42
|
-
});
|
|
43
|
-
HTMLElement.prototype.hidePopover = vi.fn(function (this: HTMLElement) {
|
|
44
|
-
this.removeAttribute('open');
|
|
45
|
-
});
|
|
46
|
-
HTMLDialogElement.prototype.close = vi.fn(function (this: HTMLDialogElement) {
|
|
47
|
-
this.removeAttribute('open');
|
|
48
|
-
});
|
|
49
|
-
if (!Element.prototype.setPointerCapture) {
|
|
50
|
-
Element.prototype.setPointerCapture = vi.fn();
|
|
51
|
-
Element.prototype.releasePointerCapture = vi.fn();
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
vi.stubGlobal(
|
|
55
|
-
'matchMedia',
|
|
56
|
-
vi.fn().mockReturnValue({
|
|
57
|
-
matches: false,
|
|
58
|
-
media: '',
|
|
59
|
-
onchange: null,
|
|
60
|
-
addEventListener: vi.fn(),
|
|
61
|
-
removeEventListener: vi.fn(),
|
|
62
|
-
addListener: vi.fn(),
|
|
63
|
-
removeListener: vi.fn(),
|
|
64
|
-
dispatchEvent: vi.fn(),
|
|
65
|
-
}),
|
|
66
|
-
);
|
|
67
|
-
vi.stubGlobal(
|
|
68
|
-
'requestAnimationFrame',
|
|
69
|
-
vi.fn((callback: FrameRequestCallback) => {
|
|
70
|
-
callback(0);
|
|
71
|
-
return 1;
|
|
72
|
-
}),
|
|
73
|
-
);
|
|
74
|
-
vi.stubGlobal('cancelAnimationFrame', vi.fn());
|
|
75
|
-
window.scrollTo = vi.fn();
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
afterEach(() => {
|
|
79
|
-
vi.unstubAllGlobals();
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
function getSheet(): HTMLElement {
|
|
83
|
-
const sheet = document.querySelector<HTMLElement>('.astryx-bottom-sheet');
|
|
84
|
-
if (!sheet) {
|
|
85
|
-
throw new Error('sheet panel not found');
|
|
86
|
-
}
|
|
87
|
-
return sheet;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
function getBody(): HTMLElement {
|
|
91
|
-
const body = getSheet().lastElementChild;
|
|
92
|
-
if (!(body instanceof HTMLElement)) {
|
|
93
|
-
throw new Error('sheet scroll body not found');
|
|
94
|
-
}
|
|
95
|
-
return body;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
function getPositioner(): HTMLElement {
|
|
99
|
-
const positioner = getSheet().parentElement;
|
|
100
|
-
if (!(positioner instanceof HTMLElement)) {
|
|
101
|
-
throw new Error('sheet positioner not found');
|
|
102
|
-
}
|
|
103
|
-
return positioner;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
function rect({top, bottom}: {top: number; bottom: number}): DOMRect {
|
|
107
|
-
return {
|
|
108
|
-
x: 0,
|
|
109
|
-
y: top,
|
|
110
|
-
top,
|
|
111
|
-
right: 400,
|
|
112
|
-
bottom,
|
|
113
|
-
left: 0,
|
|
114
|
-
width: 400,
|
|
115
|
-
height: bottom - top,
|
|
116
|
-
toJSON: () => ({}),
|
|
117
|
-
};
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
function resizeEntry(
|
|
121
|
-
borderBoxHeight: number,
|
|
122
|
-
contentBoxHeight = borderBoxHeight,
|
|
123
|
-
): ResizeObserverEntry {
|
|
124
|
-
// Core typechecks its tests, so this partial stands in for the full entry
|
|
125
|
-
// the way sharedResizeObserver does: only the fields the sheet reads.
|
|
126
|
-
const entry: Partial<ResizeObserverEntry> = {
|
|
127
|
-
borderBoxSize: [{blockSize: borderBoxHeight, inlineSize: 100}],
|
|
128
|
-
contentRect: rect({top: 0, bottom: contentBoxHeight}),
|
|
129
|
-
};
|
|
130
|
-
return entry as ResizeObserverEntry;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
function mockVisualViewport(height: number, offsetTop = 0) {
|
|
134
|
-
const viewport = Object.assign(new EventTarget(), {
|
|
135
|
-
height,
|
|
136
|
-
offsetTop,
|
|
137
|
-
});
|
|
138
|
-
vi.stubGlobal('visualViewport', viewport);
|
|
139
|
-
return viewport;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
// The layout viewport — `100dvh`, `window.innerHeight` — which is what the
|
|
143
|
-
// sheet's height budget and its detents are measured against. Deliberately
|
|
144
|
-
// separate from the visual viewport above: the mobile keyboard shrinks that
|
|
145
|
-
// one and leaves this one alone, and the sheet has to tell them apart.
|
|
146
|
-
function mockWindowHeight(height: number) {
|
|
147
|
-
vi.stubGlobal('innerHeight', height);
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
function mockIOSWebKit() {
|
|
151
|
-
const navigatorMock = Object.create(window.navigator);
|
|
152
|
-
Object.defineProperties(navigatorMock, {
|
|
153
|
-
userAgent: {
|
|
154
|
-
configurable: true,
|
|
155
|
-
value:
|
|
156
|
-
'Mozilla/5.0 (iPhone; CPU iPhone OS 18_0 like Mac OS X) AppleWebKit/605.1.15',
|
|
157
|
-
},
|
|
158
|
-
maxTouchPoints: {configurable: true, value: 5},
|
|
159
|
-
});
|
|
160
|
-
vi.stubGlobal('navigator', navigatorMock);
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
interface ResizeObserverRecord {
|
|
164
|
-
callback: ResizeObserverCallback;
|
|
165
|
-
observed: Set<Element>;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
function mockResizeObserverInstances(): ResizeObserverRecord[] {
|
|
169
|
-
const observers: ResizeObserverRecord[] = [];
|
|
170
|
-
class ResizeObserverMock {
|
|
171
|
-
callback: ResizeObserverCallback;
|
|
172
|
-
observed = new Set<Element>();
|
|
173
|
-
|
|
174
|
-
constructor(callback: ResizeObserverCallback) {
|
|
175
|
-
this.callback = callback;
|
|
176
|
-
observers.push(this);
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
observe(target: Element) {
|
|
180
|
-
this.observed.add(target);
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
unobserve(target: Element) {
|
|
184
|
-
this.observed.delete(target);
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
disconnect() {
|
|
188
|
-
this.observed.clear();
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
vi.stubGlobal('ResizeObserver', ResizeObserverMock);
|
|
192
|
-
return observers;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
function finishSheetExit() {
|
|
196
|
-
fireEvent.transitionEnd(getSheet(), {propertyName: 'transform'});
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
function ExitHarness({
|
|
200
|
-
hasScrim,
|
|
201
|
-
snapPoints,
|
|
202
|
-
}: {
|
|
203
|
-
hasScrim?: boolean;
|
|
204
|
-
snapPoints?: ReadonlyArray<number>;
|
|
205
|
-
}) {
|
|
206
|
-
const [isOpen, setIsOpen] = useState(true);
|
|
207
|
-
return (
|
|
208
|
-
<BottomSheet
|
|
209
|
-
isOpen={isOpen}
|
|
210
|
-
onOpenChange={setIsOpen}
|
|
211
|
-
label="Filters"
|
|
212
|
-
snapPoints={snapPoints}
|
|
213
|
-
hasScrim={hasScrim}>
|
|
214
|
-
<button type="button" onClick={() => setIsOpen(false)}>
|
|
215
|
-
Close sheet
|
|
216
|
-
</button>
|
|
217
|
-
</BottomSheet>
|
|
218
|
-
);
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
// The grab handle is the panel's first child (decorative, aria-hidden).
|
|
222
|
-
function getHandle(): HTMLElement {
|
|
223
|
-
const handle = getSheet().querySelector<HTMLElement>('[aria-hidden="true"]');
|
|
224
|
-
if (!handle) {
|
|
225
|
-
throw new Error('grab handle not found');
|
|
226
|
-
}
|
|
227
|
-
return handle;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
// Drive a pointer drag on the grab handle. jsdom PointerEvents don't carry
|
|
231
|
-
// clientY, so dispatch plain events with the coords the handlers read.
|
|
232
|
-
function drag(
|
|
233
|
-
handle: HTMLElement,
|
|
234
|
-
points: {y: number}[],
|
|
235
|
-
pointerType: 'mouse' | 'touch' | 'pen' = 'mouse',
|
|
236
|
-
) {
|
|
237
|
-
const [down, ...rest] = points;
|
|
238
|
-
fireEvent.pointerDown(handle, {
|
|
239
|
-
pointerId: 1,
|
|
240
|
-
clientY: down.y,
|
|
241
|
-
button: 0,
|
|
242
|
-
isPrimary: true,
|
|
243
|
-
pointerType,
|
|
244
|
-
});
|
|
245
|
-
for (const p of rest) {
|
|
246
|
-
fireEvent.pointerMove(handle, {pointerId: 1, clientY: p.y, pointerType});
|
|
247
|
-
}
|
|
248
|
-
const last = points[points.length - 1];
|
|
249
|
-
fireEvent.pointerUp(handle, {pointerId: 1, clientY: last.y, pointerType});
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
function fireTimedPointer(
|
|
253
|
-
target: Element,
|
|
254
|
-
type: 'pointerdown' | 'pointermove' | 'pointerup',
|
|
255
|
-
{time, y}: {time: number; y: number},
|
|
256
|
-
) {
|
|
257
|
-
const event = new Event(type, {bubbles: true, cancelable: true});
|
|
258
|
-
Object.defineProperties(event, {
|
|
259
|
-
button: {value: 0},
|
|
260
|
-
clientY: {value: y},
|
|
261
|
-
isPrimary: {value: true},
|
|
262
|
-
pointerId: {value: 1},
|
|
263
|
-
timeStamp: {value: time},
|
|
264
|
-
});
|
|
265
|
-
return fireEvent(target, event);
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
describe('BottomSheet', () => {
|
|
269
|
-
it('renders children when open and applies the accessible label', () => {
|
|
270
|
-
render(
|
|
271
|
-
<BottomSheet isOpen onOpenChange={() => {}} label="Filters">
|
|
272
|
-
Sheet content
|
|
273
|
-
</BottomSheet>,
|
|
274
|
-
);
|
|
275
|
-
const dialog = screen.getByRole('dialog');
|
|
276
|
-
expect(dialog).toBeInTheDocument();
|
|
277
|
-
expect(dialog).toHaveAccessibleName('Filters');
|
|
278
|
-
expect(screen.getByText('Sheet content')).toBeInTheDocument();
|
|
279
|
-
});
|
|
280
|
-
|
|
281
|
-
it('forwards DOM props and refs to the visual panel, not the dialog host', () => {
|
|
282
|
-
const panelRef = createRef<HTMLDivElement>();
|
|
283
|
-
const onClick = vi.fn();
|
|
284
|
-
render(
|
|
285
|
-
<BottomSheet
|
|
286
|
-
ref={panelRef}
|
|
287
|
-
isOpen
|
|
288
|
-
onOpenChange={() => {}}
|
|
289
|
-
label="Filters"
|
|
290
|
-
data-testid="filters-panel"
|
|
291
|
-
data-sheet-owner="search"
|
|
292
|
-
className="custom-panel"
|
|
293
|
-
onClick={onClick}>
|
|
294
|
-
Content
|
|
295
|
-
</BottomSheet>,
|
|
296
|
-
);
|
|
297
|
-
|
|
298
|
-
const panel = screen.getByTestId('filters-panel');
|
|
299
|
-
const dialog = screen.getByRole('dialog');
|
|
300
|
-
expect(panelRef.current).toBe(panel);
|
|
301
|
-
expect(panel.tagName).toBe('DIV');
|
|
302
|
-
expect(panel).toHaveClass('astryx-bottom-sheet', 'custom-panel');
|
|
303
|
-
expect(panel).toHaveAttribute('data-sheet-owner', 'search');
|
|
304
|
-
expect(dialog).not.toHaveAttribute('data-testid');
|
|
305
|
-
|
|
306
|
-
fireEvent.click(panel);
|
|
307
|
-
expect(onClick).toHaveBeenCalledTimes(1);
|
|
308
|
-
});
|
|
309
|
-
|
|
310
|
-
it('keeps consumer content first and last inside the observed content box', () => {
|
|
311
|
-
render(
|
|
312
|
-
<BottomSheet isOpen onOpenChange={() => {}} label="Filters">
|
|
313
|
-
<div data-testid="consumer-content">Sheet content</div>
|
|
314
|
-
</BottomSheet>,
|
|
315
|
-
);
|
|
316
|
-
const body = getBody();
|
|
317
|
-
const consumer = screen.getByTestId('consumer-content');
|
|
318
|
-
|
|
319
|
-
expect(body.children).toHaveLength(1);
|
|
320
|
-
const content = body.lastElementChild;
|
|
321
|
-
expect(content).toHaveAttribute('data-scroll-content');
|
|
322
|
-
expect(content?.lastElementChild).toBe(consumer);
|
|
323
|
-
expect(consumer.matches(':first-child')).toBe(true);
|
|
324
|
-
expect(consumer.matches(':last-child')).toBe(true);
|
|
325
|
-
});
|
|
326
|
-
|
|
327
|
-
it('does not show when isOpen is false', () => {
|
|
328
|
-
render(
|
|
329
|
-
<BottomSheet isOpen={false} onOpenChange={() => {}} label="Filters">
|
|
330
|
-
Hidden
|
|
331
|
-
</BottomSheet>,
|
|
332
|
-
);
|
|
333
|
-
expect(HTMLDialogElement.prototype.showModal).not.toHaveBeenCalled();
|
|
334
|
-
});
|
|
335
|
-
|
|
336
|
-
it('opens modally (showModal + aria-modal)', () => {
|
|
337
|
-
render(
|
|
338
|
-
<BottomSheet isOpen onOpenChange={() => {}} label="Filters">
|
|
339
|
-
Content
|
|
340
|
-
</BottomSheet>,
|
|
341
|
-
);
|
|
342
|
-
expect(HTMLDialogElement.prototype.showModal).toHaveBeenCalled();
|
|
343
|
-
expect(screen.getByRole('dialog')).toHaveAttribute('aria-modal', 'true');
|
|
344
|
-
});
|
|
345
|
-
|
|
346
|
-
it('requests close on Escape', () => {
|
|
347
|
-
const onOpenChange = vi.fn();
|
|
348
|
-
render(
|
|
349
|
-
<BottomSheet isOpen onOpenChange={onOpenChange} label="Filters">
|
|
350
|
-
Content
|
|
351
|
-
</BottomSheet>,
|
|
352
|
-
);
|
|
353
|
-
fireEvent.keyDown(screen.getByRole('dialog'), {key: 'Escape'});
|
|
354
|
-
expect(onOpenChange).toHaveBeenCalledWith(false);
|
|
355
|
-
});
|
|
356
|
-
|
|
357
|
-
it('ignores Escape while an IME composition is active', () => {
|
|
358
|
-
const onOpenChange = vi.fn();
|
|
359
|
-
render(
|
|
360
|
-
<BottomSheet isOpen onOpenChange={onOpenChange} label="Filters">
|
|
361
|
-
<input aria-label="Search" />
|
|
362
|
-
</BottomSheet>,
|
|
363
|
-
);
|
|
364
|
-
const dialog = screen.getByRole('dialog');
|
|
365
|
-
|
|
366
|
-
fireEvent.keyDown(dialog, {key: 'Escape', isComposing: true});
|
|
367
|
-
fireEvent.keyDown(dialog, {key: 'Escape', keyCode: 229});
|
|
368
|
-
|
|
369
|
-
expect(onOpenChange).not.toHaveBeenCalled();
|
|
370
|
-
});
|
|
371
|
-
|
|
372
|
-
it('claims the composing Escape so no native close request follows', () => {
|
|
373
|
-
const onOpenChange = vi.fn();
|
|
374
|
-
render(
|
|
375
|
-
<BottomSheet isOpen onOpenChange={onOpenChange} label="Filters">
|
|
376
|
-
<input aria-label="Search" />
|
|
377
|
-
</BottomSheet>,
|
|
378
|
-
);
|
|
379
|
-
const dialog = screen.getByRole('dialog');
|
|
380
|
-
|
|
381
|
-
// An unclaimed Escape lets the browser raise its own close request, which
|
|
382
|
-
// arrives as `cancel` and dismisses the sheet on the same keypress. The
|
|
383
|
-
// guard has to swallow the composing Escape, not merely skip dismissal.
|
|
384
|
-
const wasNotClaimed = fireEvent.keyDown(dialog, {
|
|
385
|
-
key: 'Escape',
|
|
386
|
-
isComposing: true,
|
|
387
|
-
});
|
|
388
|
-
|
|
389
|
-
expect(wasNotClaimed).toBe(false);
|
|
390
|
-
expect(onOpenChange).not.toHaveBeenCalled();
|
|
391
|
-
});
|
|
392
|
-
|
|
393
|
-
it('keeps a form sheet open when Escape cancels a field composition', () => {
|
|
394
|
-
const onOpenChange = vi.fn();
|
|
395
|
-
render(
|
|
396
|
-
<BottomSheet
|
|
397
|
-
isOpen
|
|
398
|
-
purpose="form"
|
|
399
|
-
onOpenChange={onOpenChange}
|
|
400
|
-
label="Edit profile">
|
|
401
|
-
<input aria-label="Name" />
|
|
402
|
-
</BottomSheet>,
|
|
403
|
-
);
|
|
404
|
-
|
|
405
|
-
// The reported path: the keydown starts at the composing field and bubbles
|
|
406
|
-
// to the sheet's <dialog>, so the guard has to survive the trip.
|
|
407
|
-
fireEvent.keyDown(screen.getByRole('textbox', {name: 'Name'}), {
|
|
408
|
-
key: 'Escape',
|
|
409
|
-
isComposing: true,
|
|
410
|
-
});
|
|
411
|
-
|
|
412
|
-
expect(onOpenChange).not.toHaveBeenCalled();
|
|
413
|
-
});
|
|
414
|
-
|
|
415
|
-
it('still dismisses on the Escape after a composition is cancelled', () => {
|
|
416
|
-
const onOpenChange = vi.fn();
|
|
417
|
-
render(
|
|
418
|
-
<BottomSheet isOpen onOpenChange={onOpenChange} label="Filters">
|
|
419
|
-
<input aria-label="Search" />
|
|
420
|
-
</BottomSheet>,
|
|
421
|
-
);
|
|
422
|
-
const dialog = screen.getByRole('dialog');
|
|
423
|
-
|
|
424
|
-
fireEvent.keyDown(dialog, {key: 'Escape', isComposing: true});
|
|
425
|
-
expect(onOpenChange).not.toHaveBeenCalled();
|
|
426
|
-
|
|
427
|
-
fireEvent.keyDown(dialog, {key: 'Escape'});
|
|
428
|
-
|
|
429
|
-
expect(onOpenChange).toHaveBeenCalledWith(false);
|
|
430
|
-
});
|
|
431
|
-
|
|
432
|
-
it('leaves the IME the composition keys that are not Escape', () => {
|
|
433
|
-
const onOpenChange = vi.fn();
|
|
434
|
-
render(
|
|
435
|
-
<BottomSheet isOpen onOpenChange={onOpenChange} label="Filters">
|
|
436
|
-
<input aria-label="Search" />
|
|
437
|
-
</BottomSheet>,
|
|
438
|
-
);
|
|
439
|
-
const dialog = screen.getByRole('dialog');
|
|
440
|
-
|
|
441
|
-
// Enter commits a candidate and the arrows walk the candidate window. The
|
|
442
|
-
// sheet must claim neither, or the IME loses keys it owns.
|
|
443
|
-
const enterUnclaimed = fireEvent.keyDown(dialog, {
|
|
444
|
-
key: 'Enter',
|
|
445
|
-
isComposing: true,
|
|
446
|
-
});
|
|
447
|
-
const arrowUnclaimed = fireEvent.keyDown(dialog, {
|
|
448
|
-
key: 'ArrowDown',
|
|
449
|
-
isComposing: true,
|
|
450
|
-
});
|
|
451
|
-
|
|
452
|
-
expect(enterUnclaimed).toBe(true);
|
|
453
|
-
expect(arrowUnclaimed).toBe(true);
|
|
454
|
-
expect(onOpenChange).not.toHaveBeenCalled();
|
|
455
|
-
});
|
|
456
|
-
|
|
457
|
-
it('requests close when the scrim (dialog element itself) is clicked', () => {
|
|
458
|
-
const onOpenChange = vi.fn();
|
|
459
|
-
render(
|
|
460
|
-
<BottomSheet isOpen onOpenChange={onOpenChange} label="Filters">
|
|
461
|
-
Content
|
|
462
|
-
</BottomSheet>,
|
|
463
|
-
);
|
|
464
|
-
fireEvent.click(screen.getByRole('dialog'));
|
|
465
|
-
expect(onOpenChange).toHaveBeenCalledWith(false);
|
|
466
|
-
});
|
|
467
|
-
|
|
468
|
-
it('purpose=form blocks scrim and swipe dismissal but allows Escape', () => {
|
|
469
|
-
const onOpenChange = vi.fn();
|
|
470
|
-
render(
|
|
471
|
-
<BottomSheet
|
|
472
|
-
isOpen
|
|
473
|
-
purpose="form"
|
|
474
|
-
onOpenChange={onOpenChange}
|
|
475
|
-
label="Edit profile">
|
|
476
|
-
Content
|
|
477
|
-
</BottomSheet>,
|
|
478
|
-
);
|
|
479
|
-
const dialog = screen.getByRole('dialog');
|
|
480
|
-
|
|
481
|
-
fireEvent.click(dialog);
|
|
482
|
-
drag(getHandle(), [{y: 0}, {y: 40}, {y: 120}]);
|
|
483
|
-
|
|
484
|
-
expect(onOpenChange).not.toHaveBeenCalled();
|
|
485
|
-
expect(dialog).toHaveStyle({'--_sheet-scrim-opacity': '1'});
|
|
486
|
-
|
|
487
|
-
fireEvent.keyDown(dialog, {key: 'Escape'});
|
|
488
|
-
fireEvent(dialog, new Event('cancel', {cancelable: true}));
|
|
489
|
-
|
|
490
|
-
expect(onOpenChange).toHaveBeenCalledTimes(2);
|
|
491
|
-
expect(onOpenChange).toHaveBeenNthCalledWith(1, false);
|
|
492
|
-
expect(onOpenChange).toHaveBeenNthCalledWith(2, false);
|
|
493
|
-
});
|
|
494
|
-
|
|
495
|
-
it('purpose=required blocks every implicit dismissal path', () => {
|
|
496
|
-
const onOpenChange = vi.fn();
|
|
497
|
-
render(
|
|
498
|
-
<BottomSheet
|
|
499
|
-
isOpen
|
|
500
|
-
purpose="required"
|
|
501
|
-
onOpenChange={onOpenChange}
|
|
502
|
-
label="Required action">
|
|
503
|
-
Content
|
|
504
|
-
</BottomSheet>,
|
|
505
|
-
);
|
|
506
|
-
const dialog = screen.getByRole('alertdialog');
|
|
507
|
-
|
|
508
|
-
fireEvent.click(dialog);
|
|
509
|
-
fireEvent.keyDown(dialog, {key: 'Escape'});
|
|
510
|
-
fireEvent(dialog, new Event('cancel', {cancelable: true}));
|
|
511
|
-
drag(getHandle(), [{y: 0}, {y: 40}, {y: 120}]);
|
|
512
|
-
|
|
513
|
-
expect(onOpenChange).not.toHaveBeenCalled();
|
|
514
|
-
expect(dialog).toHaveStyle({'--_sheet-scrim-opacity': '1'});
|
|
515
|
-
});
|
|
516
|
-
|
|
517
|
-
it("the internal Escape gate (Popover's sheet) blocks Escape and the close request, not the scrim or swipe", () => {
|
|
518
|
-
const onOpenChange = vi.fn();
|
|
519
|
-
render(
|
|
520
|
-
<BottomSheetEscapeContext value={false}>
|
|
521
|
-
<BottomSheet isOpen onOpenChange={onOpenChange} label="Filters">
|
|
522
|
-
Content
|
|
523
|
-
</BottomSheet>
|
|
524
|
-
</BottomSheetEscapeContext>,
|
|
525
|
-
);
|
|
526
|
-
const dialog = screen.getByRole('dialog');
|
|
527
|
-
|
|
528
|
-
fireEvent.keyDown(dialog, {key: 'Escape'});
|
|
529
|
-
fireEvent(dialog, new Event('cancel', {cancelable: true}));
|
|
530
|
-
expect(onOpenChange).not.toHaveBeenCalled();
|
|
531
|
-
|
|
532
|
-
fireEvent.click(dialog);
|
|
533
|
-
expect(onOpenChange).toHaveBeenCalledTimes(1);
|
|
534
|
-
drag(getHandle(), [{y: 0}, {y: 40}, {y: 120}]);
|
|
535
|
-
expect(onOpenChange).toHaveBeenCalledTimes(2);
|
|
536
|
-
expect(onOpenChange).toHaveBeenNthCalledWith(2, false);
|
|
537
|
-
});
|
|
538
|
-
|
|
539
|
-
it('stays open when a file picker inside it is dismissed, and still closes on its own close request', () => {
|
|
540
|
-
// A file input fires a bubbling `cancel` at itself when its picker is
|
|
541
|
-
// dismissed. It reaches the <dialog> like a close request but is not one.
|
|
542
|
-
const onOpenChange = vi.fn();
|
|
543
|
-
render(
|
|
544
|
-
<BottomSheet
|
|
545
|
-
isOpen
|
|
546
|
-
purpose="form"
|
|
547
|
-
onOpenChange={onOpenChange}
|
|
548
|
-
label="Upload">
|
|
549
|
-
<input type="file" aria-label="Picture" />
|
|
550
|
-
</BottomSheet>,
|
|
551
|
-
);
|
|
552
|
-
|
|
553
|
-
const fromPicker = new Event('cancel', {bubbles: true, cancelable: true});
|
|
554
|
-
fireEvent(screen.getByLabelText('Picture'), fromPicker);
|
|
555
|
-
|
|
556
|
-
expect(onOpenChange).not.toHaveBeenCalled();
|
|
557
|
-
expect(fromPicker.defaultPrevented).toBe(false);
|
|
558
|
-
|
|
559
|
-
fireEvent(
|
|
560
|
-
screen.getByRole('dialog'),
|
|
561
|
-
new Event('cancel', {cancelable: true}),
|
|
562
|
-
);
|
|
563
|
-
|
|
564
|
-
expect(onOpenChange).toHaveBeenCalledTimes(1);
|
|
565
|
-
expect(onOpenChange).toHaveBeenCalledWith(false);
|
|
566
|
-
});
|
|
567
|
-
|
|
568
|
-
it('keeps a standalone modal sheet presented through its exit animation', () => {
|
|
569
|
-
render(<ExitHarness />);
|
|
570
|
-
const dialog = screen.getByRole('dialog', {name: 'Filters'});
|
|
571
|
-
|
|
572
|
-
fireEvent.click(screen.getByRole('button', {name: 'Close sheet'}));
|
|
573
|
-
|
|
574
|
-
expect(dialog).toHaveAttribute('open');
|
|
575
|
-
expect(dialog).toHaveAttribute('inert');
|
|
576
|
-
expect(dialog).toHaveAttribute('aria-hidden', 'true');
|
|
577
|
-
expect(dialog).not.toHaveAttribute('aria-modal');
|
|
578
|
-
expect(dialog).toHaveStyle({'--_sheet-scrim-opacity': '0'});
|
|
579
|
-
expect(document.body.style.position).toBe('fixed');
|
|
580
|
-
|
|
581
|
-
finishSheetExit();
|
|
582
|
-
|
|
583
|
-
expect(dialog).not.toHaveAttribute('open');
|
|
584
|
-
expect(document.body.style.position).not.toBe('fixed');
|
|
585
|
-
});
|
|
586
|
-
|
|
587
|
-
it('does not dismiss when the sheet surface itself is clicked', () => {
|
|
588
|
-
const onOpenChange = vi.fn();
|
|
589
|
-
render(
|
|
590
|
-
<BottomSheet isOpen onOpenChange={onOpenChange} label="Filters">
|
|
591
|
-
Content
|
|
592
|
-
</BottomSheet>,
|
|
593
|
-
);
|
|
594
|
-
// Only a click that lands on the dialog (the transparent area) dismisses;
|
|
595
|
-
// clicks bubbling up from the sheet must not.
|
|
596
|
-
fireEvent.click(screen.getByText('Content'));
|
|
597
|
-
expect(onOpenChange).not.toHaveBeenCalled();
|
|
598
|
-
});
|
|
599
|
-
|
|
600
|
-
describe('hasScrim={false} (non-modal)', () => {
|
|
601
|
-
it('opens non-modally: a manual popover instead of showModal(), no aria-modal', () => {
|
|
602
|
-
render(
|
|
603
|
-
<BottomSheet
|
|
604
|
-
isOpen
|
|
605
|
-
onOpenChange={() => {}}
|
|
606
|
-
label="Filters"
|
|
607
|
-
hasScrim={false}>
|
|
608
|
-
Content
|
|
609
|
-
</BottomSheet>,
|
|
610
|
-
);
|
|
611
|
-
// In the top layer as a manual popover (no light dismiss, no backdrop)
|
|
612
|
-
// so sheets order by presentation without a page-level z-index.
|
|
613
|
-
expect(HTMLElement.prototype.showPopover).toHaveBeenCalled();
|
|
614
|
-
expect(HTMLDialogElement.prototype.show).not.toHaveBeenCalled();
|
|
615
|
-
expect(HTMLDialogElement.prototype.showModal).not.toHaveBeenCalled();
|
|
616
|
-
expect(screen.getByRole('dialog')).toHaveAttribute('popover', 'manual');
|
|
617
|
-
expect(screen.getByRole('dialog')).not.toHaveAttribute('aria-modal');
|
|
618
|
-
});
|
|
619
|
-
|
|
620
|
-
it('focuses the panel, as show() did, when the browser focuses the popover on show (an engine whose show() lands on the panel container)', () => {
|
|
621
|
-
// Chromium's `show()` focusing steps land on the panel (tabindex -1,
|
|
622
|
-
// no ring); `showPopover()` lands on the first control and draws a
|
|
623
|
-
// ring. The sheet puts focus on the panel so a lone non-modal sheet
|
|
624
|
-
// focuses exactly as it did. The engine is told apart by what its
|
|
625
|
-
// `show()` does: here it focuses the tabindex -1 container.
|
|
626
|
-
const show = HTMLDialogElement.prototype.show;
|
|
627
|
-
HTMLDialogElement.prototype.show = vi.fn(function (
|
|
628
|
-
this: HTMLDialogElement,
|
|
629
|
-
) {
|
|
630
|
-
this.setAttribute('open', '');
|
|
631
|
-
this.querySelector<HTMLElement>('[tabindex="-1"]')?.focus();
|
|
632
|
-
});
|
|
633
|
-
const showPopover = HTMLElement.prototype.showPopover;
|
|
634
|
-
HTMLElement.prototype.showPopover = vi.fn(function (this: HTMLElement) {
|
|
635
|
-
this.setAttribute('open', '');
|
|
636
|
-
// The popover focusing steps: the autofocus delegate when there is
|
|
637
|
-
// one, else (as Chromium does) the first control.
|
|
638
|
-
(
|
|
639
|
-
this.querySelector<HTMLElement>('[autofocus]') ??
|
|
640
|
-
this.querySelector<HTMLElement>('button')
|
|
641
|
-
)?.focus();
|
|
642
|
-
});
|
|
643
|
-
try {
|
|
644
|
-
const opener = document.createElement('button');
|
|
645
|
-
document.body.appendChild(opener);
|
|
646
|
-
opener.focus();
|
|
647
|
-
const events: string[] = [];
|
|
648
|
-
render(
|
|
649
|
-
<BottomSheet
|
|
650
|
-
isOpen
|
|
651
|
-
onOpenChange={() => {}}
|
|
652
|
-
label="Filters"
|
|
653
|
-
hasScrim={false}>
|
|
654
|
-
<button
|
|
655
|
-
type="button"
|
|
656
|
-
onFocus={() => events.push('focus')}
|
|
657
|
-
onBlur={() => events.push('blur')}>
|
|
658
|
-
First action
|
|
659
|
-
</button>
|
|
660
|
-
</BottomSheet>,
|
|
661
|
-
);
|
|
662
|
-
expect(document.activeElement).toBe(getSheet());
|
|
663
|
-
// The control is never focused on the way to the panel, so a field
|
|
664
|
-
// validating on blur would not fire.
|
|
665
|
-
expect(events).toEqual([]);
|
|
666
|
-
expect(getSheet()).not.toHaveAttribute('autofocus');
|
|
667
|
-
opener.remove();
|
|
668
|
-
} finally {
|
|
669
|
-
HTMLElement.prototype.showPopover = showPopover;
|
|
670
|
-
HTMLDialogElement.prototype.show = show;
|
|
671
|
-
}
|
|
672
|
-
});
|
|
673
|
-
|
|
674
|
-
it('measures the engine inside its own dialog, so a first non-modal sheet opened inside an open modal does not cache the wrong answer', () => {
|
|
675
|
-
// A browser moves no focus into a dialog that sits under an open
|
|
676
|
-
// modal's inertness. The stub models that for `show()`: a dialog
|
|
677
|
-
// appended to the body while a modal outside it is open focuses
|
|
678
|
-
// nothing; one inside the modal (or with no modal open) focuses its
|
|
679
|
-
// tabindex -1 container, as Chromium's show() does.
|
|
680
|
-
const show = HTMLDialogElement.prototype.show;
|
|
681
|
-
HTMLDialogElement.prototype.show = vi.fn(function (
|
|
682
|
-
this: HTMLDialogElement,
|
|
683
|
-
) {
|
|
684
|
-
this.setAttribute('open', '');
|
|
685
|
-
const modal = Array.from(
|
|
686
|
-
document.querySelectorAll<HTMLDialogElement>(
|
|
687
|
-
'dialog[open][aria-modal="true"]',
|
|
688
|
-
),
|
|
689
|
-
).find(d => !d.contains(this));
|
|
690
|
-
if (modal != null) {
|
|
691
|
-
return;
|
|
692
|
-
}
|
|
693
|
-
this.querySelector<HTMLElement>('[tabindex="-1"]')?.focus();
|
|
694
|
-
});
|
|
695
|
-
const showPopover = HTMLElement.prototype.showPopover;
|
|
696
|
-
HTMLElement.prototype.showPopover = vi.fn(function (this: HTMLElement) {
|
|
697
|
-
this.setAttribute('open', '');
|
|
698
|
-
(
|
|
699
|
-
this.querySelector<HTMLElement>('[autofocus]') ??
|
|
700
|
-
this.querySelector<HTMLElement>('button')
|
|
701
|
-
)?.focus();
|
|
702
|
-
});
|
|
703
|
-
function App() {
|
|
704
|
-
const [isModalOpen, setIsModalOpen] = useState(true);
|
|
705
|
-
const [isHintOpen, setIsHintOpen] = useState(false);
|
|
706
|
-
const [isLoneOpen, setIsLoneOpen] = useState(false);
|
|
707
|
-
return (
|
|
708
|
-
<>
|
|
709
|
-
<BottomSheet
|
|
710
|
-
isOpen={isModalOpen}
|
|
711
|
-
onOpenChange={setIsModalOpen}
|
|
712
|
-
label="Settings">
|
|
713
|
-
<button type="button" onClick={() => setIsHintOpen(true)}>
|
|
714
|
-
Show hint
|
|
715
|
-
</button>
|
|
716
|
-
<button type="button" onClick={() => setIsModalOpen(false)}>
|
|
717
|
-
Done
|
|
718
|
-
</button>
|
|
719
|
-
<BottomSheet
|
|
720
|
-
isOpen={isHintOpen}
|
|
721
|
-
onOpenChange={setIsHintOpen}
|
|
722
|
-
hasScrim={false}
|
|
723
|
-
label="Hint">
|
|
724
|
-
<button type="button">Got it</button>
|
|
725
|
-
</BottomSheet>
|
|
726
|
-
</BottomSheet>
|
|
727
|
-
<button type="button" onClick={() => setIsLoneOpen(true)}>
|
|
728
|
-
Open filters
|
|
729
|
-
</button>
|
|
730
|
-
<BottomSheet
|
|
731
|
-
isOpen={isLoneOpen}
|
|
732
|
-
onOpenChange={setIsLoneOpen}
|
|
733
|
-
hasScrim={false}
|
|
734
|
-
label="Filters">
|
|
735
|
-
<button type="button">First action</button>
|
|
736
|
-
</BottomSheet>
|
|
737
|
-
</>
|
|
738
|
-
);
|
|
739
|
-
}
|
|
740
|
-
try {
|
|
741
|
-
render(<App />);
|
|
742
|
-
// The first non-modal sheet of the page opens inside the open modal:
|
|
743
|
-
// the engine is measured there, where show() can move focus.
|
|
744
|
-
fireEvent.click(screen.getByRole('button', {name: 'Show hint'}));
|
|
745
|
-
const hint = screen.getByRole('dialog', {name: 'Hint'});
|
|
746
|
-
expect(document.activeElement).toBe(
|
|
747
|
-
hint.querySelector('.astryx-bottom-sheet'),
|
|
748
|
-
);
|
|
749
|
-
fireEvent.click(screen.getByRole('button', {name: 'Done'}));
|
|
750
|
-
// A later lone non-modal sheet focuses as show() would: the panel.
|
|
751
|
-
fireEvent.click(screen.getByRole('button', {name: 'Open filters'}));
|
|
752
|
-
const filters = screen.getByRole('dialog', {name: 'Filters'});
|
|
753
|
-
expect(document.activeElement).toBe(
|
|
754
|
-
filters.querySelector('.astryx-bottom-sheet'),
|
|
755
|
-
);
|
|
756
|
-
} finally {
|
|
757
|
-
HTMLElement.prototype.showPopover = showPopover;
|
|
758
|
-
HTMLDialogElement.prototype.show = show;
|
|
759
|
-
}
|
|
760
|
-
});
|
|
761
|
-
|
|
762
|
-
it('leaves focus on the first control, as show() did, in an engine whose show() skips the panel container', () => {
|
|
763
|
-
// Firefox's `show()` and `showPopover()` both land on the first
|
|
764
|
-
// control in the sheet. Nothing is re-targeted, so a lone non-modal
|
|
765
|
-
// sheet focuses as it did with `show()` there too.
|
|
766
|
-
const show = HTMLDialogElement.prototype.show;
|
|
767
|
-
HTMLDialogElement.prototype.show = vi.fn(function (
|
|
768
|
-
this: HTMLDialogElement,
|
|
769
|
-
) {
|
|
770
|
-
this.setAttribute('open', '');
|
|
771
|
-
this.querySelector<HTMLElement>('button')?.focus();
|
|
772
|
-
});
|
|
773
|
-
const showPopover = HTMLElement.prototype.showPopover;
|
|
774
|
-
HTMLElement.prototype.showPopover = vi.fn(function (this: HTMLElement) {
|
|
775
|
-
this.setAttribute('open', '');
|
|
776
|
-
(
|
|
777
|
-
this.querySelector<HTMLElement>('[autofocus]') ??
|
|
778
|
-
this.querySelector<HTMLElement>('button')
|
|
779
|
-
)?.focus();
|
|
780
|
-
});
|
|
781
|
-
try {
|
|
782
|
-
const opener = document.createElement('button');
|
|
783
|
-
document.body.appendChild(opener);
|
|
784
|
-
opener.focus();
|
|
785
|
-
render(
|
|
786
|
-
<BottomSheet
|
|
787
|
-
isOpen
|
|
788
|
-
onOpenChange={() => {}}
|
|
789
|
-
label="Filters"
|
|
790
|
-
hasScrim={false}>
|
|
791
|
-
<button type="button">First action</button>
|
|
792
|
-
</BottomSheet>,
|
|
793
|
-
);
|
|
794
|
-
expect(document.activeElement).toBe(
|
|
795
|
-
screen.getByRole('button', {name: 'First action'}),
|
|
796
|
-
);
|
|
797
|
-
expect(getSheet()).not.toHaveAttribute('autofocus');
|
|
798
|
-
opener.remove();
|
|
799
|
-
} finally {
|
|
800
|
-
HTMLElement.prototype.showPopover = showPopover;
|
|
801
|
-
HTMLDialogElement.prototype.show = show;
|
|
802
|
-
}
|
|
803
|
-
});
|
|
804
|
-
|
|
805
|
-
it('returns focus to its opener on close when the person was still in the sheet', () => {
|
|
806
|
-
const opener = document.createElement('button');
|
|
807
|
-
opener.textContent = 'Open';
|
|
808
|
-
document.body.appendChild(opener);
|
|
809
|
-
opener.focus();
|
|
810
|
-
try {
|
|
811
|
-
render(<ExitHarness hasScrim={false} />);
|
|
812
|
-
// The person moved into the sheet (e.g. tabbed to its control).
|
|
813
|
-
screen.getByRole('button', {name: 'Close sheet'}).focus();
|
|
814
|
-
fireEvent.keyDown(screen.getByRole('dialog'), {key: 'Escape'});
|
|
815
|
-
finishSheetExit();
|
|
816
|
-
expect(screen.getByRole('dialog', {hidden: true})).not.toHaveAttribute(
|
|
817
|
-
'open',
|
|
818
|
-
);
|
|
819
|
-
expect(document.activeElement).toBe(opener);
|
|
820
|
-
} finally {
|
|
821
|
-
opener.remove();
|
|
822
|
-
}
|
|
823
|
-
});
|
|
824
|
-
|
|
825
|
-
it('always focuses finalFocusRef on close, even when focus had left the sheet', () => {
|
|
826
|
-
const elsewhere = document.createElement('input');
|
|
827
|
-
const target = document.createElement('button');
|
|
828
|
-
target.textContent = 'Target';
|
|
829
|
-
document.body.append(elsewhere, target);
|
|
830
|
-
const finalFocusRef = {current: target as HTMLElement | null};
|
|
831
|
-
function Harness() {
|
|
832
|
-
const [isOpen, setIsOpen] = useState(true);
|
|
833
|
-
return (
|
|
834
|
-
<>
|
|
835
|
-
<button type="button" onClick={() => setIsOpen(false)}>
|
|
836
|
-
Close from page
|
|
837
|
-
</button>
|
|
838
|
-
<BottomSheet
|
|
839
|
-
isOpen={isOpen}
|
|
840
|
-
onOpenChange={setIsOpen}
|
|
841
|
-
hasScrim={false}
|
|
842
|
-
finalFocusRef={finalFocusRef}
|
|
843
|
-
label="Filters">
|
|
844
|
-
Content
|
|
845
|
-
</BottomSheet>
|
|
846
|
-
</>
|
|
847
|
-
);
|
|
848
|
-
}
|
|
849
|
-
try {
|
|
850
|
-
render(<Harness />);
|
|
851
|
-
// The page stayed live: the person moved on, then a page control
|
|
852
|
-
// closed the sheet. The explicit target still receives focus.
|
|
853
|
-
elsewhere.focus();
|
|
854
|
-
fireEvent.click(screen.getByRole('button', {name: 'Close from page'}));
|
|
855
|
-
finishSheetExit();
|
|
856
|
-
expect(document.activeElement).toBe(target);
|
|
857
|
-
} finally {
|
|
858
|
-
elsewhere.remove();
|
|
859
|
-
target.remove();
|
|
860
|
-
}
|
|
861
|
-
});
|
|
862
|
-
|
|
863
|
-
it('leaves focus where the person took it when the sheet closes from the page', () => {
|
|
864
|
-
const opener = document.createElement('button');
|
|
865
|
-
const elsewhere = document.createElement('input');
|
|
866
|
-
document.body.append(opener, elsewhere);
|
|
867
|
-
opener.focus();
|
|
868
|
-
try {
|
|
869
|
-
render(<ExitHarness hasScrim={false} />);
|
|
870
|
-
// The page stayed live: the person moved on, then the sheet closed
|
|
871
|
-
// (a timer, a page control).
|
|
872
|
-
elsewhere.focus();
|
|
873
|
-
fireEvent.click(screen.getByRole('button', {name: 'Close sheet'}));
|
|
874
|
-
finishSheetExit();
|
|
875
|
-
expect(document.activeElement).toBe(elsewhere);
|
|
876
|
-
} finally {
|
|
877
|
-
opener.remove();
|
|
878
|
-
elsewhere.remove();
|
|
879
|
-
}
|
|
880
|
-
});
|
|
881
|
-
|
|
882
|
-
it('leaves focus alone when the browser does not move it on show', () => {
|
|
883
|
-
const opener = document.createElement('button');
|
|
884
|
-
document.body.appendChild(opener);
|
|
885
|
-
opener.focus();
|
|
886
|
-
render(
|
|
887
|
-
<BottomSheet
|
|
888
|
-
isOpen
|
|
889
|
-
onOpenChange={() => {}}
|
|
890
|
-
label="Filters"
|
|
891
|
-
hasScrim={false}>
|
|
892
|
-
<button type="button">First action</button>
|
|
893
|
-
</BottomSheet>,
|
|
894
|
-
);
|
|
895
|
-
expect(document.activeElement).toBe(opener);
|
|
896
|
-
opener.remove();
|
|
897
|
-
});
|
|
898
|
-
|
|
899
|
-
it('falls back to show() below the Popover API support floor', () => {
|
|
900
|
-
const showPopover = HTMLElement.prototype.showPopover;
|
|
901
|
-
// @ts-expect-error -- emulate a browser without the Popover API
|
|
902
|
-
delete HTMLElement.prototype.showPopover;
|
|
903
|
-
try {
|
|
904
|
-
render(
|
|
905
|
-
<BottomSheet
|
|
906
|
-
isOpen
|
|
907
|
-
onOpenChange={() => {}}
|
|
908
|
-
label="Filters"
|
|
909
|
-
hasScrim={false}>
|
|
910
|
-
Content
|
|
911
|
-
</BottomSheet>,
|
|
912
|
-
);
|
|
913
|
-
expect(HTMLDialogElement.prototype.show).toHaveBeenCalled();
|
|
914
|
-
expect(HTMLDialogElement.prototype.showModal).not.toHaveBeenCalled();
|
|
915
|
-
} finally {
|
|
916
|
-
HTMLElement.prototype.showPopover = showPopover;
|
|
917
|
-
}
|
|
918
|
-
});
|
|
919
|
-
|
|
920
|
-
it('does not dismiss when the shell (dialog element itself) is clicked', () => {
|
|
921
|
-
// No scrim: a tap on the transparent shell must pass through to the page,
|
|
922
|
-
// not dismiss the sheet.
|
|
923
|
-
const onOpenChange = vi.fn();
|
|
924
|
-
render(
|
|
925
|
-
<BottomSheet
|
|
926
|
-
isOpen
|
|
927
|
-
onOpenChange={onOpenChange}
|
|
928
|
-
label="Filters"
|
|
929
|
-
hasScrim={false}>
|
|
930
|
-
Content
|
|
931
|
-
</BottomSheet>,
|
|
932
|
-
);
|
|
933
|
-
fireEvent.click(screen.getByRole('dialog'));
|
|
934
|
-
expect(onOpenChange).not.toHaveBeenCalled();
|
|
935
|
-
});
|
|
936
|
-
|
|
937
|
-
it('still closes on Escape while focus is inside', () => {
|
|
938
|
-
const onOpenChange = vi.fn();
|
|
939
|
-
render(
|
|
940
|
-
<BottomSheet
|
|
941
|
-
isOpen
|
|
942
|
-
onOpenChange={onOpenChange}
|
|
943
|
-
label="Filters"
|
|
944
|
-
hasScrim={false}>
|
|
945
|
-
Content
|
|
946
|
-
</BottomSheet>,
|
|
947
|
-
);
|
|
948
|
-
fireEvent.keyDown(screen.getByRole('dialog'), {key: 'Escape'});
|
|
949
|
-
expect(onOpenChange).toHaveBeenCalledWith(false);
|
|
950
|
-
});
|
|
951
|
-
|
|
952
|
-
it('ignores Escape while an IME composition is active', () => {
|
|
953
|
-
const onOpenChange = vi.fn();
|
|
954
|
-
render(
|
|
955
|
-
<BottomSheet
|
|
956
|
-
isOpen
|
|
957
|
-
hasScrim={false}
|
|
958
|
-
onOpenChange={onOpenChange}
|
|
959
|
-
label="Filters">
|
|
960
|
-
<input aria-label="Search" />
|
|
961
|
-
</BottomSheet>,
|
|
962
|
-
);
|
|
963
|
-
|
|
964
|
-
// Escape is this sheet's only keyboard route out — there is no native
|
|
965
|
-
// close request behind it — so the guard is all that stands between a
|
|
966
|
-
// composing CJK user and a dismissed sheet.
|
|
967
|
-
fireEvent.keyDown(screen.getByRole('dialog'), {
|
|
968
|
-
key: 'Escape',
|
|
969
|
-
isComposing: true,
|
|
970
|
-
});
|
|
971
|
-
|
|
972
|
-
expect(onOpenChange).not.toHaveBeenCalled();
|
|
973
|
-
});
|
|
974
|
-
|
|
975
|
-
it('still dismisses on a downward swipe past the threshold', () => {
|
|
976
|
-
const onOpenChange = vi.fn();
|
|
977
|
-
render(
|
|
978
|
-
<BottomSheet
|
|
979
|
-
isOpen
|
|
980
|
-
onOpenChange={onOpenChange}
|
|
981
|
-
label="Filters"
|
|
982
|
-
hasScrim={false}>
|
|
983
|
-
Content
|
|
984
|
-
</BottomSheet>,
|
|
985
|
-
);
|
|
986
|
-
drag(getHandle(), [{y: 0}, {y: 40}, {y: 120}]);
|
|
987
|
-
expect(onOpenChange).toHaveBeenCalledWith(false);
|
|
988
|
-
});
|
|
989
|
-
|
|
990
|
-
it('does not steal focus onto the panel on open', () => {
|
|
991
|
-
render(
|
|
992
|
-
<BottomSheet
|
|
993
|
-
isOpen
|
|
994
|
-
onOpenChange={() => {}}
|
|
995
|
-
label="Filters"
|
|
996
|
-
hasScrim={false}>
|
|
997
|
-
<button type="button">First action</button>
|
|
998
|
-
</BottomSheet>,
|
|
999
|
-
);
|
|
1000
|
-
// The background stays interactive, so the sheet must not grab focus.
|
|
1001
|
-
expect(document.activeElement).not.toBe(getSheet());
|
|
1002
|
-
});
|
|
1003
|
-
|
|
1004
|
-
it('still honors a descendant with data-autofocus', () => {
|
|
1005
|
-
render(
|
|
1006
|
-
<BottomSheet
|
|
1007
|
-
isOpen
|
|
1008
|
-
onOpenChange={() => {}}
|
|
1009
|
-
label="Filters"
|
|
1010
|
-
hasScrim={false}>
|
|
1011
|
-
<input data-autofocus aria-label="Search" />
|
|
1012
|
-
</BottomSheet>,
|
|
1013
|
-
);
|
|
1014
|
-
expect(document.activeElement).toBe(
|
|
1015
|
-
screen.getByRole('textbox', {name: 'Search'}),
|
|
1016
|
-
);
|
|
1017
|
-
});
|
|
1018
|
-
|
|
1019
|
-
it('keeps a standalone non-modal sheet visible until its exit ends', () => {
|
|
1020
|
-
render(<ExitHarness hasScrim={false} />);
|
|
1021
|
-
const dialog = screen.getByRole('dialog', {name: 'Filters'});
|
|
1022
|
-
|
|
1023
|
-
fireEvent.click(screen.getByRole('button', {name: 'Close sheet'}));
|
|
1024
|
-
|
|
1025
|
-
expect(dialog).toHaveAttribute('open');
|
|
1026
|
-
expect(dialog).toHaveAttribute('inert');
|
|
1027
|
-
expect(document.body.style.position).not.toBe('fixed');
|
|
1028
|
-
|
|
1029
|
-
finishSheetExit();
|
|
1030
|
-
|
|
1031
|
-
expect(dialog).not.toHaveAttribute('open');
|
|
1032
|
-
});
|
|
1033
|
-
});
|
|
1034
|
-
|
|
1035
|
-
describe('grab handle', () => {
|
|
1036
|
-
it('renders a decorative handle hidden from assistive tech', () => {
|
|
1037
|
-
render(
|
|
1038
|
-
<BottomSheet isOpen onOpenChange={() => {}} label="Filters">
|
|
1039
|
-
Content
|
|
1040
|
-
</BottomSheet>,
|
|
1041
|
-
);
|
|
1042
|
-
const handle = getHandle();
|
|
1043
|
-
expect(handle).toHaveAttribute('aria-hidden', 'true');
|
|
1044
|
-
});
|
|
1045
|
-
});
|
|
1046
|
-
|
|
1047
|
-
describe('swipe to dismiss', () => {
|
|
1048
|
-
it("a sheet opened from inside another sheet's content drags to dismiss by its own handle, and the covered host neither drags nor takes the pointer (FR6, FR7)", () => {
|
|
1049
|
-
const onInnerOpenChange = vi.fn();
|
|
1050
|
-
const onOuterOpenChange = vi.fn();
|
|
1051
|
-
render(
|
|
1052
|
-
<BottomSheet isOpen onOpenChange={onOuterOpenChange} label="Outer">
|
|
1053
|
-
Outer content
|
|
1054
|
-
<BottomSheet isOpen onOpenChange={onInnerOpenChange} label="Inner">
|
|
1055
|
-
Inner content
|
|
1056
|
-
</BottomSheet>
|
|
1057
|
-
</BottomSheet>,
|
|
1058
|
-
);
|
|
1059
|
-
const dialogs = screen.getAllByRole('dialog', {hidden: true});
|
|
1060
|
-
expect(dialogs).toHaveLength(2);
|
|
1061
|
-
const [outerDialog, innerDialog] = dialogs;
|
|
1062
|
-
// The inner sheet's dialog is rendered inside the outer sheet's body,
|
|
1063
|
-
// so its events bubble through the outer body's gesture listeners.
|
|
1064
|
-
expect(outerDialog.contains(innerDialog)).toBe(true);
|
|
1065
|
-
const outerPanel = outerDialog.querySelector<HTMLElement>(
|
|
1066
|
-
':scope > .astryx-bottom-sheet',
|
|
1067
|
-
)!;
|
|
1068
|
-
const outerBody = outerPanel.querySelector<HTMLElement>(
|
|
1069
|
-
':scope > :not([aria-hidden])',
|
|
1070
|
-
)!;
|
|
1071
|
-
const innerHandle = innerDialog.querySelector<HTMLElement>(
|
|
1072
|
-
'.astryx-bottom-sheet > [aria-hidden="true"]',
|
|
1073
|
-
)!;
|
|
1074
|
-
// `setPointerCapture` is a prototype stub here, so record who captures.
|
|
1075
|
-
const captured: Element[] = [];
|
|
1076
|
-
const capture = vi
|
|
1077
|
-
.spyOn(Element.prototype, 'setPointerCapture')
|
|
1078
|
-
.mockImplementation(function (this: Element) {
|
|
1079
|
-
captured.push(this);
|
|
1080
|
-
});
|
|
1081
|
-
drag(innerHandle, [{y: 0}, {y: 40}, {y: 120}], 'touch');
|
|
1082
|
-
capture.mockRestore();
|
|
1083
|
-
// The inner sheet's own drag ran to its dismissal.
|
|
1084
|
-
expect(onInnerOpenChange).toHaveBeenCalledWith(false);
|
|
1085
|
-
// The covered host saw the same pointer bubble up and did nothing with
|
|
1086
|
-
// it: no drag of its own (its panel keeps its resting transform) and no
|
|
1087
|
-
// pointer capture, which would have cancelled the inner handle's drag.
|
|
1088
|
-
expect(onOuterOpenChange).not.toHaveBeenCalled();
|
|
1089
|
-
expect(captured).toContain(innerHandle);
|
|
1090
|
-
expect(captured).not.toContain(outerBody);
|
|
1091
|
-
expect(outerPanel.style.transform).toBe('');
|
|
1092
|
-
});
|
|
1093
|
-
|
|
1094
|
-
it('requests close when dragged past the dismiss threshold', () => {
|
|
1095
|
-
const onOpenChange = vi.fn();
|
|
1096
|
-
render(
|
|
1097
|
-
<BottomSheet isOpen onOpenChange={onOpenChange} label="Filters">
|
|
1098
|
-
Content
|
|
1099
|
-
</BottomSheet>,
|
|
1100
|
-
);
|
|
1101
|
-
// No measured height in jsdom (0) -> any downward drag dismisses via
|
|
1102
|
-
// the distance branch (offset > 0.25) once released downward.
|
|
1103
|
-
drag(getHandle(), [{y: 0}, {y: 40}, {y: 120}]);
|
|
1104
|
-
expect(onOpenChange).toHaveBeenCalledWith(false);
|
|
1105
|
-
});
|
|
1106
|
-
|
|
1107
|
-
it('restores the sheet when a context menu interrupts an active drag', () => {
|
|
1108
|
-
const onOpenChange = vi.fn();
|
|
1109
|
-
render(
|
|
1110
|
-
<BottomSheet isOpen onOpenChange={onOpenChange} label="Filters">
|
|
1111
|
-
Content
|
|
1112
|
-
</BottomSheet>,
|
|
1113
|
-
);
|
|
1114
|
-
const handle = getHandle();
|
|
1115
|
-
const dialog = screen.getByRole('dialog');
|
|
1116
|
-
|
|
1117
|
-
fireEvent.pointerDown(handle, {
|
|
1118
|
-
pointerId: 1,
|
|
1119
|
-
clientY: 0,
|
|
1120
|
-
button: 0,
|
|
1121
|
-
isPrimary: true,
|
|
1122
|
-
});
|
|
1123
|
-
fireEvent.pointerMove(handle, {pointerId: 1, clientY: 300});
|
|
1124
|
-
expect(getSheet().style.transform).toBe('translateY(300px)');
|
|
1125
|
-
|
|
1126
|
-
expect(fireEvent.contextMenu(handle)).toBe(false);
|
|
1127
|
-
|
|
1128
|
-
expect(getSheet().style.transform).toBe('');
|
|
1129
|
-
expect(dialog).toHaveStyle({'--_sheet-scrim-opacity': '1'});
|
|
1130
|
-
expect(onOpenChange).not.toHaveBeenCalled();
|
|
1131
|
-
});
|
|
1132
|
-
});
|
|
1133
|
-
|
|
1134
|
-
describe('height', () => {
|
|
1135
|
-
it('renders for each named height without error', () => {
|
|
1136
|
-
for (const height of ['hug', 'capped', 'tall'] as const) {
|
|
1137
|
-
const {unmount} = render(
|
|
1138
|
-
<BottomSheet
|
|
1139
|
-
isOpen
|
|
1140
|
-
onOpenChange={() => {}}
|
|
1141
|
-
label="Filters"
|
|
1142
|
-
height={height}>
|
|
1143
|
-
Content
|
|
1144
|
-
</BottomSheet>,
|
|
1145
|
-
);
|
|
1146
|
-
expect(screen.getByRole('dialog')).toBeInTheDocument();
|
|
1147
|
-
unmount();
|
|
1148
|
-
}
|
|
1149
|
-
});
|
|
1150
|
-
|
|
1151
|
-
it('accepts a freeform height (number or CSS length)', () => {
|
|
1152
|
-
for (const height of [480, '70dvh'] as const) {
|
|
1153
|
-
const {unmount} = render(
|
|
1154
|
-
<BottomSheet
|
|
1155
|
-
isOpen
|
|
1156
|
-
onOpenChange={() => {}}
|
|
1157
|
-
label="Filters"
|
|
1158
|
-
height={height}>
|
|
1159
|
-
Content
|
|
1160
|
-
</BottomSheet>,
|
|
1161
|
-
);
|
|
1162
|
-
expect(screen.getByRole('dialog')).toBeInTheDocument();
|
|
1163
|
-
unmount();
|
|
1164
|
-
}
|
|
1165
|
-
});
|
|
1166
|
-
|
|
1167
|
-
it('keeps the full layout height at the peek detent', () => {
|
|
1168
|
-
const observers = mockResizeObserverInstances();
|
|
1169
|
-
mockVisualViewport(800);
|
|
1170
|
-
mockWindowHeight(800);
|
|
1171
|
-
render(
|
|
1172
|
-
<BottomSheet
|
|
1173
|
-
isOpen
|
|
1174
|
-
onOpenChange={() => {}}
|
|
1175
|
-
label="Release notes"
|
|
1176
|
-
snapPoints={SNAP_POINTS}
|
|
1177
|
-
height="tall">
|
|
1178
|
-
Content
|
|
1179
|
-
</BottomSheet>,
|
|
1180
|
-
);
|
|
1181
|
-
const sheet = getSheet();
|
|
1182
|
-
const sheetObserver = observers.find(instance =>
|
|
1183
|
-
instance.observed.has(sheet),
|
|
1184
|
-
);
|
|
1185
|
-
act(() => {
|
|
1186
|
-
sheetObserver?.callback(
|
|
1187
|
-
[resizeEntry(784, 736)],
|
|
1188
|
-
sheetObserver as unknown as ResizeObserver,
|
|
1189
|
-
);
|
|
1190
|
-
});
|
|
1191
|
-
|
|
1192
|
-
// Settle on the shortest 112px peek: offset 736 - 112 = 624.
|
|
1193
|
-
fireTimedPointer(getHandle(), 'pointerdown', {time: 0, y: 0});
|
|
1194
|
-
fireTimedPointer(getHandle(), 'pointermove', {time: 1000, y: 600});
|
|
1195
|
-
fireTimedPointer(getHandle(), 'pointerup', {time: 2000, y: 600});
|
|
1196
|
-
fireEvent.transitionEnd(sheet, {propertyName: 'transform'});
|
|
1197
|
-
|
|
1198
|
-
// A glance state does not reflow the content into a sliver: the sheet
|
|
1199
|
-
// keeps its full layout height and slides below the viewport instead.
|
|
1200
|
-
expect(sheet.style.height).toBe('');
|
|
1201
|
-
expect(sheet.style.transform).toBe('translateY(624px)');
|
|
1202
|
-
expect(getBody().style.paddingBlockEnd).toBe('');
|
|
1203
|
-
|
|
1204
|
-
// Dragging back up off the peek stays transform-only for the same
|
|
1205
|
-
// reason — there is no shortened layout to restore first.
|
|
1206
|
-
fireTimedPointer(getHandle(), 'pointerdown', {time: 3000, y: 600});
|
|
1207
|
-
fireTimedPointer(getHandle(), 'pointermove', {time: 4000, y: 400});
|
|
1208
|
-
expect(sheet.style.transform).toBe('translateY(424px)');
|
|
1209
|
-
expect(sheet.style.height).toBe('784px');
|
|
1210
|
-
expect(getBody().style.paddingBlockEnd).toBe('');
|
|
1211
|
-
|
|
1212
|
-
// Releasing at the taller p50 detent resizes the scrolling area.
|
|
1213
|
-
fireTimedPointer(getHandle(), 'pointerup', {time: 5000, y: 400});
|
|
1214
|
-
fireEvent.transitionEnd(sheet, {propertyName: 'transform'});
|
|
1215
|
-
expect(sheet.style.height).toBe('448px');
|
|
1216
|
-
expect(sheet.style.transform).toBe('');
|
|
1217
|
-
});
|
|
1218
|
-
|
|
1219
|
-
it('swaps height for transform without a transition when released on a detent', () => {
|
|
1220
|
-
const observers = mockResizeObserverInstances();
|
|
1221
|
-
mockVisualViewport(800);
|
|
1222
|
-
mockWindowHeight(800);
|
|
1223
|
-
render(
|
|
1224
|
-
<BottomSheet
|
|
1225
|
-
isOpen
|
|
1226
|
-
onOpenChange={() => {}}
|
|
1227
|
-
label="Release notes"
|
|
1228
|
-
snapPoints={SNAP_POINTS}
|
|
1229
|
-
height="tall">
|
|
1230
|
-
Content
|
|
1231
|
-
</BottomSheet>,
|
|
1232
|
-
);
|
|
1233
|
-
const sheet = getSheet();
|
|
1234
|
-
const sheetObserver = observers.find(instance =>
|
|
1235
|
-
instance.observed.has(sheet),
|
|
1236
|
-
);
|
|
1237
|
-
act(() => {
|
|
1238
|
-
sheetObserver?.callback(
|
|
1239
|
-
[resizeEntry(784, 736)],
|
|
1240
|
-
sheetObserver as unknown as ResizeObserver,
|
|
1241
|
-
);
|
|
1242
|
-
});
|
|
1243
|
-
|
|
1244
|
-
// magnetize() lands a slow drag exactly on the p50 detent (offset 336),
|
|
1245
|
-
// so the release has no travel left to animate and reconciles at once.
|
|
1246
|
-
fireTimedPointer(getHandle(), 'pointerdown', {time: 0, y: 0});
|
|
1247
|
-
fireTimedPointer(getHandle(), 'pointermove', {time: 1000, y: 336});
|
|
1248
|
-
expect(sheet.style.transform).toBe('translateY(336px)');
|
|
1249
|
-
expect(sheet.style.height).toBe('784px');
|
|
1250
|
-
|
|
1251
|
-
// Hold the reconciliation frame so the intermediate render is visible
|
|
1252
|
-
// to the assertions below.
|
|
1253
|
-
const reconciliationFrames: FrameRequestCallback[] = [];
|
|
1254
|
-
vi.mocked(requestAnimationFrame).mockImplementation(callback => {
|
|
1255
|
-
reconciliationFrames.push(callback);
|
|
1256
|
-
return reconciliationFrames.length;
|
|
1257
|
-
});
|
|
1258
|
-
fireTimedPointer(getHandle(), 'pointerup', {time: 2000, y: 336});
|
|
1259
|
-
|
|
1260
|
-
// Height and transform swap roles in this one render. That is only
|
|
1261
|
-
// invisible with transitions off; live, the composited transform would
|
|
1262
|
-
// animate the whole 336px swap while the height jumped, and the sheet
|
|
1263
|
-
// would lurch away from the detent before coming back to it.
|
|
1264
|
-
expect(sheet.style.height).toBe('448px');
|
|
1265
|
-
expect(sheet.style.transform).toBe('');
|
|
1266
|
-
expect(sheet.style.transition).toBe('none');
|
|
1267
|
-
|
|
1268
|
-
// Transitions come back for the next gesture.
|
|
1269
|
-
act(() => reconciliationFrames.splice(0).forEach(frame => frame(0)));
|
|
1270
|
-
expect(sheet.style.transition).toBe('');
|
|
1271
|
-
});
|
|
1272
|
-
|
|
1273
|
-
it('uses transforms while dragging and resizes to the visible snapped height', () => {
|
|
1274
|
-
const observers = mockResizeObserverInstances();
|
|
1275
|
-
mockVisualViewport(800);
|
|
1276
|
-
mockWindowHeight(800);
|
|
1277
|
-
render(
|
|
1278
|
-
<BottomSheet
|
|
1279
|
-
isOpen
|
|
1280
|
-
onOpenChange={() => {}}
|
|
1281
|
-
label="Release notes"
|
|
1282
|
-
snapPoints={SNAP_POINTS}
|
|
1283
|
-
height="tall">
|
|
1284
|
-
Content
|
|
1285
|
-
</BottomSheet>,
|
|
1286
|
-
);
|
|
1287
|
-
const sheet = getSheet();
|
|
1288
|
-
const sheetObserver = observers.find(instance =>
|
|
1289
|
-
instance.observed.has(sheet),
|
|
1290
|
-
);
|
|
1291
|
-
act(() => {
|
|
1292
|
-
sheetObserver?.callback(
|
|
1293
|
-
// A Tall sheet has 736px visible height in this 800px viewport plus
|
|
1294
|
-
// the 48px border-box reserve held below the viewport.
|
|
1295
|
-
[resizeEntry(784, 736)],
|
|
1296
|
-
sheetObserver as unknown as ResizeObserver,
|
|
1297
|
-
);
|
|
1298
|
-
});
|
|
1299
|
-
|
|
1300
|
-
fireTimedPointer(getHandle(), 'pointerdown', {time: 0, y: 0});
|
|
1301
|
-
fireTimedPointer(getHandle(), 'pointermove', {time: 1000, y: 240});
|
|
1302
|
-
|
|
1303
|
-
expect(sheet.style.transform).toBe('translateY(240px)');
|
|
1304
|
-
expect(sheet.style.height).toBe('784px');
|
|
1305
|
-
expect(sheet.style.transition).toBe('none');
|
|
1306
|
-
|
|
1307
|
-
fireTimedPointer(getHandle(), 'pointerup', {time: 2000, y: 240});
|
|
1308
|
-
// Release remains transform-only until the snap finishes.
|
|
1309
|
-
// p50 is a visible 400px sheet: 784 - 48 - 400 = 336px offset.
|
|
1310
|
-
expect(sheet.style.transform).toBe('translateY(336px)');
|
|
1311
|
-
expect(sheet.style.height).toBe('784px');
|
|
1312
|
-
expect(sheet.style.transition).toBe('');
|
|
1313
|
-
const reconciliationFrames: FrameRequestCallback[] = [];
|
|
1314
|
-
vi.mocked(requestAnimationFrame).mockImplementation(callback => {
|
|
1315
|
-
reconciliationFrames.push(callback);
|
|
1316
|
-
return reconciliationFrames.length;
|
|
1317
|
-
});
|
|
1318
|
-
fireEvent.transitionEnd(sheet, {propertyName: 'transform'});
|
|
1319
|
-
expect(sheet.style.transform).toBe('');
|
|
1320
|
-
expect(sheet.style.height).toBe('448px');
|
|
1321
|
-
// The transform reset is transition-free so it cannot produce a second
|
|
1322
|
-
// fly-in animation after the snap has already reached its destination.
|
|
1323
|
-
expect(sheet.style.transition).toBe('none');
|
|
1324
|
-
expect(reconciliationFrames.length).toBeGreaterThan(0);
|
|
1325
|
-
act(() => reconciliationFrames.splice(0).forEach(frame => frame(0)));
|
|
1326
|
-
expect(sheet.style.transition).toBe('');
|
|
1327
|
-
|
|
1328
|
-
// Ignore ResizeObserver frames from the height reconciliation. The
|
|
1329
|
-
// next drag must still use the original 784px border-box height.
|
|
1330
|
-
act(() => {
|
|
1331
|
-
sheetObserver?.callback(
|
|
1332
|
-
[resizeEntry(500, 452)],
|
|
1333
|
-
sheetObserver as unknown as ResizeObserver,
|
|
1334
|
-
);
|
|
1335
|
-
});
|
|
1336
|
-
fireTimedPointer(getHandle(), 'pointerdown', {time: 3000, y: 240});
|
|
1337
|
-
fireTimedPointer(getHandle(), 'pointermove', {time: 4000, y: 140});
|
|
1338
|
-
expect(sheet.style.transform).toBe('translateY(236px)');
|
|
1339
|
-
expect(sheet.style.height).toBe('784px');
|
|
1340
|
-
expect(getBody().style.paddingBlockEnd).toBe('336px');
|
|
1341
|
-
|
|
1342
|
-
// Reversing below the settled point restores the settled height and
|
|
1343
|
-
// translates only the distance traveled from that detent. The temporary
|
|
1344
|
-
// scroll-preservation inset leaves with the temporary expanded layout.
|
|
1345
|
-
fireTimedPointer(getHandle(), 'pointermove', {time: 5000, y: 340});
|
|
1346
|
-
expect(sheet.style.transform).toBe('translateY(100px)');
|
|
1347
|
-
expect(sheet.style.height).toBe('448px');
|
|
1348
|
-
expect(getBody().style.paddingBlockEnd).toBe('');
|
|
1349
|
-
});
|
|
1350
|
-
|
|
1351
|
-
it('reconciles the snapped height immediately when transitions are disabled', () => {
|
|
1352
|
-
const observers = mockResizeObserverInstances();
|
|
1353
|
-
mockVisualViewport(800);
|
|
1354
|
-
mockWindowHeight(800);
|
|
1355
|
-
render(
|
|
1356
|
-
<BottomSheet
|
|
1357
|
-
isOpen
|
|
1358
|
-
onOpenChange={() => {}}
|
|
1359
|
-
label="Release notes"
|
|
1360
|
-
snapPoints={SNAP_POINTS}
|
|
1361
|
-
height="tall"
|
|
1362
|
-
style={{transition: 'none'}}>
|
|
1363
|
-
Content
|
|
1364
|
-
</BottomSheet>,
|
|
1365
|
-
);
|
|
1366
|
-
const sheet = getSheet();
|
|
1367
|
-
const sheetObserver = observers.find(instance =>
|
|
1368
|
-
instance.observed.has(sheet),
|
|
1369
|
-
);
|
|
1370
|
-
act(() => {
|
|
1371
|
-
sheetObserver?.callback(
|
|
1372
|
-
[resizeEntry(784, 736)],
|
|
1373
|
-
sheetObserver as unknown as ResizeObserver,
|
|
1374
|
-
);
|
|
1375
|
-
});
|
|
1376
|
-
|
|
1377
|
-
fireTimedPointer(getHandle(), 'pointerdown', {time: 0, y: 0});
|
|
1378
|
-
fireTimedPointer(getHandle(), 'pointermove', {time: 1000, y: 240});
|
|
1379
|
-
fireTimedPointer(getHandle(), 'pointerup', {time: 2000, y: 240});
|
|
1380
|
-
|
|
1381
|
-
expect(sheet.style.transform).toBe('');
|
|
1382
|
-
expect(sheet.style.height).toBe('448px');
|
|
1383
|
-
});
|
|
1384
|
-
|
|
1385
|
-
it('restores the maximum height before an upward drag and reconciles at snap', () => {
|
|
1386
|
-
const observers = mockResizeObserverInstances();
|
|
1387
|
-
mockVisualViewport(800);
|
|
1388
|
-
mockWindowHeight(800);
|
|
1389
|
-
render(
|
|
1390
|
-
<BottomSheet
|
|
1391
|
-
isOpen
|
|
1392
|
-
onOpenChange={() => {}}
|
|
1393
|
-
label="Release notes"
|
|
1394
|
-
snapPoints={SNAP_POINTS}
|
|
1395
|
-
height="tall">
|
|
1396
|
-
Content
|
|
1397
|
-
</BottomSheet>,
|
|
1398
|
-
);
|
|
1399
|
-
const sheet = getSheet();
|
|
1400
|
-
const sheetObserver = observers.find(instance =>
|
|
1401
|
-
instance.observed.has(sheet),
|
|
1402
|
-
);
|
|
1403
|
-
act(() => {
|
|
1404
|
-
sheetObserver?.callback(
|
|
1405
|
-
[resizeEntry(784, 736)],
|
|
1406
|
-
sheetObserver as unknown as ResizeObserver,
|
|
1407
|
-
);
|
|
1408
|
-
});
|
|
1409
|
-
|
|
1410
|
-
// Start at the middle 400px detent.
|
|
1411
|
-
fireTimedPointer(getHandle(), 'pointerdown', {time: 0, y: 0});
|
|
1412
|
-
fireTimedPointer(getHandle(), 'pointermove', {time: 1000, y: 240});
|
|
1413
|
-
fireTimedPointer(getHandle(), 'pointerup', {time: 2000, y: 240});
|
|
1414
|
-
fireEvent.transitionEnd(sheet, {propertyName: 'transform'});
|
|
1415
|
-
expect(sheet.style.height).toBe('448px');
|
|
1416
|
-
|
|
1417
|
-
// The upward gesture renders the full 784px surface below the viewport
|
|
1418
|
-
// and translates it to preserve the visible top edge.
|
|
1419
|
-
fireTimedPointer(getHandle(), 'pointerdown', {time: 3000, y: 240});
|
|
1420
|
-
fireTimedPointer(getHandle(), 'pointermove', {time: 4000, y: 0});
|
|
1421
|
-
expect(sheet.style.transform).toBe('translateY(96px)');
|
|
1422
|
-
expect(sheet.style.height).toBe('784px');
|
|
1423
|
-
expect(getBody().style.paddingBlockEnd).toBe('336px');
|
|
1424
|
-
|
|
1425
|
-
// Release first animates only the transform, keeping the source layout
|
|
1426
|
-
// and scroll range fixed for the entire snap.
|
|
1427
|
-
fireTimedPointer(getHandle(), 'pointerup', {time: 5000, y: 0});
|
|
1428
|
-
expect(sheet.style.transform).toBe('');
|
|
1429
|
-
expect(sheet.style.height).toBe('784px');
|
|
1430
|
-
expect(getBody().style.paddingBlockEnd).toBe('336px');
|
|
1431
|
-
|
|
1432
|
-
// At transition end, height and preservation spacing reconcile in one
|
|
1433
|
-
// render with the same visible geometry.
|
|
1434
|
-
fireEvent.transitionEnd(sheet, {propertyName: 'transform'});
|
|
1435
|
-
expect(sheet.style.transform).toBe('');
|
|
1436
|
-
expect(sheet.style.height).toBe('');
|
|
1437
|
-
expect(getBody().style.paddingBlockEnd).toBe('336px');
|
|
1438
|
-
|
|
1439
|
-
// Once ordinary scrolling brings the content back within its natural
|
|
1440
|
-
// range, the retained end padding is no longer needed and is discarded.
|
|
1441
|
-
const body = getBody();
|
|
1442
|
-
Object.defineProperties(body, {
|
|
1443
|
-
clientHeight: {configurable: true, value: 600},
|
|
1444
|
-
scrollHeight: {configurable: true, value: 1000},
|
|
1445
|
-
scrollTop: {configurable: true, value: 64, writable: true},
|
|
1446
|
-
});
|
|
1447
|
-
fireEvent.scroll(body);
|
|
1448
|
-
expect(body.style.paddingBlockEnd).toBe('');
|
|
1449
|
-
});
|
|
1450
|
-
|
|
1451
|
-
it('keeps the settled height stable while dismissing', () => {
|
|
1452
|
-
const observers = mockResizeObserverInstances();
|
|
1453
|
-
mockVisualViewport(800);
|
|
1454
|
-
mockWindowHeight(800);
|
|
1455
|
-
render(<ExitHarness snapPoints={SNAP_POINTS} />);
|
|
1456
|
-
const sheet = getSheet();
|
|
1457
|
-
const sheetObserver = observers.find(instance =>
|
|
1458
|
-
instance.observed.has(sheet),
|
|
1459
|
-
);
|
|
1460
|
-
act(() => {
|
|
1461
|
-
sheetObserver?.callback(
|
|
1462
|
-
[resizeEntry(784, 736)],
|
|
1463
|
-
sheetObserver as unknown as ResizeObserver,
|
|
1464
|
-
);
|
|
1465
|
-
});
|
|
1466
|
-
|
|
1467
|
-
// First settle at the middle detent.
|
|
1468
|
-
fireTimedPointer(getHandle(), 'pointerdown', {time: 0, y: 0});
|
|
1469
|
-
fireTimedPointer(getHandle(), 'pointermove', {time: 1000, y: 240});
|
|
1470
|
-
fireTimedPointer(getHandle(), 'pointerup', {time: 2000, y: 240});
|
|
1471
|
-
fireEvent.transitionEnd(sheet, {propertyName: 'transform'});
|
|
1472
|
-
expect(sheet.style.height).toBe('448px');
|
|
1473
|
-
|
|
1474
|
-
// A later dismiss stays transform-only and preserves that settled
|
|
1475
|
-
// scroll-area height throughout the exit.
|
|
1476
|
-
fireTimedPointer(getHandle(), 'pointerdown', {time: 3000, y: 240});
|
|
1477
|
-
fireTimedPointer(getHandle(), 'pointermove', {time: 4000, y: 1000});
|
|
1478
|
-
expect(sheet.style.transform).toBe('translateY(760px)');
|
|
1479
|
-
expect(sheet.style.height).toBe('448px');
|
|
1480
|
-
fireTimedPointer(getHandle(), 'pointerup', {time: 5000, y: 1000});
|
|
1481
|
-
expect(sheet.style.height).toBe('448px');
|
|
1482
|
-
|
|
1483
|
-
act(() => {
|
|
1484
|
-
sheetObserver?.callback(
|
|
1485
|
-
[resizeEntry(200, 152)],
|
|
1486
|
-
sheetObserver as unknown as ResizeObserver,
|
|
1487
|
-
);
|
|
1488
|
-
});
|
|
1489
|
-
expect(sheet.style.height).toBe('448px');
|
|
1490
|
-
});
|
|
1491
|
-
|
|
1492
|
-
it('re-resolves the settled detent when the window resizes', () => {
|
|
1493
|
-
const observers = mockResizeObserverInstances();
|
|
1494
|
-
mockVisualViewport(800);
|
|
1495
|
-
mockWindowHeight(800);
|
|
1496
|
-
render(
|
|
1497
|
-
<BottomSheet
|
|
1498
|
-
isOpen
|
|
1499
|
-
onOpenChange={() => {}}
|
|
1500
|
-
label="Release notes"
|
|
1501
|
-
snapPoints={SNAP_POINTS}
|
|
1502
|
-
height="tall">
|
|
1503
|
-
Content
|
|
1504
|
-
</BottomSheet>,
|
|
1505
|
-
);
|
|
1506
|
-
const sheet = getSheet();
|
|
1507
|
-
const sheetObserver = observers.find(instance =>
|
|
1508
|
-
instance.observed.has(sheet),
|
|
1509
|
-
);
|
|
1510
|
-
act(() => {
|
|
1511
|
-
sheetObserver?.callback(
|
|
1512
|
-
[resizeEntry(784, 736)],
|
|
1513
|
-
sheetObserver as unknown as ResizeObserver,
|
|
1514
|
-
);
|
|
1515
|
-
});
|
|
1516
|
-
|
|
1517
|
-
// Settle on the half-height detent: 784 - 48 - 400 = 336px of travel.
|
|
1518
|
-
fireTimedPointer(getHandle(), 'pointerdown', {time: 0, y: 0});
|
|
1519
|
-
fireTimedPointer(getHandle(), 'pointermove', {time: 1000, y: 336});
|
|
1520
|
-
fireTimedPointer(getHandle(), 'pointerup', {time: 2000, y: 336});
|
|
1521
|
-
const reconciliationFrames: FrameRequestCallback[] = [];
|
|
1522
|
-
vi.mocked(requestAnimationFrame).mockImplementation(callback => {
|
|
1523
|
-
reconciliationFrames.push(callback);
|
|
1524
|
-
return reconciliationFrames.length;
|
|
1525
|
-
});
|
|
1526
|
-
fireEvent.transitionEnd(sheet, {propertyName: 'transform'});
|
|
1527
|
-
act(() => reconciliationFrames.splice(0).forEach(frame => frame(0)));
|
|
1528
|
-
// 448px of layout height shows 400px of sheet: half the 800px window.
|
|
1529
|
-
expect(sheet.style.height).toBe('448px');
|
|
1530
|
-
|
|
1531
|
-
// Shrink the window. The sheet's budget is `92dvh`, so its border box
|
|
1532
|
-
// follows: 0.92 * 600 + the 48px reserve = 600px.
|
|
1533
|
-
vi.spyOn(sheet, 'getBoundingClientRect').mockImplementation(() =>
|
|
1534
|
-
rect({
|
|
1535
|
-
top: 0,
|
|
1536
|
-
bottom: sheet.style.height
|
|
1537
|
-
? Number.parseFloat(sheet.style.height)
|
|
1538
|
-
: 600,
|
|
1539
|
-
}),
|
|
1540
|
-
);
|
|
1541
|
-
mockWindowHeight(600);
|
|
1542
|
-
act(() => {
|
|
1543
|
-
window.dispatchEvent(new Event('resize'));
|
|
1544
|
-
});
|
|
1545
|
-
|
|
1546
|
-
// Still the half-height detent, re-resolved against the new window:
|
|
1547
|
-
// 348px of layout height shows 300px, half of 600. Before this, the
|
|
1548
|
-
// sheet kept its 448px and showed 400px — three quarters of the window.
|
|
1549
|
-
expect(sheet.style.height).toBe('348px');
|
|
1550
|
-
expect(sheet.style.transform).toBe('');
|
|
1551
|
-
// Re-anchoring is not a gesture, so it does not animate.
|
|
1552
|
-
expect(sheet.style.transition).toBe('none');
|
|
1553
|
-
act(() => reconciliationFrames.splice(0).forEach(frame => frame(0)));
|
|
1554
|
-
expect(sheet.style.transition).toBe('');
|
|
1555
|
-
});
|
|
1556
|
-
|
|
1557
|
-
it('keeps its detents when the keyboard shrinks the visual viewport', () => {
|
|
1558
|
-
const observers = mockResizeObserverInstances();
|
|
1559
|
-
const viewport = mockVisualViewport(800);
|
|
1560
|
-
mockWindowHeight(800);
|
|
1561
|
-
render(
|
|
1562
|
-
<BottomSheet
|
|
1563
|
-
isOpen
|
|
1564
|
-
onOpenChange={() => {}}
|
|
1565
|
-
label="Release notes"
|
|
1566
|
-
snapPoints={SNAP_POINTS}
|
|
1567
|
-
height="tall">
|
|
1568
|
-
Content
|
|
1569
|
-
</BottomSheet>,
|
|
1570
|
-
);
|
|
1571
|
-
const sheet = getSheet();
|
|
1572
|
-
const sheetObserver = observers.find(instance =>
|
|
1573
|
-
instance.observed.has(sheet),
|
|
1574
|
-
);
|
|
1575
|
-
act(() => {
|
|
1576
|
-
sheetObserver?.callback(
|
|
1577
|
-
[resizeEntry(784, 736)],
|
|
1578
|
-
sheetObserver as unknown as ResizeObserver,
|
|
1579
|
-
);
|
|
1580
|
-
});
|
|
1581
|
-
|
|
1582
|
-
fireTimedPointer(getHandle(), 'pointerdown', {time: 0, y: 0});
|
|
1583
|
-
fireTimedPointer(getHandle(), 'pointermove', {time: 1000, y: 336});
|
|
1584
|
-
fireTimedPointer(getHandle(), 'pointerup', {time: 2000, y: 336});
|
|
1585
|
-
fireEvent.transitionEnd(sheet, {propertyName: 'transform'});
|
|
1586
|
-
expect(sheet.style.height).toBe('448px');
|
|
1587
|
-
|
|
1588
|
-
// The keyboard opens: the visual viewport shrinks, the layout viewport
|
|
1589
|
-
// the sheet is measured in does not.
|
|
1590
|
-
act(() => {
|
|
1591
|
-
viewport.height = 500;
|
|
1592
|
-
viewport.dispatchEvent(new Event('resize'));
|
|
1593
|
-
});
|
|
1594
|
-
expect(sheet.style.height).toBe('448px');
|
|
1595
|
-
|
|
1596
|
-
// The next drag still snaps to the window's detents, not to fractions
|
|
1597
|
-
// of the space the keyboard left over.
|
|
1598
|
-
fireTimedPointer(getHandle(), 'pointerdown', {time: 3000, y: 0});
|
|
1599
|
-
fireTimedPointer(getHandle(), 'pointermove', {time: 4000, y: 288});
|
|
1600
|
-
fireTimedPointer(getHandle(), 'pointerup', {time: 5000, y: 288});
|
|
1601
|
-
// The peek of an 800px window: 736 - 112 = 624px of travel.
|
|
1602
|
-
expect(sheet.style.transform).toBe('translateY(624px)');
|
|
1603
|
-
});
|
|
1604
|
-
});
|
|
1605
|
-
|
|
1606
|
-
describe('snapPoints', () => {
|
|
1607
|
-
// A Tall sheet in an 800px window: a 784px border box, 48px of which is
|
|
1608
|
-
// the reserve below the fold, so 736px of it is visible.
|
|
1609
|
-
function renderTallSheet(
|
|
1610
|
-
snapPoints?: ReadonlyArray<number | string>,
|
|
1611
|
-
onOpenChange: (isOpen: boolean) => void = () => {},
|
|
1612
|
-
) {
|
|
1613
|
-
const observers = mockResizeObserverInstances();
|
|
1614
|
-
mockVisualViewport(800);
|
|
1615
|
-
mockWindowHeight(800);
|
|
1616
|
-
const view = render(
|
|
1617
|
-
<BottomSheet
|
|
1618
|
-
isOpen
|
|
1619
|
-
onOpenChange={onOpenChange}
|
|
1620
|
-
label="Release notes"
|
|
1621
|
-
snapPoints={snapPoints}
|
|
1622
|
-
height="tall">
|
|
1623
|
-
Content
|
|
1624
|
-
</BottomSheet>,
|
|
1625
|
-
);
|
|
1626
|
-
const sheet = getSheet();
|
|
1627
|
-
const sheetObserver = observers.find(instance =>
|
|
1628
|
-
instance.observed.has(sheet),
|
|
1629
|
-
);
|
|
1630
|
-
act(() => {
|
|
1631
|
-
sheetObserver?.callback(
|
|
1632
|
-
[resizeEntry(784, 736)],
|
|
1633
|
-
sheetObserver as unknown as ResizeObserver,
|
|
1634
|
-
);
|
|
1635
|
-
});
|
|
1636
|
-
return {sheet, view};
|
|
1637
|
-
}
|
|
1638
|
-
|
|
1639
|
-
function dragHandleTo(y: number) {
|
|
1640
|
-
fireTimedPointer(getHandle(), 'pointerdown', {time: 0, y: 0});
|
|
1641
|
-
fireTimedPointer(getHandle(), 'pointermove', {time: 1000, y});
|
|
1642
|
-
fireTimedPointer(getHandle(), 'pointerup', {time: 2000, y});
|
|
1643
|
-
fireEvent.transitionEnd(getSheet(), {propertyName: 'transform'});
|
|
1644
|
-
}
|
|
1645
|
-
|
|
1646
|
-
it('has no stops of its own, so a released drag springs back', () => {
|
|
1647
|
-
const onOpenChange = vi.fn();
|
|
1648
|
-
const {sheet} = renderTallSheet(undefined, onOpenChange);
|
|
1649
|
-
|
|
1650
|
-
// 200px down is well short of the dismiss threshold, and there is no
|
|
1651
|
-
// stop to catch it, so the sheet returns to fully open.
|
|
1652
|
-
dragHandleTo(200);
|
|
1653
|
-
|
|
1654
|
-
expect(sheet.style.transform).toBe('');
|
|
1655
|
-
expect(sheet.style.height).toBe('');
|
|
1656
|
-
expect(onOpenChange).not.toHaveBeenCalled();
|
|
1657
|
-
});
|
|
1658
|
-
|
|
1659
|
-
it('rests at a stop given as a fraction of the viewport', () => {
|
|
1660
|
-
const {sheet} = renderTallSheet([0.5]);
|
|
1661
|
-
|
|
1662
|
-
// Half of the 800px window is 400px of visible sheet: 736 - 400 = 336px
|
|
1663
|
-
// of travel, taken as layout height once the snap lands.
|
|
1664
|
-
dragHandleTo(336);
|
|
1665
|
-
|
|
1666
|
-
expect(sheet.style.height).toBe('448px');
|
|
1667
|
-
expect(sheet.style.transform).toBe('');
|
|
1668
|
-
});
|
|
1669
|
-
|
|
1670
|
-
it('reads a percentage as the same stop as the fraction', () => {
|
|
1671
|
-
const {sheet} = renderTallSheet(['50%']);
|
|
1672
|
-
dragHandleTo(336);
|
|
1673
|
-
expect(sheet.style.height).toBe('448px');
|
|
1674
|
-
});
|
|
1675
|
-
|
|
1676
|
-
it('rests at a stop given as an absolute px length', () => {
|
|
1677
|
-
const {sheet} = renderTallSheet(['320px']);
|
|
1678
|
-
|
|
1679
|
-
// A 320px stop sits 736 - 320 = 416px down, whatever the window does.
|
|
1680
|
-
dragHandleTo(416);
|
|
1681
|
-
|
|
1682
|
-
expect(sheet.style.height).toBe('368px');
|
|
1683
|
-
expect(sheet.style.transform).toBe('');
|
|
1684
|
-
});
|
|
1685
|
-
|
|
1686
|
-
it('re-anchors to the same stop when the points change under a resting sheet', () => {
|
|
1687
|
-
const {sheet, view} = renderTallSheet([0.5]);
|
|
1688
|
-
dragHandleTo(336);
|
|
1689
|
-
expect(sheet.style.height).toBe('448px');
|
|
1690
|
-
|
|
1691
|
-
// Without an inline height the sheet renders its natural 92dvh budget.
|
|
1692
|
-
vi.spyOn(sheet, 'getBoundingClientRect').mockImplementation(() =>
|
|
1693
|
-
rect({
|
|
1694
|
-
top: 0,
|
|
1695
|
-
bottom: sheet.style.height
|
|
1696
|
-
? Number.parseFloat(sheet.style.height)
|
|
1697
|
-
: 784,
|
|
1698
|
-
}),
|
|
1699
|
-
);
|
|
1700
|
-
|
|
1701
|
-
// The host moves its one stop from half the window to a quarter of it.
|
|
1702
|
-
// The sheet is resting on that stop, so it follows — no gesture, and
|
|
1703
|
-
// nothing to animate.
|
|
1704
|
-
view.rerender(
|
|
1705
|
-
<BottomSheet
|
|
1706
|
-
isOpen
|
|
1707
|
-
onOpenChange={() => {}}
|
|
1708
|
-
label="Release notes"
|
|
1709
|
-
snapPoints={[0.25]}
|
|
1710
|
-
height="tall">
|
|
1711
|
-
Content
|
|
1712
|
-
</BottomSheet>,
|
|
1713
|
-
);
|
|
1714
|
-
|
|
1715
|
-
// 200px of visible sheet is 736 - 200 = 536px of travel.
|
|
1716
|
-
expect(sheet.style.height).toBe('248px');
|
|
1717
|
-
expect(sheet.style.transform).toBe('');
|
|
1718
|
-
});
|
|
1719
|
-
|
|
1720
|
-
it('ignores a stop it cannot resolve, and warns which one', () => {
|
|
1721
|
-
const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
1722
|
-
// 200 is the px mistake: a bare number is a fraction, never a length.
|
|
1723
|
-
const {sheet} = renderTallSheet([0.5, 200]);
|
|
1724
|
-
|
|
1725
|
-
expect(
|
|
1726
|
-
warn.mock.calls.some(args => String(args[0]).includes('200')),
|
|
1727
|
-
).toBe(true);
|
|
1728
|
-
|
|
1729
|
-
// The stop it could read still works.
|
|
1730
|
-
dragHandleTo(336);
|
|
1731
|
-
expect(sheet.style.height).toBe('448px');
|
|
1732
|
-
warn.mockRestore();
|
|
1733
|
-
});
|
|
1734
|
-
});
|
|
1735
|
-
|
|
1736
|
-
describe('mobile keyboard', () => {
|
|
1737
|
-
it('claims a transition between fields and delivers it with preventScroll', () => {
|
|
1738
|
-
mockIOSWebKit();
|
|
1739
|
-
render(
|
|
1740
|
-
<BottomSheet
|
|
1741
|
-
isOpen
|
|
1742
|
-
onOpenChange={() => {}}
|
|
1743
|
-
label="Add a comment"
|
|
1744
|
-
height="tall">
|
|
1745
|
-
<input aria-label="Title" />
|
|
1746
|
-
<input aria-label="Comment" />
|
|
1747
|
-
</BottomSheet>,
|
|
1748
|
-
);
|
|
1749
|
-
const title = screen.getByRole('textbox', {name: 'Title'});
|
|
1750
|
-
const comment = screen.getByRole('textbox', {name: 'Comment'});
|
|
1751
|
-
title.focus();
|
|
1752
|
-
const focus = vi.spyOn(comment, 'focus');
|
|
1753
|
-
|
|
1754
|
-
// The keyboard's Next, Tab, and a programmatic focus() all arrive as this
|
|
1755
|
-
// one transition, named by relatedTarget on the outgoing blur.
|
|
1756
|
-
fireEvent.blur(title, {relatedTarget: comment});
|
|
1757
|
-
|
|
1758
|
-
// Delivered by us, with the browser's reveal refused.
|
|
1759
|
-
expect(focus).toHaveBeenCalledWith({preventScroll: true});
|
|
1760
|
-
});
|
|
1761
|
-
|
|
1762
|
-
it('does not park focus on a sheet that is closing', () => {
|
|
1763
|
-
mockIOSWebKit();
|
|
1764
|
-
const content = (isOpen: boolean) => (
|
|
1765
|
-
<BottomSheet
|
|
1766
|
-
isOpen={isOpen}
|
|
1767
|
-
onOpenChange={() => {}}
|
|
1768
|
-
label="Add a comment"
|
|
1769
|
-
height="tall">
|
|
1770
|
-
<input aria-label="Comment" />
|
|
1771
|
-
</BottomSheet>
|
|
1772
|
-
);
|
|
1773
|
-
const {rerender} = render(content(true));
|
|
1774
|
-
const sheet = getSheet();
|
|
1775
|
-
const input = screen.getByRole('textbox', {name: 'Comment'});
|
|
1776
|
-
input.focus();
|
|
1777
|
-
const sheetFocus = vi.spyOn(sheet, 'focus');
|
|
1778
|
-
|
|
1779
|
-
// Closing blurs the field as well, and that blur names no destination —
|
|
1780
|
-
// the same shape as Done. There is no next tap to keep claimable here,
|
|
1781
|
-
// and the host is about to hand focus back to whatever opened the sheet.
|
|
1782
|
-
void act(() => rerender(content(false)));
|
|
1783
|
-
|
|
1784
|
-
expect(sheetFocus).not.toHaveBeenCalled();
|
|
1785
|
-
});
|
|
1786
|
-
|
|
1787
|
-
it('autofocuses a field without letting the browser reveal it', () => {
|
|
1788
|
-
mockIOSWebKit();
|
|
1789
|
-
const focus = vi.spyOn(HTMLInputElement.prototype, 'focus');
|
|
1790
|
-
render(
|
|
1791
|
-
<BottomSheet
|
|
1792
|
-
isOpen
|
|
1793
|
-
onOpenChange={() => {}}
|
|
1794
|
-
label="Add a comment"
|
|
1795
|
-
height="tall">
|
|
1796
|
-
<input aria-label="Comment" data-autofocus />
|
|
1797
|
-
</BottomSheet>,
|
|
1798
|
-
);
|
|
1799
|
-
// A prototype spy outlives this test unless it is put back by hand.
|
|
1800
|
-
const calls = [...focus.mock.calls];
|
|
1801
|
-
focus.mockRestore();
|
|
1802
|
-
const input = screen.getByRole('textbox', {name: 'Comment'});
|
|
1803
|
-
|
|
1804
|
-
// Opening focuses the field itself, so there is no transition to claim —
|
|
1805
|
-
// nothing was focused to blur. The presenting call has to refuse the
|
|
1806
|
-
// reveal on its own, or the browser scrolls the page to show a field the
|
|
1807
|
-
// sheet was about to show anyway.
|
|
1808
|
-
expect(document.activeElement).toBe(input);
|
|
1809
|
-
expect(calls).toContainEqual([{preventScroll: true}]);
|
|
1810
|
-
});
|
|
1811
|
-
|
|
1812
|
-
it('parks focus on the sheet when the keyboard Done button takes it', () => {
|
|
1813
|
-
mockIOSWebKit();
|
|
1814
|
-
render(
|
|
1815
|
-
<BottomSheet
|
|
1816
|
-
isOpen
|
|
1817
|
-
onOpenChange={() => {}}
|
|
1818
|
-
label="Add a comment"
|
|
1819
|
-
height="tall">
|
|
1820
|
-
<input aria-label="Comment" />
|
|
1821
|
-
</BottomSheet>,
|
|
1822
|
-
);
|
|
1823
|
-
const sheet = getSheet();
|
|
1824
|
-
const input = screen.getByRole('textbox', {name: 'Comment'});
|
|
1825
|
-
input.focus();
|
|
1826
|
-
const sheetFocus = vi.spyOn(sheet, 'focus');
|
|
1827
|
-
|
|
1828
|
-
// Done dismisses the keyboard and drops focus on the body. Left there,
|
|
1829
|
-
// the field is still document.activeElement on the next tap, so no
|
|
1830
|
-
// transition fires and the browser reveals it its own way. Parking focus
|
|
1831
|
-
// on the sheet keeps the next tap a transition this hook can claim.
|
|
1832
|
-
fireEvent.blur(input, {relatedTarget: null});
|
|
1833
|
-
|
|
1834
|
-
expect(sheetFocus).toHaveBeenCalledWith({preventScroll: true});
|
|
1835
|
-
});
|
|
1836
|
-
|
|
1837
|
-
it('does not alter ordinary desktop focus when the viewport is unobstructed', () => {
|
|
1838
|
-
mockVisualViewport(800);
|
|
1839
|
-
mockWindowHeight(800);
|
|
1840
|
-
const onFocus = vi.fn();
|
|
1841
|
-
render(
|
|
1842
|
-
<BottomSheet
|
|
1843
|
-
isOpen
|
|
1844
|
-
onOpenChange={() => {}}
|
|
1845
|
-
label="Add a comment"
|
|
1846
|
-
height="tall">
|
|
1847
|
-
<input aria-label="Comment" onFocus={onFocus} />
|
|
1848
|
-
</BottomSheet>,
|
|
1849
|
-
);
|
|
1850
|
-
const input = screen.getByRole('textbox', {name: 'Comment'});
|
|
1851
|
-
const focus = vi.spyOn(input, 'focus');
|
|
1852
|
-
|
|
1853
|
-
input.focus();
|
|
1854
|
-
|
|
1855
|
-
expect(focus).not.toHaveBeenCalledWith({preventScroll: true});
|
|
1856
|
-
expect(onFocus).toHaveBeenCalledTimes(1);
|
|
1857
|
-
expect(getBody().style.getPropertyValue('--_sheet-keyboard-inset')).toBe(
|
|
1858
|
-
'0px',
|
|
1859
|
-
);
|
|
1860
|
-
});
|
|
1861
|
-
|
|
1862
|
-
it('keeps Tall geometry fixed while extending and cleaning up its internal scroll range', () => {
|
|
1863
|
-
const viewport = mockVisualViewport(500);
|
|
1864
|
-
render(
|
|
1865
|
-
<BottomSheet
|
|
1866
|
-
isOpen
|
|
1867
|
-
onOpenChange={() => {}}
|
|
1868
|
-
label="Add a comment"
|
|
1869
|
-
height="tall">
|
|
1870
|
-
<input aria-label="Comment" />
|
|
1871
|
-
</BottomSheet>,
|
|
1872
|
-
);
|
|
1873
|
-
const sheet = getSheet();
|
|
1874
|
-
const positioner = getPositioner();
|
|
1875
|
-
const body = getBody();
|
|
1876
|
-
const input = screen.getByRole('textbox', {name: 'Comment'});
|
|
1877
|
-
vi.spyOn(sheet, 'getBoundingClientRect').mockReturnValue(
|
|
1878
|
-
rect({top: 0, bottom: 800}),
|
|
1879
|
-
);
|
|
1880
|
-
vi.spyOn(body, 'getBoundingClientRect').mockReturnValue(
|
|
1881
|
-
rect({top: 100, bottom: 800}),
|
|
1882
|
-
);
|
|
1883
|
-
vi.spyOn(input, 'getBoundingClientRect').mockImplementation(() =>
|
|
1884
|
-
rect({
|
|
1885
|
-
top: 660 - body.scrollTop,
|
|
1886
|
-
bottom: 700 - body.scrollTop,
|
|
1887
|
-
}),
|
|
1888
|
-
);
|
|
1889
|
-
|
|
1890
|
-
const initialRect = sheet.getBoundingClientRect();
|
|
1891
|
-
|
|
1892
|
-
input.focus();
|
|
1893
|
-
|
|
1894
|
-
expect(sheet.getBoundingClientRect()).toEqual(initialRect);
|
|
1895
|
-
expect(sheet.style.height).toBe('');
|
|
1896
|
-
expect(sheet.style.getPropertyValue('--_sheet-budget')).toBe('92dvh');
|
|
1897
|
-
expect(positioner.style.getPropertyValue('--_sheet-keyboard-lift')).toBe(
|
|
1898
|
-
'',
|
|
1899
|
-
);
|
|
1900
|
-
// 300px keyboard overlap + 48px room for Android suggestion UI.
|
|
1901
|
-
expect(body.style.getPropertyValue('--_sheet-keyboard-inset')).toBe(
|
|
1902
|
-
'348px',
|
|
1903
|
-
);
|
|
1904
|
-
// Visible bottom is 500px; preserve the same 48px focus gap.
|
|
1905
|
-
expect(body.scrollTop).toBe(248);
|
|
1906
|
-
|
|
1907
|
-
viewport.height = 800;
|
|
1908
|
-
void act(() => viewport.dispatchEvent(new Event('resize')));
|
|
1909
|
-
expect(body.style.getPropertyValue('--_sheet-keyboard-inset')).toBe(
|
|
1910
|
-
'0px',
|
|
1911
|
-
);
|
|
1912
|
-
expect(sheet.getBoundingClientRect()).toEqual(initialRect);
|
|
1913
|
-
expect(sheet.style.height).toBe('');
|
|
1914
|
-
});
|
|
1915
|
-
|
|
1916
|
-
it('smoothly scrolls a focused Tall control above the keyboard', () => {
|
|
1917
|
-
mockVisualViewport(500);
|
|
1918
|
-
render(
|
|
1919
|
-
<BottomSheet
|
|
1920
|
-
isOpen
|
|
1921
|
-
onOpenChange={() => {}}
|
|
1922
|
-
label="Add a comment"
|
|
1923
|
-
height="tall">
|
|
1924
|
-
<input aria-label="Comment" />
|
|
1925
|
-
</BottomSheet>,
|
|
1926
|
-
);
|
|
1927
|
-
const body = getBody();
|
|
1928
|
-
const input = screen.getByRole('textbox', {name: 'Comment'});
|
|
1929
|
-
const scrollBy = vi.fn((options: ScrollToOptions) => {
|
|
1930
|
-
body.scrollTop += options.top ?? 0;
|
|
1931
|
-
});
|
|
1932
|
-
Object.defineProperty(body, 'scrollBy', {
|
|
1933
|
-
configurable: true,
|
|
1934
|
-
value: scrollBy,
|
|
1935
|
-
});
|
|
1936
|
-
vi.spyOn(body, 'getBoundingClientRect').mockReturnValue(
|
|
1937
|
-
rect({top: 100, bottom: 800}),
|
|
1938
|
-
);
|
|
1939
|
-
vi.spyOn(input, 'getBoundingClientRect').mockImplementation(() =>
|
|
1940
|
-
rect({
|
|
1941
|
-
top: 660 - body.scrollTop,
|
|
1942
|
-
bottom: 700 - body.scrollTop,
|
|
1943
|
-
}),
|
|
1944
|
-
);
|
|
1945
|
-
|
|
1946
|
-
input.focus();
|
|
1947
|
-
|
|
1948
|
-
expect(scrollBy).toHaveBeenCalledWith({top: 248, behavior: 'smooth'});
|
|
1949
|
-
});
|
|
1950
|
-
|
|
1951
|
-
it('does not add clearance or scroll when the viewport is unobstructed', () => {
|
|
1952
|
-
mockVisualViewport(800);
|
|
1953
|
-
mockWindowHeight(800);
|
|
1954
|
-
render(
|
|
1955
|
-
<BottomSheet
|
|
1956
|
-
isOpen
|
|
1957
|
-
onOpenChange={() => {}}
|
|
1958
|
-
label="Add a comment"
|
|
1959
|
-
height="tall">
|
|
1960
|
-
<input aria-label="Comment" />
|
|
1961
|
-
</BottomSheet>,
|
|
1962
|
-
);
|
|
1963
|
-
const body = getBody();
|
|
1964
|
-
const input = screen.getByRole('textbox', {name: 'Comment'});
|
|
1965
|
-
vi.spyOn(body, 'getBoundingClientRect').mockReturnValue(
|
|
1966
|
-
rect({top: 100, bottom: 800}),
|
|
1967
|
-
);
|
|
1968
|
-
vi.spyOn(input, 'getBoundingClientRect').mockReturnValue(
|
|
1969
|
-
rect({top: 760, bottom: 790}),
|
|
1970
|
-
);
|
|
1971
|
-
|
|
1972
|
-
input.focus();
|
|
1973
|
-
|
|
1974
|
-
expect(body.style.getPropertyValue('--_sheet-keyboard-inset')).toBe(
|
|
1975
|
-
'0px',
|
|
1976
|
-
);
|
|
1977
|
-
expect(body.scrollTop).toBe(0);
|
|
1978
|
-
});
|
|
1979
|
-
|
|
1980
|
-
it('does not accommodate the keyboard at a shorter Tall detent', () => {
|
|
1981
|
-
mockIOSWebKit();
|
|
1982
|
-
const observers = mockResizeObserverInstances();
|
|
1983
|
-
const viewport = mockVisualViewport(800);
|
|
1984
|
-
mockWindowHeight(800);
|
|
1985
|
-
render(
|
|
1986
|
-
<BottomSheet
|
|
1987
|
-
isOpen
|
|
1988
|
-
onOpenChange={() => {}}
|
|
1989
|
-
label="Add a comment"
|
|
1990
|
-
snapPoints={SNAP_POINTS}
|
|
1991
|
-
height="tall">
|
|
1992
|
-
<input aria-label="Comment" />
|
|
1993
|
-
</BottomSheet>,
|
|
1994
|
-
);
|
|
1995
|
-
const sheet = getSheet();
|
|
1996
|
-
const body = getBody();
|
|
1997
|
-
const input = screen.getByRole('textbox', {name: 'Comment'});
|
|
1998
|
-
const sheetObserver = observers.find(instance =>
|
|
1999
|
-
instance.observed.has(sheet),
|
|
2000
|
-
);
|
|
2001
|
-
expect(sheetObserver).toBeDefined();
|
|
2002
|
-
act(() => {
|
|
2003
|
-
sheetObserver?.callback(
|
|
2004
|
-
[resizeEntry(784)],
|
|
2005
|
-
sheetObserver as unknown as ResizeObserver,
|
|
2006
|
-
);
|
|
2007
|
-
});
|
|
2008
|
-
|
|
2009
|
-
fireTimedPointer(getHandle(), 'pointerdown', {time: 0, y: 0});
|
|
2010
|
-
fireTimedPointer(getHandle(), 'pointermove', {time: 1000, y: 240});
|
|
2011
|
-
fireTimedPointer(getHandle(), 'pointerup', {time: 2000, y: 240});
|
|
2012
|
-
fireEvent.transitionEnd(sheet, {propertyName: 'transform'});
|
|
2013
|
-
// Settled at the p50 detent: 400px of visible sheet plus the 48px
|
|
2014
|
-
// border-box reserve held below the viewport.
|
|
2015
|
-
expect(sheet.style.transform).toBe('');
|
|
2016
|
-
expect(sheet.style.height).toBe('448px');
|
|
2017
|
-
|
|
2018
|
-
vi.spyOn(body, 'getBoundingClientRect').mockReturnValue(
|
|
2019
|
-
rect({top: 500, bottom: 1200}),
|
|
2020
|
-
);
|
|
2021
|
-
vi.spyOn(input, 'getBoundingClientRect').mockReturnValue(
|
|
2022
|
-
rect({top: 700, bottom: 740}),
|
|
2023
|
-
);
|
|
2024
|
-
viewport.height = 500;
|
|
2025
|
-
const focus = vi.spyOn(input, 'focus');
|
|
2026
|
-
// A transition the browser would drive: at a shorter detent the hook
|
|
2027
|
-
// must not claim it, so nothing focuses the field but the caller.
|
|
2028
|
-
fireEvent.blur(sheet, {relatedTarget: input});
|
|
2029
|
-
expect(focus).not.toHaveBeenCalled();
|
|
2030
|
-
|
|
2031
|
-
focus.mockRestore();
|
|
2032
|
-
input.focus();
|
|
2033
|
-
void act(() => viewport.dispatchEvent(new Event('resize')));
|
|
2034
|
-
|
|
2035
|
-
expect(body.style.getPropertyValue('--_sheet-keyboard-inset')).toBe(
|
|
2036
|
-
'0px',
|
|
2037
|
-
);
|
|
2038
|
-
expect(body.scrollTop).toBe(0);
|
|
2039
|
-
// The keyboard moved nothing: the sheet still rests at that detent.
|
|
2040
|
-
expect(sheet.style.transform).toBe('');
|
|
2041
|
-
expect(sheet.style.height).toBe('448px');
|
|
2042
|
-
});
|
|
2043
|
-
|
|
2044
|
-
it.each([
|
|
2045
|
-
['Hug', 'hug'],
|
|
2046
|
-
['Capped', 'capped'],
|
|
2047
|
-
['numeric', 480],
|
|
2048
|
-
['custom CSS', '70dvh'],
|
|
2049
|
-
] as const)(
|
|
2050
|
-
'does not add keyboard behavior to a %s height',
|
|
2051
|
-
(_label, height) => {
|
|
2052
|
-
mockIOSWebKit();
|
|
2053
|
-
const viewport = mockVisualViewport(500);
|
|
2054
|
-
render(
|
|
2055
|
-
<BottomSheet
|
|
2056
|
-
isOpen
|
|
2057
|
-
onOpenChange={() => {}}
|
|
2058
|
-
label="Add a comment"
|
|
2059
|
-
height={height}>
|
|
2060
|
-
<input aria-label="Comment" />
|
|
2061
|
-
</BottomSheet>,
|
|
2062
|
-
);
|
|
2063
|
-
const sheet = getSheet();
|
|
2064
|
-
const positioner = getPositioner();
|
|
2065
|
-
const body = getBody();
|
|
2066
|
-
const input = screen.getByRole('textbox', {name: 'Comment'});
|
|
2067
|
-
const focus = vi.spyOn(input, 'focus');
|
|
2068
|
-
const sheetFocus = vi.spyOn(sheet, 'focus');
|
|
2069
|
-
vi.spyOn(body, 'getBoundingClientRect').mockReturnValue(
|
|
2070
|
-
rect({top: 400, bottom: 800}),
|
|
2071
|
-
);
|
|
2072
|
-
vi.spyOn(input, 'getBoundingClientRect').mockReturnValue(
|
|
2073
|
-
rect({top: 700, bottom: 740}),
|
|
2074
|
-
);
|
|
2075
|
-
body.scrollTop = 20;
|
|
2076
|
-
|
|
2077
|
-
fireEvent.touchStart(input);
|
|
2078
|
-
// These heights opt out, so a browser-driven transition is left alone.
|
|
2079
|
-
fireEvent.blur(sheet, {relatedTarget: input});
|
|
2080
|
-
fireEvent.pointerDown(input, {pointerId: 1, clientY: 200});
|
|
2081
|
-
body.scrollTop = 120;
|
|
2082
|
-
fireEvent.focus(input, {relatedTarget: null});
|
|
2083
|
-
void act(() => viewport.dispatchEvent(new Event('resize')));
|
|
2084
|
-
|
|
2085
|
-
expect(body.scrollTop).toBe(120);
|
|
2086
|
-
expect(focus).not.toHaveBeenCalled();
|
|
2087
|
-
expect(sheetFocus).not.toHaveBeenCalled();
|
|
2088
|
-
expect(body.style.getPropertyValue('--_sheet-keyboard-inset')).toBe('');
|
|
2089
|
-
expect(
|
|
2090
|
-
positioner.style.getPropertyValue('--_sheet-keyboard-lift'),
|
|
2091
|
-
).toBe('');
|
|
2092
|
-
expect(sheet.style.height).toBe('');
|
|
2093
|
-
},
|
|
2094
|
-
);
|
|
2095
|
-
|
|
2096
|
-
it.each([
|
|
2097
|
-
['standalone no-scrim', 'standalone', false],
|
|
2098
|
-
['modal switcher', 'switcher', true],
|
|
2099
|
-
['no-scrim switcher', 'switcher', false],
|
|
2100
|
-
] as const)(
|
|
2101
|
-
'supports Tall internal keyboard scrolling in a %s presentation',
|
|
2102
|
-
(_label, host, hasScrim) => {
|
|
2103
|
-
mockVisualViewport(500);
|
|
2104
|
-
const content = <input aria-label="Comment" />;
|
|
2105
|
-
const result =
|
|
2106
|
-
host === 'standalone'
|
|
2107
|
-
? render(
|
|
2108
|
-
<BottomSheet
|
|
2109
|
-
isOpen
|
|
2110
|
-
onOpenChange={() => {}}
|
|
2111
|
-
label="Add a comment"
|
|
2112
|
-
height="tall"
|
|
2113
|
-
hasScrim={hasScrim}>
|
|
2114
|
-
{content}
|
|
2115
|
-
</BottomSheet>,
|
|
2116
|
-
)
|
|
2117
|
-
: render(
|
|
2118
|
-
<BottomSheetSwitcher
|
|
2119
|
-
activeSheet="comment"
|
|
2120
|
-
onActiveSheetChange={() => {}}
|
|
2121
|
-
hasScrim={hasScrim}>
|
|
2122
|
-
<BottomSheet
|
|
2123
|
-
sheetId="comment"
|
|
2124
|
-
label="Add a comment"
|
|
2125
|
-
height="tall">
|
|
2126
|
-
{content}
|
|
2127
|
-
</BottomSheet>
|
|
2128
|
-
</BottomSheetSwitcher>,
|
|
2129
|
-
);
|
|
2130
|
-
const body = getBody();
|
|
2131
|
-
const input = screen.getByRole('textbox', {name: 'Comment'});
|
|
2132
|
-
vi.spyOn(body, 'getBoundingClientRect').mockReturnValue(
|
|
2133
|
-
rect({top: 100, bottom: 800}),
|
|
2134
|
-
);
|
|
2135
|
-
vi.spyOn(input, 'getBoundingClientRect').mockImplementation(() =>
|
|
2136
|
-
rect({
|
|
2137
|
-
top: 660 - body.scrollTop,
|
|
2138
|
-
bottom: 700 - body.scrollTop,
|
|
2139
|
-
}),
|
|
2140
|
-
);
|
|
2141
|
-
|
|
2142
|
-
input.focus();
|
|
2143
|
-
|
|
2144
|
-
expect(body.style.getPropertyValue('--_sheet-keyboard-inset')).toBe(
|
|
2145
|
-
'348px',
|
|
2146
|
-
);
|
|
2147
|
-
expect(body.scrollTop).toBe(248);
|
|
2148
|
-
result.unmount();
|
|
2149
|
-
},
|
|
2150
|
-
);
|
|
2151
|
-
|
|
2152
|
-
it('re-reveals a focused Tall control when content layout changes', () => {
|
|
2153
|
-
const observers = mockResizeObserverInstances();
|
|
2154
|
-
mockVisualViewport(500);
|
|
2155
|
-
render(
|
|
2156
|
-
<BottomSheet
|
|
2157
|
-
isOpen
|
|
2158
|
-
onOpenChange={() => {}}
|
|
2159
|
-
label="Add a comment"
|
|
2160
|
-
height="tall">
|
|
2161
|
-
<input aria-label="Comment" />
|
|
2162
|
-
</BottomSheet>,
|
|
2163
|
-
);
|
|
2164
|
-
const body = getBody();
|
|
2165
|
-
const input = screen.getByRole('textbox', {name: 'Comment'});
|
|
2166
|
-
let layoutShift = 0;
|
|
2167
|
-
vi.spyOn(body, 'getBoundingClientRect').mockReturnValue(
|
|
2168
|
-
rect({top: 100, bottom: 800}),
|
|
2169
|
-
);
|
|
2170
|
-
vi.spyOn(input, 'getBoundingClientRect').mockImplementation(() =>
|
|
2171
|
-
rect({
|
|
2172
|
-
top: 660 + layoutShift - body.scrollTop,
|
|
2173
|
-
bottom: 700 + layoutShift - body.scrollTop,
|
|
2174
|
-
}),
|
|
2175
|
-
);
|
|
2176
|
-
|
|
2177
|
-
input.focus();
|
|
2178
|
-
expect(body.scrollTop).toBe(248);
|
|
2179
|
-
|
|
2180
|
-
layoutShift = 200;
|
|
2181
|
-
const observer = observers.find(instance => instance.observed.has(input));
|
|
2182
|
-
expect(observer).toBeDefined();
|
|
2183
|
-
act(() => {
|
|
2184
|
-
observer?.callback([], observer as unknown as ResizeObserver);
|
|
2185
|
-
});
|
|
2186
|
-
|
|
2187
|
-
expect(body.scrollTop).toBe(448);
|
|
2188
|
-
});
|
|
2189
|
-
|
|
2190
|
-
it('retains Tall keyboard scroll space during travel until the viewport recovers', () => {
|
|
2191
|
-
const viewport = mockVisualViewport(500);
|
|
2192
|
-
render(
|
|
2193
|
-
<BottomSheet
|
|
2194
|
-
isOpen
|
|
2195
|
-
onOpenChange={() => {}}
|
|
2196
|
-
label="Add a comment"
|
|
2197
|
-
height="tall">
|
|
2198
|
-
<input aria-label="Comment" />
|
|
2199
|
-
</BottomSheet>,
|
|
2200
|
-
);
|
|
2201
|
-
const sheet = getSheet();
|
|
2202
|
-
const positioner = getPositioner();
|
|
2203
|
-
const body = getBody();
|
|
2204
|
-
const input = screen.getByRole('textbox', {name: 'Comment'});
|
|
2205
|
-
vi.spyOn(sheet, 'getBoundingClientRect').mockReturnValue(
|
|
2206
|
-
rect({top: 0, bottom: 800}),
|
|
2207
|
-
);
|
|
2208
|
-
vi.spyOn(body, 'getBoundingClientRect').mockReturnValue(
|
|
2209
|
-
rect({top: 100, bottom: 800}),
|
|
2210
|
-
);
|
|
2211
|
-
vi.spyOn(input, 'getBoundingClientRect').mockImplementation(() =>
|
|
2212
|
-
rect({
|
|
2213
|
-
top: 660 - body.scrollTop,
|
|
2214
|
-
bottom: 700 - body.scrollTop,
|
|
2215
|
-
}),
|
|
2216
|
-
);
|
|
2217
|
-
|
|
2218
|
-
input.focus();
|
|
2219
|
-
expect(body.scrollTop).toBe(248);
|
|
2220
|
-
expect(body.style.getPropertyValue('--_sheet-keyboard-inset')).toBe(
|
|
2221
|
-
'348px',
|
|
2222
|
-
);
|
|
2223
|
-
|
|
2224
|
-
const pointerDownAllowed = fireEvent.pointerDown(getHandle(), {
|
|
2225
|
-
pointerId: 1,
|
|
2226
|
-
clientY: 0,
|
|
2227
|
-
button: 0,
|
|
2228
|
-
isPrimary: true,
|
|
2229
|
-
});
|
|
2230
|
-
expect(pointerDownAllowed).toBe(false);
|
|
2231
|
-
expect(document.activeElement).toBe(input);
|
|
2232
|
-
|
|
2233
|
-
fireEvent.pointerMove(getHandle(), {pointerId: 1, clientY: 40});
|
|
2234
|
-
|
|
2235
|
-
expect(document.activeElement).toBe(sheet);
|
|
2236
|
-
expect(positioner.style.getPropertyValue('--_sheet-keyboard-lift')).toBe(
|
|
2237
|
-
'',
|
|
2238
|
-
);
|
|
2239
|
-
expect(body.style.getPropertyValue('--_sheet-keyboard-inset')).toBe(
|
|
2240
|
-
'348px',
|
|
2241
|
-
);
|
|
2242
|
-
expect(body.scrollTop).toBe(248);
|
|
2243
|
-
|
|
2244
|
-
viewport.height = 800;
|
|
2245
|
-
void act(() => viewport.dispatchEvent(new Event('resize')));
|
|
2246
|
-
expect(body.style.getPropertyValue('--_sheet-keyboard-inset')).toBe(
|
|
2247
|
-
'0px',
|
|
2248
|
-
);
|
|
2249
|
-
});
|
|
2250
|
-
|
|
2251
|
-
it('retains Tall keyboard scroll space after blur until the viewport recovers', () => {
|
|
2252
|
-
const viewport = mockVisualViewport(500);
|
|
2253
|
-
render(
|
|
2254
|
-
<BottomSheet
|
|
2255
|
-
isOpen
|
|
2256
|
-
onOpenChange={() => {}}
|
|
2257
|
-
label="Add a comment"
|
|
2258
|
-
height="tall">
|
|
2259
|
-
<input aria-label="Comment" />
|
|
2260
|
-
</BottomSheet>,
|
|
2261
|
-
);
|
|
2262
|
-
const body = getBody();
|
|
2263
|
-
const input = screen.getByRole('textbox', {name: 'Comment'});
|
|
2264
|
-
vi.spyOn(body, 'getBoundingClientRect').mockReturnValue(
|
|
2265
|
-
rect({top: 100, bottom: 800}),
|
|
2266
|
-
);
|
|
2267
|
-
vi.spyOn(input, 'getBoundingClientRect').mockImplementation(() =>
|
|
2268
|
-
rect({
|
|
2269
|
-
top: 660 - body.scrollTop,
|
|
2270
|
-
bottom: 700 - body.scrollTop,
|
|
2271
|
-
}),
|
|
2272
|
-
);
|
|
2273
|
-
input.focus();
|
|
2274
|
-
expect(body.style.getPropertyValue('--_sheet-keyboard-inset')).toBe(
|
|
2275
|
-
'348px',
|
|
2276
|
-
);
|
|
2277
|
-
|
|
2278
|
-
input.blur();
|
|
2279
|
-
|
|
2280
|
-
expect(body.style.getPropertyValue('--_sheet-keyboard-inset')).toBe(
|
|
2281
|
-
'348px',
|
|
2282
|
-
);
|
|
2283
|
-
|
|
2284
|
-
viewport.height = 800;
|
|
2285
|
-
void act(() => viewport.dispatchEvent(new Event('resize')));
|
|
2286
|
-
expect(body.style.getPropertyValue('--_sheet-keyboard-inset')).toBe(
|
|
2287
|
-
'0px',
|
|
2288
|
-
);
|
|
2289
|
-
});
|
|
2290
|
-
|
|
2291
|
-
it('blurs the focused Tall field and retains its inset until the viewport recovers', () => {
|
|
2292
|
-
const viewport = mockVisualViewport(500);
|
|
2293
|
-
const onOpenChange = vi.fn();
|
|
2294
|
-
const content = (isOpen: boolean) => (
|
|
2295
|
-
<BottomSheet
|
|
2296
|
-
isOpen={isOpen}
|
|
2297
|
-
onOpenChange={onOpenChange}
|
|
2298
|
-
label="Add a comment"
|
|
2299
|
-
height="tall">
|
|
2300
|
-
<input aria-label="Comment" />
|
|
2301
|
-
</BottomSheet>
|
|
2302
|
-
);
|
|
2303
|
-
const {rerender} = render(content(true));
|
|
2304
|
-
const body = getBody();
|
|
2305
|
-
const input = screen.getByRole('textbox', {name: 'Comment'});
|
|
2306
|
-
vi.spyOn(body, 'getBoundingClientRect').mockReturnValue(
|
|
2307
|
-
rect({top: 100, bottom: 800}),
|
|
2308
|
-
);
|
|
2309
|
-
vi.spyOn(input, 'getBoundingClientRect').mockImplementation(() =>
|
|
2310
|
-
rect({
|
|
2311
|
-
top: 660 - body.scrollTop,
|
|
2312
|
-
bottom: 700 - body.scrollTop,
|
|
2313
|
-
}),
|
|
2314
|
-
);
|
|
2315
|
-
input.focus();
|
|
2316
|
-
expect(body.style.getPropertyValue('--_sheet-keyboard-inset')).toBe(
|
|
2317
|
-
'348px',
|
|
2318
|
-
);
|
|
2319
|
-
|
|
2320
|
-
rerender(content(false));
|
|
2321
|
-
|
|
2322
|
-
expect(document.activeElement).not.toBe(input);
|
|
2323
|
-
expect(body.style.getPropertyValue('--_sheet-keyboard-inset')).toBe(
|
|
2324
|
-
'348px',
|
|
2325
|
-
);
|
|
2326
|
-
|
|
2327
|
-
viewport.height = 800;
|
|
2328
|
-
void act(() => viewport.dispatchEvent(new Event('resize')));
|
|
2329
|
-
expect(body.style.getPropertyValue('--_sheet-keyboard-inset')).toBe(
|
|
2330
|
-
'0px',
|
|
2331
|
-
);
|
|
2332
|
-
});
|
|
2333
|
-
|
|
2334
|
-
it('retains Tall keyboard scroll space through a switcher handoff until the viewport recovers', () => {
|
|
2335
|
-
const viewport = mockVisualViewport(500);
|
|
2336
|
-
const content = (activeSheet: string) => (
|
|
2337
|
-
<BottomSheetSwitcher
|
|
2338
|
-
activeSheet={activeSheet}
|
|
2339
|
-
onActiveSheetChange={() => {}}>
|
|
2340
|
-
<BottomSheet sheetId="comment" label="Add a comment" height="tall">
|
|
2341
|
-
<input aria-label="Comment" />
|
|
2342
|
-
</BottomSheet>
|
|
2343
|
-
<BottomSheet sheetId="confirmation" label="Confirmation">
|
|
2344
|
-
Confirmation
|
|
2345
|
-
</BottomSheet>
|
|
2346
|
-
</BottomSheetSwitcher>
|
|
2347
|
-
);
|
|
2348
|
-
const {rerender} = render(content('comment'));
|
|
2349
|
-
const body = getBody();
|
|
2350
|
-
const input = screen.getByRole('textbox', {name: 'Comment'});
|
|
2351
|
-
vi.spyOn(body, 'getBoundingClientRect').mockReturnValue(
|
|
2352
|
-
rect({top: 100, bottom: 800}),
|
|
2353
|
-
);
|
|
2354
|
-
vi.spyOn(input, 'getBoundingClientRect').mockImplementation(() =>
|
|
2355
|
-
rect({
|
|
2356
|
-
top: 660 - body.scrollTop,
|
|
2357
|
-
bottom: 700 - body.scrollTop,
|
|
2358
|
-
}),
|
|
2359
|
-
);
|
|
2360
|
-
input.focus();
|
|
2361
|
-
expect(body.style.getPropertyValue('--_sheet-keyboard-inset')).toBe(
|
|
2362
|
-
'348px',
|
|
2363
|
-
);
|
|
2364
|
-
|
|
2365
|
-
rerender(content('confirmation'));
|
|
2366
|
-
|
|
2367
|
-
expect(document.activeElement).not.toBe(input);
|
|
2368
|
-
expect(body.style.getPropertyValue('--_sheet-keyboard-inset')).toBe(
|
|
2369
|
-
'348px',
|
|
2370
|
-
);
|
|
2371
|
-
|
|
2372
|
-
viewport.height = 800;
|
|
2373
|
-
void act(() => viewport.dispatchEvent(new Event('resize')));
|
|
2374
|
-
expect(body.style.getPropertyValue('--_sheet-keyboard-inset')).toBe(
|
|
2375
|
-
'0px',
|
|
2376
|
-
);
|
|
2377
|
-
});
|
|
2378
|
-
|
|
2379
|
-
it('puts back a document scroll the browser makes to reveal a field', () => {
|
|
2380
|
-
mockVisualViewport(377);
|
|
2381
|
-
render(
|
|
2382
|
-
<BottomSheet
|
|
2383
|
-
isOpen
|
|
2384
|
-
onOpenChange={() => {}}
|
|
2385
|
-
label="Add a comment"
|
|
2386
|
-
height="tall">
|
|
2387
|
-
<input aria-label="Comment" />
|
|
2388
|
-
</BottomSheet>,
|
|
2389
|
-
);
|
|
2390
|
-
const body = getBody();
|
|
2391
|
-
const input = screen.getByRole('textbox', {name: 'Comment'});
|
|
2392
|
-
vi.spyOn(body, 'getBoundingClientRect').mockReturnValue(
|
|
2393
|
-
rect({top: 57, bottom: 714}),
|
|
2394
|
-
);
|
|
2395
|
-
vi.spyOn(input, 'getBoundingClientRect').mockImplementation(() =>
|
|
2396
|
-
rect({top: 600 - body.scrollTop, bottom: 640 - body.scrollTop}),
|
|
2397
|
-
);
|
|
2398
|
-
input.focus();
|
|
2399
|
-
const scrollTo = vi.mocked(window.scrollTo);
|
|
2400
|
-
scrollTo.mockClear();
|
|
2401
|
-
const scrolledBy = body.scrollTop;
|
|
2402
|
-
|
|
2403
|
-
// The page numbers an iPhone 17 produces when the browser reveals a
|
|
2404
|
-
// focused field in a scroll-locked, fixed sheet: it scrolls the DOCUMENT,
|
|
2405
|
-
// and the sheet — fixed — travels with it.
|
|
2406
|
-
Object.defineProperty(window, 'scrollY', {
|
|
2407
|
-
configurable: true,
|
|
2408
|
-
value: 337,
|
|
2409
|
-
});
|
|
2410
|
-
fireEvent.scroll(window);
|
|
2411
|
-
|
|
2412
|
-
expect(scrollTo).toHaveBeenCalledWith(0, 0);
|
|
2413
|
-
// …and the control is still inside the safe area afterwards: the sheet's
|
|
2414
|
-
// own scroller holds it there, so putting the document back does not
|
|
2415
|
-
// hide what the browser was trying to reveal.
|
|
2416
|
-
expect(input.getBoundingClientRect().bottom).toBeLessThanOrEqual(
|
|
2417
|
-
377 - 48,
|
|
2418
|
-
);
|
|
2419
|
-
expect(body.scrollTop).toBe(scrolledBy);
|
|
2420
|
-
Object.defineProperty(window, 'scrollY', {configurable: true, value: 0});
|
|
2421
|
-
});
|
|
2422
|
-
|
|
2423
|
-
it('leaves the document alone when no keyboard is measured', () => {
|
|
2424
|
-
mockVisualViewport(800);
|
|
2425
|
-
render(
|
|
2426
|
-
<BottomSheet
|
|
2427
|
-
isOpen
|
|
2428
|
-
onOpenChange={() => {}}
|
|
2429
|
-
label="Add a comment"
|
|
2430
|
-
height="tall">
|
|
2431
|
-
<input aria-label="Comment" />
|
|
2432
|
-
</BottomSheet>,
|
|
2433
|
-
);
|
|
2434
|
-
const body = getBody();
|
|
2435
|
-
const input = screen.getByRole('textbox', {name: 'Comment'});
|
|
2436
|
-
vi.spyOn(body, 'getBoundingClientRect').mockReturnValue(
|
|
2437
|
-
rect({top: 57, bottom: 700}),
|
|
2438
|
-
);
|
|
2439
|
-
vi.spyOn(input, 'getBoundingClientRect').mockReturnValue(
|
|
2440
|
-
rect({top: 600, bottom: 640}),
|
|
2441
|
-
);
|
|
2442
|
-
input.focus();
|
|
2443
|
-
const scrollTo = vi.mocked(window.scrollTo);
|
|
2444
|
-
scrollTo.mockClear();
|
|
2445
|
-
|
|
2446
|
-
// An ordinary page scroll with no keyboard up is the user's, not the
|
|
2447
|
-
// browser's, and a non-modal sheet leaves the page scrollable.
|
|
2448
|
-
Object.defineProperty(window, 'scrollY', {
|
|
2449
|
-
configurable: true,
|
|
2450
|
-
value: 120,
|
|
2451
|
-
});
|
|
2452
|
-
fireEvent.scroll(window);
|
|
2453
|
-
|
|
2454
|
-
expect(scrollTo).not.toHaveBeenCalled();
|
|
2455
|
-
Object.defineProperty(window, 'scrollY', {configurable: true, value: 0});
|
|
2456
|
-
});
|
|
2457
|
-
|
|
2458
|
-
it('delivers a browser-driven transition itself, then reveals the field', () => {
|
|
2459
|
-
mockIOSWebKit();
|
|
2460
|
-
mockVisualViewport(500);
|
|
2461
|
-
render(
|
|
2462
|
-
<BottomSheet
|
|
2463
|
-
isOpen
|
|
2464
|
-
onOpenChange={() => {}}
|
|
2465
|
-
label="Add a comment"
|
|
2466
|
-
height="tall">
|
|
2467
|
-
<input aria-label="Title" />
|
|
2468
|
-
<input aria-label="Comment" />
|
|
2469
|
-
</BottomSheet>,
|
|
2470
|
-
);
|
|
2471
|
-
const body = getBody();
|
|
2472
|
-
const title = screen.getByRole('textbox', {name: 'Title'});
|
|
2473
|
-
const comment = screen.getByRole('textbox', {name: 'Comment'});
|
|
2474
|
-
const scrolls: {
|
|
2475
|
-
top: number;
|
|
2476
|
-
behavior?: ScrollBehavior;
|
|
2477
|
-
focusLanded: boolean;
|
|
2478
|
-
}[] = [];
|
|
2479
|
-
Object.defineProperty(body, 'scrollBy', {
|
|
2480
|
-
configurable: true,
|
|
2481
|
-
value: (options: ScrollToOptions) => {
|
|
2482
|
-
body.scrollTop += options.top ?? 0;
|
|
2483
|
-
scrolls.push({
|
|
2484
|
-
top: options.top ?? 0,
|
|
2485
|
-
behavior: options.behavior,
|
|
2486
|
-
focusLanded: document.activeElement === comment,
|
|
2487
|
-
});
|
|
2488
|
-
},
|
|
2489
|
-
});
|
|
2490
|
-
vi.spyOn(body, 'getBoundingClientRect').mockReturnValue(
|
|
2491
|
-
rect({top: 100, bottom: 800}),
|
|
2492
|
-
);
|
|
2493
|
-
vi.spyOn(title, 'getBoundingClientRect').mockImplementation(() =>
|
|
2494
|
-
rect({top: 150 - body.scrollTop, bottom: 190 - body.scrollTop}),
|
|
2495
|
-
);
|
|
2496
|
-
vi.spyOn(comment, 'getBoundingClientRect').mockImplementation(() =>
|
|
2497
|
-
rect({top: 660 - body.scrollTop, bottom: 700 - body.scrollTop}),
|
|
2498
|
-
);
|
|
2499
|
-
|
|
2500
|
-
// The first field opens the keyboard and is already inside the safe
|
|
2501
|
-
// area, so nothing has to move for it.
|
|
2502
|
-
title.focus();
|
|
2503
|
-
expect(body.style.getPropertyValue('--_sheet-keyboard-inset')).toBe(
|
|
2504
|
-
'348px',
|
|
2505
|
-
);
|
|
2506
|
-
expect(scrolls).toEqual([]);
|
|
2507
|
-
|
|
2508
|
-
// A keyboard accessory "Next" or a Tab arrives as a blur naming the
|
|
2509
|
-
// destination. We deliver that focus with preventScroll — refusing the
|
|
2510
|
-
// browser's reveal — and then reveal the field ourselves, inside the
|
|
2511
|
-
// sheet.
|
|
2512
|
-
fireEvent.blur(title, {relatedTarget: comment});
|
|
2513
|
-
|
|
2514
|
-
expect(document.activeElement).toBe(comment);
|
|
2515
|
-
expect(scrolls).toEqual([
|
|
2516
|
-
{top: 248, behavior: 'smooth', focusLanded: true},
|
|
2517
|
-
]);
|
|
2518
|
-
expect(body.scrollTop).toBe(248);
|
|
2519
|
-
});
|
|
2520
|
-
|
|
2521
|
-
it('does not scroll a browser-driven transition when the viewport is unobstructed', () => {
|
|
2522
|
-
mockIOSWebKit();
|
|
2523
|
-
mockVisualViewport(800);
|
|
2524
|
-
render(
|
|
2525
|
-
<BottomSheet
|
|
2526
|
-
isOpen
|
|
2527
|
-
onOpenChange={() => {}}
|
|
2528
|
-
label="Add a comment"
|
|
2529
|
-
height="tall">
|
|
2530
|
-
<input aria-label="Title" />
|
|
2531
|
-
<input aria-label="Comment" />
|
|
2532
|
-
</BottomSheet>,
|
|
2533
|
-
);
|
|
2534
|
-
const body = getBody();
|
|
2535
|
-
const title = screen.getByRole('textbox', {name: 'Title'});
|
|
2536
|
-
const comment = screen.getByRole('textbox', {name: 'Comment'});
|
|
2537
|
-
const scrolls: number[] = [];
|
|
2538
|
-
Object.defineProperty(body, 'scrollBy', {
|
|
2539
|
-
configurable: true,
|
|
2540
|
-
// Apply the scroll, as the real scroller would: a reveal that has
|
|
2541
|
-
// already happened must read as no distance left to travel, otherwise
|
|
2542
|
-
// every later reveal looks like a fresh one.
|
|
2543
|
-
value: (options: ScrollToOptions) => {
|
|
2544
|
-
body.scrollTop += options.top ?? 0;
|
|
2545
|
-
scrolls.push(options.top ?? 0);
|
|
2546
|
-
},
|
|
2547
|
-
});
|
|
2548
|
-
vi.spyOn(body, 'getBoundingClientRect').mockReturnValue(
|
|
2549
|
-
rect({top: 100, bottom: 800}),
|
|
2550
|
-
);
|
|
2551
|
-
vi.spyOn(title, 'getBoundingClientRect').mockReturnValue(
|
|
2552
|
-
rect({top: 150, bottom: 190}),
|
|
2553
|
-
);
|
|
2554
|
-
// Below the body's visible area, so a reveal has somewhere to travel.
|
|
2555
|
-
vi.spyOn(comment, 'getBoundingClientRect').mockImplementation(() =>
|
|
2556
|
-
rect({top: 860 - body.scrollTop, bottom: 900 - body.scrollTop}),
|
|
2557
|
-
);
|
|
2558
|
-
|
|
2559
|
-
title.focus();
|
|
2560
|
-
scrolls.length = 0;
|
|
2561
|
-
const scrollTo = vi.mocked(window.scrollTo);
|
|
2562
|
-
scrollTo.mockClear();
|
|
2563
|
-
fireEvent.blur(title, {relatedTarget: comment});
|
|
2564
|
-
|
|
2565
|
-
// With no keyboard there is no clearance to leave and nothing to race,
|
|
2566
|
-
// but the control still has to end up visible — brought there once, by
|
|
2567
|
-
// the sheet's own scroller, with the page left alone.
|
|
2568
|
-
expect(scrolls).toEqual([100]);
|
|
2569
|
-
expect(scrollTo).not.toHaveBeenCalled();
|
|
2570
|
-
});
|
|
2571
|
-
|
|
2572
|
-
it('leaves the page alone behind a non-modal sheet', () => {
|
|
2573
|
-
const layoutBottom = window.innerHeight;
|
|
2574
|
-
mockVisualViewport(500);
|
|
2575
|
-
render(
|
|
2576
|
-
<BottomSheet
|
|
2577
|
-
isOpen
|
|
2578
|
-
onOpenChange={() => {}}
|
|
2579
|
-
label="Add a comment"
|
|
2580
|
-
height="tall"
|
|
2581
|
-
hasScrim={false}>
|
|
2582
|
-
<input aria-label="Comment" />
|
|
2583
|
-
</BottomSheet>,
|
|
2584
|
-
);
|
|
2585
|
-
const body = getBody();
|
|
2586
|
-
const input = screen.getByRole('textbox', {name: 'Comment'});
|
|
2587
|
-
vi.spyOn(body, 'getBoundingClientRect').mockReturnValue(
|
|
2588
|
-
rect({top: 100, bottom: layoutBottom}),
|
|
2589
|
-
);
|
|
2590
|
-
vi.spyOn(input, 'getBoundingClientRect').mockImplementation(() =>
|
|
2591
|
-
rect({top: 600 - body.scrollTop, bottom: 640 - body.scrollTop}),
|
|
2592
|
-
);
|
|
2593
|
-
input.focus();
|
|
2594
|
-
const scrollTo = vi.mocked(window.scrollTo);
|
|
2595
|
-
scrollTo.mockClear();
|
|
2596
|
-
|
|
2597
|
-
// Without a scrim the page behind stays scrollable, so a document scroll
|
|
2598
|
-
// is the user's. Putting it back would fight them.
|
|
2599
|
-
Object.defineProperty(window, 'scrollY', {
|
|
2600
|
-
configurable: true,
|
|
2601
|
-
value: 200,
|
|
2602
|
-
});
|
|
2603
|
-
fireEvent.scroll(window);
|
|
2604
|
-
|
|
2605
|
-
expect(scrollTo).not.toHaveBeenCalled();
|
|
2606
|
-
Object.defineProperty(window, 'scrollY', {configurable: true, value: 0});
|
|
2607
|
-
});
|
|
2608
|
-
|
|
2609
|
-
it('holds the keyboard scroll range through a pan, on the blur path', () => {
|
|
2610
|
-
// A fully expanded Tall sheet — the only shape this hook runs in — is
|
|
2611
|
-
// pinned to the layout viewport bottom, so the body's bottom IS
|
|
2612
|
-
// innerHeight. Giving it a cushion below that would hide every bug in
|
|
2613
|
-
// this file: the cushion, not the measurement, would keep the overlap
|
|
2614
|
-
// positive under a pan.
|
|
2615
|
-
const layoutBottom = window.innerHeight;
|
|
2616
|
-
const viewport = mockVisualViewport(500);
|
|
2617
|
-
render(
|
|
2618
|
-
<BottomSheet
|
|
2619
|
-
isOpen
|
|
2620
|
-
onOpenChange={() => {}}
|
|
2621
|
-
label="Add a comment"
|
|
2622
|
-
height="tall">
|
|
2623
|
-
<input aria-label="Comment" />
|
|
2624
|
-
</BottomSheet>,
|
|
2625
|
-
);
|
|
2626
|
-
const body = getBody();
|
|
2627
|
-
const input = screen.getByRole('textbox', {name: 'Comment'});
|
|
2628
|
-
vi.spyOn(body, 'getBoundingClientRect').mockReturnValue(
|
|
2629
|
-
rect({top: 100, bottom: layoutBottom}),
|
|
2630
|
-
);
|
|
2631
|
-
vi.spyOn(input, 'getBoundingClientRect').mockImplementation(() =>
|
|
2632
|
-
rect({top: 660 - body.scrollTop, bottom: 700 - body.scrollTop}),
|
|
2633
|
-
);
|
|
2634
|
-
input.focus();
|
|
2635
|
-
const inset = body.style.getPropertyValue('--_sheet-keyboard-inset');
|
|
2636
|
-
expect(inset).toBe(`${layoutBottom - (500 - 48)}px`);
|
|
2637
|
-
|
|
2638
|
-
// The browser pans the page up to reveal a field: the same 500px of
|
|
2639
|
-
// visible page, now offset so its bottom edge coincides with the layout
|
|
2640
|
-
// viewport bottom — with the keyboard still on screen. Read the bottom
|
|
2641
|
-
// and that is indistinguishable from the keyboard closing.
|
|
2642
|
-
viewport.offsetTop = window.innerHeight - 500;
|
|
2643
|
-
input.blur();
|
|
2644
|
-
|
|
2645
|
-
// The keyboard did not change size, so neither does the scroll range.
|
|
2646
|
-
expect(body.style.getPropertyValue('--_sheet-keyboard-inset')).toBe(
|
|
2647
|
-
inset,
|
|
2648
|
-
);
|
|
2649
|
-
});
|
|
2650
|
-
|
|
2651
|
-
it('keeps defending a pinned sheet after the browser has panned it once', () => {
|
|
2652
|
-
// The regression that made the device symptom permanent: one pan read as
|
|
2653
|
-
// "no keyboard" cleared the inset AND cleared hasKeyboardLayout, which
|
|
2654
|
-
// disarms the head start below — so every subsequent reveal panned, and
|
|
2655
|
-
// the sheet never recovered.
|
|
2656
|
-
const layoutBottom = window.innerHeight;
|
|
2657
|
-
mockIOSWebKit();
|
|
2658
|
-
const viewport = mockVisualViewport(500);
|
|
2659
|
-
render(
|
|
2660
|
-
<BottomSheet
|
|
2661
|
-
isOpen
|
|
2662
|
-
onOpenChange={() => {}}
|
|
2663
|
-
label="Add a comment"
|
|
2664
|
-
height="tall">
|
|
2665
|
-
<input aria-label="Title" />
|
|
2666
|
-
<input aria-label="Comment" />
|
|
2667
|
-
</BottomSheet>,
|
|
2668
|
-
);
|
|
2669
|
-
const body = getBody();
|
|
2670
|
-
const title = screen.getByRole('textbox', {name: 'Title'});
|
|
2671
|
-
const comment = screen.getByRole('textbox', {name: 'Comment'});
|
|
2672
|
-
const scrolls: {top: number; focusLanded: boolean}[] = [];
|
|
2673
|
-
Object.defineProperty(body, 'scrollBy', {
|
|
2674
|
-
configurable: true,
|
|
2675
|
-
value: (options: ScrollToOptions) => {
|
|
2676
|
-
body.scrollTop += options.top ?? 0;
|
|
2677
|
-
scrolls.push({
|
|
2678
|
-
top: options.top ?? 0,
|
|
2679
|
-
focusLanded: document.activeElement === comment,
|
|
2680
|
-
});
|
|
2681
|
-
},
|
|
2682
|
-
});
|
|
2683
|
-
vi.spyOn(body, 'getBoundingClientRect').mockReturnValue(
|
|
2684
|
-
rect({top: 100, bottom: layoutBottom}),
|
|
2685
|
-
);
|
|
2686
|
-
vi.spyOn(title, 'getBoundingClientRect').mockReturnValue(
|
|
2687
|
-
rect({top: 150, bottom: 190}),
|
|
2688
|
-
);
|
|
2689
|
-
vi.spyOn(comment, 'getBoundingClientRect').mockImplementation(() =>
|
|
2690
|
-
rect({top: 700 - body.scrollTop, bottom: 740 - body.scrollTop}),
|
|
2691
|
-
);
|
|
2692
|
-
|
|
2693
|
-
title.focus();
|
|
2694
|
-
const inset = body.style.getPropertyValue('--_sheet-keyboard-inset');
|
|
2695
|
-
expect(inset).toBe(`${layoutBottom - (500 - 48)}px`);
|
|
2696
|
-
|
|
2697
|
-
viewport.offsetTop = layoutBottom - 500;
|
|
2698
|
-
void act(() => viewport.dispatchEvent(new Event('resize')));
|
|
2699
|
-
expect(body.style.getPropertyValue('--_sheet-keyboard-inset')).toBe(
|
|
2700
|
-
inset,
|
|
2701
|
-
);
|
|
2702
|
-
|
|
2703
|
-
// And the next browser-driven transition is still claimed and revealed,
|
|
2704
|
-
// by a distance measured against the unshifted keyboard boundary.
|
|
2705
|
-
scrolls.length = 0;
|
|
2706
|
-
fireEvent.blur(title, {relatedTarget: comment});
|
|
2707
|
-
|
|
2708
|
-
expect(document.activeElement).toBe(comment);
|
|
2709
|
-
expect(scrolls[0]).toEqual({
|
|
2710
|
-
top: 740 - (500 - 48),
|
|
2711
|
-
focusLanded: true,
|
|
2712
|
-
});
|
|
2713
|
-
});
|
|
2714
|
-
});
|
|
2715
|
-
|
|
2716
|
-
describe('focus restore', () => {
|
|
2717
|
-
function Harness() {
|
|
2718
|
-
const [isOpen, setIsOpen] = useState(false);
|
|
2719
|
-
return (
|
|
2720
|
-
<>
|
|
2721
|
-
<button type="button" onClick={() => setIsOpen(true)}>
|
|
2722
|
-
Open sheet
|
|
2723
|
-
</button>
|
|
2724
|
-
<BottomSheet isOpen={isOpen} onOpenChange={setIsOpen} label="Filters">
|
|
2725
|
-
<button type="button" onClick={() => setIsOpen(false)}>
|
|
2726
|
-
Done
|
|
2727
|
-
</button>
|
|
2728
|
-
</BottomSheet>
|
|
2729
|
-
</>
|
|
2730
|
-
);
|
|
2731
|
-
}
|
|
2732
|
-
|
|
2733
|
-
it('restores focus to the opener after close', () => {
|
|
2734
|
-
render(<Harness />);
|
|
2735
|
-
const opener = screen.getByRole('button', {name: 'Open sheet'});
|
|
2736
|
-
opener.focus();
|
|
2737
|
-
fireEvent.click(opener);
|
|
2738
|
-
fireEvent.click(screen.getByRole('button', {name: 'Done'}));
|
|
2739
|
-
finishSheetExit();
|
|
2740
|
-
|
|
2741
|
-
expect(document.activeElement).toBe(opener);
|
|
2742
|
-
});
|
|
2743
|
-
|
|
2744
|
-
it('prefers an explicit final focus target', () => {
|
|
2745
|
-
const finalFocusRef = createRef<HTMLButtonElement>();
|
|
2746
|
-
function ExplicitFocusHarness() {
|
|
2747
|
-
const [isOpen, setIsOpen] = useState(true);
|
|
2748
|
-
return (
|
|
2749
|
-
<>
|
|
2750
|
-
<button ref={finalFocusRef} type="button">
|
|
2751
|
-
Adaptive trigger
|
|
2752
|
-
</button>
|
|
2753
|
-
<BottomSheet
|
|
2754
|
-
isOpen={isOpen}
|
|
2755
|
-
onOpenChange={setIsOpen}
|
|
2756
|
-
finalFocusRef={finalFocusRef}
|
|
2757
|
-
label="Filters">
|
|
2758
|
-
<button type="button" onClick={() => setIsOpen(false)}>
|
|
2759
|
-
Done
|
|
2760
|
-
</button>
|
|
2761
|
-
</BottomSheet>
|
|
2762
|
-
</>
|
|
2763
|
-
);
|
|
2764
|
-
}
|
|
2765
|
-
|
|
2766
|
-
render(<ExplicitFocusHarness />);
|
|
2767
|
-
fireEvent.click(screen.getByRole('button', {name: 'Done'}));
|
|
2768
|
-
finishSheetExit();
|
|
2769
|
-
|
|
2770
|
-
expect(document.activeElement).toBe(finalFocusRef.current);
|
|
2771
|
-
});
|
|
2772
|
-
|
|
2773
|
-
// The returned focus shows the focus-visible ring only after keyboard
|
|
2774
|
-
// input (`architecture:interaction-modality` INV1). A plain focus() leaves
|
|
2775
|
-
// that to the browser, which rings it after a pointer close: WebKit after
|
|
2776
|
-
// a tap, and Chromium when the sheet was opened from the keyboard.
|
|
2777
|
-
describe('ring on the returned focus', () => {
|
|
2778
|
-
beforeEach(() => {
|
|
2779
|
-
__resetInteractionModalityForTest();
|
|
2780
|
-
});
|
|
2781
|
-
|
|
2782
|
-
afterEach(() => {
|
|
2783
|
-
vi.restoreAllMocks();
|
|
2784
|
-
});
|
|
2785
|
-
|
|
2786
|
-
function openThenClose(closeWith: 'pointer' | 'keyboard') {
|
|
2787
|
-
render(<Harness />);
|
|
2788
|
-
const opener = screen.getByRole('button', {name: 'Open sheet'});
|
|
2789
|
-
opener.focus();
|
|
2790
|
-
fireEvent.click(opener);
|
|
2791
|
-
const done = screen.getByRole('button', {name: 'Done'});
|
|
2792
|
-
if (closeWith === 'pointer') {
|
|
2793
|
-
fireEvent.pointerDown(done);
|
|
2794
|
-
} else {
|
|
2795
|
-
fireEvent.keyDown(done, {key: 'Enter'});
|
|
2796
|
-
}
|
|
2797
|
-
fireEvent.click(done);
|
|
2798
|
-
return opener;
|
|
2799
|
-
}
|
|
2800
|
-
|
|
2801
|
-
it('asks the browser for no ring after a pointer close', () => {
|
|
2802
|
-
const opener = openThenClose('pointer');
|
|
2803
|
-
const focus = vi.spyOn(opener, 'focus');
|
|
2804
|
-
finishSheetExit();
|
|
2805
|
-
|
|
2806
|
-
expect(opener).toHaveFocus();
|
|
2807
|
-
expect(focus.mock.calls).toEqual([[{focusVisible: false}]]);
|
|
2808
|
-
});
|
|
2809
|
-
|
|
2810
|
-
it('leaves the ring to the browser after a keyboard close', () => {
|
|
2811
|
-
const opener = openThenClose('keyboard');
|
|
2812
|
-
const focus = vi.spyOn(opener, 'focus');
|
|
2813
|
-
finishSheetExit();
|
|
2814
|
-
|
|
2815
|
-
expect(opener).toHaveFocus();
|
|
2816
|
-
expect(focus.mock.calls).toEqual([[]]);
|
|
2817
|
-
});
|
|
2818
|
-
|
|
2819
|
-
it('clears a ring the native close already drew before refocusing', () => {
|
|
2820
|
-
const opener = openThenClose('pointer');
|
|
2821
|
-
// The browser's own dialog close restores focus to the opener, with a
|
|
2822
|
-
// ring, before the sheet moves focus, and focusing the focused element
|
|
2823
|
-
// changes nothing.
|
|
2824
|
-
HTMLDialogElement.prototype.close = vi.fn(function (
|
|
2825
|
-
this: HTMLDialogElement,
|
|
2826
|
-
) {
|
|
2827
|
-
this.removeAttribute('open');
|
|
2828
|
-
opener.focus();
|
|
2829
|
-
});
|
|
2830
|
-
const matches = HTMLElement.prototype.matches;
|
|
2831
|
-
vi.spyOn(HTMLElement.prototype, 'matches').mockImplementation(function (
|
|
2832
|
-
this: HTMLElement,
|
|
2833
|
-
selector: string,
|
|
2834
|
-
) {
|
|
2835
|
-
return selector === ':focus-visible'
|
|
2836
|
-
? this === opener
|
|
2837
|
-
: matches.call(this, selector);
|
|
2838
|
-
});
|
|
2839
|
-
const blur = vi.spyOn(opener, 'blur');
|
|
2840
|
-
const focus = vi.spyOn(opener, 'focus');
|
|
2841
|
-
finishSheetExit();
|
|
2842
|
-
|
|
2843
|
-
expect(blur).toHaveBeenCalledTimes(1);
|
|
2844
|
-
expect(focus.mock.calls.at(-1)).toEqual([{focusVisible: false}]);
|
|
2845
|
-
expect(blur.mock.invocationCallOrder[0]).toBeLessThan(
|
|
2846
|
-
focus.mock.invocationCallOrder.at(-1) ?? 0,
|
|
2847
|
-
);
|
|
2848
|
-
expect(opener).toHaveFocus();
|
|
2849
|
-
});
|
|
2850
|
-
|
|
2851
|
-
it('asks for no ring on an explicit final focus target too', () => {
|
|
2852
|
-
const finalFocusRef = createRef<HTMLButtonElement>();
|
|
2853
|
-
function ExplicitFocusHarness() {
|
|
2854
|
-
const [isOpen, setIsOpen] = useState(true);
|
|
2855
|
-
return (
|
|
2856
|
-
<>
|
|
2857
|
-
<button ref={finalFocusRef} type="button">
|
|
2858
|
-
Adaptive trigger
|
|
2859
|
-
</button>
|
|
2860
|
-
<BottomSheet
|
|
2861
|
-
isOpen={isOpen}
|
|
2862
|
-
onOpenChange={setIsOpen}
|
|
2863
|
-
finalFocusRef={finalFocusRef}
|
|
2864
|
-
label="Filters">
|
|
2865
|
-
<button type="button" onClick={() => setIsOpen(false)}>
|
|
2866
|
-
Done
|
|
2867
|
-
</button>
|
|
2868
|
-
</BottomSheet>
|
|
2869
|
-
</>
|
|
2870
|
-
);
|
|
2871
|
-
}
|
|
2872
|
-
|
|
2873
|
-
render(<ExplicitFocusHarness />);
|
|
2874
|
-
const done = screen.getByRole('button', {name: 'Done'});
|
|
2875
|
-
fireEvent.pointerDown(done);
|
|
2876
|
-
fireEvent.click(done);
|
|
2877
|
-
const target = finalFocusRef.current!;
|
|
2878
|
-
const focus = vi.spyOn(target, 'focus');
|
|
2879
|
-
finishSheetExit();
|
|
2880
|
-
|
|
2881
|
-
expect(target).toHaveFocus();
|
|
2882
|
-
expect(focus.mock.calls).toEqual([[{focusVisible: false}]]);
|
|
2883
|
-
});
|
|
2884
|
-
});
|
|
2885
|
-
});
|
|
2886
|
-
|
|
2887
|
-
describe('initial focus', () => {
|
|
2888
|
-
it('focuses the sheet panel on open, not the first control', () => {
|
|
2889
|
-
render(
|
|
2890
|
-
<BottomSheet isOpen onOpenChange={() => {}} label="Filters">
|
|
2891
|
-
<button type="button">First action</button>
|
|
2892
|
-
</BottomSheet>,
|
|
2893
|
-
);
|
|
2894
|
-
const panel = getSheet();
|
|
2895
|
-
expect(document.activeElement).toBe(panel);
|
|
2896
|
-
expect(document.activeElement).not.toBe(
|
|
2897
|
-
screen.getByRole('button', {name: 'First action'}),
|
|
2898
|
-
);
|
|
2899
|
-
});
|
|
2900
|
-
|
|
2901
|
-
it('honors a descendant with data-autofocus', () => {
|
|
2902
|
-
render(
|
|
2903
|
-
<BottomSheet isOpen onOpenChange={() => {}} label="Filters">
|
|
2904
|
-
<input data-autofocus aria-label="Search" />
|
|
2905
|
-
</BottomSheet>,
|
|
2906
|
-
);
|
|
2907
|
-
expect(document.activeElement).toBe(
|
|
2908
|
-
screen.getByRole('textbox', {name: 'Search'}),
|
|
2909
|
-
);
|
|
2910
|
-
});
|
|
2911
|
-
});
|
|
2912
|
-
|
|
2913
|
-
describe('accessible name', () => {
|
|
2914
|
-
it('warns in development when label is empty', () => {
|
|
2915
|
-
const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
2916
|
-
render(
|
|
2917
|
-
<BottomSheet isOpen onOpenChange={() => {}} label="">
|
|
2918
|
-
Content
|
|
2919
|
-
</BottomSheet>,
|
|
2920
|
-
);
|
|
2921
|
-
expect(
|
|
2922
|
-
warn.mock.calls.some(args => String(args[0]).includes('BottomSheet')),
|
|
2923
|
-
).toBe(true);
|
|
2924
|
-
warn.mockRestore();
|
|
2925
|
-
});
|
|
2926
|
-
});
|
|
2927
|
-
|
|
2928
|
-
describe('reduced motion', () => {
|
|
2929
|
-
it('opens without throwing when prefers-reduced-motion is set', () => {
|
|
2930
|
-
vi.stubGlobal(
|
|
2931
|
-
'matchMedia',
|
|
2932
|
-
vi.fn().mockReturnValue({
|
|
2933
|
-
matches: true,
|
|
2934
|
-
media: '(prefers-reduced-motion: reduce)',
|
|
2935
|
-
onchange: null,
|
|
2936
|
-
addEventListener: vi.fn(),
|
|
2937
|
-
removeEventListener: vi.fn(),
|
|
2938
|
-
addListener: vi.fn(),
|
|
2939
|
-
removeListener: vi.fn(),
|
|
2940
|
-
dispatchEvent: vi.fn(),
|
|
2941
|
-
}),
|
|
2942
|
-
);
|
|
2943
|
-
render(
|
|
2944
|
-
<BottomSheet isOpen onOpenChange={() => {}} label="Filters">
|
|
2945
|
-
Content
|
|
2946
|
-
</BottomSheet>,
|
|
2947
|
-
);
|
|
2948
|
-
expect(screen.getByRole('dialog')).toBeInTheDocument();
|
|
2949
|
-
});
|
|
2950
|
-
|
|
2951
|
-
function stubReducedMotion(coarse: boolean) {
|
|
2952
|
-
vi.stubGlobal(
|
|
2953
|
-
'matchMedia',
|
|
2954
|
-
vi.fn((query: string) => ({
|
|
2955
|
-
matches:
|
|
2956
|
-
query === '(prefers-reduced-motion: reduce)' ||
|
|
2957
|
-
(coarse &&
|
|
2958
|
-
query ===
|
|
2959
|
-
'(prefers-reduced-motion: reduce) and (pointer: coarse)'),
|
|
2960
|
-
media: query,
|
|
2961
|
-
onchange: null,
|
|
2962
|
-
addEventListener: vi.fn(),
|
|
2963
|
-
removeEventListener: vi.fn(),
|
|
2964
|
-
addListener: vi.fn(),
|
|
2965
|
-
removeListener: vi.fn(),
|
|
2966
|
-
dispatchEvent: vi.fn(),
|
|
2967
|
-
})),
|
|
2968
|
-
);
|
|
2969
|
-
}
|
|
2970
|
-
|
|
2971
|
-
function Controlled() {
|
|
2972
|
-
const [isOpen, setIsOpen] = useState(true);
|
|
2973
|
-
return (
|
|
2974
|
-
<BottomSheet isOpen={isOpen} onOpenChange={setIsOpen} label="Filters">
|
|
2975
|
-
<button type="button" onClick={() => setIsOpen(false)}>
|
|
2976
|
-
Close
|
|
2977
|
-
</button>
|
|
2978
|
-
</BottomSheet>
|
|
2979
|
-
);
|
|
2980
|
-
}
|
|
2981
|
-
|
|
2982
|
-
it("a touch swipe dismissal under reduced motion fades out in place on a fine pointer (design:reduced-motion DR2, a touch drag's release)", () => {
|
|
2983
|
-
stubReducedMotion(false);
|
|
2984
|
-
render(<Controlled />);
|
|
2985
|
-
const dialog = screen.getByRole('dialog');
|
|
2986
|
-
drag(getHandle(), [{y: 0}, {y: 40}, {y: 120}], 'touch');
|
|
2987
|
-
// Exiting: a transform end does not close the dialog, the fade does.
|
|
2988
|
-
fireEvent.transitionEnd(getSheet(), {propertyName: 'transform'});
|
|
2989
|
-
expect(dialog).toHaveAttribute('open');
|
|
2990
|
-
fireEvent.transitionEnd(getSheet(), {propertyName: 'opacity'});
|
|
2991
|
-
expect(dialog).not.toHaveAttribute('open');
|
|
2992
|
-
});
|
|
2993
|
-
|
|
2994
|
-
it('a touch swipe dismissal does not outlive the sheet: reopened and closed from a control, it leaves at once again', () => {
|
|
2995
|
-
stubReducedMotion(false);
|
|
2996
|
-
function Reopenable() {
|
|
2997
|
-
const [isOpen, setIsOpen] = useState(true);
|
|
2998
|
-
return (
|
|
2999
|
-
<>
|
|
3000
|
-
<button type="button" onClick={() => setIsOpen(true)}>
|
|
3001
|
-
Open
|
|
3002
|
-
</button>
|
|
3003
|
-
<BottomSheet
|
|
3004
|
-
isOpen={isOpen}
|
|
3005
|
-
onOpenChange={setIsOpen}
|
|
3006
|
-
label="Filters">
|
|
3007
|
-
<button type="button" onClick={() => setIsOpen(false)}>
|
|
3008
|
-
Close
|
|
3009
|
-
</button>
|
|
3010
|
-
</BottomSheet>
|
|
3011
|
-
</>
|
|
3012
|
-
);
|
|
3013
|
-
}
|
|
3014
|
-
render(<Reopenable />);
|
|
3015
|
-
const dialog = screen.getByRole('dialog');
|
|
3016
|
-
drag(getHandle(), [{y: 0}, {y: 40}, {y: 120}], 'touch');
|
|
3017
|
-
fireEvent.transitionEnd(getSheet(), {propertyName: 'opacity'});
|
|
3018
|
-
expect(dialog).not.toHaveAttribute('open');
|
|
3019
|
-
fireEvent.click(screen.getByRole('button', {name: 'Open'}));
|
|
3020
|
-
expect(dialog).toHaveAttribute('open');
|
|
3021
|
-
fireEvent.click(screen.getByRole('button', {name: 'Close'}));
|
|
3022
|
-
// A control dismissal: its collapsed transform end closes the dialog.
|
|
3023
|
-
fireEvent.transitionEnd(getSheet(), {propertyName: 'transform'});
|
|
3024
|
-
expect(dialog).not.toHaveAttribute('open');
|
|
3025
|
-
});
|
|
3026
|
-
|
|
3027
|
-
it('a refused touch swipe spends its request: a guarded onOpenChange keeps the sheet open, and the next close from a control leaves at once', () => {
|
|
3028
|
-
stubReducedMotion(false);
|
|
3029
|
-
function Guarded() {
|
|
3030
|
-
const [isOpen, setIsOpen] = useState(true);
|
|
3031
|
-
const [isGuarded, setIsGuarded] = useState(true);
|
|
3032
|
-
return (
|
|
3033
|
-
<BottomSheet
|
|
3034
|
-
isOpen={isOpen}
|
|
3035
|
-
// The documented replacement for the switcher's onCancel: ignore
|
|
3036
|
-
// implicit closes while the step must stay open.
|
|
3037
|
-
onOpenChange={next => {
|
|
3038
|
-
if (!next && isGuarded) {
|
|
3039
|
-
return;
|
|
3040
|
-
}
|
|
3041
|
-
setIsOpen(next);
|
|
3042
|
-
}}
|
|
3043
|
-
label="Filters">
|
|
3044
|
-
<button type="button" onClick={() => setIsGuarded(false)}>
|
|
3045
|
-
Unlock
|
|
3046
|
-
</button>
|
|
3047
|
-
<button type="button" onClick={() => setIsOpen(false)}>
|
|
3048
|
-
Close
|
|
3049
|
-
</button>
|
|
3050
|
-
</BottomSheet>
|
|
3051
|
-
);
|
|
3052
|
-
}
|
|
3053
|
-
render(<Guarded />);
|
|
3054
|
-
const dialog = screen.getByRole('dialog');
|
|
3055
|
-
drag(getHandle(), [{y: 0}, {y: 40}, {y: 120}], 'touch');
|
|
3056
|
-
// Refused: the sheet stays open.
|
|
3057
|
-
expect(dialog).toHaveAttribute('open');
|
|
3058
|
-
fireEvent.click(screen.getByRole('button', {name: 'Unlock'}));
|
|
3059
|
-
fireEvent.click(screen.getByRole('button', {name: 'Close'}));
|
|
3060
|
-
// A control close: its collapsed transform end closes the dialog; the
|
|
3061
|
-
// spent swipe request does not turn it into a cross-fade.
|
|
3062
|
-
fireEvent.transitionEnd(getSheet(), {propertyName: 'transform'});
|
|
3063
|
-
expect(dialog).not.toHaveAttribute('open');
|
|
3064
|
-
});
|
|
3065
|
-
|
|
3066
|
-
it('a mouse drag dismissal under reduced motion on a fine pointer leaves at once (DR3): a mouse or pen drag is not a touch drag', () => {
|
|
3067
|
-
stubReducedMotion(false);
|
|
3068
|
-
render(<Controlled />);
|
|
3069
|
-
const dialog = screen.getByRole('dialog');
|
|
3070
|
-
drag(getHandle(), [{y: 0}, {y: 40}, {y: 120}], 'mouse');
|
|
3071
|
-
fireEvent.transitionEnd(getSheet(), {propertyName: 'transform'});
|
|
3072
|
-
expect(dialog).not.toHaveAttribute('open');
|
|
3073
|
-
});
|
|
3074
|
-
|
|
3075
|
-
it('a dismissal from a control under reduced motion on a fine pointer leaves at once (DR3): its (collapsed) transform end closes the dialog', () => {
|
|
3076
|
-
stubReducedMotion(false);
|
|
3077
|
-
render(<Controlled />);
|
|
3078
|
-
const dialog = screen.getByRole('dialog');
|
|
3079
|
-
fireEvent.click(screen.getByRole('button', {name: 'Close'}));
|
|
3080
|
-
fireEvent.transitionEnd(getSheet(), {propertyName: 'transform'});
|
|
3081
|
-
expect(dialog).not.toHaveAttribute('open');
|
|
3082
|
-
});
|
|
3083
|
-
|
|
3084
|
-
it('a dismissal from a control under reduced motion on a coarse pointer fades out (DR2)', () => {
|
|
3085
|
-
stubReducedMotion(true);
|
|
3086
|
-
render(<Controlled />);
|
|
3087
|
-
const dialog = screen.getByRole('dialog');
|
|
3088
|
-
fireEvent.click(screen.getByRole('button', {name: 'Close'}));
|
|
3089
|
-
fireEvent.transitionEnd(getSheet(), {propertyName: 'transform'});
|
|
3090
|
-
expect(dialog).toHaveAttribute('open');
|
|
3091
|
-
fireEvent.transitionEnd(getSheet(), {propertyName: 'opacity'});
|
|
3092
|
-
expect(dialog).not.toHaveAttribute('open');
|
|
3093
|
-
});
|
|
3094
|
-
});
|
|
3095
|
-
|
|
3096
|
-
describe('container padding isolation', () => {
|
|
3097
|
-
it('resets container padding custom properties to 0px on the sheet panel element', () => {
|
|
3098
|
-
render(
|
|
3099
|
-
<BottomSheet isOpen onOpenChange={() => {}} label="Filters">
|
|
3100
|
-
Content
|
|
3101
|
-
</BottomSheet>,
|
|
3102
|
-
);
|
|
3103
|
-
const sheet = getSheet();
|
|
3104
|
-
const computed = window.getComputedStyle(sheet);
|
|
3105
|
-
expect(
|
|
3106
|
-
computed.getPropertyValue('--container-padding-inline-start'),
|
|
3107
|
-
).toBe('0px');
|
|
3108
|
-
expect(computed.getPropertyValue('--container-padding-inline-end')).toBe(
|
|
3109
|
-
'0px',
|
|
3110
|
-
);
|
|
3111
|
-
expect(computed.getPropertyValue('--container-padding-block-start')).toBe(
|
|
3112
|
-
'0px',
|
|
3113
|
-
);
|
|
3114
|
-
expect(computed.getPropertyValue('--container-padding-block-end')).toBe(
|
|
3115
|
-
'0px',
|
|
3116
|
-
);
|
|
3117
|
-
});
|
|
3118
|
-
});
|
|
3119
|
-
|
|
3120
|
-
describe('container padding', () => {
|
|
3121
|
-
it('passes padding to the content box of a standalone sheet', () => {
|
|
3122
|
-
render(
|
|
3123
|
-
<BottomSheet isOpen onOpenChange={() => {}} label="Filters" padding={0}>
|
|
3124
|
-
<span data-testid="sheet-child">Content</span>
|
|
3125
|
-
</BottomSheet>,
|
|
3126
|
-
);
|
|
3127
|
-
const box = screen.getByTestId('sheet-child').parentElement!;
|
|
3128
|
-
expect(
|
|
3129
|
-
getComputedStyle(box).getPropertyValue(
|
|
3130
|
-
'--container-padding-inline-start',
|
|
3131
|
-
),
|
|
3132
|
-
).toBe('var(--spacing-0)');
|
|
3133
|
-
expect(getSheet().hasAttribute('padding')).toBe(false);
|
|
3134
|
-
});
|
|
3135
|
-
|
|
3136
|
-
it('passes padding to the content box of a switcher sheet', () => {
|
|
3137
|
-
render(
|
|
3138
|
-
<BottomSheetSwitcher
|
|
3139
|
-
activeSheet="details"
|
|
3140
|
-
onActiveSheetChange={() => {}}>
|
|
3141
|
-
<BottomSheet sheetId="details" label="Details" padding={2}>
|
|
3142
|
-
<span data-testid="switcher-child">Content</span>
|
|
3143
|
-
</BottomSheet>
|
|
3144
|
-
</BottomSheetSwitcher>,
|
|
3145
|
-
);
|
|
3146
|
-
const box = screen.getByTestId('switcher-child').parentElement!;
|
|
3147
|
-
expect(
|
|
3148
|
-
getComputedStyle(box).getPropertyValue(
|
|
3149
|
-
'--container-padding-inline-start',
|
|
3150
|
-
),
|
|
3151
|
-
).toBe('var(--spacing-2)');
|
|
3152
|
-
});
|
|
3153
|
-
});
|
|
3154
|
-
});
|