@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,1737 +0,0 @@
|
|
|
1
|
-
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* @file TreeList.test.tsx
|
|
5
|
-
* @input Uses vitest, @testing-library/react, TreeList
|
|
6
|
-
* @output Unit tests for TreeList component
|
|
7
|
-
* @position Testing; validates TreeList.tsx implementation
|
|
8
|
-
*
|
|
9
|
-
* SYNC: When modified, update this header
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
import {describe, it, expect, vi} from 'vitest';
|
|
13
|
-
import {render, screen, fireEvent} from '@testing-library/react';
|
|
14
|
-
import type {ReactNode} from 'react';
|
|
15
|
-
import userEvent from '@testing-library/user-event';
|
|
16
|
-
import * as stylex from '@stylexjs/stylex';
|
|
17
|
-
import {TreeList} from './TreeList';
|
|
18
|
-
import type {TreeListItemData} from './TreeListTypes';
|
|
19
|
-
import {defineTheme} from '../theme/defineTheme';
|
|
20
|
-
import {generateThemeCSS} from '../theme/generateThemeRules';
|
|
21
|
-
import {FOCUS_OUTLINE} from '../utils/focusOutline.stylex';
|
|
22
|
-
|
|
23
|
-
function generateThemeTestCSS(theme: Parameters<typeof generateThemeCSS>[0]) {
|
|
24
|
-
const {prose, component} = generateThemeCSS(theme);
|
|
25
|
-
return [prose, component].filter(Boolean).join('\n\n');
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Concatenate all StyleX-injected CSS (both CSSOM sheets and <style> text) so
|
|
30
|
-
* tests can assert on compiled stylesheet rules — jsdom does not resolve the
|
|
31
|
-
* @layer cascade or compute layout, so structural CSS assertions read the
|
|
32
|
-
* generated declarations directly.
|
|
33
|
-
*/
|
|
34
|
-
function collectCssText(): string {
|
|
35
|
-
let out = '';
|
|
36
|
-
for (const sheet of Array.from(document.styleSheets)) {
|
|
37
|
-
try {
|
|
38
|
-
for (const rule of Array.from(sheet.cssRules)) {
|
|
39
|
-
out += rule.cssText + '\n';
|
|
40
|
-
}
|
|
41
|
-
} catch {
|
|
42
|
-
// ignore cross-origin sheets
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
out += Array.from(document.querySelectorAll('style'))
|
|
46
|
-
.map(s => s.textContent || '')
|
|
47
|
-
.join('\n');
|
|
48
|
-
return out;
|
|
49
|
-
}
|
|
50
|
-
const rowStyles = stylex.create({
|
|
51
|
-
custom: {opacity: 0.75},
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
const simpleItems: TreeListItemData[] = [
|
|
55
|
-
{id: 'a', label: 'Item A'},
|
|
56
|
-
{id: 'b', label: 'Item B'},
|
|
57
|
-
];
|
|
58
|
-
|
|
59
|
-
const nestedItems: TreeListItemData[] = [
|
|
60
|
-
{
|
|
61
|
-
id: 'parent',
|
|
62
|
-
label: 'Parent',
|
|
63
|
-
children: [
|
|
64
|
-
{id: 'child-1', label: 'Child 1'},
|
|
65
|
-
{id: 'child-2', label: 'Child 2'},
|
|
66
|
-
],
|
|
67
|
-
},
|
|
68
|
-
{id: 'sibling', label: 'Sibling'},
|
|
69
|
-
];
|
|
70
|
-
|
|
71
|
-
const nestedItemsExpanded: TreeListItemData[] = [
|
|
72
|
-
{
|
|
73
|
-
id: 'parent',
|
|
74
|
-
label: 'Parent',
|
|
75
|
-
isExpanded: true,
|
|
76
|
-
children: [
|
|
77
|
-
{id: 'child-1', label: 'Child 1'},
|
|
78
|
-
{id: 'child-2', label: 'Child 2'},
|
|
79
|
-
],
|
|
80
|
-
},
|
|
81
|
-
{id: 'sibling', label: 'Sibling'},
|
|
82
|
-
];
|
|
83
|
-
|
|
84
|
-
const deepItems: TreeListItemData[] = [
|
|
85
|
-
{
|
|
86
|
-
id: 'root',
|
|
87
|
-
label: 'Root',
|
|
88
|
-
isExpanded: true,
|
|
89
|
-
children: [
|
|
90
|
-
{
|
|
91
|
-
id: 'mid',
|
|
92
|
-
label: 'Mid',
|
|
93
|
-
isExpanded: true,
|
|
94
|
-
children: [{id: 'leaf', label: 'Leaf'}],
|
|
95
|
-
},
|
|
96
|
-
],
|
|
97
|
-
},
|
|
98
|
-
];
|
|
99
|
-
|
|
100
|
-
// APG keyboard fixtures (module-level to satisfy no-unstable-default-props).
|
|
101
|
-
const flatItems: TreeListItemData[] = [
|
|
102
|
-
{id: 'a', label: 'Apple'},
|
|
103
|
-
{id: 'b', label: 'Banana'},
|
|
104
|
-
{id: 'c', label: 'Cherry'},
|
|
105
|
-
];
|
|
106
|
-
|
|
107
|
-
const withDisabledItems: TreeListItemData[] = [
|
|
108
|
-
{id: 'a', label: 'Apple'},
|
|
109
|
-
{id: 'b', label: 'Banana', isDisabled: true},
|
|
110
|
-
{id: 'c', label: 'Cherry'},
|
|
111
|
-
];
|
|
112
|
-
|
|
113
|
-
const collapsedParentItems: TreeListItemData[] = [
|
|
114
|
-
{
|
|
115
|
-
id: 'parent',
|
|
116
|
-
label: 'Parent',
|
|
117
|
-
children: [
|
|
118
|
-
{id: 'child-1', label: 'Child 1'},
|
|
119
|
-
{id: 'child-2', label: 'Child 2'},
|
|
120
|
-
],
|
|
121
|
-
},
|
|
122
|
-
{id: 'sibling', label: 'Sibling'},
|
|
123
|
-
];
|
|
124
|
-
|
|
125
|
-
const expandedParentItems: TreeListItemData[] = [
|
|
126
|
-
{
|
|
127
|
-
id: 'parent',
|
|
128
|
-
label: 'Parent',
|
|
129
|
-
isExpanded: true,
|
|
130
|
-
children: [
|
|
131
|
-
{id: 'child-1', label: 'Child 1'},
|
|
132
|
-
{id: 'child-2', label: 'Child 2'},
|
|
133
|
-
],
|
|
134
|
-
},
|
|
135
|
-
{id: 'sibling', label: 'Sibling'},
|
|
136
|
-
];
|
|
137
|
-
|
|
138
|
-
describe('TreeList', () => {
|
|
139
|
-
// ===========================================================================
|
|
140
|
-
// Basic rendering
|
|
141
|
-
// ===========================================================================
|
|
142
|
-
|
|
143
|
-
it('renders items', () => {
|
|
144
|
-
render(<TreeList items={simpleItems} />);
|
|
145
|
-
expect(screen.getByText('Item A')).toBeInTheDocument();
|
|
146
|
-
expect(screen.getByText('Item B')).toBeInTheDocument();
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
it('renders with data-testid', () => {
|
|
150
|
-
render(<TreeList items={simpleItems} data-testid="tree" />);
|
|
151
|
-
expect(screen.getByTestId('tree')).toBeInTheDocument();
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
it('forwards aria-label to the tree element', () => {
|
|
155
|
-
render(<TreeList items={simpleItems} aria-label="File tree" />);
|
|
156
|
-
const tree = screen.getByRole('tree');
|
|
157
|
-
expect(tree).toHaveAttribute('aria-label', 'File tree');
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
it('forwards id to the root element', () => {
|
|
161
|
-
render(<TreeList items={simpleItems} id="file-tree" />);
|
|
162
|
-
const root = screen.getByRole('tree').parentElement;
|
|
163
|
-
expect(root).toHaveAttribute('id', 'file-tree');
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
it('renders description text', () => {
|
|
167
|
-
const items: TreeListItemData[] = [
|
|
168
|
-
{id: 'a', label: 'Label', description: 'Description text'},
|
|
169
|
-
];
|
|
170
|
-
render(<TreeList items={items} />);
|
|
171
|
-
expect(screen.getByText('Description text')).toBeInTheDocument();
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
// ===========================================================================
|
|
175
|
-
// Row accessible name and description (APG tree view: named by the label)
|
|
176
|
-
// ===========================================================================
|
|
177
|
-
|
|
178
|
-
it('names a parent row by its label alone, not the chevron toggle', () => {
|
|
179
|
-
const items: TreeListItemData[] = [
|
|
180
|
-
{id: 'docs', label: 'Documents', children: [{id: 'a', label: 'Annual'}]},
|
|
181
|
-
];
|
|
182
|
-
render(<TreeList items={items} />);
|
|
183
|
-
const row = screen.getByText('Documents').closest('li')!;
|
|
184
|
-
expect(row).toHaveAccessibleName('Documents');
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
it('names a plain row by its label and describes it with its description', () => {
|
|
188
|
-
const items: TreeListItemData[] = [
|
|
189
|
-
{id: 'a', label: 'Budget', description: 'Updated yesterday'},
|
|
190
|
-
];
|
|
191
|
-
render(<TreeList items={items} />);
|
|
192
|
-
const row = screen.getByText('Budget').closest('li')!;
|
|
193
|
-
expect(row).toHaveAccessibleName('Budget');
|
|
194
|
-
expect(row).toHaveAccessibleDescription('Updated yesterday');
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
it('names and describes link and action rows themselves, not only their inner control', () => {
|
|
198
|
-
const items: TreeListItemData[] = [
|
|
199
|
-
{id: 'l', label: 'Report', description: 'Opens the report', href: '#'},
|
|
200
|
-
{
|
|
201
|
-
id: 'b',
|
|
202
|
-
label: 'Export',
|
|
203
|
-
description: 'Downloads a file',
|
|
204
|
-
onClick: () => {},
|
|
205
|
-
},
|
|
206
|
-
];
|
|
207
|
-
render(<TreeList items={items} />);
|
|
208
|
-
const link = screen.getByText('Report').closest('li')!;
|
|
209
|
-
expect(link).toHaveAccessibleName('Report');
|
|
210
|
-
expect(link).toHaveAccessibleDescription('Opens the report');
|
|
211
|
-
const action = screen.getByText('Export').closest('li')!;
|
|
212
|
-
expect(action).toHaveAccessibleName('Export');
|
|
213
|
-
expect(action).toHaveAccessibleDescription('Downloads a file');
|
|
214
|
-
});
|
|
215
|
-
|
|
216
|
-
it('keeps start and end content, including controls, out of the row name', () => {
|
|
217
|
-
const items: TreeListItemData[] = [
|
|
218
|
-
{
|
|
219
|
-
id: 'a',
|
|
220
|
-
label: 'Invoices',
|
|
221
|
-
startContent: <span>12</span>,
|
|
222
|
-
endContent: <button type="button">Pin folder</button>,
|
|
223
|
-
},
|
|
224
|
-
];
|
|
225
|
-
render(<TreeList items={items} />);
|
|
226
|
-
const row = screen.getByText('Invoices').closest('li')!;
|
|
227
|
-
expect(row).toHaveAccessibleName('Invoices');
|
|
228
|
-
});
|
|
229
|
-
|
|
230
|
-
it('typeahead matches the label, not the description or start content', async () => {
|
|
231
|
-
const user = userEvent.setup();
|
|
232
|
-
const items: TreeListItemData[] = [
|
|
233
|
-
{id: 'a', label: 'Apple', description: 'Cherry-red fruit'},
|
|
234
|
-
{id: 'b', label: 'Banana', startContent: <span>Curved</span>},
|
|
235
|
-
{id: 'c', label: 'Cherry'},
|
|
236
|
-
];
|
|
237
|
-
render(<TreeList items={items} />);
|
|
238
|
-
const apple = screen.getByText('Apple').closest('li')!;
|
|
239
|
-
apple.focus();
|
|
240
|
-
await user.keyboard('c');
|
|
241
|
-
expect(document.activeElement).toBe(
|
|
242
|
-
screen.getByText('Cherry').closest('li'),
|
|
243
|
-
);
|
|
244
|
-
});
|
|
245
|
-
|
|
246
|
-
// ===========================================================================
|
|
247
|
-
// Semantic HTML
|
|
248
|
-
// ===========================================================================
|
|
249
|
-
|
|
250
|
-
it('renders a tree role on the list', () => {
|
|
251
|
-
render(<TreeList items={simpleItems} />);
|
|
252
|
-
expect(screen.getByRole('tree')).toBeInTheDocument();
|
|
253
|
-
});
|
|
254
|
-
|
|
255
|
-
it('renders treeitem role on items', () => {
|
|
256
|
-
render(<TreeList items={simpleItems} />);
|
|
257
|
-
const treeitems = screen.getAllByRole('treeitem');
|
|
258
|
-
expect(treeitems).toHaveLength(2);
|
|
259
|
-
});
|
|
260
|
-
|
|
261
|
-
it('renders items as <li> elements', () => {
|
|
262
|
-
const {container} = render(<TreeList items={simpleItems} />);
|
|
263
|
-
const items = container.querySelectorAll('li');
|
|
264
|
-
expect(items).toHaveLength(2);
|
|
265
|
-
});
|
|
266
|
-
|
|
267
|
-
// ===========================================================================
|
|
268
|
-
// Header with aria-labelledby
|
|
269
|
-
// ===========================================================================
|
|
270
|
-
|
|
271
|
-
it('renders header and associates via aria-labelledby', () => {
|
|
272
|
-
render(<TreeList items={simpleItems} header={<span>File Tree</span>} />);
|
|
273
|
-
expect(screen.getByText('File Tree')).toBeInTheDocument();
|
|
274
|
-
const tree = screen.getByRole('tree');
|
|
275
|
-
const headerId = tree.getAttribute('aria-labelledby');
|
|
276
|
-
expect(headerId).toBeTruthy();
|
|
277
|
-
const headerEl = document.getElementById(headerId!);
|
|
278
|
-
expect(headerEl?.textContent).toBe('File Tree');
|
|
279
|
-
});
|
|
280
|
-
|
|
281
|
-
it('does not render aria-labelledby when no header', () => {
|
|
282
|
-
render(<TreeList items={simpleItems} />);
|
|
283
|
-
const tree = screen.getByRole('tree');
|
|
284
|
-
expect(tree).not.toHaveAttribute('aria-labelledby');
|
|
285
|
-
});
|
|
286
|
-
|
|
287
|
-
it('prefers the visible header id over caller aria-labelledby when header exists', () => {
|
|
288
|
-
render(
|
|
289
|
-
<TreeList
|
|
290
|
-
items={simpleItems}
|
|
291
|
-
header={<span>File Tree</span>}
|
|
292
|
-
aria-labelledby="external-label"
|
|
293
|
-
/>,
|
|
294
|
-
);
|
|
295
|
-
const tree = screen.getByRole('tree');
|
|
296
|
-
const headerId = tree.getAttribute('aria-labelledby');
|
|
297
|
-
expect(headerId).toBeTruthy();
|
|
298
|
-
expect(headerId).not.toBe('external-label');
|
|
299
|
-
const headerEl = document.getElementById(headerId!);
|
|
300
|
-
expect(headerEl?.textContent).toBe('File Tree');
|
|
301
|
-
});
|
|
302
|
-
|
|
303
|
-
it('uses caller aria-labelledby on the headerless path', () => {
|
|
304
|
-
render(<TreeList items={simpleItems} aria-labelledby="external-label" />);
|
|
305
|
-
const tree = screen.getByRole('tree');
|
|
306
|
-
expect(tree).toHaveAttribute('aria-labelledby', 'external-label');
|
|
307
|
-
});
|
|
308
|
-
|
|
309
|
-
it('ignores caller aria-label when a header names the tree', () => {
|
|
310
|
-
render(
|
|
311
|
-
<TreeList
|
|
312
|
-
items={simpleItems}
|
|
313
|
-
header={<span>File Tree</span>}
|
|
314
|
-
aria-label="External name"
|
|
315
|
-
/>,
|
|
316
|
-
);
|
|
317
|
-
const tree = screen.getByRole('tree');
|
|
318
|
-
expect(tree).not.toHaveAttribute('aria-label');
|
|
319
|
-
const headerId = tree.getAttribute('aria-labelledby');
|
|
320
|
-
const headerEl = document.getElementById(headerId!);
|
|
321
|
-
expect(headerEl?.textContent).toBe('File Tree');
|
|
322
|
-
});
|
|
323
|
-
|
|
324
|
-
// ===========================================================================
|
|
325
|
-
// Expansion (internal state)
|
|
326
|
-
// ===========================================================================
|
|
327
|
-
|
|
328
|
-
it('does not render children by default', () => {
|
|
329
|
-
render(<TreeList items={nestedItems} />);
|
|
330
|
-
expect(screen.getByText('Parent')).toBeInTheDocument();
|
|
331
|
-
expect(screen.queryByText('Child 1')).not.toBeInTheDocument();
|
|
332
|
-
});
|
|
333
|
-
|
|
334
|
-
it('renders children when item has isExpanded: true', () => {
|
|
335
|
-
render(<TreeList items={nestedItemsExpanded} />);
|
|
336
|
-
expect(screen.getByText('Child 1')).toBeInTheDocument();
|
|
337
|
-
expect(screen.getByText('Child 2')).toBeInTheDocument();
|
|
338
|
-
});
|
|
339
|
-
|
|
340
|
-
it('sets aria-expanded on items with children', () => {
|
|
341
|
-
render(<TreeList items={nestedItemsExpanded} />);
|
|
342
|
-
const parent = screen.getByText('Parent').closest('li');
|
|
343
|
-
expect(parent).toHaveAttribute('aria-expanded', 'true');
|
|
344
|
-
});
|
|
345
|
-
|
|
346
|
-
it('sets aria-expanded=false on collapsed items with children', () => {
|
|
347
|
-
render(<TreeList items={nestedItems} />);
|
|
348
|
-
const parent = screen.getByText('Parent').closest('li');
|
|
349
|
-
expect(parent).toHaveAttribute('aria-expanded', 'false');
|
|
350
|
-
});
|
|
351
|
-
|
|
352
|
-
it('does not set aria-expanded on leaf items', () => {
|
|
353
|
-
render(<TreeList items={simpleItems} />);
|
|
354
|
-
const item = screen.getByText('Item A').closest('li');
|
|
355
|
-
expect(item).not.toHaveAttribute('aria-expanded');
|
|
356
|
-
});
|
|
357
|
-
|
|
358
|
-
it('renders a keyboard-focusable toggle button for parents without onClick/href', () => {
|
|
359
|
-
render(<TreeList items={nestedItems} />);
|
|
360
|
-
const toggle = screen.getByRole('button', {name: 'Toggle children'});
|
|
361
|
-
expect(toggle).toBeInTheDocument();
|
|
362
|
-
expect(toggle).toHaveAttribute('aria-expanded', 'false');
|
|
363
|
-
});
|
|
364
|
-
|
|
365
|
-
it('expands a parent from the keyboard via the toggle button', async () => {
|
|
366
|
-
const user = userEvent.setup();
|
|
367
|
-
render(<TreeList items={nestedItems} />);
|
|
368
|
-
// Collapsed: children are not rendered.
|
|
369
|
-
expect(screen.queryByText('Child 1')).not.toBeInTheDocument();
|
|
370
|
-
const toggle = screen.getByRole('button', {name: 'Toggle children'});
|
|
371
|
-
toggle.focus();
|
|
372
|
-
expect(toggle).toHaveFocus();
|
|
373
|
-
await user.keyboard('{Enter}');
|
|
374
|
-
expect(screen.getByText('Child 1')).toBeInTheDocument();
|
|
375
|
-
expect(toggle).toHaveAttribute('aria-expanded', 'true');
|
|
376
|
-
});
|
|
377
|
-
|
|
378
|
-
it('renders group role for nested children', () => {
|
|
379
|
-
render(<TreeList items={nestedItemsExpanded} />);
|
|
380
|
-
const groups = document.querySelectorAll('[role="group"]');
|
|
381
|
-
expect(groups.length).toBeGreaterThanOrEqual(1);
|
|
382
|
-
});
|
|
383
|
-
|
|
384
|
-
it('expands a collapsed item when clicked', async () => {
|
|
385
|
-
const user = userEvent.setup();
|
|
386
|
-
render(<TreeList items={nestedItems} />);
|
|
387
|
-
expect(screen.queryByText('Child 1')).not.toBeInTheDocument();
|
|
388
|
-
await user.click(screen.getByText('Parent'));
|
|
389
|
-
expect(screen.getByText('Child 1')).toBeInTheDocument();
|
|
390
|
-
});
|
|
391
|
-
|
|
392
|
-
it('collapses an expanded item when clicked', async () => {
|
|
393
|
-
const user = userEvent.setup();
|
|
394
|
-
render(<TreeList items={nestedItemsExpanded} />);
|
|
395
|
-
expect(screen.getByText('Child 1')).toBeInTheDocument();
|
|
396
|
-
await user.click(screen.getByText('Parent'));
|
|
397
|
-
expect(screen.queryByText('Child 1')).not.toBeInTheDocument();
|
|
398
|
-
});
|
|
399
|
-
|
|
400
|
-
// ===========================================================================
|
|
401
|
-
// Deep nesting
|
|
402
|
-
// ===========================================================================
|
|
403
|
-
|
|
404
|
-
it('renders deeply nested items when all expanded', () => {
|
|
405
|
-
render(<TreeList items={deepItems} />);
|
|
406
|
-
expect(screen.getByText('Root')).toBeInTheDocument();
|
|
407
|
-
expect(screen.getByText('Mid')).toBeInTheDocument();
|
|
408
|
-
expect(screen.getByText('Leaf')).toBeInTheDocument();
|
|
409
|
-
});
|
|
410
|
-
|
|
411
|
-
// ===========================================================================
|
|
412
|
-
// Focus-visible outline scoping (regression: focusing a parent row must not
|
|
413
|
-
// leak the ring onto descendant rows — see #4130)
|
|
414
|
-
// ===========================================================================
|
|
415
|
-
|
|
416
|
-
it('scopes the focus-visible outline to the focused row, not its descendants', () => {
|
|
417
|
-
render(<TreeList items={deepItems} />);
|
|
418
|
-
const root = screen.getByText('Root').closest('li')!;
|
|
419
|
-
const mid = screen.getByText('Mid').closest('li')!;
|
|
420
|
-
const leaf = screen.getByText('Leaf').closest('li')!;
|
|
421
|
-
|
|
422
|
-
// A keydown before .focus() establishes keyboard modality so jsdom's
|
|
423
|
-
// :focus-visible heuristic applies deterministically, regardless of
|
|
424
|
-
// pointer events left over from other tests in this file.
|
|
425
|
-
fireEvent.keyDown(document.body, {key: 'Tab'});
|
|
426
|
-
root.focus();
|
|
427
|
-
expect(root).toHaveFocus();
|
|
428
|
-
|
|
429
|
-
expect(
|
|
430
|
-
getComputedStyle(root).getPropertyValue('--_focus-outline').trim(),
|
|
431
|
-
).toBe(FOCUS_OUTLINE);
|
|
432
|
-
// Mid and Leaf are DOM descendants of Root's <li> (nested <ul role="group">
|
|
433
|
-
// subtrees) — their own outline var must stay unset, not inherit Root's.
|
|
434
|
-
expect(getComputedStyle(mid).getPropertyValue('--_focus-outline')).toBe(
|
|
435
|
-
'none',
|
|
436
|
-
);
|
|
437
|
-
expect(getComputedStyle(leaf).getPropertyValue('--_focus-outline')).toBe(
|
|
438
|
-
'none',
|
|
439
|
-
);
|
|
440
|
-
});
|
|
441
|
-
|
|
442
|
-
// ===========================================================================
|
|
443
|
-
// Interactive items
|
|
444
|
-
// ===========================================================================
|
|
445
|
-
|
|
446
|
-
it('renders an invisible button when onClick is provided', () => {
|
|
447
|
-
const items: TreeListItemData[] = [
|
|
448
|
-
{id: 'a', label: 'Clickable', onClick: () => {}},
|
|
449
|
-
];
|
|
450
|
-
const {container} = render(<TreeList items={items} />);
|
|
451
|
-
const button = container.querySelector('button');
|
|
452
|
-
expect(button).toBeInTheDocument();
|
|
453
|
-
expect(button?.textContent).toContain('Clickable');
|
|
454
|
-
});
|
|
455
|
-
|
|
456
|
-
it('fires onClick when invisible button is clicked', async () => {
|
|
457
|
-
const user = userEvent.setup();
|
|
458
|
-
const onClick = vi.fn();
|
|
459
|
-
const items: TreeListItemData[] = [{id: 'a', label: 'Clickable', onClick}];
|
|
460
|
-
render(<TreeList items={items} />);
|
|
461
|
-
await user.click(screen.getByRole('button'));
|
|
462
|
-
expect(onClick).toHaveBeenCalledTimes(1);
|
|
463
|
-
});
|
|
464
|
-
|
|
465
|
-
it('renders an invisible anchor when href is provided', () => {
|
|
466
|
-
const items: TreeListItemData[] = [{id: 'a', label: 'Link', href: '/docs'}];
|
|
467
|
-
const {container} = render(<TreeList items={items} />);
|
|
468
|
-
const anchor = container.querySelector('a');
|
|
469
|
-
expect(anchor).toBeInTheDocument();
|
|
470
|
-
expect(anchor).toHaveAttribute('href', '/docs');
|
|
471
|
-
});
|
|
472
|
-
|
|
473
|
-
it('does not render button or anchor for static items', () => {
|
|
474
|
-
const {container} = render(<TreeList items={simpleItems} />);
|
|
475
|
-
expect(container.querySelector('button')).not.toBeInTheDocument();
|
|
476
|
-
expect(container.querySelector('a')).not.toBeInTheDocument();
|
|
477
|
-
});
|
|
478
|
-
|
|
479
|
-
// ===========================================================================
|
|
480
|
-
// Disabled state
|
|
481
|
-
// ===========================================================================
|
|
482
|
-
|
|
483
|
-
it('applies aria-disabled when isDisabled', () => {
|
|
484
|
-
const items: TreeListItemData[] = [
|
|
485
|
-
{id: 'a', label: 'Disabled', isDisabled: true},
|
|
486
|
-
];
|
|
487
|
-
render(<TreeList items={items} />);
|
|
488
|
-
const li = screen.getByText('Disabled').closest('li');
|
|
489
|
-
expect(li).toHaveAttribute('aria-disabled', 'true');
|
|
490
|
-
});
|
|
491
|
-
|
|
492
|
-
// ===========================================================================
|
|
493
|
-
// Selected state
|
|
494
|
-
// ===========================================================================
|
|
495
|
-
|
|
496
|
-
it('applies aria-selected when isSelected', () => {
|
|
497
|
-
const items: TreeListItemData[] = [
|
|
498
|
-
{id: 'a', label: 'Selected', isSelected: true},
|
|
499
|
-
];
|
|
500
|
-
render(<TreeList items={items} />);
|
|
501
|
-
const li = screen.getByText('Selected').closest('li');
|
|
502
|
-
expect(li).toHaveAttribute('aria-selected', 'true');
|
|
503
|
-
});
|
|
504
|
-
|
|
505
|
-
it('does not apply aria-selected when not selected', () => {
|
|
506
|
-
render(<TreeList items={simpleItems} />);
|
|
507
|
-
const li = screen.getByText('Item A').closest('li');
|
|
508
|
-
expect(li).not.toHaveAttribute('aria-selected');
|
|
509
|
-
});
|
|
510
|
-
|
|
511
|
-
// ===========================================================================
|
|
512
|
-
// startContent and endContent
|
|
513
|
-
// ===========================================================================
|
|
514
|
-
|
|
515
|
-
it('renders startContent', () => {
|
|
516
|
-
const items: TreeListItemData[] = [
|
|
517
|
-
{
|
|
518
|
-
id: 'a',
|
|
519
|
-
label: 'With Icon',
|
|
520
|
-
startContent: <span data-testid="icon">★</span>,
|
|
521
|
-
},
|
|
522
|
-
];
|
|
523
|
-
render(<TreeList items={items} />);
|
|
524
|
-
expect(screen.getByTestId('icon')).toBeInTheDocument();
|
|
525
|
-
});
|
|
526
|
-
|
|
527
|
-
it('renders endContent', () => {
|
|
528
|
-
const items: TreeListItemData[] = [
|
|
529
|
-
{
|
|
530
|
-
id: 'a',
|
|
531
|
-
label: 'With Badge',
|
|
532
|
-
endContent: <span data-testid="badge">3</span>,
|
|
533
|
-
},
|
|
534
|
-
];
|
|
535
|
-
render(<TreeList items={items} />);
|
|
536
|
-
expect(screen.getByTestId('badge')).toBeInTheDocument();
|
|
537
|
-
});
|
|
538
|
-
|
|
539
|
-
it('forwards xstyle, className, and style to an item row', () => {
|
|
540
|
-
const items: TreeListItemData[] = [
|
|
541
|
-
{
|
|
542
|
-
id: 'a',
|
|
543
|
-
label: 'Styled row',
|
|
544
|
-
xstyle: rowStyles.custom,
|
|
545
|
-
className: 'consumer-row',
|
|
546
|
-
style: {visibility: 'visible'},
|
|
547
|
-
},
|
|
548
|
-
];
|
|
549
|
-
render(<TreeList items={items} />);
|
|
550
|
-
|
|
551
|
-
const row = screen
|
|
552
|
-
.getByText('Styled row')
|
|
553
|
-
.closest('li')!
|
|
554
|
-
.querySelector('.astryx-tree-list-item')!;
|
|
555
|
-
expect(row).toHaveClass('consumer-row');
|
|
556
|
-
expect(row.className).toContain(
|
|
557
|
-
stylex.props(rowStyles.custom).className!.split(' ')[0],
|
|
558
|
-
);
|
|
559
|
-
expect(row).toHaveStyle({visibility: 'visible'});
|
|
560
|
-
expect(row.getAttribute('style')).toContain('--_tree-indent');
|
|
561
|
-
});
|
|
562
|
-
|
|
563
|
-
// ===========================================================================
|
|
564
|
-
// Density
|
|
565
|
-
// ===========================================================================
|
|
566
|
-
|
|
567
|
-
it('renders with compact density', () => {
|
|
568
|
-
render(<TreeList items={simpleItems} density="compact" />);
|
|
569
|
-
expect(screen.getByRole('tree')).toBeInTheDocument();
|
|
570
|
-
});
|
|
571
|
-
|
|
572
|
-
it('renders with spacious density', () => {
|
|
573
|
-
render(<TreeList items={simpleItems} density="spacious" />);
|
|
574
|
-
expect(screen.getByRole('tree')).toBeInTheDocument();
|
|
575
|
-
});
|
|
576
|
-
|
|
577
|
-
// ===========================================================================
|
|
578
|
-
// Variant (guide lines)
|
|
579
|
-
// ===========================================================================
|
|
580
|
-
|
|
581
|
-
describe('variant', () => {
|
|
582
|
-
it('renders guide lines by default', () => {
|
|
583
|
-
const {container} = render(<TreeList items={nestedItemsExpanded} />);
|
|
584
|
-
expect(container.querySelector('.astryx-tree-list-guide')).not.toBeNull();
|
|
585
|
-
});
|
|
586
|
-
|
|
587
|
-
it("variant='lineGuides' renders guide lines (explicit == default)", () => {
|
|
588
|
-
const {container} = render(
|
|
589
|
-
<TreeList items={nestedItemsExpanded} variant="lineGuides" />,
|
|
590
|
-
);
|
|
591
|
-
expect(container.querySelector('.astryx-tree-list-guide')).not.toBeNull();
|
|
592
|
-
});
|
|
593
|
-
|
|
594
|
-
it("variant='noGuides' renders NO guide lines", () => {
|
|
595
|
-
const {container} = render(
|
|
596
|
-
<TreeList items={nestedItemsExpanded} variant="noGuides" />,
|
|
597
|
-
);
|
|
598
|
-
expect(container.querySelector('.astryx-tree-list-guide')).toBeNull();
|
|
599
|
-
});
|
|
600
|
-
|
|
601
|
-
it("variant='noGuides' preserves the tree structure and items", () => {
|
|
602
|
-
render(<TreeList items={nestedItemsExpanded} variant="noGuides" />);
|
|
603
|
-
// Rows, roles, and nesting are all intact — only the connectors are gone.
|
|
604
|
-
expect(screen.getByRole('tree')).toBeInTheDocument();
|
|
605
|
-
expect(screen.getAllByRole('treeitem')).toHaveLength(4);
|
|
606
|
-
expect(screen.getByText('Parent')).toBeInTheDocument();
|
|
607
|
-
expect(screen.getByText('Child 1')).toBeInTheDocument();
|
|
608
|
-
expect(screen.getByText('Child 2')).toBeInTheDocument();
|
|
609
|
-
expect(screen.getByText('Sibling')).toBeInTheDocument();
|
|
610
|
-
});
|
|
611
|
-
|
|
612
|
-
it("variant='noGuides' preserves per-level indentation on the rows", () => {
|
|
613
|
-
// Indentation lives inside the row's padding-inline-start (not the
|
|
614
|
-
// guide element), so it must survive when the connectors are
|
|
615
|
-
// suppressed. The per-row distance is published as the `--_tree-indent`
|
|
616
|
-
// custom property (not an inline longhand — see #4308), so the theme
|
|
617
|
-
// layer can override the `padding-inline-start` declaration. A deeper
|
|
618
|
-
// row is indented more than a shallower one.
|
|
619
|
-
const {container} = render(
|
|
620
|
-
<TreeList items={deepItems} variant="noGuides" />,
|
|
621
|
-
);
|
|
622
|
-
const indentOf = (text: string): string => {
|
|
623
|
-
const li = screen.getByText(text).closest('li')!;
|
|
624
|
-
const styled = li.querySelector('[style*="--_tree-indent"]');
|
|
625
|
-
return styled?.getAttribute('style') ?? '';
|
|
626
|
-
};
|
|
627
|
-
// Guides are gone…
|
|
628
|
-
expect(container.querySelector('.astryx-tree-list-guide')).toBeNull();
|
|
629
|
-
// …but each level still publishes an indent distance, and the level
|
|
630
|
-
// multiplier grows with depth (0, 1, 2).
|
|
631
|
-
expect(indentOf('Root')).toContain('--_tree-indent');
|
|
632
|
-
expect(indentOf('Mid')).toContain('--_tree-indent');
|
|
633
|
-
expect(indentOf('Leaf')).toContain('--_tree-indent');
|
|
634
|
-
const level = (text: string): number => {
|
|
635
|
-
const m = /calc\((\d+)/.exec(indentOf(text));
|
|
636
|
-
return m ? Number(m[1]) : NaN;
|
|
637
|
-
};
|
|
638
|
-
expect(level('Mid')).toBeGreaterThan(level('Root'));
|
|
639
|
-
expect(level('Leaf')).toBeGreaterThan(level('Mid'));
|
|
640
|
-
});
|
|
641
|
-
});
|
|
642
|
-
|
|
643
|
-
// ===========================================================================
|
|
644
|
-
// Guide theme target
|
|
645
|
-
// ===========================================================================
|
|
646
|
-
|
|
647
|
-
describe('guide theme target', () => {
|
|
648
|
-
it('renders the astryx-tree-list-guide target on the connector lines', () => {
|
|
649
|
-
const {container} = render(<TreeList items={nestedItemsExpanded} />);
|
|
650
|
-
const guide = container.querySelector('.astryx-tree-list-guide');
|
|
651
|
-
// A dedicated, stable target so a theme can recolor or hide the guides
|
|
652
|
-
// without hiding the built-in connectors and reimplementing them.
|
|
653
|
-
expect(guide).not.toBeNull();
|
|
654
|
-
});
|
|
655
|
-
|
|
656
|
-
it('exposes tree-list-guide as a themeable defineTheme target', () => {
|
|
657
|
-
// jsdom cannot resolve the @layer cascade, so the generated CSS is what
|
|
658
|
-
// proves a theme override reaches the guide element.
|
|
659
|
-
const theme = defineTheme({
|
|
660
|
-
name: 'tree-list-guide-test',
|
|
661
|
-
components: {
|
|
662
|
-
'tree-list-guide': {
|
|
663
|
-
base: {backgroundColor: 'var(--color-accent)'},
|
|
664
|
-
},
|
|
665
|
-
},
|
|
666
|
-
});
|
|
667
|
-
const css = generateThemeTestCSS(theme);
|
|
668
|
-
expect(css).toContain('.astryx-tree-list-guide {');
|
|
669
|
-
expect(css).toContain('background-color: var(--color-accent)');
|
|
670
|
-
});
|
|
671
|
-
|
|
672
|
-
it('lets a theme hide the guides via display: none on the target', () => {
|
|
673
|
-
// Hiding the guides is done through the theme target, not a prop — the
|
|
674
|
-
// theme rule lands in @layer astryx-theme, above StyleX's base layer.
|
|
675
|
-
const theme = defineTheme({
|
|
676
|
-
name: 'tree-list-guide-hidden-test',
|
|
677
|
-
components: {
|
|
678
|
-
'tree-list-guide': {
|
|
679
|
-
base: {display: 'none'},
|
|
680
|
-
},
|
|
681
|
-
},
|
|
682
|
-
});
|
|
683
|
-
const css = generateThemeTestCSS(theme);
|
|
684
|
-
expect(css).toContain('.astryx-tree-list-guide {');
|
|
685
|
-
expect(css).toContain('display: none');
|
|
686
|
-
});
|
|
687
|
-
});
|
|
688
|
-
|
|
689
|
-
// ===========================================================================
|
|
690
|
-
// Indent lever (--tree-list-indent)
|
|
691
|
-
// ===========================================================================
|
|
692
|
-
|
|
693
|
-
describe('indent lever', () => {
|
|
694
|
-
it('lets a theme retune the indent step via the tree-list target', () => {
|
|
695
|
-
// The per-level step is a public, themeable var (default --spacing-4) set
|
|
696
|
-
// on the tree-list root, so a theme can retune the indent metric (e.g. to
|
|
697
|
-
// --spacing-5) via defineTheme. jsdom cannot resolve the @layer cascade,
|
|
698
|
-
// so the generated CSS is what proves the override reaches the lever.
|
|
699
|
-
const theme = defineTheme({
|
|
700
|
-
name: 'tree-list-indent-test',
|
|
701
|
-
components: {
|
|
702
|
-
'tree-list': {
|
|
703
|
-
base: {'--tree-list-indent': 'var(--spacing-5)'},
|
|
704
|
-
},
|
|
705
|
-
},
|
|
706
|
-
});
|
|
707
|
-
const css = generateThemeTestCSS(theme);
|
|
708
|
-
expect(css).toContain('.astryx-tree-list {');
|
|
709
|
-
expect(css).toContain('--tree-list-indent: var(--spacing-5)');
|
|
710
|
-
});
|
|
711
|
-
|
|
712
|
-
it('rows consume the indent step in their published indent distance', () => {
|
|
713
|
-
// Each row's --_tree-indent is calc(level * var(--tree-list-indent)),
|
|
714
|
-
// so retuning the step scales every level uniformly.
|
|
715
|
-
render(<TreeList items={deepItems} variant="noGuides" />);
|
|
716
|
-
const indentOf = (text: string): string => {
|
|
717
|
-
const li = screen.getByText(text).closest('li')!;
|
|
718
|
-
const styled = li.querySelector('[style*="--_tree-indent"]');
|
|
719
|
-
return styled?.getAttribute('style') ?? '';
|
|
720
|
-
};
|
|
721
|
-
expect(indentOf('Mid')).toContain('var(--tree-list-indent)');
|
|
722
|
-
expect(indentOf('Leaf')).toContain('var(--tree-list-indent)');
|
|
723
|
-
});
|
|
724
|
-
|
|
725
|
-
it('indents inside the row so nested rows keep the full list width', () => {
|
|
726
|
-
// The row box keeps its own inline padding and stays full width; the
|
|
727
|
-
// indent is padding on a child inside it. A nested row's hover and
|
|
728
|
-
// selected fill therefore spans the list like a root row's, while its
|
|
729
|
-
// content lands where a margin indent would have put it.
|
|
730
|
-
render(<TreeList items={deepItems} />);
|
|
731
|
-
const rules = collectCssText();
|
|
732
|
-
expect(rules).toMatch(
|
|
733
|
-
/padding-inline-start:\s*var\(--_tree-indent,\s*0px\)/,
|
|
734
|
-
);
|
|
735
|
-
expect(rules).not.toMatch(/margin-inline-start:\s*var\(--_tree-indent/);
|
|
736
|
-
|
|
737
|
-
const row = screen
|
|
738
|
-
.getByText('Leaf')
|
|
739
|
-
.closest<HTMLElement>('.astryx-tree-list-item')!;
|
|
740
|
-
const indented = row.firstElementChild as HTMLElement;
|
|
741
|
-
expect(indented.className).toContain('indentedContent');
|
|
742
|
-
expect(row.className).not.toContain('indentedContent');
|
|
743
|
-
});
|
|
744
|
-
|
|
745
|
-
it("keeps the indent when a caller sets the row's padding", () => {
|
|
746
|
-
// Caller padding lands on the row box; the indent is on a separate
|
|
747
|
-
// element inside it, so the two compose instead of one replacing the
|
|
748
|
-
// other.
|
|
749
|
-
const items = [
|
|
750
|
-
{
|
|
751
|
-
id: 'root',
|
|
752
|
-
label: 'Root',
|
|
753
|
-
isExpanded: true,
|
|
754
|
-
children: [
|
|
755
|
-
{id: 'leaf', label: 'Leaf', style: {paddingInlineStart: '20px'}},
|
|
756
|
-
],
|
|
757
|
-
},
|
|
758
|
-
];
|
|
759
|
-
render(<TreeList items={items} />);
|
|
760
|
-
const row = screen
|
|
761
|
-
.getByText('Leaf')
|
|
762
|
-
.closest<HTMLElement>('.astryx-tree-list-item')!;
|
|
763
|
-
expect(row.style.paddingInlineStart).toBe('20px');
|
|
764
|
-
expect(row.getAttribute('style')).toContain('--_tree-indent');
|
|
765
|
-
expect((row.firstElementChild as HTMLElement).className).toContain(
|
|
766
|
-
'indentedContent',
|
|
767
|
-
);
|
|
768
|
-
});
|
|
769
|
-
|
|
770
|
-
describe('link rows', () => {
|
|
771
|
-
const linkItems = (endContent?: ReactNode): TreeListItemData[] => [
|
|
772
|
-
{
|
|
773
|
-
id: 'docs',
|
|
774
|
-
label: 'Docs',
|
|
775
|
-
isExpanded: true,
|
|
776
|
-
children: [
|
|
777
|
-
{id: 'report', label: 'Report', href: '/report', endContent},
|
|
778
|
-
],
|
|
779
|
-
},
|
|
780
|
-
];
|
|
781
|
-
const rowSurfaceOf = (link: HTMLElement) =>
|
|
782
|
-
link.closest<HTMLElement>('.astryx-tree-list-item')!
|
|
783
|
-
.firstElementChild as HTMLElement;
|
|
784
|
-
|
|
785
|
-
it('follows the link when the row is clicked outside its label', () => {
|
|
786
|
-
// The indent is part of the row now, so a click there must do what
|
|
787
|
-
// the row does. The click is delegated to the anchor through the
|
|
788
|
-
// shared enlarged-surface hook, modifier keys included.
|
|
789
|
-
render(<TreeList items={linkItems()} />);
|
|
790
|
-
const link = screen.getByRole('link', {name: 'Report'});
|
|
791
|
-
const clicks: MouseEvent[] = [];
|
|
792
|
-
link.addEventListener('click', e => {
|
|
793
|
-
clicks.push(e);
|
|
794
|
-
e.preventDefault();
|
|
795
|
-
});
|
|
796
|
-
|
|
797
|
-
fireEvent.click(rowSurfaceOf(link), {metaKey: true});
|
|
798
|
-
expect(clicks).toHaveLength(1);
|
|
799
|
-
expect(clicks[0].metaKey).toBe(true);
|
|
800
|
-
|
|
801
|
-
// The row box's own padding follows the link too.
|
|
802
|
-
fireEvent.click(rowSurfaceOf(link).parentElement!);
|
|
803
|
-
expect(clicks).toHaveLength(2);
|
|
804
|
-
|
|
805
|
-
// Clicking the label itself reaches the anchor once, not twice.
|
|
806
|
-
fireEvent.click(link);
|
|
807
|
-
expect(clicks).toHaveLength(3);
|
|
808
|
-
});
|
|
809
|
-
|
|
810
|
-
it('reports one click to ancestors per press', () => {
|
|
811
|
-
// The delegated click bubbles from the anchor; the original stops,
|
|
812
|
-
// so a listener above the tree sees exactly one click.
|
|
813
|
-
const onTreeClick = vi.fn();
|
|
814
|
-
render(<TreeList items={linkItems()} onClick={onTreeClick} />);
|
|
815
|
-
const link = screen.getByRole('link', {name: 'Report'});
|
|
816
|
-
link.addEventListener('click', e => e.preventDefault());
|
|
817
|
-
|
|
818
|
-
fireEvent.click(rowSurfaceOf(link));
|
|
819
|
-
expect(onTreeClick).toHaveBeenCalledTimes(1);
|
|
820
|
-
expect(onTreeClick.mock.calls[0][0].target).toBe(link);
|
|
821
|
-
});
|
|
822
|
-
|
|
823
|
-
it('leaves a role-based control in the row to itself', () => {
|
|
824
|
-
const onSwitch = vi.fn();
|
|
825
|
-
render(
|
|
826
|
-
<TreeList
|
|
827
|
-
items={linkItems(
|
|
828
|
-
<span
|
|
829
|
-
role="switch"
|
|
830
|
-
aria-checked="false"
|
|
831
|
-
aria-label="Pin"
|
|
832
|
-
tabIndex={0}
|
|
833
|
-
onClick={onSwitch}>
|
|
834
|
-
Pin
|
|
835
|
-
</span>,
|
|
836
|
-
)}
|
|
837
|
-
/>,
|
|
838
|
-
);
|
|
839
|
-
const link = screen.getByRole('link', {name: 'Report'});
|
|
840
|
-
const onLinkClick = vi.fn((e: Event) => e.preventDefault());
|
|
841
|
-
link.addEventListener('click', onLinkClick);
|
|
842
|
-
|
|
843
|
-
fireEvent.click(screen.getByRole('switch', {name: 'Pin'}));
|
|
844
|
-
expect(onSwitch).toHaveBeenCalledTimes(1);
|
|
845
|
-
expect(onLinkClick).not.toHaveBeenCalled();
|
|
846
|
-
});
|
|
847
|
-
|
|
848
|
-
it('opens the link in a new tab on a middle-click in the row', () => {
|
|
849
|
-
const open = vi.spyOn(window, 'open').mockImplementation(() => null);
|
|
850
|
-
render(<TreeList items={linkItems()} />);
|
|
851
|
-
const link = screen.getByRole('link', {name: 'Report'});
|
|
852
|
-
|
|
853
|
-
fireEvent.mouseUp(rowSurfaceOf(link), {button: 1});
|
|
854
|
-
expect(open).toHaveBeenCalledWith('/report', '_blank', 'noopener');
|
|
855
|
-
|
|
856
|
-
// A middle-click on the label is the browser's own.
|
|
857
|
-
open.mockClear();
|
|
858
|
-
fireEvent.mouseUp(link, {button: 1});
|
|
859
|
-
expect(open).not.toHaveBeenCalled();
|
|
860
|
-
open.mockRestore();
|
|
861
|
-
});
|
|
862
|
-
});
|
|
863
|
-
|
|
864
|
-
it("keeps focus on an item's inner link when the item becomes disabled (spec:AST-085 FR4)", () => {
|
|
865
|
-
const tree = (isDisabled: boolean) => [
|
|
866
|
-
{
|
|
867
|
-
id: 'docs',
|
|
868
|
-
label: 'Docs',
|
|
869
|
-
isExpanded: true,
|
|
870
|
-
children: [
|
|
871
|
-
{id: 'report', label: 'Report', href: '/report', isDisabled},
|
|
872
|
-
],
|
|
873
|
-
},
|
|
874
|
-
];
|
|
875
|
-
const {rerender} = render(<TreeList items={tree(false)} />);
|
|
876
|
-
// A pointer click leaves focus on the inner link.
|
|
877
|
-
screen.getByRole('link', {name: 'Report', hidden: true}).focus();
|
|
878
|
-
rerender(<TreeList items={tree(true)} />);
|
|
879
|
-
const disabled = screen.getByRole('link', {name: 'Report', hidden: true});
|
|
880
|
-
expect(disabled).not.toHaveAttribute('href');
|
|
881
|
-
expect(disabled).toHaveFocus();
|
|
882
|
-
});
|
|
883
|
-
|
|
884
|
-
it('does not follow a disabled link row', () => {
|
|
885
|
-
const items = [
|
|
886
|
-
{
|
|
887
|
-
id: 'docs',
|
|
888
|
-
label: 'Docs',
|
|
889
|
-
isExpanded: true,
|
|
890
|
-
children: [
|
|
891
|
-
{id: 'report', label: 'Report', href: '/report', isDisabled: true},
|
|
892
|
-
],
|
|
893
|
-
},
|
|
894
|
-
];
|
|
895
|
-
render(<TreeList items={items} />);
|
|
896
|
-
const link = screen.getByRole('link', {name: 'Report', hidden: true});
|
|
897
|
-
const onLinkClick = vi.fn((e: Event) => e.preventDefault());
|
|
898
|
-
link.addEventListener('click', onLinkClick);
|
|
899
|
-
const row = link.closest<HTMLElement>('.astryx-tree-list-item')!;
|
|
900
|
-
fireEvent.click(row.firstElementChild!);
|
|
901
|
-
expect(onLinkClick).not.toHaveBeenCalled();
|
|
902
|
-
// No destination, so a screen reader's click or a link menu cannot
|
|
903
|
-
// navigate either (spec:AST-085 FR2).
|
|
904
|
-
expect(link).toHaveAttribute('aria-disabled', 'true');
|
|
905
|
-
for (const attribute of ['href', 'target']) {
|
|
906
|
-
expect(link).not.toHaveAttribute(attribute);
|
|
907
|
-
}
|
|
908
|
-
});
|
|
909
|
-
|
|
910
|
-
describe('row clicks outside the label (FR7)', () => {
|
|
911
|
-
// Each row sits under an expanded root, so it has an indent; the click
|
|
912
|
-
// lands on the row surface (indent, reserved chevron column, padding),
|
|
913
|
-
// not on the label.
|
|
914
|
-
const underRoot = (children: TreeListItemData[]): TreeListItemData[] => [
|
|
915
|
-
{id: 'root', label: 'Root', isExpanded: true, children},
|
|
916
|
-
];
|
|
917
|
-
// The row box itself (its own padding) and the indented content inside
|
|
918
|
-
// it: FR7 covers clicks on both.
|
|
919
|
-
const rowOf = (label: string) =>
|
|
920
|
-
screen.getByText(label).closest<HTMLElement>('.astryx-tree-list-item')!;
|
|
921
|
-
const rowSurfaceOf = (label: string) =>
|
|
922
|
-
rowOf(label).firstElementChild as HTMLElement;
|
|
923
|
-
const folder: TreeListItemData = {
|
|
924
|
-
id: 'folder',
|
|
925
|
-
label: 'Folder',
|
|
926
|
-
children: [{id: 'file', label: 'File'}],
|
|
927
|
-
};
|
|
928
|
-
|
|
929
|
-
it("runs an action row's onClick once", () => {
|
|
930
|
-
const onClick = vi.fn();
|
|
931
|
-
render(
|
|
932
|
-
<TreeList items={underRoot([{id: 'run', label: 'Run', onClick}])} />,
|
|
933
|
-
);
|
|
934
|
-
fireEvent.click(rowSurfaceOf('Run'));
|
|
935
|
-
expect(onClick).toHaveBeenCalledTimes(1);
|
|
936
|
-
fireEvent.click(rowOf('Run'));
|
|
937
|
-
expect(onClick).toHaveBeenCalledTimes(2);
|
|
938
|
-
|
|
939
|
-
// The label's own button still runs it once per press, not twice.
|
|
940
|
-
fireEvent.click(screen.getByRole('button', {name: 'Run'}));
|
|
941
|
-
expect(onClick).toHaveBeenCalledTimes(3);
|
|
942
|
-
});
|
|
943
|
-
|
|
944
|
-
it('toggles a parent row', () => {
|
|
945
|
-
render(<TreeList items={underRoot([folder])} />);
|
|
946
|
-
expect(screen.queryByText('File')).not.toBeInTheDocument();
|
|
947
|
-
fireEvent.click(rowSurfaceOf('Folder'));
|
|
948
|
-
expect(screen.getByText('File')).toBeInTheDocument();
|
|
949
|
-
fireEvent.click(rowOf('Folder'));
|
|
950
|
-
expect(screen.queryByText('File')).not.toBeInTheDocument();
|
|
951
|
-
});
|
|
952
|
-
|
|
953
|
-
it('does nothing on a disabled action or parent row', () => {
|
|
954
|
-
const onClick = vi.fn();
|
|
955
|
-
render(
|
|
956
|
-
<TreeList
|
|
957
|
-
items={underRoot([
|
|
958
|
-
{id: 'run', label: 'Run', onClick, isDisabled: true},
|
|
959
|
-
{...folder, isDisabled: true},
|
|
960
|
-
])}
|
|
961
|
-
/>,
|
|
962
|
-
);
|
|
963
|
-
fireEvent.click(rowSurfaceOf('Run'));
|
|
964
|
-
fireEvent.click(rowOf('Run'));
|
|
965
|
-
expect(onClick).not.toHaveBeenCalled();
|
|
966
|
-
fireEvent.click(rowSurfaceOf('Folder'));
|
|
967
|
-
fireEvent.click(rowOf('Folder'));
|
|
968
|
-
expect(screen.queryByText('File')).not.toBeInTheDocument();
|
|
969
|
-
});
|
|
970
|
-
|
|
971
|
-
it("exposes a disabled row's toggle and action as aria-disabled and keeps them inert", () => {
|
|
972
|
-
const onClick = vi.fn();
|
|
973
|
-
const {container} = render(
|
|
974
|
-
<TreeList
|
|
975
|
-
items={underRoot([
|
|
976
|
-
{id: 'run', label: 'Run', onClick, isDisabled: true},
|
|
977
|
-
{...folder, isDisabled: true},
|
|
978
|
-
])}
|
|
979
|
-
/>,
|
|
980
|
-
);
|
|
981
|
-
// spec:AST-085 FR1/FR2: no native disabled anywhere, and a click
|
|
982
|
-
// dispatched straight on the inner controls does nothing.
|
|
983
|
-
expect(container.querySelector('[disabled]')).toBeNull();
|
|
984
|
-
const toggle = container.querySelector<HTMLElement>(
|
|
985
|
-
'[data-tree-toggle][aria-disabled="true"]',
|
|
986
|
-
)!;
|
|
987
|
-
expect(toggle).not.toBeNull();
|
|
988
|
-
toggle.click();
|
|
989
|
-
fireEvent.keyDown(toggle, {key: 'Enter'});
|
|
990
|
-
expect(screen.queryByText('File')).not.toBeInTheDocument();
|
|
991
|
-
const action = Array.from(container.querySelectorAll('button')).find(
|
|
992
|
-
button => button.textContent?.includes('Run'),
|
|
993
|
-
)!;
|
|
994
|
-
expect(action).toHaveAttribute('aria-disabled', 'true');
|
|
995
|
-
action.click();
|
|
996
|
-
expect(onClick).not.toHaveBeenCalled();
|
|
997
|
-
});
|
|
998
|
-
|
|
999
|
-
it('leaves a role-based control in start or end content to itself', () => {
|
|
1000
|
-
// Native and role-based controls alike keep their own activation,
|
|
1001
|
-
// the same rule link rows get from the shared enlarged-surface hook.
|
|
1002
|
-
const onClick = vi.fn();
|
|
1003
|
-
const onSwitch = vi.fn();
|
|
1004
|
-
const pin = (name: string) => (
|
|
1005
|
-
<span
|
|
1006
|
-
role="switch"
|
|
1007
|
-
aria-checked="false"
|
|
1008
|
-
aria-label={name}
|
|
1009
|
-
tabIndex={0}
|
|
1010
|
-
onClick={onSwitch}>
|
|
1011
|
-
{name}
|
|
1012
|
-
</span>
|
|
1013
|
-
);
|
|
1014
|
-
render(
|
|
1015
|
-
<TreeList
|
|
1016
|
-
items={underRoot([
|
|
1017
|
-
{
|
|
1018
|
-
id: 'run',
|
|
1019
|
-
label: 'Run',
|
|
1020
|
-
onClick,
|
|
1021
|
-
startContent: pin('Pin run start'),
|
|
1022
|
-
endContent: pin('Pin run end'),
|
|
1023
|
-
},
|
|
1024
|
-
{
|
|
1025
|
-
...folder,
|
|
1026
|
-
startContent: pin('Pin folder start'),
|
|
1027
|
-
endContent: pin('Pin folder end'),
|
|
1028
|
-
},
|
|
1029
|
-
])}
|
|
1030
|
-
/>,
|
|
1031
|
-
);
|
|
1032
|
-
for (const name of [
|
|
1033
|
-
'Pin run start',
|
|
1034
|
-
'Pin run end',
|
|
1035
|
-
'Pin folder start',
|
|
1036
|
-
'Pin folder end',
|
|
1037
|
-
]) {
|
|
1038
|
-
fireEvent.click(screen.getByRole('switch', {name}));
|
|
1039
|
-
}
|
|
1040
|
-
expect(onSwitch).toHaveBeenCalledTimes(4);
|
|
1041
|
-
expect(onClick).not.toHaveBeenCalled();
|
|
1042
|
-
expect(screen.queryByText('File')).not.toBeInTheDocument();
|
|
1043
|
-
});
|
|
1044
|
-
});
|
|
1045
|
-
|
|
1046
|
-
it('draws guides above full-width rows without taking pointer input', () => {
|
|
1047
|
-
// Guide columns now sit inside deeper rows' boxes. They paint above the
|
|
1048
|
-
// rows so a row fill never breaks the line, and let clicks fall through
|
|
1049
|
-
// to the row underneath.
|
|
1050
|
-
const {container} = render(<TreeList items={deepItems} />);
|
|
1051
|
-
const guide = container.querySelector('.astryx-tree-list-guide');
|
|
1052
|
-
expect(guide).not.toBeNull();
|
|
1053
|
-
const column = guide!.parentElement!;
|
|
1054
|
-
expect(getComputedStyle(column).pointerEvents).toBe('none');
|
|
1055
|
-
expect(getComputedStyle(column).zIndex).toBe('1');
|
|
1056
|
-
// The tree contains that stacking so a guide cannot climb over
|
|
1057
|
-
// positioned content outside the list.
|
|
1058
|
-
const treeRoot = screen.getByRole('tree').parentElement!;
|
|
1059
|
-
expect(treeRoot.className).toContain('astryx-tree-list');
|
|
1060
|
-
expect(getComputedStyle(treeRoot).isolation).toBe('isolate');
|
|
1061
|
-
});
|
|
1062
|
-
|
|
1063
|
-
it('lifts only rows that draw a focus ring above the guides', () => {
|
|
1064
|
-
// A plain row (no onClick, href or children) draws no focus ring, so it
|
|
1065
|
-
// stays under the guides: a focused selected plain row must not hide
|
|
1066
|
-
// them with its fill (DD3). Rows that draw the ring take the lift.
|
|
1067
|
-
render(
|
|
1068
|
-
<TreeList
|
|
1069
|
-
items={[
|
|
1070
|
-
{
|
|
1071
|
-
id: 'root',
|
|
1072
|
-
label: 'Root',
|
|
1073
|
-
isExpanded: true,
|
|
1074
|
-
children: [
|
|
1075
|
-
{id: 'plain', label: 'Plain', isSelected: true},
|
|
1076
|
-
{id: 'run', label: 'Run', onClick: () => {}},
|
|
1077
|
-
],
|
|
1078
|
-
},
|
|
1079
|
-
]}
|
|
1080
|
-
/>,
|
|
1081
|
-
);
|
|
1082
|
-
const rowOf = (label: string) =>
|
|
1083
|
-
screen.getByText(label).closest<HTMLElement>('.astryx-tree-list-item')!;
|
|
1084
|
-
expect(rowOf('Plain').className).not.toContain('focusLift');
|
|
1085
|
-
expect(rowOf('Run').className).toContain('focusLift');
|
|
1086
|
-
expect(rowOf('Root').className).toContain('focusLift');
|
|
1087
|
-
});
|
|
1088
|
-
|
|
1089
|
-
it('lifts the focused row above the guides so they never cut its ring', () => {
|
|
1090
|
-
// Each treeitem publishes its own focus as `--_tree-focus-z`, and the
|
|
1091
|
-
// row reads it as its z-index, so the focused row paints above the
|
|
1092
|
-
// guide columns (z-index 1). Nested treeitems redeclare the default.
|
|
1093
|
-
render(<TreeList items={deepItems} />);
|
|
1094
|
-
const rules = collectCssText();
|
|
1095
|
-
expect(rules).toMatch(/--_tree-focus-z:\s*2/);
|
|
1096
|
-
expect(rules).toMatch(/z-index:\s*var\(--_tree-focus-z,\s*auto\)/);
|
|
1097
|
-
});
|
|
1098
|
-
});
|
|
1099
|
-
|
|
1100
|
-
// ===========================================================================
|
|
1101
|
-
// Leaf chevron-column offset (group-expandable-sibling awareness)
|
|
1102
|
-
// ===========================================================================
|
|
1103
|
-
|
|
1104
|
-
describe('leaf chevron-column offset', () => {
|
|
1105
|
-
// A row publishes its indent as the inline `--_tree-indent` custom property.
|
|
1106
|
-
// A leaf that reserves the chevron column adds a fixed offset
|
|
1107
|
-
// (chevron width + gap) on top of its level indent; a flush leaf does not.
|
|
1108
|
-
// The reserved column is expressed as the literal `+ <spacing-4> + <spacing-2>`
|
|
1109
|
-
// suffix in the calc(), so its presence is what we assert (jsdom does not
|
|
1110
|
-
// resolve the token values, so we check the structure, not pixels).
|
|
1111
|
-
const indentStyleOf = (text: string): string => {
|
|
1112
|
-
const li = screen.getByText(text).closest('li')!;
|
|
1113
|
-
const styled = li.querySelector('[style*="--_tree-indent"]');
|
|
1114
|
-
return styled?.getAttribute('style') ?? '';
|
|
1115
|
-
};
|
|
1116
|
-
// A reserved chevron column adds two extra terms to the indent calc()
|
|
1117
|
-
// beyond the single `level * var(--tree-list-indent)` term.
|
|
1118
|
-
const reservesColumn = (text: string): boolean => {
|
|
1119
|
-
const style = indentStyleOf(text);
|
|
1120
|
-
// count the `var(` occurrences inside --_tree-indent: a flush row has
|
|
1121
|
-
// exactly one (the indent step); a reserving leaf adds the chevron
|
|
1122
|
-
// width + gap tokens, so it has more.
|
|
1123
|
-
const m = /--_tree-indent:\s*calc\(([^;]*)\)/.exec(style);
|
|
1124
|
-
const body = m?.[1] ?? '';
|
|
1125
|
-
return (body.match(/\bvar\(/g)?.length ?? 0) > 1 || body.includes('+');
|
|
1126
|
-
};
|
|
1127
|
-
|
|
1128
|
-
it('a leaf in a mixed group reserves the chevron column (aligns under its expandable sibling)', () => {
|
|
1129
|
-
// Group: [Parent (has children), Sibling (leaf)] → Sibling must line up
|
|
1130
|
-
// under Parent's caret, so it keeps the chevron-column offset.
|
|
1131
|
-
render(<TreeList items={nestedItems} />);
|
|
1132
|
-
expect(reservesColumn('Sibling')).toBe(true);
|
|
1133
|
-
});
|
|
1134
|
-
|
|
1135
|
-
it('a leaf under an expandable ancestor reserves the chevron column even when its own group is all leaves', () => {
|
|
1136
|
-
// 'Root' → 'Mid' → 'Leaf'; 'Leaf' is the only item in its immediate
|
|
1137
|
-
// group, but the tree has carets (Root, Mid), so Leaf must reserve the
|
|
1138
|
-
// chevron column to stay indented past its parent's label. Flushing it
|
|
1139
|
-
// here would push it left of Mid's label — the all-leaf-group bug.
|
|
1140
|
-
render(<TreeList items={deepItems} variant="noGuides" />);
|
|
1141
|
-
expect(reservesColumn('Leaf')).toBe(true);
|
|
1142
|
-
});
|
|
1143
|
-
|
|
1144
|
-
it("an expanded parent's all-leaf children reserve the chevron column (do not flush left of the parent label)", () => {
|
|
1145
|
-
// Regression: Parent (caret) → [Child 1, Child 2] is an all-leaf group
|
|
1146
|
-
// nested under an expandable parent. Per-group flushing wrongly dropped
|
|
1147
|
-
// these children's chevron column, pushing them LEFT of Parent's own
|
|
1148
|
-
// label. Because the tree has a caret, they must reserve the column and
|
|
1149
|
-
// stay indented past Parent.
|
|
1150
|
-
render(<TreeList items={nestedItemsExpanded} />);
|
|
1151
|
-
expect(reservesColumn('Child 1')).toBe(true);
|
|
1152
|
-
expect(reservesColumn('Child 2')).toBe(true);
|
|
1153
|
-
});
|
|
1154
|
-
|
|
1155
|
-
it('every row in a flat (all-leaf) tree sits flush — no chevron column reserved', () => {
|
|
1156
|
-
render(<TreeList items={flatItems} />);
|
|
1157
|
-
expect(reservesColumn('Apple')).toBe(false);
|
|
1158
|
-
expect(reservesColumn('Banana')).toBe(false);
|
|
1159
|
-
expect(reservesColumn('Cherry')).toBe(false);
|
|
1160
|
-
});
|
|
1161
|
-
|
|
1162
|
-
it('leaves flush in an all-leaf group have the same indent structure as a parent at that level', () => {
|
|
1163
|
-
// In a mixed group the reserving leaf indent has the extra terms; in an
|
|
1164
|
-
// all-leaf group the leaf indent is the bare level step, exactly like a
|
|
1165
|
-
// parent row's indent.
|
|
1166
|
-
render(<TreeList items={flatItems} />);
|
|
1167
|
-
const flush = indentStyleOf('Apple');
|
|
1168
|
-
// bare level step: one var(--tree-list-indent), no additive chevron terms
|
|
1169
|
-
expect(flush).toContain('var(--tree-list-indent)');
|
|
1170
|
-
expect(/--_tree-indent:\s*calc\([^;]*\+[^;]*\)/.test(flush)).toBe(false);
|
|
1171
|
-
});
|
|
1172
|
-
});
|
|
1173
|
-
|
|
1174
|
-
// ===========================================================================
|
|
1175
|
-
// Inter-row gap lever (--tree-list-row-gap) + guide spanning
|
|
1176
|
-
// ===========================================================================
|
|
1177
|
-
|
|
1178
|
-
describe('row gap lever', () => {
|
|
1179
|
-
it('defaults the row gap to a subtle 2px separation', () => {
|
|
1180
|
-
// The lever is published on the tree-list root; its default is
|
|
1181
|
-
// --spacing-0-5 (2px) so rows have a light separation out of the box. A
|
|
1182
|
-
// theme can widen or close it via the tree-list target. jsdom does not
|
|
1183
|
-
// resolve token vars, so the declared value is the --spacing-0-5 token.
|
|
1184
|
-
render(<TreeList items={simpleItems} data-testid="tree" />);
|
|
1185
|
-
const root = screen.getByTestId('tree');
|
|
1186
|
-
expect(
|
|
1187
|
-
getComputedStyle(root).getPropertyValue('--tree-list-row-gap').trim(),
|
|
1188
|
-
).toBe('var(--spacing-0-5)');
|
|
1189
|
-
});
|
|
1190
|
-
|
|
1191
|
-
it('lets a theme open a row gap via the tree-list target', () => {
|
|
1192
|
-
// jsdom cannot resolve the @layer cascade, so the generated CSS is what
|
|
1193
|
-
// proves the override reaches the lever.
|
|
1194
|
-
const theme = defineTheme({
|
|
1195
|
-
name: 'tree-list-row-gap-test',
|
|
1196
|
-
components: {
|
|
1197
|
-
'tree-list': {
|
|
1198
|
-
base: {'--tree-list-row-gap': 'var(--spacing-1)'},
|
|
1199
|
-
},
|
|
1200
|
-
},
|
|
1201
|
-
});
|
|
1202
|
-
const css = generateThemeTestCSS(theme);
|
|
1203
|
-
expect(css).toContain('.astryx-tree-list {');
|
|
1204
|
-
expect(css).toContain('--tree-list-row-gap: var(--spacing-1)');
|
|
1205
|
-
});
|
|
1206
|
-
|
|
1207
|
-
it('the guide of a row with a sibling below spans into it (verticalFull)', () => {
|
|
1208
|
-
// A row that is NOT last in its group bridges the 1px hairline into the
|
|
1209
|
-
// next contiguous sibling so the connector reads as one continuous line.
|
|
1210
|
-
// Assert the APPLIED class on that row's own connector — not a global
|
|
1211
|
-
// stylesheet regex — so the isLast ? verticalLast : verticalFull branch
|
|
1212
|
-
// is actually exercised (Child 1 has Child 2 below it).
|
|
1213
|
-
render(<TreeList items={nestedItemsExpanded} />);
|
|
1214
|
-
const child1 = screen.getByText('Child 1').closest('li')!;
|
|
1215
|
-
const guide = child1.firstElementChild!.querySelector(
|
|
1216
|
-
'.astryx-tree-list-guide',
|
|
1217
|
-
);
|
|
1218
|
-
expect(guide).not.toBeNull();
|
|
1219
|
-
expect(guide!.className).toContain('verticalFull');
|
|
1220
|
-
expect(guide!.className).not.toContain('verticalLast');
|
|
1221
|
-
});
|
|
1222
|
-
|
|
1223
|
-
it('clamps the last-in-group guide so it does not overhang the gap (verticalLast)', () => {
|
|
1224
|
-
// The last row in a group has nothing below it, so its connector must NOT
|
|
1225
|
-
// run through the row wrapper's bottom padding into empty space — it uses
|
|
1226
|
-
// verticalLast, which subtracts half the row gap. Assert the applied class
|
|
1227
|
-
// on the last child's own connector (Child 2 is last in its group), so a
|
|
1228
|
-
// reverted branch is caught.
|
|
1229
|
-
render(<TreeList items={nestedItemsExpanded} />);
|
|
1230
|
-
const child2 = screen.getByText('Child 2').closest('li')!;
|
|
1231
|
-
const guide = child2.firstElementChild!.querySelector(
|
|
1232
|
-
'.astryx-tree-list-guide',
|
|
1233
|
-
);
|
|
1234
|
-
expect(guide).not.toBeNull();
|
|
1235
|
-
expect(guide!.className).toContain('verticalLast');
|
|
1236
|
-
expect(guide!.className).not.toContain('verticalFull');
|
|
1237
|
-
});
|
|
1238
|
-
|
|
1239
|
-
it('carries the inter-row gap as collapse-proof padding on the row wrapper, not the paint target', () => {
|
|
1240
|
-
// Finding from review: the gap must be `padding-block` (which cannot
|
|
1241
|
-
// collapse) on the row WRAPPER, not `margin-block` on the row box — a
|
|
1242
|
-
// margin there collapses through the position:relative wrapper and the
|
|
1243
|
-
// <li>, delivering only half the configured gap. It also must not sit on
|
|
1244
|
-
// `tree-list-item`, which is a paint seam (per the theming-target
|
|
1245
|
-
// guidelines): layout longhands do not belong on a paintable target.
|
|
1246
|
-
const {container} = render(<TreeList items={simpleItems} />);
|
|
1247
|
-
const item = container.querySelector<HTMLElement>(
|
|
1248
|
-
'.astryx-tree-list-item',
|
|
1249
|
-
);
|
|
1250
|
-
expect(item).not.toBeNull();
|
|
1251
|
-
const rowWrapper = item!.parentElement!;
|
|
1252
|
-
|
|
1253
|
-
const rules = collectCssText();
|
|
1254
|
-
// Map the rowWrapper's compiled class to its declaration block and assert
|
|
1255
|
-
// it declares padding-block from the row-gap lever (half above/below).
|
|
1256
|
-
const rowWrapperClass = Array.from(rowWrapper.classList).find(c =>
|
|
1257
|
-
c.includes('rowWrapper'),
|
|
1258
|
-
);
|
|
1259
|
-
expect(rowWrapperClass).toBeDefined();
|
|
1260
|
-
// The gap rides padding-block (collapse-proof), keyed off the lever.
|
|
1261
|
-
expect(rules).toMatch(
|
|
1262
|
-
/padding-block:\s*calc\(\s*var\(--tree-list-row-gap[^)]*\)\s*\/\s*2\s*\)/,
|
|
1263
|
-
);
|
|
1264
|
-
// And the paint target (tree-list-item / contentWrapper) must NOT carry a
|
|
1265
|
-
// margin-block gap — the layout seam has moved off the paintable element.
|
|
1266
|
-
const itemClass = Array.from(item!.classList).find(c =>
|
|
1267
|
-
c.includes('contentWrapper'),
|
|
1268
|
-
);
|
|
1269
|
-
expect(itemClass).toBeDefined();
|
|
1270
|
-
expect(rules).not.toMatch(
|
|
1271
|
-
/margin-block:\s*calc\([^;]*var\(--tree-list-row-gap/,
|
|
1272
|
-
);
|
|
1273
|
-
});
|
|
1274
|
-
});
|
|
1275
|
-
|
|
1276
|
-
// ===========================================================================
|
|
1277
|
-
// xds class name
|
|
1278
|
-
// ===========================================================================
|
|
1279
|
-
|
|
1280
|
-
it('applies astryx-tree-list class name', () => {
|
|
1281
|
-
render(<TreeList items={simpleItems} data-testid="tree" />);
|
|
1282
|
-
const root = screen.getByTestId('tree');
|
|
1283
|
-
expect(root.className).toContain('astryx-tree-list');
|
|
1284
|
-
});
|
|
1285
|
-
|
|
1286
|
-
// ===========================================================================
|
|
1287
|
-
// Chevron theme target
|
|
1288
|
-
// ===========================================================================
|
|
1289
|
-
|
|
1290
|
-
describe('chevron theme target', () => {
|
|
1291
|
-
it('renders the astryx-tree-list-chevron target on the toggle button', () => {
|
|
1292
|
-
render(<TreeList items={nestedItems} />);
|
|
1293
|
-
const toggle = screen
|
|
1294
|
-
.getByText('Parent')
|
|
1295
|
-
.closest('li')!
|
|
1296
|
-
.querySelector('[data-tree-toggle]')!;
|
|
1297
|
-
|
|
1298
|
-
// Dedicated, stable theme target on the expand/collapse control, so a
|
|
1299
|
-
// theme can restyle the chevron without a fragile [data-tree-toggle] hook.
|
|
1300
|
-
expect(toggle).toHaveClass('astryx-tree-list-chevron');
|
|
1301
|
-
// Open/closed state is reflected so a theme can target each state alone.
|
|
1302
|
-
expect(toggle).toHaveAttribute('data-state', 'collapsed');
|
|
1303
|
-
});
|
|
1304
|
-
|
|
1305
|
-
it('reflects the expanded state on the toggle when open', () => {
|
|
1306
|
-
render(<TreeList items={nestedItemsExpanded} />);
|
|
1307
|
-
const toggle = screen
|
|
1308
|
-
.getByText('Parent')
|
|
1309
|
-
.closest('li')!
|
|
1310
|
-
.querySelector('[data-tree-toggle]')!;
|
|
1311
|
-
|
|
1312
|
-
expect(toggle).toHaveClass('astryx-tree-list-chevron');
|
|
1313
|
-
expect(toggle).toHaveAttribute('data-state', 'expanded');
|
|
1314
|
-
});
|
|
1315
|
-
|
|
1316
|
-
it('keeps the functional data-tree-toggle hook alongside the new target', () => {
|
|
1317
|
-
// The theme target is additive — the toggle is still a real <button> and
|
|
1318
|
-
// still carries the functional activation attribute TreeList relies on.
|
|
1319
|
-
render(<TreeList items={nestedItems} />);
|
|
1320
|
-
const toggle = screen
|
|
1321
|
-
.getByText('Parent')
|
|
1322
|
-
.closest('li')!
|
|
1323
|
-
.querySelector('[data-tree-toggle]')!;
|
|
1324
|
-
expect(toggle.tagName).toBe('BUTTON');
|
|
1325
|
-
expect(toggle).toHaveAttribute('data-tree-toggle');
|
|
1326
|
-
});
|
|
1327
|
-
|
|
1328
|
-
it('exposes tree-list-chevron as a themeable defineTheme target', () => {
|
|
1329
|
-
// The generated CSS is what proves the target is reachable by a theme:
|
|
1330
|
-
// jsdom cannot resolve the @layer cascade, so the DOM-class assertions
|
|
1331
|
-
// above and this generation assertion together cover the seam.
|
|
1332
|
-
const theme = defineTheme({
|
|
1333
|
-
name: 'tree-list-chevron-test',
|
|
1334
|
-
components: {
|
|
1335
|
-
'tree-list-chevron': {
|
|
1336
|
-
base: {color: 'var(--color-accent)'},
|
|
1337
|
-
'state:expanded': {color: 'var(--color-text-primary)'},
|
|
1338
|
-
},
|
|
1339
|
-
},
|
|
1340
|
-
});
|
|
1341
|
-
const css = generateThemeTestCSS(theme);
|
|
1342
|
-
expect(css).toContain('.astryx-tree-list-chevron {');
|
|
1343
|
-
expect(css).toContain('color: var(--color-accent)');
|
|
1344
|
-
expect(css).toContain('.astryx-tree-list-chevron[data-state="expanded"]');
|
|
1345
|
-
expect(css).toContain('color: var(--color-text-primary)');
|
|
1346
|
-
});
|
|
1347
|
-
});
|
|
1348
|
-
|
|
1349
|
-
// ===========================================================================
|
|
1350
|
-
// Item label theme target
|
|
1351
|
-
// ===========================================================================
|
|
1352
|
-
|
|
1353
|
-
describe('item label theme target', () => {
|
|
1354
|
-
it('renders the astryx-tree-list-item-label target on the label span', () => {
|
|
1355
|
-
render(<TreeList items={simpleItems} />);
|
|
1356
|
-
const label = screen.getByText('Item A');
|
|
1357
|
-
|
|
1358
|
-
// Dedicated, stable theme target on the label text, so a theme can style
|
|
1359
|
-
// just the label without a fragile `button:not([data-tree-toggle]) > span`
|
|
1360
|
-
// structural selector.
|
|
1361
|
-
expect(label).toHaveClass('astryx-tree-list-item-label');
|
|
1362
|
-
// A non-selected item's label carries no selected reflection.
|
|
1363
|
-
expect(label).not.toHaveAttribute('data-selected');
|
|
1364
|
-
});
|
|
1365
|
-
|
|
1366
|
-
it('reflects the selected state on the selected item label', () => {
|
|
1367
|
-
render(
|
|
1368
|
-
<TreeList items={[{id: 'a', label: 'Item A', isSelected: true}]} />,
|
|
1369
|
-
);
|
|
1370
|
-
const label = screen.getByText('Item A');
|
|
1371
|
-
expect(label).toHaveClass('astryx-tree-list-item-label');
|
|
1372
|
-
expect(label).toHaveAttribute('data-selected', 'selected');
|
|
1373
|
-
});
|
|
1374
|
-
|
|
1375
|
-
it('keeps the label linked to its row via aria-labelledby', () => {
|
|
1376
|
-
// The theme target is additive — the label still owns the id the
|
|
1377
|
-
// interactive row references for its accessible name.
|
|
1378
|
-
render(
|
|
1379
|
-
<TreeList items={[{id: 'a', label: 'Item A', onClick: () => {}}]} />,
|
|
1380
|
-
);
|
|
1381
|
-
const label = screen.getByText('Item A');
|
|
1382
|
-
const action = screen.getByRole('button');
|
|
1383
|
-
expect(action).toHaveAttribute('aria-labelledby', label.id);
|
|
1384
|
-
});
|
|
1385
|
-
|
|
1386
|
-
it('exposes tree-list-item-label as a themeable defineTheme target', () => {
|
|
1387
|
-
// The generated CSS is what proves the target is reachable by a theme:
|
|
1388
|
-
// jsdom cannot resolve the @layer cascade, so the DOM-class assertions
|
|
1389
|
-
// above and this generation assertion together cover the seam.
|
|
1390
|
-
const theme = defineTheme({
|
|
1391
|
-
name: 'tree-list-item-label-test',
|
|
1392
|
-
components: {
|
|
1393
|
-
'tree-list-item-label': {
|
|
1394
|
-
base: {color: 'var(--color-text-primary)'},
|
|
1395
|
-
selected: {fontWeight: 'var(--font-weight-bold)'},
|
|
1396
|
-
},
|
|
1397
|
-
},
|
|
1398
|
-
});
|
|
1399
|
-
const css = generateThemeTestCSS(theme);
|
|
1400
|
-
expect(css).toContain('.astryx-tree-list-item-label {');
|
|
1401
|
-
expect(css).toContain('color: var(--color-text-primary)');
|
|
1402
|
-
expect(css).toContain(
|
|
1403
|
-
'.astryx-tree-list-item-label[data-selected="selected"]',
|
|
1404
|
-
);
|
|
1405
|
-
expect(css).toContain('font-weight: var(--font-weight-bold)');
|
|
1406
|
-
});
|
|
1407
|
-
});
|
|
1408
|
-
|
|
1409
|
-
// ===========================================================================
|
|
1410
|
-
// APG structural ARIA (aria-level / aria-posinset / aria-setsize)
|
|
1411
|
-
// ===========================================================================
|
|
1412
|
-
|
|
1413
|
-
it('sets aria-level, aria-posinset, and aria-setsize at the top level', () => {
|
|
1414
|
-
render(<TreeList items={flatItems} />);
|
|
1415
|
-
const apple = screen.getByText('Apple').closest('li');
|
|
1416
|
-
expect(apple).toHaveAttribute('aria-level', '1');
|
|
1417
|
-
expect(apple).toHaveAttribute('aria-posinset', '1');
|
|
1418
|
-
expect(apple).toHaveAttribute('aria-setsize', '3');
|
|
1419
|
-
|
|
1420
|
-
const cherry = screen.getByText('Cherry').closest('li');
|
|
1421
|
-
expect(cherry).toHaveAttribute('aria-posinset', '3');
|
|
1422
|
-
expect(cherry).toHaveAttribute('aria-setsize', '3');
|
|
1423
|
-
});
|
|
1424
|
-
|
|
1425
|
-
it('sets aria-level/posinset/setsize correctly at deeper levels', () => {
|
|
1426
|
-
render(<TreeList items={expandedParentItems} />);
|
|
1427
|
-
const parent = screen.getByText('Parent').closest('li');
|
|
1428
|
-
expect(parent).toHaveAttribute('aria-level', '1');
|
|
1429
|
-
expect(parent).toHaveAttribute('aria-setsize', '2');
|
|
1430
|
-
|
|
1431
|
-
const child1 = screen.getByText('Child 1').closest('li');
|
|
1432
|
-
expect(child1).toHaveAttribute('aria-level', '2');
|
|
1433
|
-
expect(child1).toHaveAttribute('aria-posinset', '1');
|
|
1434
|
-
expect(child1).toHaveAttribute('aria-setsize', '2');
|
|
1435
|
-
|
|
1436
|
-
const child2 = screen.getByText('Child 2').closest('li');
|
|
1437
|
-
expect(child2).toHaveAttribute('aria-level', '2');
|
|
1438
|
-
expect(child2).toHaveAttribute('aria-posinset', '2');
|
|
1439
|
-
});
|
|
1440
|
-
|
|
1441
|
-
it('sets aria-level across three depths', () => {
|
|
1442
|
-
render(<TreeList items={deepItems} />);
|
|
1443
|
-
expect(screen.getByText('Root').closest('li')).toHaveAttribute(
|
|
1444
|
-
'aria-level',
|
|
1445
|
-
'1',
|
|
1446
|
-
);
|
|
1447
|
-
expect(screen.getByText('Mid').closest('li')).toHaveAttribute(
|
|
1448
|
-
'aria-level',
|
|
1449
|
-
'2',
|
|
1450
|
-
);
|
|
1451
|
-
expect(screen.getByText('Leaf').closest('li')).toHaveAttribute(
|
|
1452
|
-
'aria-level',
|
|
1453
|
-
'3',
|
|
1454
|
-
);
|
|
1455
|
-
});
|
|
1456
|
-
|
|
1457
|
-
// ===========================================================================
|
|
1458
|
-
// Roving tabindex
|
|
1459
|
-
// ===========================================================================
|
|
1460
|
-
|
|
1461
|
-
it('makes exactly one treeitem tabbable by default (the first enabled)', () => {
|
|
1462
|
-
render(<TreeList items={flatItems} />);
|
|
1463
|
-
const treeitems = screen.getAllByRole('treeitem');
|
|
1464
|
-
const tabbable = treeitems.filter(
|
|
1465
|
-
el => el.getAttribute('tabindex') === '0',
|
|
1466
|
-
);
|
|
1467
|
-
expect(tabbable).toHaveLength(1);
|
|
1468
|
-
expect(tabbable[0]).toBe(screen.getByText('Apple').closest('li'));
|
|
1469
|
-
expect(screen.getByText('Banana').closest('li')).toHaveAttribute(
|
|
1470
|
-
'tabindex',
|
|
1471
|
-
'-1',
|
|
1472
|
-
);
|
|
1473
|
-
});
|
|
1474
|
-
|
|
1475
|
-
it('defaults the tab stop to the selected item when one is selected', () => {
|
|
1476
|
-
const items: TreeListItemData[] = [
|
|
1477
|
-
{id: 'a', label: 'Apple'},
|
|
1478
|
-
{id: 'b', label: 'Banana', isSelected: true},
|
|
1479
|
-
{id: 'c', label: 'Cherry'},
|
|
1480
|
-
];
|
|
1481
|
-
render(<TreeList items={items} />);
|
|
1482
|
-
expect(screen.getByText('Banana').closest('li')).toHaveAttribute(
|
|
1483
|
-
'tabindex',
|
|
1484
|
-
'0',
|
|
1485
|
-
);
|
|
1486
|
-
expect(screen.getByText('Apple').closest('li')).toHaveAttribute(
|
|
1487
|
-
'tabindex',
|
|
1488
|
-
'-1',
|
|
1489
|
-
);
|
|
1490
|
-
});
|
|
1491
|
-
|
|
1492
|
-
it('moves the single tab stop when focus moves via keyboard', async () => {
|
|
1493
|
-
const user = userEvent.setup();
|
|
1494
|
-
render(<TreeList items={flatItems} />);
|
|
1495
|
-
const apple = screen.getByText('Apple').closest('li')!;
|
|
1496
|
-
apple.focus();
|
|
1497
|
-
await user.keyboard('{ArrowDown}');
|
|
1498
|
-
const treeitems = screen.getAllByRole('treeitem');
|
|
1499
|
-
const tabbable = treeitems.filter(
|
|
1500
|
-
el => el.getAttribute('tabindex') === '0',
|
|
1501
|
-
);
|
|
1502
|
-
expect(tabbable).toHaveLength(1);
|
|
1503
|
-
expect(tabbable[0]).toBe(screen.getByText('Banana').closest('li'));
|
|
1504
|
-
});
|
|
1505
|
-
|
|
1506
|
-
// ===========================================================================
|
|
1507
|
-
// APG keyboard navigation
|
|
1508
|
-
// ===========================================================================
|
|
1509
|
-
|
|
1510
|
-
it('ArrowDown / ArrowUp move focus between visible treeitems', async () => {
|
|
1511
|
-
const user = userEvent.setup();
|
|
1512
|
-
render(<TreeList items={flatItems} />);
|
|
1513
|
-
const apple = screen.getByText('Apple').closest('li')!;
|
|
1514
|
-
apple.focus();
|
|
1515
|
-
await user.keyboard('{ArrowDown}');
|
|
1516
|
-
expect(document.activeElement).toBe(
|
|
1517
|
-
screen.getByText('Banana').closest('li'),
|
|
1518
|
-
);
|
|
1519
|
-
await user.keyboard('{ArrowDown}');
|
|
1520
|
-
expect(document.activeElement).toBe(
|
|
1521
|
-
screen.getByText('Cherry').closest('li'),
|
|
1522
|
-
);
|
|
1523
|
-
await user.keyboard('{ArrowUp}');
|
|
1524
|
-
expect(document.activeElement).toBe(
|
|
1525
|
-
screen.getByText('Banana').closest('li'),
|
|
1526
|
-
);
|
|
1527
|
-
});
|
|
1528
|
-
|
|
1529
|
-
it('ArrowDown / ArrowUp skip disabled treeitems', async () => {
|
|
1530
|
-
const user = userEvent.setup();
|
|
1531
|
-
render(<TreeList items={withDisabledItems} />);
|
|
1532
|
-
const apple = screen.getByText('Apple').closest('li')!;
|
|
1533
|
-
apple.focus();
|
|
1534
|
-
await user.keyboard('{ArrowDown}');
|
|
1535
|
-
// Banana is disabled → skipped, lands on Cherry.
|
|
1536
|
-
expect(document.activeElement).toBe(
|
|
1537
|
-
screen.getByText('Cherry').closest('li'),
|
|
1538
|
-
);
|
|
1539
|
-
await user.keyboard('{ArrowUp}');
|
|
1540
|
-
expect(document.activeElement).toBe(
|
|
1541
|
-
screen.getByText('Apple').closest('li'),
|
|
1542
|
-
);
|
|
1543
|
-
});
|
|
1544
|
-
|
|
1545
|
-
it('ArrowRight expands a collapsed parent, then enters the first child', async () => {
|
|
1546
|
-
const user = userEvent.setup();
|
|
1547
|
-
render(<TreeList items={collapsedParentItems} />);
|
|
1548
|
-
const parent = screen.getByText('Parent').closest('li')!;
|
|
1549
|
-
parent.focus();
|
|
1550
|
-
expect(screen.queryByText('Child 1')).not.toBeInTheDocument();
|
|
1551
|
-
await user.keyboard('{ArrowRight}');
|
|
1552
|
-
// First ArrowRight expands.
|
|
1553
|
-
expect(screen.getByText('Child 1')).toBeInTheDocument();
|
|
1554
|
-
expect(document.activeElement).toBe(parent);
|
|
1555
|
-
await user.keyboard('{ArrowRight}');
|
|
1556
|
-
// Second ArrowRight moves into first child.
|
|
1557
|
-
expect(document.activeElement).toBe(
|
|
1558
|
-
screen.getByText('Child 1').closest('li'),
|
|
1559
|
-
);
|
|
1560
|
-
});
|
|
1561
|
-
|
|
1562
|
-
it('ArrowRight on a leaf is a no-op', async () => {
|
|
1563
|
-
const user = userEvent.setup();
|
|
1564
|
-
render(<TreeList items={flatItems} />);
|
|
1565
|
-
const apple = screen.getByText('Apple').closest('li')!;
|
|
1566
|
-
apple.focus();
|
|
1567
|
-
await user.keyboard('{ArrowRight}');
|
|
1568
|
-
expect(document.activeElement).toBe(apple);
|
|
1569
|
-
});
|
|
1570
|
-
|
|
1571
|
-
it('ArrowLeft collapses an expanded parent, then moves to parent', async () => {
|
|
1572
|
-
const user = userEvent.setup();
|
|
1573
|
-
render(<TreeList items={expandedParentItems} />);
|
|
1574
|
-
const parent = screen.getByText('Parent').closest('li')!;
|
|
1575
|
-
// Focus a child first.
|
|
1576
|
-
const child1 = screen.getByText('Child 1').closest('li')!;
|
|
1577
|
-
child1.focus();
|
|
1578
|
-
await user.keyboard('{ArrowLeft}');
|
|
1579
|
-
// Child is a leaf → ArrowLeft moves to parent.
|
|
1580
|
-
expect(document.activeElement).toBe(parent);
|
|
1581
|
-
await user.keyboard('{ArrowLeft}');
|
|
1582
|
-
// Parent is expanded → ArrowLeft collapses it.
|
|
1583
|
-
expect(screen.queryByText('Child 1')).not.toBeInTheDocument();
|
|
1584
|
-
});
|
|
1585
|
-
|
|
1586
|
-
it('Home and End move to the first and last visible treeitems', async () => {
|
|
1587
|
-
const user = userEvent.setup();
|
|
1588
|
-
render(<TreeList items={flatItems} />);
|
|
1589
|
-
const banana = screen.getByText('Banana').closest('li')!;
|
|
1590
|
-
banana.focus();
|
|
1591
|
-
await user.keyboard('{End}');
|
|
1592
|
-
expect(document.activeElement).toBe(
|
|
1593
|
-
screen.getByText('Cherry').closest('li'),
|
|
1594
|
-
);
|
|
1595
|
-
await user.keyboard('{Home}');
|
|
1596
|
-
expect(document.activeElement).toBe(
|
|
1597
|
-
screen.getByText('Apple').closest('li'),
|
|
1598
|
-
);
|
|
1599
|
-
});
|
|
1600
|
-
|
|
1601
|
-
it('Enter activates the item onClick', async () => {
|
|
1602
|
-
const user = userEvent.setup();
|
|
1603
|
-
const onClick = vi.fn();
|
|
1604
|
-
const items: TreeListItemData[] = [{id: 'a', label: 'Apple', onClick}];
|
|
1605
|
-
render(<TreeList items={items} />);
|
|
1606
|
-
const apple = screen.getByText('Apple').closest('li')!;
|
|
1607
|
-
apple.focus();
|
|
1608
|
-
await user.keyboard('{Enter}');
|
|
1609
|
-
expect(onClick).toHaveBeenCalledTimes(1);
|
|
1610
|
-
});
|
|
1611
|
-
|
|
1612
|
-
it('Space activates the item onClick', async () => {
|
|
1613
|
-
const user = userEvent.setup();
|
|
1614
|
-
const onClick = vi.fn();
|
|
1615
|
-
const items: TreeListItemData[] = [{id: 'a', label: 'Apple', onClick}];
|
|
1616
|
-
render(<TreeList items={items} />);
|
|
1617
|
-
const apple = screen.getByText('Apple').closest('li')!;
|
|
1618
|
-
apple.focus();
|
|
1619
|
-
await user.keyboard(' ');
|
|
1620
|
-
expect(onClick).toHaveBeenCalledTimes(1);
|
|
1621
|
-
});
|
|
1622
|
-
|
|
1623
|
-
it('Enter toggles expansion for a parent without its own action', async () => {
|
|
1624
|
-
const user = userEvent.setup();
|
|
1625
|
-
render(<TreeList items={collapsedParentItems} />);
|
|
1626
|
-
const parent = screen.getByText('Parent').closest('li')!;
|
|
1627
|
-
parent.focus();
|
|
1628
|
-
expect(screen.queryByText('Child 1')).not.toBeInTheDocument();
|
|
1629
|
-
await user.keyboard('{Enter}');
|
|
1630
|
-
expect(screen.getByText('Child 1')).toBeInTheDocument();
|
|
1631
|
-
});
|
|
1632
|
-
|
|
1633
|
-
it('typeahead moves focus to the next item matching typed characters', async () => {
|
|
1634
|
-
const user = userEvent.setup();
|
|
1635
|
-
render(<TreeList items={flatItems} />);
|
|
1636
|
-
const apple = screen.getByText('Apple').closest('li')!;
|
|
1637
|
-
apple.focus();
|
|
1638
|
-
await user.keyboard('c');
|
|
1639
|
-
expect(document.activeElement).toBe(
|
|
1640
|
-
screen.getByText('Cherry').closest('li'),
|
|
1641
|
-
);
|
|
1642
|
-
});
|
|
1643
|
-
|
|
1644
|
-
it('lets a consumer prevent built-in TreeList keyboard navigation while receiving root div as event.currentTarget', () => {
|
|
1645
|
-
let capturedCurrentTarget: Element | null = null;
|
|
1646
|
-
const ref = {current: null as HTMLDivElement | null};
|
|
1647
|
-
render(
|
|
1648
|
-
<TreeList
|
|
1649
|
-
ref={el => {
|
|
1650
|
-
ref.current = el;
|
|
1651
|
-
}}
|
|
1652
|
-
items={[
|
|
1653
|
-
{id: 'one', label: 'One'},
|
|
1654
|
-
{id: 'two', label: 'Two'},
|
|
1655
|
-
]}
|
|
1656
|
-
onKeyDown={event => {
|
|
1657
|
-
capturedCurrentTarget = event.currentTarget;
|
|
1658
|
-
event.preventDefault();
|
|
1659
|
-
}}
|
|
1660
|
-
/>,
|
|
1661
|
-
);
|
|
1662
|
-
|
|
1663
|
-
const items = screen.getAllByRole('treeitem');
|
|
1664
|
-
items[0].focus();
|
|
1665
|
-
fireEvent.keyDown(items[0], {key: 'ArrowDown'});
|
|
1666
|
-
|
|
1667
|
-
expect(capturedCurrentTarget).toBe(ref.current);
|
|
1668
|
-
expect(ref.current).toBeInstanceOf(HTMLDivElement);
|
|
1669
|
-
expect(items[0]).toHaveFocus();
|
|
1670
|
-
expect(items[0]).toHaveAttribute('tabindex', '0');
|
|
1671
|
-
expect(items[1]).toHaveAttribute('tabindex', '-1');
|
|
1672
|
-
});
|
|
1673
|
-
|
|
1674
|
-
it('fires non-canceling consumer onKeyDown with root div as event.currentTarget while built-in navigation works', () => {
|
|
1675
|
-
let capturedCurrentTarget: Element | null = null;
|
|
1676
|
-
const ref = {current: null as HTMLDivElement | null};
|
|
1677
|
-
const onKeyDown = vi.fn((event: React.KeyboardEvent<HTMLDivElement>) => {
|
|
1678
|
-
capturedCurrentTarget = event.currentTarget;
|
|
1679
|
-
});
|
|
1680
|
-
render(
|
|
1681
|
-
<TreeList
|
|
1682
|
-
ref={el => {
|
|
1683
|
-
ref.current = el;
|
|
1684
|
-
}}
|
|
1685
|
-
items={[
|
|
1686
|
-
{id: 'one', label: 'One'},
|
|
1687
|
-
{id: 'two', label: 'Two'},
|
|
1688
|
-
]}
|
|
1689
|
-
onKeyDown={onKeyDown}
|
|
1690
|
-
/>,
|
|
1691
|
-
);
|
|
1692
|
-
|
|
1693
|
-
const items = screen.getAllByRole('treeitem');
|
|
1694
|
-
items[0].focus();
|
|
1695
|
-
fireEvent.keyDown(items[0], {key: 'ArrowDown'});
|
|
1696
|
-
|
|
1697
|
-
expect(onKeyDown).toHaveBeenCalledTimes(1);
|
|
1698
|
-
expect(capturedCurrentTarget).toBe(ref.current);
|
|
1699
|
-
expect(ref.current).toBeInstanceOf(HTMLDivElement);
|
|
1700
|
-
expect(items[1]).toHaveFocus();
|
|
1701
|
-
expect(items[1]).toHaveAttribute('tabindex', '0');
|
|
1702
|
-
});
|
|
1703
|
-
|
|
1704
|
-
it('does not trigger built-in tree navigation when keydown originates inside the header slot', () => {
|
|
1705
|
-
let capturedCurrentTarget: Element | null = null;
|
|
1706
|
-
const ref = {current: null as HTMLDivElement | null};
|
|
1707
|
-
const onKeyDown = vi.fn((event: React.KeyboardEvent<HTMLDivElement>) => {
|
|
1708
|
-
capturedCurrentTarget = event.currentTarget;
|
|
1709
|
-
});
|
|
1710
|
-
|
|
1711
|
-
render(
|
|
1712
|
-
<TreeList
|
|
1713
|
-
ref={el => {
|
|
1714
|
-
ref.current = el;
|
|
1715
|
-
}}
|
|
1716
|
-
header={<button type="button">Header Action</button>}
|
|
1717
|
-
items={[
|
|
1718
|
-
{id: 'one', label: 'One'},
|
|
1719
|
-
{id: 'two', label: 'Two'},
|
|
1720
|
-
]}
|
|
1721
|
-
onKeyDown={onKeyDown}
|
|
1722
|
-
/>,
|
|
1723
|
-
);
|
|
1724
|
-
|
|
1725
|
-
const headerButton = screen.getByRole('button', {name: 'Header Action'});
|
|
1726
|
-
headerButton.focus();
|
|
1727
|
-
fireEvent.keyDown(headerButton, {key: 'ArrowDown'});
|
|
1728
|
-
|
|
1729
|
-
expect(onKeyDown).toHaveBeenCalledTimes(1);
|
|
1730
|
-
expect(capturedCurrentTarget).toBe(ref.current);
|
|
1731
|
-
expect(ref.current).toBeInstanceOf(HTMLDivElement);
|
|
1732
|
-
// Focus remains on header button, internal tree navigation was not triggered
|
|
1733
|
-
expect(document.activeElement).toBe(headerButton);
|
|
1734
|
-
const items = screen.getAllByRole('treeitem');
|
|
1735
|
-
expect(items[0]).toHaveAttribute('tabindex', '0');
|
|
1736
|
-
});
|
|
1737
|
-
});
|