@astryxdesign/core 0.7.6-canary.f73623d → 0.7.6-canary.f899313
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/Badge/Badge.d.ts.map +1 -1
- package/dist/Badge/Badge.js +2 -1
- package/dist/ComplexSelector/ComplexSelector.d.ts.map +1 -1
- package/dist/ComplexSelector/ComplexSelector.js +8 -2
- package/dist/DateInput/MonthScroller.d.ts.map +1 -1
- package/dist/DateInput/MonthScroller.js +17 -18
- package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
- package/dist/DateRangeInput/DateRangeInput.js +8 -4
- package/dist/DropdownMenu/DropdownMenuGroup.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenuGroup.js +2 -1
- package/dist/Field/FieldLabel.d.ts.map +1 -1
- package/dist/Field/FieldLabel.js +27 -2
- package/dist/FieldStatus/FieldStatus.d.ts +3 -0
- package/dist/FieldStatus/FieldStatus.d.ts.map +1 -1
- package/dist/FieldStatus/FieldStatus.js +1 -0
- package/dist/FileInput/FileInput.d.ts +3 -0
- package/dist/FileInput/FileInput.d.ts.map +1 -1
- package/dist/FileInput/FileInput.js +59 -33
- package/dist/Layer/useTouchTrigger.d.ts +9 -0
- package/dist/Layer/useTouchTrigger.d.ts.map +1 -1
- package/dist/Layer/useTouchTrigger.js +1 -1
- package/dist/Link/Link.d.ts.map +1 -1
- package/dist/Link/Link.js +10 -1
- package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
- package/dist/MultiSelector/MultiSelector.js +19 -9
- package/dist/NumberInput/NumberInput.d.ts.map +1 -1
- package/dist/NumberInput/NumberInput.js +11 -15
- package/dist/Selector/Selector.d.ts.map +1 -1
- package/dist/Selector/Selector.js +9 -6
- package/dist/Selector/hooks.js +2 -2
- package/dist/Slider/Slider.d.ts.map +1 -1
- package/dist/Slider/Slider.js +82 -10
- 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.map +1 -1
- package/dist/Tokenizer/Tokenizer.js +16 -1
- package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
- package/dist/Typeahead/BaseTypeahead.js +10 -3
- package/dist/Typeahead/Typeahead.d.ts.map +1 -1
- package/dist/Typeahead/Typeahead.js +36 -10
- package/dist/astryx.css +1 -1
- 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/package.json +18 -5
- package/src/Badge/Badge.tsx +2 -1
- package/src/Chat/ChatMessage.spec.md +64 -65
- package/src/Citation/Citation.spec.md +60 -47
- package/src/ComplexSelector/ComplexSelector.tsx +19 -2
- package/src/DateInput/DateInput.spec.md +74 -57
- package/src/DateInput/MonthScroller.tsx +15 -17
- package/src/DateRangeInput/DateRangeInput.tsx +18 -3
- package/src/DropdownMenu/DropdownMenuGroup.tsx +2 -1
- package/src/Field/Field.doc.mjs +2 -1
- package/src/Field/FieldLabel.doc.mjs +4 -2
- package/src/Field/FieldLabel.spec.md +30 -21
- package/src/Field/FieldLabel.tsx +37 -3
- package/src/FieldStatus/FieldStatus.tsx +7 -0
- package/src/FileInput/FileInput.tsx +69 -28
- package/src/FormLayout/FormLayout.spec.md +11 -19
- package/src/Layer/useTouchTrigger.ts +1 -1
- package/src/Lightbox/Lightbox.spec.md +249 -61
- package/src/Link/Link.tsx +9 -2
- package/src/MultiSelector/MultiSelector.spec.md +121 -114
- package/src/MultiSelector/MultiSelector.tsx +19 -5
- package/src/NumberInput/NumberInput.tsx +11 -18
- package/src/ProgressBar/ProgressBar.spec.md +143 -119
- package/src/Selector/Selector.tsx +16 -6
- package/src/Selector/hooks.ts +2 -2
- package/src/Slider/Slider.spec.md +18 -16
- package/src/Slider/Slider.tsx +101 -9
- package/src/Token/Token.spec.md +24 -23
- package/src/Token/Token.tsx +15 -0
- package/src/Tokenizer/Tokenizer.tsx +10 -0
- package/src/Typeahead/BaseTypeahead.tsx +18 -2
- package/src/Typeahead/Typeahead.tsx +40 -9
- package/src/i18n/useListFormatter.ts +42 -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.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.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.test.tsx +0 -398
- package/src/AspectRatio/__tests__/AspectRatioFitLayers.a11y.browser.spec.ts +0 -158
- package/src/Avatar/Avatar.test.tsx +0 -867
- package/src/Avatar/AvatarStatusDot.test.tsx +0 -306
- package/src/Avatar/__tests__/AvatarForcedColors.a11y.chromium.spec.ts +0 -126
- package/src/AvatarGroup/AvatarGroup.test.tsx +0 -313
- package/src/AvatarGroup/AvatarGroupOverflow.test.tsx +0 -263
- package/src/Badge/Badge.test.tsx +0 -125
- package/src/Badge/__tests__/BadgeLargerText.a11y.chromium.spec.ts +0 -260
- package/src/Banner/Banner.test.tsx +0 -879
- package/src/Blockquote/Blockquote.test.tsx +0 -121
- 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.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/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.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.test.tsx +0 -1025
- 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.test.tsx +0 -202
- 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.test.tsx +0 -406
- package/src/Chat/ChatComposer.test.tsx +0 -364
- package/src/Chat/ChatComposerDrawer.test.tsx +0 -130
- package/src/Chat/ChatComposerInput.test.tsx +0 -2136
- package/src/Chat/ChatComposerTokenElement.test.tsx +0 -85
- package/src/Chat/ChatDictationButton.test.tsx +0 -108
- package/src/Chat/ChatLayout.test.tsx +0 -311
- package/src/Chat/ChatLayoutScrollButton.test.tsx +0 -99
- package/src/Chat/ChatMessage.test.tsx +0 -344
- package/src/Chat/ChatMessageBubble.test.tsx +0 -294
- package/src/Chat/ChatMessageList.test.tsx +0 -198
- package/src/Chat/ChatMessageMetadata.test.tsx +0 -66
- package/src/Chat/ChatPastedTextToken.test.tsx +0 -71
- package/src/Chat/ChatSendButton.test.tsx +0 -309
- package/src/Chat/ChatSystemMessage.test.tsx +0 -189
- package/src/Chat/ChatTokenizedText.test.tsx +0 -163
- 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.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.test.tsx +0 -1159
- package/src/CheckboxList/__tests__/CheckboxList.a11y.chromium.spec.ts +0 -1998
- package/src/Citation/Citation.test.tsx +0 -361
- package/src/ClickableCard/ClickableCard.test.tsx +0 -382
- package/src/Code/Code.test.tsx +0 -75
- package/src/CodeBlock/CodeBlock.highlightStyles.test.tsx +0 -87
- 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.test.tsx +0 -620
- 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.test.tsx +0 -750
- 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.test.tsx +0 -539
- package/src/CommandPalette/CommandPaletteItem.test.tsx +0 -417
- 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.test.tsx +0 -1171
- package/src/ComplexSelector/__tests__/ComplexSelector.a11y.chromium.spec.ts +0 -965
- package/src/ContextMenu/ContextMenu.test.tsx +0 -1617
- package/src/ContextMenu/ContextMenu.triggerProps.test.tsx +0 -54
- 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.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.test.tsx +0 -1151
- package/src/DateTimeInput/DateTimeInput.fr10.test.tsx +0 -319
- 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.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/useImperativeDialog.test.tsx +0 -78
- package/src/Divider/Divider.test.tsx +0 -155
- package/src/Drawer/Drawer.test.tsx +0 -1335
- package/src/Drawer/DrawerDirection.test.tsx +0 -127
- 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.ssr.test.tsx +0 -93
- package/src/DropdownMenu/DropdownMenu.test.tsx +0 -4498
- package/src/DropdownMenu/DropdownMenuContext.public.test.ts +0 -22
- package/src/DropdownMenu/DropdownMenuLazySheet.test.tsx +0 -126
- package/src/DropdownMenu/DropdownMenuSelectable.test.tsx +0 -635
- package/src/DropdownMenu/DropdownMenuSheetFirstOpen.test.tsx +0 -72
- 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/EmptyState/EmptyState.announce.test.tsx +0 -227
- package/src/EmptyState/EmptyState.test.tsx +0 -284
- package/src/Field/Field.test.tsx +0 -800
- 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.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.test.tsx +0 -1291
- package/src/FormLayout/FormLayout.test.tsx +0 -444
- package/src/FormLayout/__snapshots__/FormLayout.test.tsx.snap +0 -46
- package/src/Grid/Grid.test.tsx +0 -509
- package/src/HStack/HStack.test.tsx +0 -129
- package/src/Heading/Heading.test.tsx +0 -328
- package/src/HoverCard/HoverCard.test.tsx +0 -1339
- package/src/Icon/Icon.test.tsx +0 -990
- package/src/Icon/globalIconRegistry.test.tsx +0 -613
- package/src/IconButton/IconButton.test.tsx +0 -156
- package/src/Indicator/Indicator.test.tsx +0 -389
- package/src/Indicator/indicatorRegistry.test.tsx +0 -106
- package/src/InputGroup/InputGroup.test.tsx +0 -502
- package/src/InteractiveRoleContext/InteractiveRoleContext.test.tsx +0 -185
- package/src/Item/Item.source-build.test.mjs +0 -88
- 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.test.tsx +0 -299
- 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.test.tsx +0 -375
- 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.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.test.tsx +0 -457
- 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.test.tsx +0 -1052
- package/src/List/ListItem.source-build.test.mjs +0 -41
- 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.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/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.test.tsx +0 -383
- 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.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.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.test.tsx +0 -41
- 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.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.test.tsx +0 -1134
- package/src/Outline/OutlineFocus.a11y.chromium.spec.ts +0 -551
- package/src/Outline/parseOutlineFromMarkdown.test.ts +0 -104
- package/src/OverflowList/OverflowList.test.tsx +0 -1073
- package/src/Overlay/Overlay.test.tsx +0 -424
- package/src/Pagination/Pagination.test.tsx +0 -1754
- 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.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.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.test.tsx +0 -1051
- package/src/RadioList/RadioListItem.press.source-build.test.mjs +0 -80
- 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/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.test.tsx +0 -304
- package/src/Section/Section.test.tsx +0 -376
- package/src/SegmentedControl/SegmentedControl.test.tsx +0 -981
- 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.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.test.tsx +0 -4275
- package/src/SideNav/SideNavRailMotion.test.tsx +0 -620
- package/src/SideNav/__tests__/SideNavRailMotion.interaction.browser.spec.ts +0 -542
- package/src/SizeContext/SizeContext.test.tsx +0 -226
- package/src/Skeleton/Skeleton.test.tsx +0 -148
- package/src/Slider/Slider.fr10.test.tsx +0 -93
- 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.test.tsx +0 -484
- package/src/Stack/Stack.test.tsx +0 -473
- package/src/Stack/StackItem.test.tsx +0 -105
- package/src/StatusDot/StatusDot.test.tsx +0 -213
- package/src/Stepper/Stepper.public.test.ts +0 -91
- package/src/Stepper/Stepper.test.tsx +0 -2130
- package/src/Switch/Switch.focusRing.source-build.test.mjs +0 -81
- 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/TabList.test.tsx +0 -1755
- 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.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/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/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.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/tableContextMenu.test.tsx +0 -208
- 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.test.tsx +0 -1317
- package/src/TextInput/TextInput.fr10.test.tsx +0 -21
- 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.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.test.tsx +0 -907
- package/src/Timer/Timer.test.tsx +0 -302
- package/src/Timestamp/Timestamp.test.tsx +0 -1661
- package/src/Timestamp/tooltipEntries.test.ts +0 -566
- 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.test.tsx +0 -1000
- 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.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.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.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.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.test.tsx +0 -863
- package/src/TopNav/TopNavMegaMenu.reduced-motion.source-build.test.mjs +0 -83
- package/src/TopNav/TopNavMegaMenu.test.tsx +0 -1153
- package/src/TopNav/TopNavMenu.test.tsx +0 -502
- package/src/TopNav/__tests__/TopNavMobileLongHeading.a11y.chromium.spec.ts +0 -75
- 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.test.tsx +0 -553
- package/src/Typeahead/Typeahead.fr10.test.tsx +0 -28
- 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.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/__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,2010 +0,0 @@
|
|
|
1
|
-
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* @file BottomSheetSwitcher.test.tsx
|
|
5
|
-
* @input Uses vitest, Testing Library, BottomSheet, BottomSheetSwitcher
|
|
6
|
-
* @output Tests mutually exclusive sheet selection, dismissal, and focus handoff
|
|
7
|
-
* @position Core tests for BottomSheetSwitcher
|
|
8
|
-
*
|
|
9
|
-
* SYNC: When BottomSheetSwitcher.tsx or its BottomSheet integration changes,
|
|
10
|
-
* update these tests to match the public behavior.
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
import {act, fireEvent, render, screen} from '@testing-library/react';
|
|
14
|
-
import {
|
|
15
|
-
StrictMode,
|
|
16
|
-
createRef,
|
|
17
|
-
useEffect,
|
|
18
|
-
useLayoutEffect,
|
|
19
|
-
useState,
|
|
20
|
-
type KeyboardEvent as ReactKeyboardEvent,
|
|
21
|
-
} from 'react';
|
|
22
|
-
import {afterEach, beforeEach, describe, expect, it, vi} from 'vitest';
|
|
23
|
-
import {hasActiveFocusTrapEscape, useFocusTrap} from '../hooks';
|
|
24
|
-
import {useLayerDismissal} from '../Layer/useLayerDismissal';
|
|
25
|
-
import {Dialog} from '../Dialog';
|
|
26
|
-
import {hydrateRoot} from 'react-dom/client';
|
|
27
|
-
import {renderToString} from 'react-dom/server';
|
|
28
|
-
import {BottomSheet} from './BottomSheet';
|
|
29
|
-
import {BottomSheetSwitcher} from './BottomSheetSwitcher';
|
|
30
|
-
|
|
31
|
-
// jsdom implements no <dialog> presentation. The stubs record which open
|
|
32
|
-
// dialogs are modal, standing in for the `:modal` pseudo-class jsdom cannot
|
|
33
|
-
// match.
|
|
34
|
-
const modalDialogs = new WeakSet<HTMLDialogElement>();
|
|
35
|
-
beforeEach(() => {
|
|
36
|
-
HTMLDialogElement.prototype.showModal = vi.fn(function (
|
|
37
|
-
this: HTMLDialogElement,
|
|
38
|
-
) {
|
|
39
|
-
this.setAttribute('open', '');
|
|
40
|
-
modalDialogs.add(this);
|
|
41
|
-
});
|
|
42
|
-
HTMLDialogElement.prototype.show = vi.fn(function (this: HTMLDialogElement) {
|
|
43
|
-
this.setAttribute('open', '');
|
|
44
|
-
});
|
|
45
|
-
HTMLDialogElement.prototype.close = vi.fn(function (this: HTMLDialogElement) {
|
|
46
|
-
this.removeAttribute('open');
|
|
47
|
-
modalDialogs.delete(this);
|
|
48
|
-
});
|
|
49
|
-
window.scrollTo = vi.fn();
|
|
50
|
-
vi.stubGlobal(
|
|
51
|
-
'matchMedia',
|
|
52
|
-
vi.fn().mockReturnValue({
|
|
53
|
-
matches: false,
|
|
54
|
-
media: '',
|
|
55
|
-
onchange: null,
|
|
56
|
-
addEventListener: vi.fn(),
|
|
57
|
-
removeEventListener: vi.fn(),
|
|
58
|
-
addListener: vi.fn(),
|
|
59
|
-
removeListener: vi.fn(),
|
|
60
|
-
dispatchEvent: vi.fn(),
|
|
61
|
-
}),
|
|
62
|
-
);
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
afterEach(() => {
|
|
66
|
-
vi.unstubAllGlobals();
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
function Flow() {
|
|
70
|
-
const [activeSheet, setActiveSheet] = useState<string | null>(null);
|
|
71
|
-
return (
|
|
72
|
-
<>
|
|
73
|
-
<button type="button" onClick={() => setActiveSheet('details')}>
|
|
74
|
-
Start flow
|
|
75
|
-
</button>
|
|
76
|
-
<BottomSheetSwitcher
|
|
77
|
-
activeSheet={activeSheet}
|
|
78
|
-
onActiveSheetChange={setActiveSheet}>
|
|
79
|
-
<BottomSheet
|
|
80
|
-
sheetId="details"
|
|
81
|
-
label="Details"
|
|
82
|
-
data-testid="details-sheet">
|
|
83
|
-
<button type="button" onClick={() => setActiveSheet('confirm')}>
|
|
84
|
-
Continue
|
|
85
|
-
</button>
|
|
86
|
-
</BottomSheet>
|
|
87
|
-
<BottomSheet
|
|
88
|
-
sheetId="confirm"
|
|
89
|
-
label="Confirm"
|
|
90
|
-
data-testid="confirm-sheet">
|
|
91
|
-
<button type="button" onClick={() => setActiveSheet('details')}>
|
|
92
|
-
Back
|
|
93
|
-
</button>
|
|
94
|
-
</BottomSheet>
|
|
95
|
-
</BottomSheetSwitcher>
|
|
96
|
-
</>
|
|
97
|
-
);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
function ConditionalFlow() {
|
|
101
|
-
const [activeSheet, setActiveSheet] = useState<string | null>(null);
|
|
102
|
-
return (
|
|
103
|
-
<>
|
|
104
|
-
<button type="button" onClick={() => setActiveSheet('details')}>
|
|
105
|
-
Start conditional flow
|
|
106
|
-
</button>
|
|
107
|
-
<BottomSheetSwitcher
|
|
108
|
-
activeSheet={activeSheet}
|
|
109
|
-
onActiveSheetChange={setActiveSheet}>
|
|
110
|
-
{activeSheet != null && (
|
|
111
|
-
<BottomSheet sheetId={activeSheet} label="Conditional details">
|
|
112
|
-
Content
|
|
113
|
-
</BottomSheet>
|
|
114
|
-
)}
|
|
115
|
-
</BottomSheetSwitcher>
|
|
116
|
-
</>
|
|
117
|
-
);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
function NestedStandaloneSheetFlow() {
|
|
121
|
-
const [isNestedOpen, setIsNestedOpen] = useState(false);
|
|
122
|
-
return (
|
|
123
|
-
<BottomSheetSwitcher activeSheet="details" onActiveSheetChange={() => {}}>
|
|
124
|
-
<BottomSheet sheetId="details" label="Details">
|
|
125
|
-
<button type="button" onClick={() => setIsNestedOpen(true)}>
|
|
126
|
-
Open nested sheet
|
|
127
|
-
</button>
|
|
128
|
-
<BottomSheet
|
|
129
|
-
isOpen={isNestedOpen}
|
|
130
|
-
onOpenChange={setIsNestedOpen}
|
|
131
|
-
label="Nested standalone sheet">
|
|
132
|
-
Nested content
|
|
133
|
-
</BottomSheet>
|
|
134
|
-
</BottomSheet>
|
|
135
|
-
</BottomSheetSwitcher>
|
|
136
|
-
);
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
function NestedEscapeTrap({onEscape}: {onEscape: () => void}) {
|
|
140
|
-
const {containerRef} = useFocusTrap<HTMLDivElement>({
|
|
141
|
-
isActive: true,
|
|
142
|
-
onEscape,
|
|
143
|
-
});
|
|
144
|
-
return (
|
|
145
|
-
<div ref={containerRef} data-testid="nested-escape-trap">
|
|
146
|
-
Nested layer
|
|
147
|
-
</div>
|
|
148
|
-
);
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
function NestedDismissibleLayer({onDismiss}: {onDismiss: () => void}) {
|
|
152
|
-
const [isOpen, setIsOpen] = useState(true);
|
|
153
|
-
useLayerDismissal({
|
|
154
|
-
isActive: isOpen,
|
|
155
|
-
onDismiss: () => {
|
|
156
|
-
setIsOpen(false);
|
|
157
|
-
onDismiss();
|
|
158
|
-
},
|
|
159
|
-
});
|
|
160
|
-
return (
|
|
161
|
-
<button type="button">
|
|
162
|
-
{isOpen ? 'Nested layer open' : 'Nested layer closed'}
|
|
163
|
-
</button>
|
|
164
|
-
);
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
const panelRefA = (_element: HTMLDivElement | null) => {};
|
|
168
|
-
const panelRefB = (_element: HTMLDivElement | null) => {};
|
|
169
|
-
|
|
170
|
-
function CallbackRefFlow() {
|
|
171
|
-
const [activeSheet, setActiveSheet] = useState<string | null>('details');
|
|
172
|
-
const [useAlternateRef, setUseAlternateRef] = useState(false);
|
|
173
|
-
return (
|
|
174
|
-
<>
|
|
175
|
-
<button
|
|
176
|
-
type="button"
|
|
177
|
-
onClick={() => setUseAlternateRef(current => !current)}>
|
|
178
|
-
Rerender parent
|
|
179
|
-
</button>
|
|
180
|
-
<BottomSheetSwitcher
|
|
181
|
-
activeSheet={activeSheet}
|
|
182
|
-
onActiveSheetChange={setActiveSheet}>
|
|
183
|
-
<BottomSheet
|
|
184
|
-
ref={useAlternateRef ? panelRefB : panelRefA}
|
|
185
|
-
sheetId="details"
|
|
186
|
-
label="Details"
|
|
187
|
-
data-testid="callback-details-sheet">
|
|
188
|
-
<button type="button" onClick={() => setActiveSheet('confirm')}>
|
|
189
|
-
Continue with callback ref
|
|
190
|
-
</button>
|
|
191
|
-
</BottomSheet>
|
|
192
|
-
<BottomSheet
|
|
193
|
-
sheetId="confirm"
|
|
194
|
-
label="Confirm"
|
|
195
|
-
data-testid="callback-confirm-sheet">
|
|
196
|
-
Confirmation
|
|
197
|
-
</BottomSheet>
|
|
198
|
-
</BottomSheetSwitcher>
|
|
199
|
-
</>
|
|
200
|
-
);
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
function ModeSwitchFlow() {
|
|
204
|
-
const [activeSheet, setActiveSheet] = useState<string | null>(null);
|
|
205
|
-
const [hasScrim, setHasScrim] = useState(true);
|
|
206
|
-
return (
|
|
207
|
-
<>
|
|
208
|
-
<button type="button" onClick={() => setActiveSheet('details')}>
|
|
209
|
-
Open first modal
|
|
210
|
-
</button>
|
|
211
|
-
<button
|
|
212
|
-
type="button"
|
|
213
|
-
onClick={() => {
|
|
214
|
-
setHasScrim(true);
|
|
215
|
-
setActiveSheet('details');
|
|
216
|
-
}}>
|
|
217
|
-
Open second modal
|
|
218
|
-
</button>
|
|
219
|
-
<BottomSheetSwitcher
|
|
220
|
-
activeSheet={activeSheet}
|
|
221
|
-
onActiveSheetChange={setActiveSheet}
|
|
222
|
-
hasScrim={hasScrim}>
|
|
223
|
-
<BottomSheet
|
|
224
|
-
sheetId="details"
|
|
225
|
-
label="Details"
|
|
226
|
-
data-testid="mode-details-sheet">
|
|
227
|
-
<button type="button" onClick={() => setHasScrim(false)}>
|
|
228
|
-
Make non-modal
|
|
229
|
-
</button>
|
|
230
|
-
<button type="button" onClick={() => setActiveSheet(null)}>
|
|
231
|
-
Close flow
|
|
232
|
-
</button>
|
|
233
|
-
</BottomSheet>
|
|
234
|
-
</BottomSheetSwitcher>
|
|
235
|
-
</>
|
|
236
|
-
);
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
function RequiredConfirmFlow() {
|
|
240
|
-
const [activeSheet, setActiveSheet] = useState<string | null>('details');
|
|
241
|
-
return (
|
|
242
|
-
<BottomSheetSwitcher
|
|
243
|
-
activeSheet={activeSheet}
|
|
244
|
-
onActiveSheetChange={setActiveSheet}>
|
|
245
|
-
<BottomSheet
|
|
246
|
-
sheetId="details"
|
|
247
|
-
label="Details"
|
|
248
|
-
data-testid="details-sheet">
|
|
249
|
-
<button type="button" onClick={() => setActiveSheet('confirm')}>
|
|
250
|
-
Continue
|
|
251
|
-
</button>
|
|
252
|
-
</BottomSheet>
|
|
253
|
-
<BottomSheet
|
|
254
|
-
sheetId="confirm"
|
|
255
|
-
label="Confirm"
|
|
256
|
-
purpose="required"
|
|
257
|
-
data-testid="confirm-sheet">
|
|
258
|
-
<button type="button" onClick={() => setActiveSheet(null)}>
|
|
259
|
-
Done
|
|
260
|
-
</button>
|
|
261
|
-
</BottomSheet>
|
|
262
|
-
</BottomSheetSwitcher>
|
|
263
|
-
);
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
/**
|
|
267
|
-
* A three-step flow that mounts closed, for the host-dialog contract: the
|
|
268
|
-
* switcher's ref, `id`, and `data-testid`. `onMountLayout` and
|
|
269
|
-
* `onMountPassive` run in the consumer's own mount effects, where a parent
|
|
270
|
-
* would first read the ref.
|
|
271
|
-
*/
|
|
272
|
-
function HostFlow({
|
|
273
|
-
dialogRef,
|
|
274
|
-
dialogProps,
|
|
275
|
-
hasScrim = true,
|
|
276
|
-
initialSheet = null,
|
|
277
|
-
onMountLayout,
|
|
278
|
-
onMountPassive,
|
|
279
|
-
}: {
|
|
280
|
-
dialogRef?: React.Ref<HTMLDialogElement>;
|
|
281
|
-
dialogProps?: Record<string, string>;
|
|
282
|
-
hasScrim?: boolean;
|
|
283
|
-
initialSheet?: string | null;
|
|
284
|
-
onMountLayout?: () => void;
|
|
285
|
-
onMountPassive?: () => void;
|
|
286
|
-
}) {
|
|
287
|
-
const [activeSheet, setActiveSheet] = useState<string | null>(initialSheet);
|
|
288
|
-
useLayoutEffect(() => onMountLayout?.(), [onMountLayout]);
|
|
289
|
-
useEffect(() => onMountPassive?.(), [onMountPassive]);
|
|
290
|
-
return (
|
|
291
|
-
<>
|
|
292
|
-
<button type="button" onClick={() => setActiveSheet('details')}>
|
|
293
|
-
Start flow
|
|
294
|
-
</button>
|
|
295
|
-
<BottomSheetSwitcher
|
|
296
|
-
ref={dialogRef}
|
|
297
|
-
{...dialogProps}
|
|
298
|
-
activeSheet={activeSheet}
|
|
299
|
-
onActiveSheetChange={setActiveSheet}
|
|
300
|
-
hasScrim={hasScrim}>
|
|
301
|
-
<BottomSheet
|
|
302
|
-
sheetId="details"
|
|
303
|
-
label="Details"
|
|
304
|
-
data-testid="details-sheet">
|
|
305
|
-
<button type="button" onClick={() => setActiveSheet('confirm')}>
|
|
306
|
-
Continue
|
|
307
|
-
</button>
|
|
308
|
-
</BottomSheet>
|
|
309
|
-
<BottomSheet
|
|
310
|
-
sheetId="confirm"
|
|
311
|
-
label="Confirm"
|
|
312
|
-
data-testid="confirm-sheet">
|
|
313
|
-
<button type="button" onClick={() => setActiveSheet(null)}>
|
|
314
|
-
Done
|
|
315
|
-
</button>
|
|
316
|
-
</BottomSheet>
|
|
317
|
-
<BottomSheet sheetId="extra" label="Extra">
|
|
318
|
-
Content
|
|
319
|
-
</BottomSheet>
|
|
320
|
-
</BottomSheetSwitcher>
|
|
321
|
-
</>
|
|
322
|
-
);
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
/**
|
|
326
|
-
* The dialog hosting the active sheet. A retained sheet's dialog is inert and
|
|
327
|
-
* `aria-hidden`, so only the active sheet's dialog is exposed by role.
|
|
328
|
-
*/
|
|
329
|
-
function getActiveDialog(): HTMLDialogElement {
|
|
330
|
-
return screen.getByRole<HTMLDialogElement>('dialog');
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
function getSheetDialog(testId: string): HTMLDialogElement {
|
|
334
|
-
const dialog = screen.getByTestId(testId).closest('dialog');
|
|
335
|
-
if (dialog == null) {
|
|
336
|
-
throw new Error('sheet dialog not found');
|
|
337
|
-
}
|
|
338
|
-
return dialog;
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
function openDialogs(): HTMLDialogElement[] {
|
|
342
|
-
return Array.from(
|
|
343
|
-
document.querySelectorAll<HTMLDialogElement>('dialog[open]'),
|
|
344
|
-
);
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
/**
|
|
348
|
-
* Whether the shared page-scroll lock (useScrollLock, as Dialog uses) is
|
|
349
|
-
* engaged: it pins <body>. The browser spec measures the lock's effect on
|
|
350
|
-
* scrolling; this reads its pin.
|
|
351
|
-
*/
|
|
352
|
-
function isScrollLockEngaged(): boolean {
|
|
353
|
-
return document.body.style.position === 'fixed';
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
/** A two-step flow whose second step opens a Core Dialog above itself. */
|
|
357
|
-
function DialogInStepFlow() {
|
|
358
|
-
const [activeSheet, setActiveSheet] = useState<string | null>(null);
|
|
359
|
-
const [isDialogOpen, setIsDialogOpen] = useState(false);
|
|
360
|
-
return (
|
|
361
|
-
<>
|
|
362
|
-
<button type="button" onClick={() => setActiveSheet('details')}>
|
|
363
|
-
Start flow
|
|
364
|
-
</button>
|
|
365
|
-
<BottomSheetSwitcher
|
|
366
|
-
activeSheet={activeSheet}
|
|
367
|
-
onActiveSheetChange={setActiveSheet}>
|
|
368
|
-
<BottomSheet
|
|
369
|
-
sheetId="details"
|
|
370
|
-
label="Details"
|
|
371
|
-
data-testid="details-sheet">
|
|
372
|
-
<button type="button" onClick={() => setActiveSheet('confirm')}>
|
|
373
|
-
Continue
|
|
374
|
-
</button>
|
|
375
|
-
</BottomSheet>
|
|
376
|
-
<BottomSheet
|
|
377
|
-
sheetId="confirm"
|
|
378
|
-
label="Confirm"
|
|
379
|
-
data-testid="confirm-sheet">
|
|
380
|
-
<button type="button" onClick={() => setIsDialogOpen(true)}>
|
|
381
|
-
Open dialog
|
|
382
|
-
</button>
|
|
383
|
-
<Dialog
|
|
384
|
-
isOpen={isDialogOpen}
|
|
385
|
-
onOpenChange={setIsDialogOpen}
|
|
386
|
-
aria-label="Are you sure">
|
|
387
|
-
<button type="button" onClick={() => setIsDialogOpen(false)}>
|
|
388
|
-
Close dialog
|
|
389
|
-
</button>
|
|
390
|
-
</Dialog>
|
|
391
|
-
<button type="button" onClick={() => setActiveSheet(null)}>
|
|
392
|
-
Done
|
|
393
|
-
</button>
|
|
394
|
-
</BottomSheet>
|
|
395
|
-
</BottomSheetSwitcher>
|
|
396
|
-
</>
|
|
397
|
-
);
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
/**
|
|
401
|
-
* Records every native dialog call with the dialog's accessible label, and
|
|
402
|
-
* returns focus the way Chromium and WebKit do: close() on a dialog presented
|
|
403
|
-
* with showModal() focuses the element that was focused when it was
|
|
404
|
-
* presented, wherever focus is by then (measured: both engines return it
|
|
405
|
-
* even when focus sits in another open dialog).
|
|
406
|
-
*/
|
|
407
|
-
function recordDialogCalls(): string[] {
|
|
408
|
-
const log: string[] = [];
|
|
409
|
-
const focusedAtPresent = new WeakMap<HTMLDialogElement, Element | null>();
|
|
410
|
-
const record = (name: string) =>
|
|
411
|
-
vi.fn(function (this: HTMLDialogElement) {
|
|
412
|
-
log.push(`${name}:${this.getAttribute('aria-label') ?? '?'}`);
|
|
413
|
-
if (name === 'close') {
|
|
414
|
-
this.removeAttribute('open');
|
|
415
|
-
const wasModal = modalDialogs.delete(this);
|
|
416
|
-
const previouslyFocused = focusedAtPresent.get(this);
|
|
417
|
-
focusedAtPresent.delete(this);
|
|
418
|
-
if (
|
|
419
|
-
wasModal &&
|
|
420
|
-
previouslyFocused instanceof HTMLElement &&
|
|
421
|
-
previouslyFocused.isConnected
|
|
422
|
-
) {
|
|
423
|
-
previouslyFocused.focus();
|
|
424
|
-
}
|
|
425
|
-
} else {
|
|
426
|
-
if (name === 'showModal') {
|
|
427
|
-
focusedAtPresent.set(this, document.activeElement);
|
|
428
|
-
modalDialogs.add(this);
|
|
429
|
-
}
|
|
430
|
-
this.setAttribute('open', '');
|
|
431
|
-
}
|
|
432
|
-
});
|
|
433
|
-
HTMLDialogElement.prototype.showModal = record('showModal');
|
|
434
|
-
HTMLDialogElement.prototype.show = record('show');
|
|
435
|
-
HTMLDialogElement.prototype.close = record('close');
|
|
436
|
-
return log;
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
function finishSheetTransition(
|
|
440
|
-
element: HTMLElement,
|
|
441
|
-
propertyName: 'transform' | 'opacity',
|
|
442
|
-
) {
|
|
443
|
-
const sheet = getSheetPanel(element);
|
|
444
|
-
fireEvent.transitionEnd(sheet, {propertyName});
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
function getSheetPanel(element: HTMLElement): HTMLElement {
|
|
448
|
-
if (element.classList.contains('astryx-bottom-sheet')) {
|
|
449
|
-
return element;
|
|
450
|
-
}
|
|
451
|
-
const sheet = element.querySelector<HTMLElement>('.astryx-bottom-sheet');
|
|
452
|
-
if (!sheet) {
|
|
453
|
-
throw new Error('sheet panel not found');
|
|
454
|
-
}
|
|
455
|
-
return sheet;
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
function getSheetLayer(testId: string): HTMLElement {
|
|
459
|
-
const panel = screen.getByTestId(testId);
|
|
460
|
-
const layer = panel.parentElement;
|
|
461
|
-
if (layer == null) {
|
|
462
|
-
throw new Error('sheet layer not found');
|
|
463
|
-
}
|
|
464
|
-
return layer;
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
function mockSheetTop(sheet: HTMLElement, top: number) {
|
|
468
|
-
const rect = {
|
|
469
|
-
x: 0,
|
|
470
|
-
y: top,
|
|
471
|
-
top,
|
|
472
|
-
right: 640,
|
|
473
|
-
bottom: 800,
|
|
474
|
-
left: 0,
|
|
475
|
-
width: 640,
|
|
476
|
-
height: 800 - top,
|
|
477
|
-
toJSON: () => {},
|
|
478
|
-
};
|
|
479
|
-
vi.spyOn(sheet, 'getBoundingClientRect').mockReturnValue(rect);
|
|
480
|
-
if (sheet.parentElement) {
|
|
481
|
-
vi.spyOn(sheet.parentElement, 'getBoundingClientRect').mockReturnValue(
|
|
482
|
-
rect,
|
|
483
|
-
);
|
|
484
|
-
}
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
describe('BottomSheetSwitcher', () => {
|
|
488
|
-
it('opens only the sheet selected by activeSheet', () => {
|
|
489
|
-
render(<Flow />);
|
|
490
|
-
|
|
491
|
-
fireEvent.click(screen.getByRole('button', {name: 'Start flow'}));
|
|
492
|
-
|
|
493
|
-
expect(getSheetLayer('details-sheet')).not.toHaveAttribute('hidden');
|
|
494
|
-
expect(getSheetLayer('confirm-sheet')).toHaveAttribute('hidden');
|
|
495
|
-
expect(getSheetLayer('confirm-sheet')).toHaveStyle({display: 'none'});
|
|
496
|
-
expect(document.querySelectorAll('dialog[open]')).toHaveLength(1);
|
|
497
|
-
expect(HTMLDialogElement.prototype.showModal).toHaveBeenCalledTimes(1);
|
|
498
|
-
expect(HTMLDialogElement.prototype.show).not.toHaveBeenCalled();
|
|
499
|
-
expect(getActiveDialog()).toHaveAttribute('aria-modal', 'true');
|
|
500
|
-
expect(getActiveDialog()).toHaveAccessibleName('Details');
|
|
501
|
-
});
|
|
502
|
-
|
|
503
|
-
it('forwards sheet DOM props and refs to its panel in its dialog', () => {
|
|
504
|
-
const panelRef = createRef<HTMLDivElement>();
|
|
505
|
-
|
|
506
|
-
render(
|
|
507
|
-
<BottomSheetSwitcher activeSheet="details" onActiveSheetChange={() => {}}>
|
|
508
|
-
<BottomSheet
|
|
509
|
-
ref={panelRef}
|
|
510
|
-
sheetId="details"
|
|
511
|
-
label="Details"
|
|
512
|
-
data-testid="details-layer"
|
|
513
|
-
data-sheet-owner="settings">
|
|
514
|
-
Content
|
|
515
|
-
</BottomSheet>
|
|
516
|
-
</BottomSheetSwitcher>,
|
|
517
|
-
);
|
|
518
|
-
|
|
519
|
-
const panel = screen.getByTestId('details-layer');
|
|
520
|
-
expect(panelRef.current).toBe(panel);
|
|
521
|
-
expect(panel).toHaveClass('astryx-bottom-sheet');
|
|
522
|
-
expect(panel).toHaveAttribute('data-sheet-owner', 'settings');
|
|
523
|
-
expect(getActiveDialog()).toContainElement(panel);
|
|
524
|
-
});
|
|
525
|
-
|
|
526
|
-
it("forwards switcher DOM props, events, styles, and refs to the active sheet's dialog", () => {
|
|
527
|
-
const dialogRef = createRef<HTMLDialogElement>();
|
|
528
|
-
const onClick = vi.fn();
|
|
529
|
-
const onActiveSheetChange = vi.fn();
|
|
530
|
-
|
|
531
|
-
render(
|
|
532
|
-
<BottomSheetSwitcher
|
|
533
|
-
ref={dialogRef}
|
|
534
|
-
activeSheet="details"
|
|
535
|
-
onActiveSheetChange={onActiveSheetChange}
|
|
536
|
-
aria-label="Notification setup"
|
|
537
|
-
data-flow-owner="settings"
|
|
538
|
-
className="consumer-switcher"
|
|
539
|
-
style={{insetInlineStart: 12}}
|
|
540
|
-
onClick={onClick}>
|
|
541
|
-
<BottomSheet sheetId="details" label="Details">
|
|
542
|
-
Content
|
|
543
|
-
</BottomSheet>
|
|
544
|
-
</BottomSheetSwitcher>,
|
|
545
|
-
);
|
|
546
|
-
|
|
547
|
-
const dialog = getActiveDialog();
|
|
548
|
-
expect(dialogRef.current).toBe(dialog);
|
|
549
|
-
expect(dialog).toHaveAccessibleName('Notification setup');
|
|
550
|
-
expect(dialog).toHaveAttribute('data-flow-owner', 'settings');
|
|
551
|
-
expect(dialog).toHaveClass('consumer-switcher');
|
|
552
|
-
expect(dialog).toHaveStyle({insetInlineStart: '12px'});
|
|
553
|
-
|
|
554
|
-
fireEvent.click(dialog);
|
|
555
|
-
expect(onClick).toHaveBeenCalledTimes(1);
|
|
556
|
-
expect(onActiveSheetChange).toHaveBeenCalledWith(null);
|
|
557
|
-
});
|
|
558
|
-
|
|
559
|
-
it('resets switcher context for a sheet nested inside item content', () => {
|
|
560
|
-
render(<NestedStandaloneSheetFlow />);
|
|
561
|
-
const opener = screen.getByRole('button', {name: 'Open nested sheet'});
|
|
562
|
-
opener.focus();
|
|
563
|
-
fireEvent.click(opener);
|
|
564
|
-
|
|
565
|
-
const nestedDialog = screen.getByRole('dialog', {
|
|
566
|
-
name: 'Nested standalone sheet',
|
|
567
|
-
});
|
|
568
|
-
expect(nestedDialog).toHaveAttribute('open');
|
|
569
|
-
expect(getSheetPanel(nestedDialog)).toHaveFocus();
|
|
570
|
-
expect(document.querySelectorAll('dialog[open]')).toHaveLength(2);
|
|
571
|
-
expect(HTMLDialogElement.prototype.showModal).toHaveBeenCalledTimes(2);
|
|
572
|
-
|
|
573
|
-
fireEvent.keyDown(nestedDialog, {key: 'Escape'});
|
|
574
|
-
finishSheetTransition(nestedDialog, 'transform');
|
|
575
|
-
|
|
576
|
-
expect(nestedDialog).not.toHaveAttribute('open');
|
|
577
|
-
expect(getActiveDialog()).toHaveAttribute('open');
|
|
578
|
-
expect(opener).toHaveFocus();
|
|
579
|
-
});
|
|
580
|
-
|
|
581
|
-
it('keeps the previous sheet stationary until the new entrance finishes, then fades it', () => {
|
|
582
|
-
render(<Flow />);
|
|
583
|
-
fireEvent.click(screen.getByRole('button', {name: 'Start flow'}));
|
|
584
|
-
const detailsDialog = getSheetDialog('details-sheet');
|
|
585
|
-
const confirmDialog = getSheetDialog('confirm-sheet');
|
|
586
|
-
expect(getActiveDialog()).toBe(detailsDialog);
|
|
587
|
-
const detailsSheet = getSheetLayer('details-sheet');
|
|
588
|
-
const confirmSheet = getSheetLayer('confirm-sheet');
|
|
589
|
-
|
|
590
|
-
fireEvent.click(screen.getByRole('button', {name: 'Continue'}));
|
|
591
|
-
|
|
592
|
-
expect(detailsSheet).not.toHaveAttribute('hidden');
|
|
593
|
-
expect(detailsSheet).toHaveAttribute('inert');
|
|
594
|
-
expect(detailsSheet).toHaveAttribute('aria-hidden', 'true');
|
|
595
|
-
expect(confirmSheet).not.toHaveAttribute('hidden');
|
|
596
|
-
expect(confirmSheet).not.toHaveAttribute('inert');
|
|
597
|
-
// Both dialogs stay open through the handoff; the retained one is inert.
|
|
598
|
-
expect(openDialogs()).toEqual([detailsDialog, confirmDialog]);
|
|
599
|
-
expect(detailsDialog).toHaveAttribute('inert');
|
|
600
|
-
expect(getActiveDialog()).toBe(confirmDialog);
|
|
601
|
-
expect(confirmDialog).toHaveAccessibleName('Confirm');
|
|
602
|
-
|
|
603
|
-
// The previous sheet is covered, not exiting: neither transform nor
|
|
604
|
-
// opacity completion may release it before the new entrance completes.
|
|
605
|
-
finishSheetTransition(detailsSheet, 'transform');
|
|
606
|
-
finishSheetTransition(detailsSheet, 'opacity');
|
|
607
|
-
expect(detailsSheet).not.toHaveAttribute('hidden');
|
|
608
|
-
|
|
609
|
-
finishSheetTransition(confirmSheet, 'transform');
|
|
610
|
-
expect(detailsSheet).not.toHaveAttribute('hidden');
|
|
611
|
-
|
|
612
|
-
finishSheetTransition(detailsSheet, 'opacity');
|
|
613
|
-
|
|
614
|
-
expect(detailsSheet).toHaveAttribute('hidden');
|
|
615
|
-
expect(confirmSheet).not.toHaveAttribute('hidden');
|
|
616
|
-
expect(openDialogs()).toEqual([confirmDialog]);
|
|
617
|
-
expect(getActiveDialog()).toBe(confirmDialog);
|
|
618
|
-
|
|
619
|
-
fireEvent.click(screen.getByRole('button', {name: 'Back'}));
|
|
620
|
-
|
|
621
|
-
expect(detailsSheet).not.toHaveAttribute('hidden');
|
|
622
|
-
expect(confirmSheet).not.toHaveAttribute('hidden');
|
|
623
|
-
expect(confirmSheet).toHaveAttribute('inert');
|
|
624
|
-
finishSheetTransition(detailsSheet, 'transform');
|
|
625
|
-
expect(confirmSheet).not.toHaveAttribute('hidden');
|
|
626
|
-
finishSheetTransition(confirmSheet, 'opacity');
|
|
627
|
-
|
|
628
|
-
expect(detailsSheet).not.toHaveAttribute('hidden');
|
|
629
|
-
expect(confirmSheet).toHaveAttribute('hidden');
|
|
630
|
-
expect(openDialogs()).toEqual([detailsDialog]);
|
|
631
|
-
expect(getActiveDialog()).toBe(detailsDialog);
|
|
632
|
-
});
|
|
633
|
-
|
|
634
|
-
it('moves a taller previous sheet down while the shorter new sheet enters, then waits for both', () => {
|
|
635
|
-
render(<Flow />);
|
|
636
|
-
fireEvent.click(screen.getByRole('button', {name: 'Start flow'}));
|
|
637
|
-
const detailsSheet = getSheetLayer('details-sheet');
|
|
638
|
-
const confirmSheet = getSheetLayer('confirm-sheet');
|
|
639
|
-
const detailsPanel = getSheetPanel(detailsSheet);
|
|
640
|
-
const confirmPanel = getSheetPanel(confirmSheet);
|
|
641
|
-
mockSheetTop(detailsPanel, 100);
|
|
642
|
-
mockSheetTop(confirmPanel, 300);
|
|
643
|
-
|
|
644
|
-
fireEvent.click(screen.getByRole('button', {name: 'Continue'}));
|
|
645
|
-
|
|
646
|
-
expect(detailsSheet).not.toHaveAttribute('hidden');
|
|
647
|
-
expect(detailsPanel).toHaveStyle({transform: 'translateY(200px)'});
|
|
648
|
-
|
|
649
|
-
// The incoming entrance may finish first, but opacity cannot hide the
|
|
650
|
-
// retained sheet until its concurrent alignment also completes.
|
|
651
|
-
finishSheetTransition(confirmSheet, 'transform');
|
|
652
|
-
finishSheetTransition(detailsSheet, 'opacity');
|
|
653
|
-
expect(detailsSheet).not.toHaveAttribute('hidden');
|
|
654
|
-
finishSheetTransition(detailsSheet, 'transform');
|
|
655
|
-
expect(detailsSheet).not.toHaveAttribute('hidden');
|
|
656
|
-
expect(detailsPanel).toHaveStyle({transform: 'translateY(200px)'});
|
|
657
|
-
finishSheetTransition(detailsSheet, 'opacity');
|
|
658
|
-
|
|
659
|
-
expect(detailsSheet).toHaveAttribute('hidden');
|
|
660
|
-
expect(confirmSheet).not.toHaveAttribute('hidden');
|
|
661
|
-
});
|
|
662
|
-
|
|
663
|
-
it('waits for the incoming entrance when top-edge alignment finishes first', () => {
|
|
664
|
-
render(<Flow />);
|
|
665
|
-
fireEvent.click(screen.getByRole('button', {name: 'Start flow'}));
|
|
666
|
-
const detailsSheet = getSheetLayer('details-sheet');
|
|
667
|
-
const confirmSheet = getSheetLayer('confirm-sheet');
|
|
668
|
-
const detailsPanel = getSheetPanel(detailsSheet);
|
|
669
|
-
mockSheetTop(detailsPanel, 100);
|
|
670
|
-
mockSheetTop(getSheetPanel(confirmSheet), 300);
|
|
671
|
-
|
|
672
|
-
fireEvent.click(screen.getByRole('button', {name: 'Continue'}));
|
|
673
|
-
expect(detailsPanel).toHaveStyle({transform: 'translateY(200px)'});
|
|
674
|
-
|
|
675
|
-
finishSheetTransition(detailsSheet, 'transform');
|
|
676
|
-
finishSheetTransition(detailsSheet, 'opacity');
|
|
677
|
-
expect(detailsSheet).not.toHaveAttribute('hidden');
|
|
678
|
-
finishSheetTransition(confirmSheet, 'transform');
|
|
679
|
-
finishSheetTransition(detailsSheet, 'opacity');
|
|
680
|
-
|
|
681
|
-
expect(detailsSheet).toHaveAttribute('hidden');
|
|
682
|
-
expect(confirmSheet).not.toHaveAttribute('hidden');
|
|
683
|
-
});
|
|
684
|
-
|
|
685
|
-
it('replaces an unfinished outgoing sheet during rapid navigation', () => {
|
|
686
|
-
render(<Flow />);
|
|
687
|
-
fireEvent.click(screen.getByRole('button', {name: 'Start flow'}));
|
|
688
|
-
const detailsSheet = getSheetLayer('details-sheet');
|
|
689
|
-
const confirmSheet = getSheetLayer('confirm-sheet');
|
|
690
|
-
|
|
691
|
-
fireEvent.click(screen.getByRole('button', {name: 'Continue'}));
|
|
692
|
-
fireEvent.click(screen.getByRole('button', {name: 'Back'}));
|
|
693
|
-
|
|
694
|
-
expect(detailsSheet).not.toHaveAttribute('hidden');
|
|
695
|
-
expect(detailsSheet).not.toHaveAttribute('inert');
|
|
696
|
-
expect(confirmSheet).not.toHaveAttribute('hidden');
|
|
697
|
-
expect(confirmSheet).toHaveAttribute('inert');
|
|
698
|
-
// The returning sheet re-presents above the one it replaces: a modal
|
|
699
|
-
// dialog beneath another modal dialog is inert in the top layer.
|
|
700
|
-
expect(openDialogs()).toHaveLength(2);
|
|
701
|
-
expect(HTMLDialogElement.prototype.showModal).toHaveBeenCalledTimes(3);
|
|
702
|
-
expect(getActiveDialog()).toBe(getSheetDialog('details-sheet'));
|
|
703
|
-
|
|
704
|
-
finishSheetTransition(confirmSheet, 'opacity');
|
|
705
|
-
expect(detailsSheet).not.toHaveAttribute('hidden');
|
|
706
|
-
expect(confirmSheet).toHaveAttribute('hidden');
|
|
707
|
-
expect(openDialogs()).toEqual([getSheetDialog('details-sheet')]);
|
|
708
|
-
});
|
|
709
|
-
|
|
710
|
-
it('re-presents a returning sheet with its backdrop cleared instantly, so the handed-over scrim is never doubled', () => {
|
|
711
|
-
// Going back while the previous sheet is still fading re-presents its
|
|
712
|
-
// dialog beneath the new scrim owner. That dialog closed with its
|
|
713
|
-
// backdrop at 1; the clear must apply with the scrim transition pinned
|
|
714
|
-
// to 0s across showModal(), or the backdrop eases 1 -> 0 on top of the
|
|
715
|
-
// other dialog's and the page is dimmed twice for a dozen frames.
|
|
716
|
-
const atPresent: {label: string; duration: string; opacity: string}[] = [];
|
|
717
|
-
HTMLDialogElement.prototype.showModal = vi.fn(function (
|
|
718
|
-
this: HTMLDialogElement,
|
|
719
|
-
) {
|
|
720
|
-
this.setAttribute('open', '');
|
|
721
|
-
modalDialogs.add(this);
|
|
722
|
-
atPresent.push({
|
|
723
|
-
label: this.getAttribute('aria-label') ?? '?',
|
|
724
|
-
duration: this.style.getPropertyValue('--_sheet-scrim-duration'),
|
|
725
|
-
opacity: this.style.getPropertyValue('--_sheet-scrim-opacity'),
|
|
726
|
-
});
|
|
727
|
-
});
|
|
728
|
-
render(<Flow />);
|
|
729
|
-
fireEvent.click(screen.getByRole('button', {name: 'Start flow'}));
|
|
730
|
-
fireEvent.click(screen.getByRole('button', {name: 'Continue'}));
|
|
731
|
-
const detailsDialog = getSheetDialog('details-sheet');
|
|
732
|
-
const confirmDialog = getSheetDialog('confirm-sheet');
|
|
733
|
-
// Mid-handoff: Details retains the scrim, Confirm entered clear.
|
|
734
|
-
expect(detailsDialog).toHaveStyle({'--_sheet-scrim-opacity': '1'});
|
|
735
|
-
expect(confirmDialog).toHaveStyle({'--_sheet-scrim-opacity': '0'});
|
|
736
|
-
|
|
737
|
-
fireEvent.click(screen.getByRole('button', {name: 'Back'}));
|
|
738
|
-
|
|
739
|
-
// Confirm, now retained, took the scrim over; Details re-presented clear
|
|
740
|
-
// with the transition pinned for the present, and the pin lifted after.
|
|
741
|
-
expect(atPresent.at(-1)).toEqual({
|
|
742
|
-
label: 'Details',
|
|
743
|
-
duration: '0s',
|
|
744
|
-
opacity: '0',
|
|
745
|
-
});
|
|
746
|
-
expect(detailsDialog).toHaveStyle({'--_sheet-scrim-opacity': '0'});
|
|
747
|
-
expect(
|
|
748
|
-
detailsDialog.style.getPropertyValue('--_sheet-scrim-duration'),
|
|
749
|
-
).toBe('');
|
|
750
|
-
expect(confirmDialog).toHaveStyle({'--_sheet-scrim-opacity': '1'});
|
|
751
|
-
expect(
|
|
752
|
-
confirmDialog.style.getPropertyValue('--_sheet-scrim-duration'),
|
|
753
|
-
).toBe('');
|
|
754
|
-
// Exactly one dialog carries the scrim.
|
|
755
|
-
expect(
|
|
756
|
-
openDialogs().filter(
|
|
757
|
-
dialog =>
|
|
758
|
-
dialog.style.getPropertyValue('--_sheet-scrim-opacity') === '1',
|
|
759
|
-
),
|
|
760
|
-
).toEqual([confirmDialog]);
|
|
761
|
-
});
|
|
762
|
-
|
|
763
|
-
it('ignores late scrim updates from an outgoing sheet gesture', () => {
|
|
764
|
-
render(<Flow />);
|
|
765
|
-
fireEvent.click(screen.getByRole('button', {name: 'Start flow'}));
|
|
766
|
-
|
|
767
|
-
const detailsPanel = getSheetPanel(getSheetLayer('details-sheet'));
|
|
768
|
-
mockSheetTop(detailsPanel, 100);
|
|
769
|
-
const handle = detailsPanel.firstElementChild;
|
|
770
|
-
if (!(handle instanceof HTMLElement)) {
|
|
771
|
-
throw new Error('sheet handle not found');
|
|
772
|
-
}
|
|
773
|
-
|
|
774
|
-
fireEvent.pointerDown(handle, {
|
|
775
|
-
pointerId: 1,
|
|
776
|
-
clientY: 100,
|
|
777
|
-
timeStamp: 0,
|
|
778
|
-
button: 0,
|
|
779
|
-
isPrimary: true,
|
|
780
|
-
});
|
|
781
|
-
fireEvent.pointerMove(handle, {
|
|
782
|
-
pointerId: 1,
|
|
783
|
-
clientY: 600,
|
|
784
|
-
timeStamp: 16,
|
|
785
|
-
});
|
|
786
|
-
const detailsDialog = getSheetDialog('details-sheet');
|
|
787
|
-
expect(
|
|
788
|
-
detailsDialog.style.getPropertyValue('--_sheet-scrim-opacity'),
|
|
789
|
-
).not.toBe('1');
|
|
790
|
-
|
|
791
|
-
// The retained dialog owns the flow's scrim through the handoff; the
|
|
792
|
-
// entering dialog's backdrop stays clear above it.
|
|
793
|
-
fireEvent.click(screen.getByRole('button', {name: 'Continue'}));
|
|
794
|
-
expect(detailsDialog).toHaveStyle({'--_sheet-scrim-opacity': '1'});
|
|
795
|
-
expect(getActiveDialog()).toHaveStyle({'--_sheet-scrim-opacity': '0'});
|
|
796
|
-
|
|
797
|
-
fireEvent.pointerMove(handle, {
|
|
798
|
-
pointerId: 1,
|
|
799
|
-
clientY: 680,
|
|
800
|
-
timeStamp: 32,
|
|
801
|
-
});
|
|
802
|
-
expect(detailsDialog).toHaveStyle({'--_sheet-scrim-opacity': '1'});
|
|
803
|
-
expect(getActiveDialog()).toHaveStyle({'--_sheet-scrim-opacity': '0'});
|
|
804
|
-
});
|
|
805
|
-
|
|
806
|
-
it('keeps an active handoff when a consumer callback ref changes', () => {
|
|
807
|
-
render(<CallbackRefFlow />);
|
|
808
|
-
const detailsSheet = getSheetLayer('callback-details-sheet');
|
|
809
|
-
const confirmSheet = getSheetLayer('callback-confirm-sheet');
|
|
810
|
-
|
|
811
|
-
fireEvent.click(
|
|
812
|
-
screen.getByRole('button', {name: 'Continue with callback ref'}),
|
|
813
|
-
);
|
|
814
|
-
expect(detailsSheet).not.toHaveAttribute('hidden');
|
|
815
|
-
expect(detailsSheet).toHaveAttribute('inert');
|
|
816
|
-
expect(confirmSheet).not.toHaveAttribute('hidden');
|
|
817
|
-
|
|
818
|
-
fireEvent.click(screen.getByRole('button', {name: 'Rerender parent'}));
|
|
819
|
-
|
|
820
|
-
expect(detailsSheet).not.toHaveAttribute('hidden');
|
|
821
|
-
expect(detailsSheet).toHaveAttribute('inert');
|
|
822
|
-
expect(confirmSheet).not.toHaveAttribute('hidden');
|
|
823
|
-
});
|
|
824
|
-
|
|
825
|
-
it("dismisses the flow from the active sheet's scrim", () => {
|
|
826
|
-
render(<Flow />);
|
|
827
|
-
fireEvent.click(screen.getByRole('button', {name: 'Start flow'}));
|
|
828
|
-
const activeDialog = getActiveDialog();
|
|
829
|
-
|
|
830
|
-
fireEvent.click(activeDialog);
|
|
831
|
-
|
|
832
|
-
const outgoingSheet = getSheetLayer('details-sheet');
|
|
833
|
-
expect(outgoingSheet).not.toHaveAttribute('hidden');
|
|
834
|
-
expect(outgoingSheet).toHaveAttribute('inert');
|
|
835
|
-
expect(activeDialog).toHaveStyle({'--_sheet-scrim-opacity': '0'});
|
|
836
|
-
// Scroll stays locked through the exit.
|
|
837
|
-
expect(isScrollLockEngaged()).toBe(true);
|
|
838
|
-
|
|
839
|
-
finishSheetTransition(outgoingSheet, 'transform');
|
|
840
|
-
|
|
841
|
-
expect(activeDialog).not.toHaveAttribute('open');
|
|
842
|
-
expect(isScrollLockEngaged()).toBe(false);
|
|
843
|
-
});
|
|
844
|
-
|
|
845
|
-
it('releases the modal layer when the closing sheet unmounts immediately', () => {
|
|
846
|
-
render(<ConditionalFlow />);
|
|
847
|
-
fireEvent.click(
|
|
848
|
-
screen.getByRole('button', {name: 'Start conditional flow'}),
|
|
849
|
-
);
|
|
850
|
-
const dialog = screen.getByRole('dialog', {
|
|
851
|
-
name: 'Conditional details',
|
|
852
|
-
});
|
|
853
|
-
|
|
854
|
-
fireEvent.keyDown(dialog, {key: 'Escape'});
|
|
855
|
-
|
|
856
|
-
expect(dialog).not.toHaveAttribute('open');
|
|
857
|
-
expect(isScrollLockEngaged()).toBe(false);
|
|
858
|
-
});
|
|
859
|
-
|
|
860
|
-
it('keeps a sheet dialog inline and opens it modally', () => {
|
|
861
|
-
render(
|
|
862
|
-
<div
|
|
863
|
-
data-testid="clipping-ancestor"
|
|
864
|
-
style={{overflow: 'hidden', transform: 'translateY(100px)'}}>
|
|
865
|
-
<BottomSheetSwitcher
|
|
866
|
-
activeSheet="details"
|
|
867
|
-
onActiveSheetChange={() => {}}>
|
|
868
|
-
<BottomSheet sheetId="details" label="Inline modal details">
|
|
869
|
-
Content
|
|
870
|
-
</BottomSheet>
|
|
871
|
-
</BottomSheetSwitcher>
|
|
872
|
-
</div>,
|
|
873
|
-
);
|
|
874
|
-
|
|
875
|
-
const clippingAncestor = screen.getByTestId('clipping-ancestor');
|
|
876
|
-
const dialog = screen.getByRole('dialog', {name: 'Inline modal details'});
|
|
877
|
-
expect(clippingAncestor).toContainElement(dialog);
|
|
878
|
-
expect(dialog).toHaveAttribute('open');
|
|
879
|
-
expect(HTMLDialogElement.prototype.showModal).toHaveBeenCalledTimes(1);
|
|
880
|
-
});
|
|
881
|
-
|
|
882
|
-
it('focuses the panel of a modal sheet with no tabbable controls and leaves Tab to the native modal dialog', () => {
|
|
883
|
-
render(
|
|
884
|
-
<>
|
|
885
|
-
<button type="button">Background action</button>
|
|
886
|
-
<BottomSheetSwitcher
|
|
887
|
-
activeSheet="details"
|
|
888
|
-
onActiveSheetChange={() => {}}>
|
|
889
|
-
<BottomSheet sheetId="details" label="Read-only details">
|
|
890
|
-
Read-only content
|
|
891
|
-
</BottomSheet>
|
|
892
|
-
</BottomSheetSwitcher>
|
|
893
|
-
</>,
|
|
894
|
-
);
|
|
895
|
-
|
|
896
|
-
const dialog = screen.getByRole<HTMLDialogElement>('dialog', {
|
|
897
|
-
name: 'Read-only details',
|
|
898
|
-
});
|
|
899
|
-
const panel = getSheetPanel(dialog);
|
|
900
|
-
expect(panel).toHaveFocus();
|
|
901
|
-
// Containment is the browser's: the dialog was presented with showModal(),
|
|
902
|
-
// which makes everything outside it inert, so no script intercepts Tab.
|
|
903
|
-
// BottomSheetNativeModal.a11y.browser.spec.ts proves the key in engines.
|
|
904
|
-
expect(modalDialogs.has(dialog)).toBe(true);
|
|
905
|
-
expect(dialog).toHaveAttribute('aria-modal', 'true');
|
|
906
|
-
expect(fireEvent.keyDown(panel, {key: 'Tab'})).toBe(true);
|
|
907
|
-
expect(panel).toHaveFocus();
|
|
908
|
-
});
|
|
909
|
-
|
|
910
|
-
it('can coordinate a non-modal flow without rendering a scrim', () => {
|
|
911
|
-
render(
|
|
912
|
-
<BottomSheetSwitcher
|
|
913
|
-
activeSheet="details"
|
|
914
|
-
onActiveSheetChange={() => {}}
|
|
915
|
-
hasScrim={false}>
|
|
916
|
-
<BottomSheet sheetId="details" label="Details">
|
|
917
|
-
Content
|
|
918
|
-
</BottomSheet>
|
|
919
|
-
</BottomSheetSwitcher>,
|
|
920
|
-
);
|
|
921
|
-
|
|
922
|
-
const dialog = getActiveDialog();
|
|
923
|
-
expect(dialog).not.toHaveAttribute('aria-modal');
|
|
924
|
-
expect(dialog).toHaveAttribute('open');
|
|
925
|
-
expect(HTMLDialogElement.prototype.show).toHaveBeenCalledTimes(1);
|
|
926
|
-
expect(HTMLDialogElement.prototype.showModal).not.toHaveBeenCalled();
|
|
927
|
-
expect(isScrollLockEngaged()).toBe(false);
|
|
928
|
-
});
|
|
929
|
-
|
|
930
|
-
it('requests activeSheet=null when the active sheet dismisses', () => {
|
|
931
|
-
const onActiveSheetChange = vi.fn();
|
|
932
|
-
render(
|
|
933
|
-
<BottomSheetSwitcher
|
|
934
|
-
activeSheet="details"
|
|
935
|
-
onActiveSheetChange={onActiveSheetChange}>
|
|
936
|
-
<BottomSheet sheetId="details" label="Details">
|
|
937
|
-
Content
|
|
938
|
-
</BottomSheet>
|
|
939
|
-
<BottomSheet sheetId="confirm" label="Confirm">
|
|
940
|
-
Content
|
|
941
|
-
</BottomSheet>
|
|
942
|
-
</BottomSheetSwitcher>,
|
|
943
|
-
);
|
|
944
|
-
|
|
945
|
-
fireEvent.keyDown(screen.getByRole('dialog', {name: 'Details'}), {
|
|
946
|
-
key: 'Escape',
|
|
947
|
-
});
|
|
948
|
-
|
|
949
|
-
expect(onActiveSheetChange).toHaveBeenCalledWith(null);
|
|
950
|
-
});
|
|
951
|
-
|
|
952
|
-
it('honors purpose=form for a switcher-managed sheet', () => {
|
|
953
|
-
const onActiveSheetChange = vi.fn();
|
|
954
|
-
render(
|
|
955
|
-
<BottomSheetSwitcher
|
|
956
|
-
activeSheet="details"
|
|
957
|
-
onActiveSheetChange={onActiveSheetChange}>
|
|
958
|
-
<BottomSheet sheetId="details" label="Edit details" purpose="form">
|
|
959
|
-
Content
|
|
960
|
-
</BottomSheet>
|
|
961
|
-
</BottomSheetSwitcher>,
|
|
962
|
-
);
|
|
963
|
-
const dialog = getActiveDialog();
|
|
964
|
-
const panel = getSheetPanel(dialog);
|
|
965
|
-
const handle = panel.firstElementChild;
|
|
966
|
-
if (!(handle instanceof HTMLElement)) {
|
|
967
|
-
throw new Error('sheet handle not found');
|
|
968
|
-
}
|
|
969
|
-
|
|
970
|
-
fireEvent.click(dialog);
|
|
971
|
-
fireEvent.pointerDown(handle, {
|
|
972
|
-
pointerId: 1,
|
|
973
|
-
clientY: 0,
|
|
974
|
-
button: 0,
|
|
975
|
-
isPrimary: true,
|
|
976
|
-
});
|
|
977
|
-
fireEvent.pointerMove(handle, {pointerId: 1, clientY: 120});
|
|
978
|
-
fireEvent.pointerUp(handle, {pointerId: 1, clientY: 120});
|
|
979
|
-
|
|
980
|
-
expect(onActiveSheetChange).not.toHaveBeenCalled();
|
|
981
|
-
expect(dialog).toHaveStyle({'--_sheet-scrim-opacity': '1'});
|
|
982
|
-
|
|
983
|
-
fireEvent.keyDown(dialog, {key: 'Escape'});
|
|
984
|
-
fireEvent(dialog, new Event('cancel', {cancelable: true}));
|
|
985
|
-
|
|
986
|
-
expect(onActiveSheetChange).toHaveBeenCalledTimes(2);
|
|
987
|
-
expect(onActiveSheetChange).toHaveBeenNthCalledWith(1, null);
|
|
988
|
-
expect(onActiveSheetChange).toHaveBeenNthCalledWith(2, null);
|
|
989
|
-
});
|
|
990
|
-
|
|
991
|
-
it('keeps the flow open when a file picker inside a step is dismissed, and still closes on its own close request', () => {
|
|
992
|
-
// A file input fires a bubbling `cancel` at itself when its picker is
|
|
993
|
-
// dismissed. It bubbles to the step's own dialog, which leaves it alone.
|
|
994
|
-
const onActiveSheetChange = vi.fn();
|
|
995
|
-
render(
|
|
996
|
-
<BottomSheetSwitcher
|
|
997
|
-
activeSheet="upload"
|
|
998
|
-
onActiveSheetChange={onActiveSheetChange}>
|
|
999
|
-
<BottomSheet sheetId="upload" label="Upload" purpose="form">
|
|
1000
|
-
<input type="file" aria-label="Picture" />
|
|
1001
|
-
</BottomSheet>
|
|
1002
|
-
</BottomSheetSwitcher>,
|
|
1003
|
-
);
|
|
1004
|
-
|
|
1005
|
-
const fromPicker = new Event('cancel', {bubbles: true, cancelable: true});
|
|
1006
|
-
fireEvent(screen.getByLabelText('Picture'), fromPicker);
|
|
1007
|
-
|
|
1008
|
-
expect(onActiveSheetChange).not.toHaveBeenCalled();
|
|
1009
|
-
expect(fromPicker.defaultPrevented).toBe(false);
|
|
1010
|
-
|
|
1011
|
-
fireEvent(getActiveDialog(), new Event('cancel', {cancelable: true}));
|
|
1012
|
-
|
|
1013
|
-
expect(onActiveSheetChange).toHaveBeenCalledTimes(1);
|
|
1014
|
-
expect(onActiveSheetChange).toHaveBeenCalledWith(null);
|
|
1015
|
-
});
|
|
1016
|
-
|
|
1017
|
-
it('honors purpose=required for a switcher-managed sheet', () => {
|
|
1018
|
-
const onActiveSheetChange = vi.fn();
|
|
1019
|
-
render(
|
|
1020
|
-
<BottomSheetSwitcher
|
|
1021
|
-
activeSheet="details"
|
|
1022
|
-
onActiveSheetChange={onActiveSheetChange}>
|
|
1023
|
-
<BottomSheet
|
|
1024
|
-
sheetId="details"
|
|
1025
|
-
label="Required details"
|
|
1026
|
-
purpose="required">
|
|
1027
|
-
Content
|
|
1028
|
-
</BottomSheet>
|
|
1029
|
-
</BottomSheetSwitcher>,
|
|
1030
|
-
);
|
|
1031
|
-
const dialog = screen.getByRole('alertdialog');
|
|
1032
|
-
const panel = getSheetPanel(dialog);
|
|
1033
|
-
const handle = panel.firstElementChild;
|
|
1034
|
-
if (!(handle instanceof HTMLElement)) {
|
|
1035
|
-
throw new Error('sheet handle not found');
|
|
1036
|
-
}
|
|
1037
|
-
|
|
1038
|
-
fireEvent.click(dialog);
|
|
1039
|
-
fireEvent.keyDown(dialog, {key: 'Escape'});
|
|
1040
|
-
fireEvent(dialog, new Event('cancel', {cancelable: true}));
|
|
1041
|
-
fireEvent.pointerDown(handle, {
|
|
1042
|
-
pointerId: 1,
|
|
1043
|
-
clientY: 0,
|
|
1044
|
-
button: 0,
|
|
1045
|
-
isPrimary: true,
|
|
1046
|
-
});
|
|
1047
|
-
fireEvent.pointerMove(handle, {pointerId: 1, clientY: 120});
|
|
1048
|
-
fireEvent.pointerUp(handle, {pointerId: 1, clientY: 120});
|
|
1049
|
-
|
|
1050
|
-
expect(onActiveSheetChange).not.toHaveBeenCalled();
|
|
1051
|
-
expect(dialog).toHaveStyle({'--_sheet-scrim-opacity': '1'});
|
|
1052
|
-
});
|
|
1053
|
-
|
|
1054
|
-
it('restores the active sheet when a context menu interrupts its drag', () => {
|
|
1055
|
-
const onActiveSheetChange = vi.fn();
|
|
1056
|
-
render(
|
|
1057
|
-
<BottomSheetSwitcher
|
|
1058
|
-
activeSheet="details"
|
|
1059
|
-
onActiveSheetChange={onActiveSheetChange}>
|
|
1060
|
-
<BottomSheet sheetId="details" label="Details">
|
|
1061
|
-
Content
|
|
1062
|
-
</BottomSheet>
|
|
1063
|
-
</BottomSheetSwitcher>,
|
|
1064
|
-
);
|
|
1065
|
-
const dialog = getActiveDialog();
|
|
1066
|
-
const panel = getSheetPanel(dialog);
|
|
1067
|
-
const handle = panel.firstElementChild;
|
|
1068
|
-
if (!(handle instanceof HTMLElement)) {
|
|
1069
|
-
throw new Error('sheet handle not found');
|
|
1070
|
-
}
|
|
1071
|
-
|
|
1072
|
-
fireEvent.pointerDown(handle, {
|
|
1073
|
-
pointerId: 1,
|
|
1074
|
-
clientY: 0,
|
|
1075
|
-
button: 0,
|
|
1076
|
-
isPrimary: true,
|
|
1077
|
-
});
|
|
1078
|
-
fireEvent.pointerMove(handle, {pointerId: 1, clientY: 300});
|
|
1079
|
-
expect(panel.style.transform).toBe('translateY(300px)');
|
|
1080
|
-
|
|
1081
|
-
expect(fireEvent.contextMenu(handle)).toBe(false);
|
|
1082
|
-
|
|
1083
|
-
expect(panel.style.transform).toBe('');
|
|
1084
|
-
expect(dialog).toHaveStyle({'--_sheet-scrim-opacity': '1'});
|
|
1085
|
-
expect(onActiveSheetChange).not.toHaveBeenCalled();
|
|
1086
|
-
});
|
|
1087
|
-
|
|
1088
|
-
it('ignores Escape while an IME composition is active', () => {
|
|
1089
|
-
const onActiveSheetChange = vi.fn();
|
|
1090
|
-
render(
|
|
1091
|
-
<BottomSheetSwitcher
|
|
1092
|
-
activeSheet="details"
|
|
1093
|
-
onActiveSheetChange={onActiveSheetChange}>
|
|
1094
|
-
<BottomSheet sheetId="details" label="Details">
|
|
1095
|
-
Content
|
|
1096
|
-
</BottomSheet>
|
|
1097
|
-
</BottomSheetSwitcher>,
|
|
1098
|
-
);
|
|
1099
|
-
|
|
1100
|
-
const dialog = screen.getByRole('dialog', {name: 'Details'});
|
|
1101
|
-
fireEvent.keyDown(dialog, {key: 'Escape', isComposing: true});
|
|
1102
|
-
fireEvent.keyDown(dialog, {key: 'Escape', keyCode: 229});
|
|
1103
|
-
|
|
1104
|
-
expect(onActiveSheetChange).not.toHaveBeenCalled();
|
|
1105
|
-
});
|
|
1106
|
-
|
|
1107
|
-
it('lets a nested focus trap handle Escape before the switcher', () => {
|
|
1108
|
-
const onActiveSheetChange = vi.fn();
|
|
1109
|
-
const onNestedEscape = vi.fn();
|
|
1110
|
-
render(
|
|
1111
|
-
<BottomSheetSwitcher
|
|
1112
|
-
activeSheet="details"
|
|
1113
|
-
onActiveSheetChange={onActiveSheetChange}>
|
|
1114
|
-
<BottomSheet sheetId="details" label="Details">
|
|
1115
|
-
<NestedEscapeTrap onEscape={onNestedEscape} />
|
|
1116
|
-
</BottomSheet>
|
|
1117
|
-
</BottomSheetSwitcher>,
|
|
1118
|
-
);
|
|
1119
|
-
|
|
1120
|
-
fireEvent.keyDown(screen.getByTestId('nested-escape-trap'), {
|
|
1121
|
-
key: 'Escape',
|
|
1122
|
-
});
|
|
1123
|
-
|
|
1124
|
-
expect(onNestedEscape).toHaveBeenCalledTimes(1);
|
|
1125
|
-
expect(onActiveSheetChange).not.toHaveBeenCalled();
|
|
1126
|
-
});
|
|
1127
|
-
|
|
1128
|
-
it('preserves the exported focus-trap Escape signal for a modal switcher', () => {
|
|
1129
|
-
expect(hasActiveFocusTrapEscape()).toBe(false);
|
|
1130
|
-
|
|
1131
|
-
const {unmount} = render(
|
|
1132
|
-
<BottomSheetSwitcher activeSheet="details" onActiveSheetChange={() => {}}>
|
|
1133
|
-
<BottomSheet sheetId="details" label="Details">
|
|
1134
|
-
Content
|
|
1135
|
-
</BottomSheet>
|
|
1136
|
-
</BottomSheetSwitcher>,
|
|
1137
|
-
);
|
|
1138
|
-
|
|
1139
|
-
expect(hasActiveFocusTrapEscape()).toBe(true);
|
|
1140
|
-
unmount();
|
|
1141
|
-
expect(hasActiveFocusTrapEscape()).toBe(false);
|
|
1142
|
-
});
|
|
1143
|
-
|
|
1144
|
-
it('dismisses a non-modal flow when a consumer stops Escape propagation', () => {
|
|
1145
|
-
const onActiveSheetChange = vi.fn();
|
|
1146
|
-
const onKeyDown = vi.fn((event: ReactKeyboardEvent) => {
|
|
1147
|
-
event.stopPropagation();
|
|
1148
|
-
});
|
|
1149
|
-
render(
|
|
1150
|
-
<BottomSheetSwitcher
|
|
1151
|
-
activeSheet="details"
|
|
1152
|
-
onActiveSheetChange={onActiveSheetChange}
|
|
1153
|
-
hasScrim={false}
|
|
1154
|
-
onKeyDown={onKeyDown}>
|
|
1155
|
-
<BottomSheet sheetId="details" label="Details">
|
|
1156
|
-
Content
|
|
1157
|
-
</BottomSheet>
|
|
1158
|
-
</BottomSheetSwitcher>,
|
|
1159
|
-
);
|
|
1160
|
-
|
|
1161
|
-
fireEvent.keyDown(getActiveDialog(), {key: 'Escape'});
|
|
1162
|
-
|
|
1163
|
-
expect(onKeyDown).toHaveBeenCalledTimes(1);
|
|
1164
|
-
expect(onActiveSheetChange).toHaveBeenCalledWith(null);
|
|
1165
|
-
});
|
|
1166
|
-
|
|
1167
|
-
it('honors a consumer that prevents the default Escape dismissal', () => {
|
|
1168
|
-
const onActiveSheetChange = vi.fn();
|
|
1169
|
-
const onKeyDown = vi.fn((event: ReactKeyboardEvent) => {
|
|
1170
|
-
event.preventDefault();
|
|
1171
|
-
});
|
|
1172
|
-
render(
|
|
1173
|
-
<BottomSheetSwitcher
|
|
1174
|
-
activeSheet="details"
|
|
1175
|
-
onActiveSheetChange={onActiveSheetChange}
|
|
1176
|
-
hasScrim={false}
|
|
1177
|
-
onKeyDown={onKeyDown}>
|
|
1178
|
-
<BottomSheet sheetId="details" label="Details">
|
|
1179
|
-
Content
|
|
1180
|
-
</BottomSheet>
|
|
1181
|
-
</BottomSheetSwitcher>,
|
|
1182
|
-
);
|
|
1183
|
-
|
|
1184
|
-
fireEvent.keyDown(getActiveDialog(), {key: 'Escape'});
|
|
1185
|
-
|
|
1186
|
-
expect(onKeyDown).toHaveBeenCalledTimes(1);
|
|
1187
|
-
expect(onActiveSheetChange).not.toHaveBeenCalled();
|
|
1188
|
-
});
|
|
1189
|
-
|
|
1190
|
-
it('lets a nested registered layer handle Escape before a non-modal switcher', () => {
|
|
1191
|
-
const onActiveSheetChange = vi.fn();
|
|
1192
|
-
const onNestedDismiss = vi.fn();
|
|
1193
|
-
render(
|
|
1194
|
-
<BottomSheetSwitcher
|
|
1195
|
-
activeSheet="details"
|
|
1196
|
-
onActiveSheetChange={onActiveSheetChange}
|
|
1197
|
-
hasScrim={false}>
|
|
1198
|
-
<BottomSheet sheetId="details" label="Details">
|
|
1199
|
-
<NestedDismissibleLayer onDismiss={onNestedDismiss} />
|
|
1200
|
-
</BottomSheet>
|
|
1201
|
-
</BottomSheetSwitcher>,
|
|
1202
|
-
);
|
|
1203
|
-
|
|
1204
|
-
const trigger = screen.getByRole('button', {name: 'Nested layer open'});
|
|
1205
|
-
trigger.focus();
|
|
1206
|
-
fireEvent.keyDown(trigger, {key: 'Escape'});
|
|
1207
|
-
|
|
1208
|
-
expect(onNestedDismiss).toHaveBeenCalledTimes(1);
|
|
1209
|
-
expect(onActiveSheetChange).not.toHaveBeenCalled();
|
|
1210
|
-
|
|
1211
|
-
fireEvent.keyDown(document, {key: 'Escape'});
|
|
1212
|
-
|
|
1213
|
-
expect(onNestedDismiss).toHaveBeenCalledTimes(1);
|
|
1214
|
-
expect(onActiveSheetChange).toHaveBeenCalledWith(null);
|
|
1215
|
-
});
|
|
1216
|
-
|
|
1217
|
-
it('keeps a non-modal switcher open when a platform close targets it under a nested layer', () => {
|
|
1218
|
-
const onActiveSheetChange = vi.fn();
|
|
1219
|
-
const onNestedDismiss = vi.fn();
|
|
1220
|
-
render(
|
|
1221
|
-
<BottomSheetSwitcher
|
|
1222
|
-
activeSheet="details"
|
|
1223
|
-
onActiveSheetChange={onActiveSheetChange}
|
|
1224
|
-
hasScrim={false}>
|
|
1225
|
-
<BottomSheet sheetId="details" label="Details">
|
|
1226
|
-
<NestedDismissibleLayer onDismiss={onNestedDismiss} />
|
|
1227
|
-
</BottomSheet>
|
|
1228
|
-
</BottomSheetSwitcher>,
|
|
1229
|
-
);
|
|
1230
|
-
|
|
1231
|
-
const event = new Event('cancel', {cancelable: true});
|
|
1232
|
-
getActiveDialog().dispatchEvent(event);
|
|
1233
|
-
|
|
1234
|
-
expect(event.defaultPrevented).toBe(true);
|
|
1235
|
-
expect(onNestedDismiss).not.toHaveBeenCalled();
|
|
1236
|
-
expect(onActiveSheetChange).not.toHaveBeenCalled();
|
|
1237
|
-
});
|
|
1238
|
-
|
|
1239
|
-
it('returns focus to the original opener after a multi-sheet flow ends', () => {
|
|
1240
|
-
render(<Flow />);
|
|
1241
|
-
const opener = screen.getByRole('button', {name: 'Start flow'});
|
|
1242
|
-
opener.focus();
|
|
1243
|
-
fireEvent.click(opener);
|
|
1244
|
-
fireEvent.click(screen.getByRole('button', {name: 'Continue'}));
|
|
1245
|
-
|
|
1246
|
-
fireEvent.keyDown(screen.getByRole('dialog', {name: 'Confirm'}), {
|
|
1247
|
-
key: 'Escape',
|
|
1248
|
-
});
|
|
1249
|
-
expect(document.activeElement).not.toBe(opener);
|
|
1250
|
-
finishSheetTransition(getSheetLayer('confirm-sheet'), 'transform');
|
|
1251
|
-
|
|
1252
|
-
expect(document.activeElement).toBe(opener);
|
|
1253
|
-
});
|
|
1254
|
-
|
|
1255
|
-
it('captures a new focus trigger after switching through non-modal mode', () => {
|
|
1256
|
-
render(<ModeSwitchFlow />);
|
|
1257
|
-
const firstOpener = screen.getByRole('button', {name: 'Open first modal'});
|
|
1258
|
-
const secondOpener = screen.getByRole('button', {
|
|
1259
|
-
name: 'Open second modal',
|
|
1260
|
-
});
|
|
1261
|
-
|
|
1262
|
-
firstOpener.focus();
|
|
1263
|
-
fireEvent.click(firstOpener);
|
|
1264
|
-
fireEvent.click(screen.getByRole('button', {name: 'Make non-modal'}));
|
|
1265
|
-
fireEvent.click(screen.getByRole('button', {name: 'Close flow'}));
|
|
1266
|
-
finishSheetTransition(getSheetLayer('mode-details-sheet'), 'transform');
|
|
1267
|
-
|
|
1268
|
-
secondOpener.focus();
|
|
1269
|
-
fireEvent.click(secondOpener);
|
|
1270
|
-
fireEvent.click(screen.getByRole('button', {name: 'Close flow'}));
|
|
1271
|
-
finishSheetTransition(getSheetLayer('mode-details-sheet'), 'transform');
|
|
1272
|
-
|
|
1273
|
-
expect(document.activeElement).toBe(secondOpener);
|
|
1274
|
-
});
|
|
1275
|
-
|
|
1276
|
-
it('does not refocus the panel when an incoming transition completes', () => {
|
|
1277
|
-
render(<Flow />);
|
|
1278
|
-
fireEvent.click(screen.getByRole('button', {name: 'Start flow'}));
|
|
1279
|
-
fireEvent.click(screen.getByRole('button', {name: 'Continue'}));
|
|
1280
|
-
|
|
1281
|
-
const confirmSheet = getSheetLayer('confirm-sheet');
|
|
1282
|
-
const backButton = screen.getByRole('button', {name: 'Back'});
|
|
1283
|
-
backButton.focus();
|
|
1284
|
-
finishSheetTransition(confirmSheet, 'transform');
|
|
1285
|
-
|
|
1286
|
-
expect(backButton).toHaveFocus();
|
|
1287
|
-
});
|
|
1288
|
-
|
|
1289
|
-
describe('one native dialog per sheet', () => {
|
|
1290
|
-
it('renders a closed dialog for every sheet and presents only the selected one', () => {
|
|
1291
|
-
render(<Flow />);
|
|
1292
|
-
const detailsDialog = getSheetDialog('details-sheet');
|
|
1293
|
-
const confirmDialog = getSheetDialog('confirm-sheet');
|
|
1294
|
-
expect(detailsDialog).not.toBe(confirmDialog);
|
|
1295
|
-
expect(openDialogs()).toEqual([]);
|
|
1296
|
-
|
|
1297
|
-
fireEvent.click(screen.getByRole('button', {name: 'Start flow'}));
|
|
1298
|
-
|
|
1299
|
-
expect(openDialogs()).toEqual([detailsDialog]);
|
|
1300
|
-
expect(HTMLDialogElement.prototype.showModal).toHaveBeenCalledTimes(1);
|
|
1301
|
-
expect(confirmDialog).not.toHaveAttribute('open');
|
|
1302
|
-
expect(confirmDialog).not.toHaveAttribute('aria-modal');
|
|
1303
|
-
expect(detailsDialog).toHaveAttribute('aria-modal', 'true');
|
|
1304
|
-
expect(detailsDialog).toContainElement(
|
|
1305
|
-
screen.getByTestId('details-sheet'),
|
|
1306
|
-
);
|
|
1307
|
-
expect(confirmDialog).toContainElement(
|
|
1308
|
-
screen.getByTestId('confirm-sheet'),
|
|
1309
|
-
);
|
|
1310
|
-
});
|
|
1311
|
-
|
|
1312
|
-
it('presents the entering dialog once and closes the retained dialog only after it fades', () => {
|
|
1313
|
-
const log = recordDialogCalls();
|
|
1314
|
-
render(<Flow />);
|
|
1315
|
-
fireEvent.click(screen.getByRole('button', {name: 'Start flow'}));
|
|
1316
|
-
expect(log).toEqual(['showModal:Details']);
|
|
1317
|
-
|
|
1318
|
-
fireEvent.click(screen.getByRole('button', {name: 'Continue'}));
|
|
1319
|
-
expect(log).toEqual(['showModal:Details', 'showModal:Confirm']);
|
|
1320
|
-
|
|
1321
|
-
finishSheetTransition(getSheetLayer('confirm-sheet'), 'transform');
|
|
1322
|
-
expect(log).toEqual(['showModal:Details', 'showModal:Confirm']);
|
|
1323
|
-
finishSheetTransition(getSheetLayer('details-sheet'), 'opacity');
|
|
1324
|
-
expect(log).toEqual([
|
|
1325
|
-
'showModal:Details',
|
|
1326
|
-
'showModal:Confirm',
|
|
1327
|
-
'close:Details',
|
|
1328
|
-
]);
|
|
1329
|
-
|
|
1330
|
-
fireEvent.keyDown(getActiveDialog(), {key: 'Escape'});
|
|
1331
|
-
expect(log).toHaveLength(3);
|
|
1332
|
-
finishSheetTransition(getSheetLayer('confirm-sheet'), 'transform');
|
|
1333
|
-
expect(log.at(-1)).toBe('close:Confirm');
|
|
1334
|
-
expect(openDialogs()).toEqual([]);
|
|
1335
|
-
});
|
|
1336
|
-
|
|
1337
|
-
it('holds one page scroll lock for the whole flow: engaged continuously through a handoff, released once after the exit', () => {
|
|
1338
|
-
// The shared lock restores the saved offset with window.scrollTo on its
|
|
1339
|
-
// last release, so the call count is the release count: a flow that
|
|
1340
|
-
// released and re-acquired between steps would scroll the page back
|
|
1341
|
-
// mid-handoff.
|
|
1342
|
-
vi.spyOn(window, 'scrollY', 'get').mockReturnValue(300);
|
|
1343
|
-
render(<Flow />);
|
|
1344
|
-
expect(isScrollLockEngaged()).toBe(false);
|
|
1345
|
-
|
|
1346
|
-
fireEvent.click(screen.getByRole('button', {name: 'Start flow'}));
|
|
1347
|
-
expect(isScrollLockEngaged()).toBe(true);
|
|
1348
|
-
expect(document.body.style.top).toBe('-300px');
|
|
1349
|
-
expect(window.scrollTo).not.toHaveBeenCalled();
|
|
1350
|
-
|
|
1351
|
-
fireEvent.click(screen.getByRole('button', {name: 'Continue'}));
|
|
1352
|
-
// Two modal sheet dialogs are open: still the one lock.
|
|
1353
|
-
expect(openDialogs()).toHaveLength(2);
|
|
1354
|
-
expect(isScrollLockEngaged()).toBe(true);
|
|
1355
|
-
expect(window.scrollTo).not.toHaveBeenCalled();
|
|
1356
|
-
|
|
1357
|
-
finishSheetTransition(getSheetLayer('confirm-sheet'), 'transform');
|
|
1358
|
-
finishSheetTransition(getSheetLayer('details-sheet'), 'opacity');
|
|
1359
|
-
expect(openDialogs()).toHaveLength(1);
|
|
1360
|
-
expect(isScrollLockEngaged()).toBe(true);
|
|
1361
|
-
expect(window.scrollTo).not.toHaveBeenCalled();
|
|
1362
|
-
|
|
1363
|
-
fireEvent.click(screen.getByRole('button', {name: 'Back'}));
|
|
1364
|
-
finishSheetTransition(getSheetLayer('details-sheet'), 'transform');
|
|
1365
|
-
finishSheetTransition(getSheetLayer('confirm-sheet'), 'opacity');
|
|
1366
|
-
expect(isScrollLockEngaged()).toBe(true);
|
|
1367
|
-
expect(window.scrollTo).not.toHaveBeenCalled();
|
|
1368
|
-
|
|
1369
|
-
fireEvent.keyDown(getActiveDialog(), {key: 'Escape'});
|
|
1370
|
-
// Still held through the exit motion.
|
|
1371
|
-
expect(isScrollLockEngaged()).toBe(true);
|
|
1372
|
-
expect(window.scrollTo).not.toHaveBeenCalled();
|
|
1373
|
-
finishSheetTransition(getSheetLayer('details-sheet'), 'transform');
|
|
1374
|
-
expect(isScrollLockEngaged()).toBe(false);
|
|
1375
|
-
expect(document.body.style.top).toBe('');
|
|
1376
|
-
expect(window.scrollTo).toHaveBeenCalledTimes(1);
|
|
1377
|
-
expect(window.scrollTo).toHaveBeenCalledWith(0, 300);
|
|
1378
|
-
});
|
|
1379
|
-
|
|
1380
|
-
it('keeps the lock through a StrictMode replay of a flow mounted open, and releases it when the flow unmounts while presented', () => {
|
|
1381
|
-
vi.spyOn(window, 'scrollY', 'get').mockReturnValue(120);
|
|
1382
|
-
const mounted = render(
|
|
1383
|
-
<StrictMode>
|
|
1384
|
-
<HostFlow initialSheet="details" />
|
|
1385
|
-
</StrictMode>,
|
|
1386
|
-
);
|
|
1387
|
-
// StrictMode runs the lock's effect, its cleanup, and the effect again
|
|
1388
|
-
// in one commit: the pin must stand afterwards, and the one real
|
|
1389
|
-
// release has not happened.
|
|
1390
|
-
expect(isScrollLockEngaged()).toBe(true);
|
|
1391
|
-
expect(document.body.style.top).toBe('-120px');
|
|
1392
|
-
expect(window.scrollTo).toHaveBeenCalledTimes(1);
|
|
1393
|
-
vi.mocked(window.scrollTo).mockClear();
|
|
1394
|
-
|
|
1395
|
-
fireEvent.click(screen.getByRole('button', {name: 'Continue'}));
|
|
1396
|
-
expect(openDialogs()).toHaveLength(2);
|
|
1397
|
-
expect(isScrollLockEngaged()).toBe(true);
|
|
1398
|
-
expect(window.scrollTo).not.toHaveBeenCalled();
|
|
1399
|
-
|
|
1400
|
-
// Unmounted while presented, two dialogs open: one release.
|
|
1401
|
-
mounted.unmount();
|
|
1402
|
-
expect(isScrollLockEngaged()).toBe(false);
|
|
1403
|
-
expect(document.body.style.top).toBe('');
|
|
1404
|
-
expect(window.scrollTo).toHaveBeenCalledTimes(1);
|
|
1405
|
-
expect(window.scrollTo).toHaveBeenCalledWith(0, 120);
|
|
1406
|
-
});
|
|
1407
|
-
|
|
1408
|
-
it('keeps the lock engaged under a Core Dialog opened from a step, and the Dialog paints above the step', () => {
|
|
1409
|
-
render(<DialogInStepFlow />);
|
|
1410
|
-
fireEvent.click(screen.getByRole('button', {name: 'Start flow'}));
|
|
1411
|
-
fireEvent.click(screen.getByRole('button', {name: 'Continue'}));
|
|
1412
|
-
finishSheetTransition(getSheetLayer('confirm-sheet'), 'transform');
|
|
1413
|
-
finishSheetTransition(getSheetLayer('details-sheet'), 'opacity');
|
|
1414
|
-
const confirmDialog = getSheetDialog('confirm-sheet');
|
|
1415
|
-
|
|
1416
|
-
fireEvent.click(screen.getByRole('button', {name: 'Open dialog'}));
|
|
1417
|
-
const dialog = screen.getByRole('dialog', {name: 'Are you sure'});
|
|
1418
|
-
expect(dialog).toHaveAttribute('open');
|
|
1419
|
-
// The Dialog was presented after the step's dialog, so the top layer
|
|
1420
|
-
// paints it above; the Dialog and the flow share the lock's counter,
|
|
1421
|
-
// so the step beneath keeps the pin when the Dialog gives its hold back.
|
|
1422
|
-
expect(HTMLDialogElement.prototype.showModal).toHaveBeenLastCalledWith();
|
|
1423
|
-
expect(
|
|
1424
|
-
vi.mocked(HTMLDialogElement.prototype.showModal).mock.contexts.at(-1),
|
|
1425
|
-
).toBe(dialog);
|
|
1426
|
-
expect(confirmDialog).toHaveAttribute('open');
|
|
1427
|
-
expect(isScrollLockEngaged()).toBe(true);
|
|
1428
|
-
|
|
1429
|
-
expect(window.scrollTo).not.toHaveBeenCalled();
|
|
1430
|
-
|
|
1431
|
-
// Escape reaches the Dialog first, not the step beneath it.
|
|
1432
|
-
fireEvent.keyDown(dialog, {key: 'Escape'});
|
|
1433
|
-
expect(dialog).not.toHaveAttribute('open');
|
|
1434
|
-
expect(confirmDialog).toHaveAttribute('open');
|
|
1435
|
-
expect(isScrollLockEngaged()).toBe(true);
|
|
1436
|
-
expect(window.scrollTo).not.toHaveBeenCalled();
|
|
1437
|
-
|
|
1438
|
-
fireEvent.click(screen.getByRole('button', {name: 'Done'}));
|
|
1439
|
-
finishSheetTransition(getSheetLayer('confirm-sheet'), 'transform');
|
|
1440
|
-
expect(isScrollLockEngaged()).toBe(false);
|
|
1441
|
-
expect(window.scrollTo).toHaveBeenCalledTimes(1);
|
|
1442
|
-
});
|
|
1443
|
-
|
|
1444
|
-
it("resolves the switcher ref to the host sheet's dialog and never to null while a sheet is mounted, from the first commit on", () => {
|
|
1445
|
-
const dialogRef = createRef<HTMLDialogElement>();
|
|
1446
|
-
const seenAtMount: {layout?: Element | null; passive?: Element | null} =
|
|
1447
|
-
{};
|
|
1448
|
-
const {unmount} = render(
|
|
1449
|
-
<HostFlow
|
|
1450
|
-
dialogRef={dialogRef}
|
|
1451
|
-
onMountLayout={() => {
|
|
1452
|
-
seenAtMount.layout = dialogRef.current;
|
|
1453
|
-
}}
|
|
1454
|
-
onMountPassive={() => {
|
|
1455
|
-
seenAtMount.passive = dialogRef.current;
|
|
1456
|
-
}}
|
|
1457
|
-
/>,
|
|
1458
|
-
);
|
|
1459
|
-
// Closed, never opened: the first sheet's closed dialog, as the shared
|
|
1460
|
-
// dialog once was a closed element rather than null -- already in the
|
|
1461
|
-
// consumer's own mount effects, layout and passive alike.
|
|
1462
|
-
const detailsDialog = getSheetDialog('details-sheet');
|
|
1463
|
-
expect(seenAtMount.layout).toBe(detailsDialog);
|
|
1464
|
-
expect(seenAtMount.passive).toBe(detailsDialog);
|
|
1465
|
-
expect(dialogRef.current).toBe(detailsDialog);
|
|
1466
|
-
expect(dialogRef.current).not.toHaveAttribute('open');
|
|
1467
|
-
|
|
1468
|
-
fireEvent.click(screen.getByRole('button', {name: 'Start flow'}));
|
|
1469
|
-
expect(dialogRef.current).toBe(detailsDialog);
|
|
1470
|
-
expect(dialogRef.current).toHaveAttribute('open');
|
|
1471
|
-
|
|
1472
|
-
fireEvent.click(screen.getByRole('button', {name: 'Continue'}));
|
|
1473
|
-
expect(dialogRef.current).toBe(getSheetDialog('confirm-sheet'));
|
|
1474
|
-
|
|
1475
|
-
fireEvent.click(screen.getByRole('button', {name: 'Done'}));
|
|
1476
|
-
// The exiting sheet's dialog hosts the flow through its exit motion and
|
|
1477
|
-
// stays the host once the flow is closed.
|
|
1478
|
-
expect(dialogRef.current).toBe(getSheetDialog('confirm-sheet'));
|
|
1479
|
-
finishSheetTransition(getSheetLayer('confirm-sheet'), 'transform');
|
|
1480
|
-
expect(dialogRef.current).toBe(getSheetDialog('confirm-sheet'));
|
|
1481
|
-
expect(dialogRef.current).not.toHaveAttribute('open');
|
|
1482
|
-
|
|
1483
|
-
unmount();
|
|
1484
|
-
expect(dialogRef.current).toBeNull();
|
|
1485
|
-
});
|
|
1486
|
-
|
|
1487
|
-
it('runs a callback ref cleanup exactly once per detach and never passes it null', () => {
|
|
1488
|
-
const received: (HTMLDialogElement | null)[] = [];
|
|
1489
|
-
const cleanups: HTMLDialogElement[] = [];
|
|
1490
|
-
const callbackRef = (dialog: HTMLDialogElement | null) => {
|
|
1491
|
-
received.push(dialog);
|
|
1492
|
-
return () => {
|
|
1493
|
-
if (dialog != null) {
|
|
1494
|
-
cleanups.push(dialog);
|
|
1495
|
-
}
|
|
1496
|
-
};
|
|
1497
|
-
};
|
|
1498
|
-
const {unmount} = render(<HostFlow dialogRef={callbackRef} />);
|
|
1499
|
-
const detailsDialog = getSheetDialog('details-sheet');
|
|
1500
|
-
const confirmDialog = getSheetDialog('confirm-sheet');
|
|
1501
|
-
expect(received).toEqual([detailsDialog]);
|
|
1502
|
-
expect(cleanups).toEqual([]);
|
|
1503
|
-
|
|
1504
|
-
fireEvent.click(screen.getByRole('button', {name: 'Start flow'}));
|
|
1505
|
-
// Same host: the ref is not detached and re-attached.
|
|
1506
|
-
expect(received).toEqual([detailsDialog]);
|
|
1507
|
-
|
|
1508
|
-
fireEvent.click(screen.getByRole('button', {name: 'Continue'}));
|
|
1509
|
-
// Host moved: the Details attachment is cleaned up once, then Confirm
|
|
1510
|
-
// is attached; no `null` call stands in for the cleanup.
|
|
1511
|
-
expect(cleanups).toEqual([detailsDialog]);
|
|
1512
|
-
expect(received).toEqual([detailsDialog, confirmDialog]);
|
|
1513
|
-
|
|
1514
|
-
fireEvent.click(screen.getByRole('button', {name: 'Done'}));
|
|
1515
|
-
finishSheetTransition(getSheetLayer('confirm-sheet'), 'transform');
|
|
1516
|
-
expect(cleanups).toEqual([detailsDialog]);
|
|
1517
|
-
expect(received).toEqual([detailsDialog, confirmDialog]);
|
|
1518
|
-
|
|
1519
|
-
unmount();
|
|
1520
|
-
expect(cleanups).toEqual([detailsDialog, confirmDialog]);
|
|
1521
|
-
expect(received).toEqual([detailsDialog, confirmDialog]);
|
|
1522
|
-
expect(received).not.toContain(null);
|
|
1523
|
-
});
|
|
1524
|
-
|
|
1525
|
-
it('renders the switcher id and test id once, on the host dialog, and moves them with it', () => {
|
|
1526
|
-
render(
|
|
1527
|
-
<HostFlow
|
|
1528
|
-
dialogProps={{
|
|
1529
|
-
id: 'checkout',
|
|
1530
|
-
'data-testid': 'checkout-flow',
|
|
1531
|
-
'data-flow-owner': 'settings',
|
|
1532
|
-
}}
|
|
1533
|
-
/>,
|
|
1534
|
-
);
|
|
1535
|
-
const dialogs = Array.from(document.querySelectorAll('dialog'));
|
|
1536
|
-
expect(dialogs).toHaveLength(3);
|
|
1537
|
-
const withId = () =>
|
|
1538
|
-
Array.from(document.querySelectorAll('[id="checkout"]'));
|
|
1539
|
-
// Closed: exactly one element carries the id, the first sheet's dialog.
|
|
1540
|
-
expect(withId()).toEqual([dialogs[0]]);
|
|
1541
|
-
expect(screen.getByTestId('checkout-flow')).toBe(dialogs[0]);
|
|
1542
|
-
// The remaining dialog props still reach every sheet dialog.
|
|
1543
|
-
for (const dialog of dialogs) {
|
|
1544
|
-
expect(dialog).toHaveAttribute('data-flow-owner', 'settings');
|
|
1545
|
-
}
|
|
1546
|
-
|
|
1547
|
-
fireEvent.click(screen.getByRole('button', {name: 'Start flow'}));
|
|
1548
|
-
expect(withId()).toEqual([dialogs[0]]);
|
|
1549
|
-
expect(document.getElementById('checkout')).toHaveAttribute('open');
|
|
1550
|
-
|
|
1551
|
-
fireEvent.click(screen.getByRole('button', {name: 'Continue'}));
|
|
1552
|
-
// Mid-handoff two dialogs are open; the id names the active one only.
|
|
1553
|
-
expect(openDialogs()).toHaveLength(2);
|
|
1554
|
-
expect(withId()).toEqual([dialogs[1]]);
|
|
1555
|
-
expect(screen.getByTestId('checkout-flow')).toBe(dialogs[1]);
|
|
1556
|
-
expect(document.getElementById('checkout')).toHaveAccessibleName(
|
|
1557
|
-
'Confirm',
|
|
1558
|
-
);
|
|
1559
|
-
|
|
1560
|
-
fireEvent.click(screen.getByRole('button', {name: 'Done'}));
|
|
1561
|
-
finishSheetTransition(getSheetLayer('confirm-sheet'), 'transform');
|
|
1562
|
-
expect(withId()).toEqual([dialogs[1]]);
|
|
1563
|
-
});
|
|
1564
|
-
|
|
1565
|
-
it("renders the switcher id and data-testid once, on the active sheet's dialog, in the server markup of a flow that mounts open", () => {
|
|
1566
|
-
// The active sheet is known from props, so the host needs no commit:
|
|
1567
|
-
// server markup carries both attributes on that sheet's dialog, once.
|
|
1568
|
-
const markup = renderToString(
|
|
1569
|
-
<BottomSheetSwitcher
|
|
1570
|
-
id="checkout"
|
|
1571
|
-
data-testid="checkout-flow"
|
|
1572
|
-
activeSheet="confirm"
|
|
1573
|
-
onActiveSheetChange={() => {}}>
|
|
1574
|
-
<BottomSheet sheetId="details" label="Details">
|
|
1575
|
-
Content
|
|
1576
|
-
</BottomSheet>
|
|
1577
|
-
<BottomSheet sheetId="confirm" label="Confirm">
|
|
1578
|
-
Content
|
|
1579
|
-
</BottomSheet>
|
|
1580
|
-
</BottomSheetSwitcher>,
|
|
1581
|
-
);
|
|
1582
|
-
const dialogs = markup.match(/<dialog\b[^>]*>/g) ?? [];
|
|
1583
|
-
expect(dialogs).toHaveLength(2);
|
|
1584
|
-
expect(markup.match(/id="checkout"/g)).toHaveLength(1);
|
|
1585
|
-
expect(markup.match(/data-testid="checkout-flow"/g)).toHaveLength(1);
|
|
1586
|
-
const host = dialogs.find(tag => tag.includes('id="checkout"'));
|
|
1587
|
-
expect(host).toContain('aria-label="Confirm"');
|
|
1588
|
-
expect(host).toContain('data-testid="checkout-flow"');
|
|
1589
|
-
expect(
|
|
1590
|
-
dialogs.find(tag => tag.includes('aria-label="Details"')),
|
|
1591
|
-
).not.toContain('id=');
|
|
1592
|
-
});
|
|
1593
|
-
|
|
1594
|
-
it('renders every sheet dialog closed in server markup, with the switcher id on no element until the first commit', () => {
|
|
1595
|
-
// Which sheet mounts first is known once its dialog registers during
|
|
1596
|
-
// the first commit; server rendering has no commit, so the id waits for
|
|
1597
|
-
// the client. What server markup must still get right: one closed
|
|
1598
|
-
// dialog per sheet, none `open`, and the id never duplicated.
|
|
1599
|
-
const markup = renderToString(
|
|
1600
|
-
<BottomSheetSwitcher
|
|
1601
|
-
id="checkout"
|
|
1602
|
-
data-testid="checkout-flow"
|
|
1603
|
-
activeSheet={null}
|
|
1604
|
-
onActiveSheetChange={() => {}}>
|
|
1605
|
-
<BottomSheet sheetId="details" label="Details">
|
|
1606
|
-
Content
|
|
1607
|
-
</BottomSheet>
|
|
1608
|
-
<BottomSheet sheetId="confirm" label="Confirm">
|
|
1609
|
-
Content
|
|
1610
|
-
</BottomSheet>
|
|
1611
|
-
</BottomSheetSwitcher>,
|
|
1612
|
-
);
|
|
1613
|
-
expect(markup.match(/<dialog\b/g)).toHaveLength(2);
|
|
1614
|
-
expect(markup).not.toMatch(/<dialog[^>]*\sopen/);
|
|
1615
|
-
expect(markup.match(/id="checkout"/g) ?? []).toHaveLength(0);
|
|
1616
|
-
|
|
1617
|
-
// Hydrating the same tree on the client: the first commit's synchronous
|
|
1618
|
-
// re-render puts the id on the first sheet's dialog, exactly once.
|
|
1619
|
-
const container = document.createElement('div');
|
|
1620
|
-
container.innerHTML = markup;
|
|
1621
|
-
document.body.appendChild(container);
|
|
1622
|
-
act(() => {
|
|
1623
|
-
hydrateRoot(
|
|
1624
|
-
container,
|
|
1625
|
-
<BottomSheetSwitcher
|
|
1626
|
-
id="checkout"
|
|
1627
|
-
data-testid="checkout-flow"
|
|
1628
|
-
activeSheet={null}
|
|
1629
|
-
onActiveSheetChange={() => {}}>
|
|
1630
|
-
<BottomSheet sheetId="details" label="Details">
|
|
1631
|
-
Content
|
|
1632
|
-
</BottomSheet>
|
|
1633
|
-
<BottomSheet sheetId="confirm" label="Confirm">
|
|
1634
|
-
Content
|
|
1635
|
-
</BottomSheet>
|
|
1636
|
-
</BottomSheetSwitcher>,
|
|
1637
|
-
);
|
|
1638
|
-
});
|
|
1639
|
-
const withId = container.querySelectorAll('[id="checkout"]');
|
|
1640
|
-
expect(withId).toHaveLength(1);
|
|
1641
|
-
expect(withId[0]).toBe(container.querySelector('dialog'));
|
|
1642
|
-
expect(withId[0]).toHaveAttribute('data-testid', 'checkout-flow');
|
|
1643
|
-
expect(withId[0]).toHaveAttribute('aria-label', 'Details');
|
|
1644
|
-
container.remove();
|
|
1645
|
-
});
|
|
1646
|
-
|
|
1647
|
-
it('orders a non-modal handoff inside the switcher-owned isolation surface, with no page-wide z-index on a sheet dialog', () => {
|
|
1648
|
-
render(<HostFlow hasScrim={false} initialSheet="details" />);
|
|
1649
|
-
const detailsDialog = getSheetDialog('details-sheet');
|
|
1650
|
-
const confirmDialog = getSheetDialog('confirm-sheet');
|
|
1651
|
-
const flow = detailsDialog.parentElement;
|
|
1652
|
-
if (flow == null) {
|
|
1653
|
-
throw new Error('flow wrapper not found');
|
|
1654
|
-
}
|
|
1655
|
-
expect(flow).toBe(confirmDialog.parentElement);
|
|
1656
|
-
// The wrapper is the isolated surface for the flow (`spec:AST-027`
|
|
1657
|
-
// FR2) and carries the one overlay value the shared shell carried.
|
|
1658
|
-
expect(getComputedStyle(flow).isolation).toBe('isolate');
|
|
1659
|
-
expect(getComputedStyle(flow).zIndex).toBe('1000');
|
|
1660
|
-
|
|
1661
|
-
fireEvent.click(screen.getByRole('button', {name: 'Continue'}));
|
|
1662
|
-
expect(openDialogs()).toEqual([detailsDialog, confirmDialog]);
|
|
1663
|
-
// Inside it the dialogs use local values only: the interactive sheet
|
|
1664
|
-
// on top at 1, the retained sheet at auto. Neither is in a page band.
|
|
1665
|
-
expect(getComputedStyle(confirmDialog).zIndex).toBe('1');
|
|
1666
|
-
// jsdom reports an unset z-index as '' where a browser says 'auto'.
|
|
1667
|
-
expect(['', 'auto']).toContain(getComputedStyle(detailsDialog).zIndex);
|
|
1668
|
-
for (const dialog of [detailsDialog, confirmDialog]) {
|
|
1669
|
-
expect(Number(getComputedStyle(dialog).zIndex) >= 1000).toBe(false);
|
|
1670
|
-
}
|
|
1671
|
-
});
|
|
1672
|
-
|
|
1673
|
-
it('applies the switcher dialog props to each sheet dialog, with a consumer role kept unless a required step forces alertdialog', () => {
|
|
1674
|
-
const onToggle = vi.fn();
|
|
1675
|
-
render(
|
|
1676
|
-
<BottomSheetSwitcher
|
|
1677
|
-
activeSheet="details"
|
|
1678
|
-
onActiveSheetChange={() => {}}
|
|
1679
|
-
className="consumer-switcher"
|
|
1680
|
-
style={{insetInlineStart: 12}}
|
|
1681
|
-
role="alertdialog"
|
|
1682
|
-
aria-describedby="flow-help"
|
|
1683
|
-
data-flow-owner="settings"
|
|
1684
|
-
onToggle={onToggle}>
|
|
1685
|
-
<BottomSheet sheetId="details" label="Details" data-testid="a">
|
|
1686
|
-
Content
|
|
1687
|
-
</BottomSheet>
|
|
1688
|
-
<BottomSheet sheetId="confirm" label="Confirm" data-testid="b">
|
|
1689
|
-
Content
|
|
1690
|
-
</BottomSheet>
|
|
1691
|
-
<BottomSheet
|
|
1692
|
-
sheetId="danger"
|
|
1693
|
-
label="Danger"
|
|
1694
|
-
purpose="required"
|
|
1695
|
-
data-testid="c">
|
|
1696
|
-
Content
|
|
1697
|
-
</BottomSheet>
|
|
1698
|
-
</BottomSheetSwitcher>,
|
|
1699
|
-
);
|
|
1700
|
-
// Every switcher prop the shared dialog honoured reaches every sheet
|
|
1701
|
-
// dialog, explicit attributes included: a consumer `role` is kept
|
|
1702
|
-
// (the shared dialog let it through) and a `required` step still
|
|
1703
|
-
// forces `alertdialog`.
|
|
1704
|
-
for (const testId of ['a', 'b', 'c']) {
|
|
1705
|
-
const dialog = getSheetDialog(testId);
|
|
1706
|
-
expect(dialog).toHaveClass('consumer-switcher');
|
|
1707
|
-
expect(dialog).toHaveStyle({insetInlineStart: '12px'});
|
|
1708
|
-
expect(dialog).toHaveAttribute('data-flow-owner', 'settings');
|
|
1709
|
-
expect(dialog).toHaveAttribute('aria-describedby', 'flow-help');
|
|
1710
|
-
expect(dialog).toHaveAttribute('role', 'alertdialog');
|
|
1711
|
-
fireEvent(dialog, new Event('toggle'));
|
|
1712
|
-
}
|
|
1713
|
-
expect(onToggle).toHaveBeenCalledTimes(3);
|
|
1714
|
-
expect(getSheetDialog('a')).toHaveAttribute('aria-label', 'Details');
|
|
1715
|
-
expect(getSheetDialog('b')).toHaveAttribute('aria-label', 'Confirm');
|
|
1716
|
-
});
|
|
1717
|
-
|
|
1718
|
-
it('forces alertdialog on a required step and leaves the other sheet dialogs with no role when the switcher sets none', () => {
|
|
1719
|
-
render(
|
|
1720
|
-
<BottomSheetSwitcher
|
|
1721
|
-
activeSheet="details"
|
|
1722
|
-
onActiveSheetChange={() => {}}>
|
|
1723
|
-
<BottomSheet sheetId="details" label="Details" data-testid="a">
|
|
1724
|
-
Content
|
|
1725
|
-
</BottomSheet>
|
|
1726
|
-
<BottomSheet
|
|
1727
|
-
sheetId="danger"
|
|
1728
|
-
label="Danger"
|
|
1729
|
-
purpose="required"
|
|
1730
|
-
data-testid="c">
|
|
1731
|
-
Content
|
|
1732
|
-
</BottomSheet>
|
|
1733
|
-
</BottomSheetSwitcher>,
|
|
1734
|
-
);
|
|
1735
|
-
expect(getSheetDialog('a')).not.toHaveAttribute('role');
|
|
1736
|
-
expect(getSheetDialog('c')).toHaveAttribute('role', 'alertdialog');
|
|
1737
|
-
});
|
|
1738
|
-
|
|
1739
|
-
it('keeps the scrim takeover pin under prefers-reduced-motion: the reduced duration reads the pinned variable', () => {
|
|
1740
|
-
render(<HostFlow initialSheet="details" />);
|
|
1741
|
-
// jsdom evaluates neither @media nor ::backdrop; read the compiled
|
|
1742
|
-
// rules the dialog's scrim class produced. The reduced-motion rule for
|
|
1743
|
-
// the backdrop's transition-duration must defer to the takeover pin
|
|
1744
|
-
// (`--_sheet-scrim-duration: 0s` for one style flush); a literal 0.01s
|
|
1745
|
-
// there would win the cascade and paint a frame with no scrim.
|
|
1746
|
-
const rules: string[] = [];
|
|
1747
|
-
for (const sheet of Array.from(document.styleSheets)) {
|
|
1748
|
-
for (const rule of Array.from(sheet.cssRules)) {
|
|
1749
|
-
rules.push(rule.cssText);
|
|
1750
|
-
}
|
|
1751
|
-
}
|
|
1752
|
-
const reducedBackdropDurations = rules.filter(
|
|
1753
|
-
text =>
|
|
1754
|
-
text.includes('prefers-reduced-motion: reduce') &&
|
|
1755
|
-
text.includes('::backdrop') &&
|
|
1756
|
-
text.includes('transition-duration'),
|
|
1757
|
-
);
|
|
1758
|
-
expect(reducedBackdropDurations.length).toBeGreaterThan(0);
|
|
1759
|
-
for (const text of reducedBackdropDurations) {
|
|
1760
|
-
expect(text).toMatch(
|
|
1761
|
-
/transition-duration:\s*var\(--_sheet-scrim-duration,\s*0?\.01s\)/,
|
|
1762
|
-
);
|
|
1763
|
-
expect(text).not.toMatch(/transition-duration:\s*0?\.01s/);
|
|
1764
|
-
}
|
|
1765
|
-
});
|
|
1766
|
-
|
|
1767
|
-
it('paints one scrim through a handoff and hands it over without a fade', () => {
|
|
1768
|
-
render(<Flow />);
|
|
1769
|
-
fireEvent.click(screen.getByRole('button', {name: 'Start flow'}));
|
|
1770
|
-
const detailsDialog = getSheetDialog('details-sheet');
|
|
1771
|
-
const confirmDialog = getSheetDialog('confirm-sheet');
|
|
1772
|
-
expect(detailsDialog).toHaveStyle({'--_sheet-scrim-opacity': '1'});
|
|
1773
|
-
|
|
1774
|
-
fireEvent.click(screen.getByRole('button', {name: 'Continue'}));
|
|
1775
|
-
// The retained dialog's backdrop is the scrim; the entering dialog's
|
|
1776
|
-
// backdrop, painted above the retained panel, stays clear.
|
|
1777
|
-
expect(detailsDialog).toHaveStyle({'--_sheet-scrim-opacity': '1'});
|
|
1778
|
-
expect(confirmDialog).toHaveStyle({'--_sheet-scrim-opacity': '0'});
|
|
1779
|
-
|
|
1780
|
-
finishSheetTransition(getSheetLayer('confirm-sheet'), 'transform');
|
|
1781
|
-
expect(detailsDialog).toHaveStyle({'--_sheet-scrim-opacity': '1'});
|
|
1782
|
-
expect(confirmDialog).toHaveStyle({'--_sheet-scrim-opacity': '0'});
|
|
1783
|
-
|
|
1784
|
-
finishSheetTransition(getSheetLayer('details-sheet'), 'opacity');
|
|
1785
|
-
expect(detailsDialog).not.toHaveAttribute('open');
|
|
1786
|
-
expect(confirmDialog).toHaveStyle({'--_sheet-scrim-opacity': '1'});
|
|
1787
|
-
// The swap pins the backdrop transition for one style flush only.
|
|
1788
|
-
expect(
|
|
1789
|
-
confirmDialog.style.getPropertyValue('--_sheet-scrim-duration'),
|
|
1790
|
-
).toBe('');
|
|
1791
|
-
|
|
1792
|
-
fireEvent.keyDown(confirmDialog, {key: 'Escape'});
|
|
1793
|
-
expect(confirmDialog).toHaveStyle({'--_sheet-scrim-opacity': '0'});
|
|
1794
|
-
});
|
|
1795
|
-
|
|
1796
|
-
it('moves focus into the entering sheet while the retained dialog is inert', () => {
|
|
1797
|
-
render(<Flow />);
|
|
1798
|
-
const opener = screen.getByRole('button', {name: 'Start flow'});
|
|
1799
|
-
opener.focus();
|
|
1800
|
-
fireEvent.click(opener);
|
|
1801
|
-
expect(getSheetPanel(getSheetDialog('details-sheet'))).toHaveFocus();
|
|
1802
|
-
|
|
1803
|
-
fireEvent.click(screen.getByRole('button', {name: 'Continue'}));
|
|
1804
|
-
|
|
1805
|
-
expect(getSheetPanel(getSheetDialog('confirm-sheet'))).toHaveFocus();
|
|
1806
|
-
expect(getSheetDialog('details-sheet')).toHaveAttribute('inert');
|
|
1807
|
-
expect(getSheetDialog('details-sheet')).toHaveAttribute(
|
|
1808
|
-
'aria-hidden',
|
|
1809
|
-
'true',
|
|
1810
|
-
);
|
|
1811
|
-
expect(getSheetDialog('confirm-sheet')).not.toHaveAttribute('inert');
|
|
1812
|
-
|
|
1813
|
-
finishSheetTransition(getSheetLayer('confirm-sheet'), 'transform');
|
|
1814
|
-
finishSheetTransition(getSheetLayer('details-sheet'), 'opacity');
|
|
1815
|
-
fireEvent.keyDown(getSheetDialog('confirm-sheet'), {key: 'Escape'});
|
|
1816
|
-
finishSheetTransition(getSheetLayer('confirm-sheet'), 'transform');
|
|
1817
|
-
|
|
1818
|
-
expect(opener).toHaveFocus();
|
|
1819
|
-
});
|
|
1820
|
-
|
|
1821
|
-
it('moves focus only when a step is entered or the flow ends: a retained dialog closing after its fade fires no focus event', () => {
|
|
1822
|
-
// close() on a modal dialog returns focus to the element focused when
|
|
1823
|
-
// it was presented -- the Continue or Back that advanced the flow, or
|
|
1824
|
-
// the opener -- and the retained dialog closes a fade after the user
|
|
1825
|
-
// is in the next step. Each step presents with nothing recorded, so
|
|
1826
|
-
// the close moves nothing: no focus/blur/focusin/focusout anywhere
|
|
1827
|
-
// while a retained dialog closes, after Continue, after Back, and
|
|
1828
|
-
// after a reversal mid-handoff.
|
|
1829
|
-
recordDialogCalls();
|
|
1830
|
-
render(<Flow />);
|
|
1831
|
-
const events: string[] = [];
|
|
1832
|
-
const record = (event: Event) => {
|
|
1833
|
-
const target = event.target as HTMLElement;
|
|
1834
|
-
events.push(
|
|
1835
|
-
`${event.type}:${target.getAttribute('aria-label') ?? target.textContent?.trim() ?? target.tagName}`,
|
|
1836
|
-
);
|
|
1837
|
-
};
|
|
1838
|
-
for (const type of ['focus', 'blur', 'focusin', 'focusout']) {
|
|
1839
|
-
document.addEventListener(type, record, true);
|
|
1840
|
-
}
|
|
1841
|
-
const take = () => events.splice(0, events.length);
|
|
1842
|
-
|
|
1843
|
-
const opener = screen.getByRole('button', {name: 'Start flow'});
|
|
1844
|
-
opener.focus();
|
|
1845
|
-
fireEvent.click(opener);
|
|
1846
|
-
const detailsPanel = getSheetPanel(getSheetDialog('details-sheet'));
|
|
1847
|
-
const confirmPanel = getSheetPanel(getSheetDialog('confirm-sheet'));
|
|
1848
|
-
expect(detailsPanel).toHaveFocus();
|
|
1849
|
-
take();
|
|
1850
|
-
|
|
1851
|
-
// Continue: focus moves once, from the control into the entering step.
|
|
1852
|
-
const continueButton = screen.getByRole('button', {name: 'Continue'});
|
|
1853
|
-
continueButton.focus();
|
|
1854
|
-
fireEvent.click(continueButton);
|
|
1855
|
-
expect(confirmPanel).toHaveFocus();
|
|
1856
|
-
take();
|
|
1857
|
-
// The retained Details dialog closes after its fade: nothing moves.
|
|
1858
|
-
finishSheetTransition(getSheetLayer('confirm-sheet'), 'transform');
|
|
1859
|
-
finishSheetTransition(getSheetLayer('details-sheet'), 'opacity');
|
|
1860
|
-
expect(getSheetDialog('details-sheet')).not.toHaveAttribute('open');
|
|
1861
|
-
expect(take()).toEqual([]);
|
|
1862
|
-
expect(confirmPanel).toHaveFocus();
|
|
1863
|
-
|
|
1864
|
-
// Back: the same, with the retained Confirm dialog closing.
|
|
1865
|
-
const backButton = screen.getByRole('button', {name: 'Back'});
|
|
1866
|
-
backButton.focus();
|
|
1867
|
-
fireEvent.click(backButton);
|
|
1868
|
-
expect(detailsPanel).toHaveFocus();
|
|
1869
|
-
take();
|
|
1870
|
-
finishSheetTransition(getSheetLayer('details-sheet'), 'transform');
|
|
1871
|
-
finishSheetTransition(getSheetLayer('confirm-sheet'), 'opacity');
|
|
1872
|
-
expect(getSheetDialog('confirm-sheet')).not.toHaveAttribute('open');
|
|
1873
|
-
expect(take()).toEqual([]);
|
|
1874
|
-
expect(detailsPanel).toHaveFocus();
|
|
1875
|
-
|
|
1876
|
-
// Reversal mid-handoff: Continue, then Back before Confirm settles.
|
|
1877
|
-
continueButton.focus();
|
|
1878
|
-
fireEvent.click(continueButton);
|
|
1879
|
-
expect(confirmPanel).toHaveFocus();
|
|
1880
|
-
backButton.focus();
|
|
1881
|
-
fireEvent.click(backButton);
|
|
1882
|
-
expect(detailsPanel).toHaveFocus();
|
|
1883
|
-
take();
|
|
1884
|
-
finishSheetTransition(getSheetLayer('details-sheet'), 'transform');
|
|
1885
|
-
finishSheetTransition(getSheetLayer('confirm-sheet'), 'opacity');
|
|
1886
|
-
expect(getSheetDialog('confirm-sheet')).not.toHaveAttribute('open');
|
|
1887
|
-
expect(take()).toEqual([]);
|
|
1888
|
-
expect(detailsPanel).toHaveFocus();
|
|
1889
|
-
|
|
1890
|
-
// Ending the flow: the switcher returns focus to the opener itself.
|
|
1891
|
-
fireEvent.keyDown(getSheetDialog('details-sheet'), {key: 'Escape'});
|
|
1892
|
-
finishSheetTransition(getSheetLayer('details-sheet'), 'transform');
|
|
1893
|
-
expect(opener).toHaveFocus();
|
|
1894
|
-
for (const type of ['focus', 'blur', 'focusin', 'focusout']) {
|
|
1895
|
-
document.removeEventListener(type, record, true);
|
|
1896
|
-
}
|
|
1897
|
-
});
|
|
1898
|
-
|
|
1899
|
-
it("answers a native cancel by the active sheet's purpose after a handoff", () => {
|
|
1900
|
-
const log = recordDialogCalls();
|
|
1901
|
-
render(<RequiredConfirmFlow />);
|
|
1902
|
-
expect(screen.queryByRole('alertdialog')).toBeNull();
|
|
1903
|
-
|
|
1904
|
-
fireEvent.click(screen.getByRole('button', {name: 'Continue'}));
|
|
1905
|
-
finishSheetTransition(getSheetLayer('confirm-sheet'), 'transform');
|
|
1906
|
-
finishSheetTransition(getSheetLayer('details-sheet'), 'opacity');
|
|
1907
|
-
|
|
1908
|
-
const confirmDialog = getSheetDialog('confirm-sheet');
|
|
1909
|
-
expect(confirmDialog).toHaveAttribute('role', 'alertdialog');
|
|
1910
|
-
expect(getSheetDialog('details-sheet')).not.toHaveAttribute('role');
|
|
1911
|
-
|
|
1912
|
-
fireEvent.keyDown(confirmDialog, {key: 'Escape'});
|
|
1913
|
-
const cancel = new Event('cancel', {cancelable: true});
|
|
1914
|
-
confirmDialog.dispatchEvent(cancel);
|
|
1915
|
-
fireEvent.click(confirmDialog);
|
|
1916
|
-
|
|
1917
|
-
// Always prevented: the browser never closes a controlled dialog.
|
|
1918
|
-
expect(cancel.defaultPrevented).toBe(true);
|
|
1919
|
-
expect(confirmDialog).toHaveAttribute('open');
|
|
1920
|
-
expect(log.filter(entry => entry.startsWith('close:'))).toEqual([
|
|
1921
|
-
'close:Details',
|
|
1922
|
-
]);
|
|
1923
|
-
|
|
1924
|
-
fireEvent.click(screen.getByRole('button', {name: 'Done'}));
|
|
1925
|
-
finishSheetTransition(getSheetLayer('confirm-sheet'), 'transform');
|
|
1926
|
-
expect(openDialogs()).toEqual([]);
|
|
1927
|
-
});
|
|
1928
|
-
|
|
1929
|
-
it("dismisses an info flow from a cancel on any sheet dialog, and from a backdrop click on the active sheet's dialog only", () => {
|
|
1930
|
-
const onActiveSheetChange = vi.fn();
|
|
1931
|
-
render(
|
|
1932
|
-
<BottomSheetSwitcher
|
|
1933
|
-
activeSheet="confirm"
|
|
1934
|
-
onActiveSheetChange={onActiveSheetChange}>
|
|
1935
|
-
<BottomSheet sheetId="details" label="Details" data-testid="a">
|
|
1936
|
-
Content
|
|
1937
|
-
</BottomSheet>
|
|
1938
|
-
<BottomSheet sheetId="confirm" label="Confirm" data-testid="b">
|
|
1939
|
-
<button type="button">Inside</button>
|
|
1940
|
-
</BottomSheet>
|
|
1941
|
-
</BottomSheetSwitcher>,
|
|
1942
|
-
);
|
|
1943
|
-
const activeDialog = getSheetDialog('b');
|
|
1944
|
-
|
|
1945
|
-
const cancel = new Event('cancel', {cancelable: true});
|
|
1946
|
-
activeDialog.dispatchEvent(cancel);
|
|
1947
|
-
expect(cancel.defaultPrevented).toBe(true);
|
|
1948
|
-
expect(onActiveSheetChange).toHaveBeenNthCalledWith(1, null);
|
|
1949
|
-
|
|
1950
|
-
// A `::backdrop` click targets the dialog element itself.
|
|
1951
|
-
fireEvent.click(activeDialog);
|
|
1952
|
-
expect(onActiveSheetChange).toHaveBeenNthCalledWith(2, null);
|
|
1953
|
-
|
|
1954
|
-
// A click that is not on a backdrop, or on a dialog other than the
|
|
1955
|
-
// host's, dismisses nothing.
|
|
1956
|
-
fireEvent.click(screen.getByRole('button', {name: 'Inside'}));
|
|
1957
|
-
fireEvent.click(getSheetDialog('a'));
|
|
1958
|
-
expect(onActiveSheetChange).toHaveBeenCalledTimes(2);
|
|
1959
|
-
|
|
1960
|
-
// The composed cancel handler is on every sheet dialog, not the host
|
|
1961
|
-
// only: the browser raises `cancel` on the dialog it presents, so in
|
|
1962
|
-
// engines this is the active sheet's, but the switcher does not guard
|
|
1963
|
-
// it by host.
|
|
1964
|
-
const cancelElsewhere = new Event('cancel', {cancelable: true});
|
|
1965
|
-
getSheetDialog('a').dispatchEvent(cancelElsewhere);
|
|
1966
|
-
expect(cancelElsewhere.defaultPrevented).toBe(true);
|
|
1967
|
-
expect(onActiveSheetChange).toHaveBeenNthCalledWith(3, null);
|
|
1968
|
-
});
|
|
1969
|
-
|
|
1970
|
-
it('presents a non-modal flow with show() on each sheet dialog', () => {
|
|
1971
|
-
const log = recordDialogCalls();
|
|
1972
|
-
render(<HostFlow hasScrim={false} initialSheet="details" />);
|
|
1973
|
-
expect(log).toEqual(['show:Details']);
|
|
1974
|
-
|
|
1975
|
-
fireEvent.click(screen.getByRole('button', {name: 'Continue'}));
|
|
1976
|
-
|
|
1977
|
-
expect(log).toEqual(['show:Details', 'show:Confirm']);
|
|
1978
|
-
expect(openDialogs()).toHaveLength(2);
|
|
1979
|
-
expect(getSheetDialog('confirm-sheet')).not.toHaveAttribute('aria-modal');
|
|
1980
|
-
expect(isScrollLockEngaged()).toBe(false);
|
|
1981
|
-
|
|
1982
|
-
finishSheetTransition(getSheetLayer('confirm-sheet'), 'transform');
|
|
1983
|
-
finishSheetTransition(getSheetLayer('details-sheet'), 'opacity');
|
|
1984
|
-
expect(log).toEqual(['show:Details', 'show:Confirm', 'close:Details']);
|
|
1985
|
-
});
|
|
1986
|
-
|
|
1987
|
-
it('re-presents a sheet that is still fading when the flow returns to it', () => {
|
|
1988
|
-
const log = recordDialogCalls();
|
|
1989
|
-
render(<Flow />);
|
|
1990
|
-
fireEvent.click(screen.getByRole('button', {name: 'Start flow'}));
|
|
1991
|
-
fireEvent.click(screen.getByRole('button', {name: 'Continue'}));
|
|
1992
|
-
fireEvent.click(screen.getByRole('button', {name: 'Back'}));
|
|
1993
|
-
|
|
1994
|
-
// Details was still open beneath Confirm in the top layer; it closes and
|
|
1995
|
-
// presents again so the interactive sheet is the topmost modal layer.
|
|
1996
|
-
expect(log).toEqual([
|
|
1997
|
-
'showModal:Details',
|
|
1998
|
-
'showModal:Confirm',
|
|
1999
|
-
'close:Details',
|
|
2000
|
-
'showModal:Details',
|
|
2001
|
-
]);
|
|
2002
|
-
expect(getActiveDialog()).toBe(getSheetDialog('details-sheet'));
|
|
2003
|
-
expect(getSheetDialog('confirm-sheet')).toHaveAttribute('inert');
|
|
2004
|
-
|
|
2005
|
-
finishSheetTransition(getSheetLayer('confirm-sheet'), 'opacity');
|
|
2006
|
-
expect(log.at(-1)).toBe('close:Confirm');
|
|
2007
|
-
expect(openDialogs()).toEqual([getSheetDialog('details-sheet')]);
|
|
2008
|
-
});
|
|
2009
|
-
});
|
|
2010
|
-
});
|