@astryxdesign/core 0.6.2 → 0.6.3-canary.0899f21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +81 -0
- package/dist/BottomSheet/BottomSheet.d.ts +1 -1
- package/dist/BottomSheet/BottomSheet.d.ts.map +1 -1
- package/dist/BottomSheet/BottomSheet.js +3 -1
- package/dist/BottomSheet/BottomSheetPanel.d.ts +7 -5
- package/dist/BottomSheet/BottomSheetPanel.d.ts.map +1 -1
- package/dist/BottomSheet/BottomSheetPanel.js +56 -19
- package/dist/Button/Button.d.ts.map +1 -1
- package/dist/Button/Button.js +13 -28
- package/dist/Chat/ChatComposer.d.ts.map +1 -1
- package/dist/Chat/ChatComposer.js +10 -5
- package/dist/Chat/ChatComposerDrawer.d.ts +19 -5
- package/dist/Chat/ChatComposerDrawer.d.ts.map +1 -1
- package/dist/Chat/ChatComposerDrawer.js +48 -23
- package/dist/Chat/ChatComposerInput.d.ts +11 -3
- package/dist/Chat/ChatComposerInput.d.ts.map +1 -1
- package/dist/Chat/ChatComposerInput.js +63 -16
- package/dist/Chat/ChatDictationButton.d.ts +3 -2
- package/dist/Chat/ChatDictationButton.d.ts.map +1 -1
- package/dist/Chat/ChatDictationButton.js +6 -4
- package/dist/Chat/ChatLayout.d.ts +19 -0
- package/dist/Chat/ChatLayout.d.ts.map +1 -1
- package/dist/Chat/ChatLayout.js +19 -0
- package/dist/Chat/ChatLayoutScrollButton.d.ts.map +1 -1
- package/dist/Chat/ChatLayoutScrollButton.js +44 -34
- package/dist/Chat/ChatMessageBubble.d.ts +2 -0
- package/dist/Chat/ChatMessageBubble.d.ts.map +1 -1
- package/dist/Chat/ChatMessageBubble.js +5 -3
- package/dist/Chat/ChatMessageList.d.ts +2 -1
- package/dist/Chat/ChatMessageList.d.ts.map +1 -1
- package/dist/Chat/ChatMessageList.js +4 -3
- package/dist/Chat/ChatMessageMetadata.d.ts +5 -4
- package/dist/Chat/ChatMessageMetadata.d.ts.map +1 -1
- package/dist/Chat/ChatMessageMetadata.js +12 -9
- package/dist/Chat/ChatSendButton.js +1 -1
- package/dist/Chat/index.d.ts +1 -1
- package/dist/Chat/index.d.ts.map +1 -1
- package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
- package/dist/CheckboxInput/CheckboxInput.js +12 -2
- package/dist/Citation/Citation.d.ts +1 -1
- package/dist/Citation/Citation.d.ts.map +1 -1
- package/dist/Citation/Citation.js +3 -2
- package/dist/CodeBlock/highlightRanges.d.ts.map +1 -1
- package/dist/CodeBlock/highlightRanges.js +14 -3
- package/dist/Collapsible/Collapsible.d.ts.map +1 -1
- package/dist/Collapsible/Collapsible.js +6 -1
- package/dist/CommandPalette/CommandPaletteInput.d.ts.map +1 -1
- package/dist/CommandPalette/CommandPaletteInput.js +1 -1
- package/dist/ComplexSelector/ComplexSelector.d.ts.map +1 -1
- package/dist/ComplexSelector/ComplexSelector.js +1 -1
- package/dist/DateInput/DateInput.d.ts.map +1 -1
- package/dist/DateInput/DateInput.js +4 -4
- package/dist/DateInput/NativeDateField.d.ts.map +1 -1
- package/dist/DateInput/NativeDateField.js +16 -16
- package/dist/DateInput/TouchDateField.d.ts.map +1 -1
- package/dist/DateInput/TouchDateField.js +2 -2
- package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
- package/dist/DateRangeInput/DateRangeInput.js +4 -4
- package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
- package/dist/DateTimeInput/DateTimeInput.js +8 -8
- package/dist/DateTimeInput/TouchDateTimeField.d.ts.map +1 -1
- package/dist/DateTimeInput/TouchDateTimeField.js +4 -4
- package/dist/DateTimeInput/nativePickerSegmentStyles.d.ts +9 -3
- package/dist/DateTimeInput/nativePickerSegmentStyles.d.ts.map +1 -1
- package/dist/DateTimeInput/nativePickerSegmentStyles.js +3 -3
- package/dist/Dialog/DialogHeader.d.ts +8 -1
- package/dist/Dialog/DialogHeader.d.ts.map +1 -1
- package/dist/Dialog/DialogHeader.js +13 -4
- package/dist/Field/InputClearButton.d.ts +4 -2
- package/dist/Field/InputClearButton.d.ts.map +1 -1
- package/dist/Field/InputClearButton.js +4 -2
- package/dist/Field/PanelSearchInput.d.ts.map +1 -1
- package/dist/Field/PanelSearchInput.js +1 -1
- package/dist/FileInput/FileInput.d.ts.map +1 -1
- package/dist/FileInput/FileInput.js +7 -7
- package/dist/Icon/Icon.d.ts +6 -31
- package/dist/Icon/Icon.d.ts.map +1 -1
- package/dist/Icon/Icon.js +8 -71
- package/dist/Icon/IconDefaultSizeContext.d.ts +4 -0
- package/dist/Icon/IconDefaultSizeContext.d.ts.map +1 -0
- package/dist/Icon/IconDefaultSizeContext.js +18 -0
- package/dist/Icon/IconSize.stylex.d.ts +65 -0
- package/dist/Icon/IconSize.stylex.d.ts.map +1 -0
- package/dist/Icon/IconSize.stylex.js +75 -0
- package/dist/Item/Item.d.ts +1 -1
- package/dist/Item/Item.d.ts.map +1 -1
- package/dist/Item/Item.js +4 -3
- package/dist/Kbd/Kbd.d.ts +5 -3
- package/dist/Kbd/Kbd.d.ts.map +1 -1
- package/dist/Kbd/Kbd.js +36 -42
- package/dist/Link/Link.d.ts.map +1 -1
- package/dist/Link/Link.js +6 -2
- package/dist/Link/useLinkComponent.d.ts +5 -15
- package/dist/Link/useLinkComponent.d.ts.map +1 -1
- package/dist/Link/useLinkComponent.js +34 -51
- package/dist/List/List.d.ts +19 -1
- package/dist/List/List.d.ts.map +1 -1
- package/dist/List/List.js +9 -2
- package/dist/List/ListContext.d.ts +1 -0
- package/dist/List/ListContext.d.ts.map +1 -1
- package/dist/List/ListContext.js +2 -1
- package/dist/List/ListItem.d.ts +1 -1
- package/dist/List/ListItem.d.ts.map +1 -1
- package/dist/List/ListItem.js +9 -2
- package/dist/Markdown/Markdown.d.ts +8 -2
- package/dist/Markdown/Markdown.d.ts.map +1 -1
- package/dist/Markdown/Markdown.fr23.bench.d.ts +2 -0
- package/dist/Markdown/Markdown.fr23.bench.d.ts.map +1 -0
- package/dist/Markdown/Markdown.fr23.bench.js +150 -0
- package/dist/Markdown/Markdown.fr23.sampling.d.ts +2 -0
- package/dist/Markdown/Markdown.fr23.sampling.d.ts.map +1 -0
- package/dist/Markdown/Markdown.fr23.sampling.js +43 -0
- package/dist/Markdown/Markdown.js +262 -109
- package/dist/Markdown/ast.d.ts +162 -0
- package/dist/Markdown/ast.d.ts.map +1 -0
- package/dist/Markdown/ast.js +74 -0
- package/dist/Markdown/index.d.ts +5 -3
- package/dist/Markdown/index.d.ts.map +1 -1
- package/dist/Markdown/index.js +3 -2
- package/dist/Markdown/parser/index.d.ts +10 -0
- package/dist/Markdown/parser/index.d.ts.map +1 -0
- package/dist/Markdown/parser/index.js +10 -0
- package/dist/Markdown/parser.d.ts +126 -58
- package/dist/Markdown/parser.d.ts.map +1 -1
- package/dist/Markdown/parser.js +716 -167
- package/dist/Markdown/plugins/frontmatter.d.ts +32 -0
- package/dist/Markdown/plugins/frontmatter.d.ts.map +1 -0
- package/dist/Markdown/plugins/frontmatter.js +132 -0
- package/dist/Markdown/plugins/index.d.ts +17 -0
- package/dist/Markdown/plugins/index.d.ts.map +1 -0
- package/dist/Markdown/plugins/index.js +14 -0
- package/dist/Markdown/plugins/protocol.d.ts +191 -0
- package/dist/Markdown/plugins/protocol.d.ts.map +1 -0
- package/dist/Markdown/plugins/protocol.js +957 -0
- package/dist/Markdown/plugins/semanticFence.d.ts +36 -0
- package/dist/Markdown/plugins/semanticFence.d.ts.map +1 -0
- package/dist/Markdown/plugins/semanticFence.js +213 -0
- package/dist/Markdown/plugins/softBreaks.d.ts +3 -0
- package/dist/Markdown/plugins/softBreaks.d.ts.map +1 -0
- package/dist/Markdown/plugins/softBreaks.js +172 -0
- package/dist/Markdown/plugins/sourceDecoration.d.ts +76 -0
- package/dist/Markdown/plugins/sourceDecoration.d.ts.map +1 -0
- package/dist/Markdown/plugins/sourceDecoration.js +393 -0
- package/dist/Markdown/plugins/textTransform.d.ts +30 -0
- package/dist/Markdown/plugins/textTransform.d.ts.map +1 -0
- package/dist/Markdown/plugins/textTransform.js +374 -0
- package/dist/Markdown/remark.d.ts +196 -0
- package/dist/Markdown/remark.d.ts.map +1 -0
- package/dist/Markdown/remark.js +1062 -0
- package/dist/Markdown/url.d.ts +6 -0
- package/dist/Markdown/url.d.ts.map +1 -0
- package/dist/Markdown/url.js +31 -0
- package/dist/Markdown/utils.d.ts +4 -2
- package/dist/Markdown/utils.d.ts.map +1 -1
- package/dist/Markdown/utils.js +2 -1
- package/dist/MetadataList/MetadataList.js +3 -3
- package/dist/NumberInput/NumberInput.d.ts.map +1 -1
- package/dist/NumberInput/NumberInput.js +4 -4
- package/dist/Outline/index.d.ts +1 -0
- package/dist/Outline/index.d.ts.map +1 -1
- package/dist/Outline/parseOutlineFromMarkdown.d.ts +7 -1
- package/dist/Outline/parseOutlineFromMarkdown.d.ts.map +1 -1
- package/dist/Outline/parseOutlineFromMarkdown.js +14 -8
- package/dist/Outline/useOutlineFromMarkdown.d.ts +3 -1
- package/dist/Outline/useOutlineFromMarkdown.d.ts.map +1 -1
- package/dist/Outline/useOutlineFromMarkdown.js +8 -3
- package/dist/PowerSearch/PowerSearchEditPopover.d.ts.map +1 -1
- package/dist/PowerSearch/PowerSearchEditPopover.js +46 -30
- package/dist/PowerSearch/PowerSearchValueEditor.d.ts.map +1 -1
- package/dist/PowerSearch/PowerSearchValueEditor.js +5 -0
- package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
- package/dist/ProgressBar/ProgressBar.js +36 -31
- package/dist/RadioList/RadioListItem.d.ts.map +1 -1
- package/dist/RadioList/RadioListItem.js +13 -1
- package/dist/ScrollableArea/ScrollableArea.d.ts +1 -0
- package/dist/ScrollableArea/ScrollableArea.d.ts.map +1 -1
- package/dist/ScrollableArea/ScrollableArea.js +1 -0
- package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
- package/dist/SegmentedControl/SegmentedControlItem.js +5 -5
- package/dist/Selector/Selector.d.ts +9 -7
- package/dist/Selector/Selector.d.ts.map +1 -1
- package/dist/Selector/Selector.js +16 -7
- package/dist/SideNav/SideNav.d.ts +2 -1
- package/dist/SideNav/SideNav.d.ts.map +1 -1
- package/dist/SideNav/SideNav.js +9 -3
- package/dist/Slider/Slider.d.ts.map +1 -1
- package/dist/Slider/Slider.js +28 -12
- package/dist/Spinner/Spinner.d.ts.map +1 -1
- package/dist/Spinner/Spinner.js +23 -7
- package/dist/Switch/Switch.d.ts.map +1 -1
- package/dist/Switch/Switch.js +11 -0
- package/dist/TabList/Tab.d.ts +1 -1
- package/dist/TabList/Tab.d.ts.map +1 -1
- package/dist/TabList/Tab.js +20 -7
- package/dist/Table/Table.d.ts +1 -0
- package/dist/Table/Table.d.ts.map +1 -1
- package/dist/Table/Table.js +29 -13
- package/dist/TextArea/TextArea.d.ts.map +1 -1
- package/dist/TextArea/TextArea.js +1 -1
- package/dist/TextInput/TextInput.d.ts.map +1 -1
- package/dist/TextInput/TextInput.js +2 -2
- package/dist/TimeInput/TimeInput.d.ts.map +1 -1
- package/dist/TimeInput/TimeInput.js +4 -4
- package/dist/Timer/Timer.d.ts +49 -0
- package/dist/Timer/Timer.d.ts.map +1 -0
- package/dist/Timer/Timer.js +158 -0
- package/dist/Timer/index.d.ts +9 -0
- package/dist/Timer/index.d.ts.map +1 -0
- package/dist/Timer/index.js +10 -0
- package/dist/ToggleButton/ToggleButton.d.ts +9 -6
- package/dist/ToggleButton/ToggleButton.d.ts.map +1 -1
- package/dist/ToggleButton/ToggleButton.js +15 -27
- package/dist/TopNav/TopNavMenu.d.ts +1 -1
- package/dist/TopNav/TopNavMenu.d.ts.map +1 -1
- package/dist/TopNav/TopNavMenu.js +3 -2
- package/dist/TreeList/TreeList.d.ts.map +1 -1
- package/dist/TreeList/TreeList.js +3 -0
- package/dist/TreeList/TreeListItem.d.ts +6 -1
- package/dist/TreeList/TreeListItem.d.ts.map +1 -1
- package/dist/TreeList/TreeListItem.js +12 -2
- package/dist/TreeList/TreeListTypes.d.ts +8 -1
- package/dist/TreeList/TreeListTypes.d.ts.map +1 -1
- package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
- package/dist/Typeahead/BaseTypeahead.js +16 -7
- package/dist/astryx.css +27 -11
- package/dist/hooks/scrollKeyboardDelegation.d.ts +3 -0
- package/dist/hooks/scrollKeyboardDelegation.d.ts.map +1 -0
- package/dist/hooks/scrollKeyboardDelegation.js +146 -0
- package/dist/hooks/useClickableContainer.d.ts +10 -1
- package/dist/hooks/useClickableContainer.d.ts.map +1 -1
- package/dist/hooks/useClickableContainer.js +15 -4
- package/dist/hooks/useScrollableArea.d.ts +6 -2
- package/dist/hooks/useScrollableArea.d.ts.map +1 -1
- package/dist/hooks/useScrollableArea.js +17 -5
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/theme/declarationBoundary.d.ts +17 -0
- package/dist/theme/declarationBoundary.d.ts.map +1 -0
- package/dist/theme/declarationBoundary.js +343 -0
- package/dist/theme/derivedVarRegistry.d.ts.map +1 -1
- package/dist/theme/derivedVarRegistry.js +4 -0
- package/dist/theme/generateThemeRules.d.ts +13 -7
- package/dist/theme/generateThemeRules.d.ts.map +1 -1
- package/dist/theme/generateThemeRules.js +140 -34
- package/dist/utils/interactionOverlay.stylex.d.ts +8 -0
- package/dist/utils/interactionOverlay.stylex.d.ts.map +1 -1
- package/dist/utils/interactionOverlay.stylex.js +9 -0
- package/dist/utils/safeUrl.d.ts +11 -0
- package/dist/utils/safeUrl.d.ts.map +1 -0
- package/dist/utils/safeUrl.js +78 -0
- package/locales/af-ZA.json +530 -1
- package/locales/ar-SA.json +490 -1
- package/locales/ca-ES.json +514 -1
- package/locales/cs-CZ.json +498 -1
- package/locales/da-DK.json +522 -1
- package/locales/de-DE.json +522 -1
- package/locales/el-GR.json +498 -1
- package/locales/es-ES.json +510 -1
- package/locales/fi-FI.json +498 -1
- package/locales/fr-FR.json +534 -1
- package/locales/he-IL.json +498 -1
- package/locales/hu-HU.json +498 -1
- package/locales/it-IT.json +510 -1
- package/locales/ja-JP.json +490 -1
- package/locales/ko-KR.json +498 -1
- package/locales/nl-NL.json +534 -1
- package/locales/no-NO.json +510 -1
- package/locales/pl-PL.json +506 -1
- package/locales/pt-BR.json +506 -1
- package/locales/pt-PT.json +506 -1
- package/locales/ro-RO.json +506 -1
- package/locales/ru-RU.json +498 -1
- package/locales/sr-SP.json +498 -1
- package/locales/sv-SE.json +502 -1
- package/locales/tr-TR.json +498 -1
- package/locales/uk-UA.json +498 -1
- package/locales/vi-VN.json +502 -1
- package/locales/zh-CN.json +490 -1
- package/locales/zh-TW.json +490 -1
- package/package.json +24 -4
- package/src/BottomSheet/BottomSheet.doc.mjs +8 -1
- package/src/BottomSheet/BottomSheet.spec.md +46 -20
- package/src/BottomSheet/BottomSheet.test.tsx +6 -3
- package/src/BottomSheet/BottomSheet.tsx +3 -1
- package/src/BottomSheet/BottomSheetKeyboard.test.tsx +195 -0
- package/src/BottomSheet/BottomSheetPanel.test.tsx +11 -1
- package/src/BottomSheet/BottomSheetPanel.tsx +49 -16
- package/src/BottomSheet/__tests__/BottomSheetKeyboard.a11y.browser.spec.ts +344 -0
- package/src/Button/Button.doc.mjs +5 -5
- package/src/Button/Button.tsx +15 -13
- package/src/Button/__tests__/Button.a11y.chromium.spec.ts +17 -1
- package/src/Button/__tests__/Button.a11y.known-failures.ts +0 -29
- package/src/Button/__tests__/Button.a11y.renders.tsx +9 -2
- package/src/Button/__tests__/Button.a11y.states.ts +9 -0
- package/src/Chat/ChatComposer.doc.mjs +14 -0
- package/src/Chat/ChatComposer.spec.md +230 -0
- package/src/Chat/ChatComposer.test.tsx +57 -0
- package/src/Chat/ChatComposer.tsx +21 -9
- package/src/Chat/ChatComposerDrawer.doc.mjs +63 -5
- package/src/Chat/ChatComposerDrawer.spec.md +267 -0
- package/src/Chat/ChatComposerDrawer.test.tsx +81 -12
- package/src/Chat/ChatComposerDrawer.tsx +38 -9
- package/src/Chat/ChatComposerInput.doc.mjs +114 -26
- package/src/Chat/ChatComposerInput.spec.md +185 -0
- package/src/Chat/ChatComposerInput.test.tsx +113 -3
- package/src/Chat/ChatComposerInput.tsx +78 -13
- package/src/Chat/ChatComposerTokenElement.spec.md +152 -0
- package/src/Chat/ChatComposerTokenElement.test.tsx +85 -0
- package/src/Chat/ChatDictationButton.doc.mjs +3 -3
- package/src/Chat/ChatDictationButton.spec.md +160 -0
- package/src/Chat/ChatDictationButton.test.tsx +92 -27
- package/src/Chat/ChatDictationButton.tsx +8 -6
- package/src/Chat/ChatLayout.doc.mjs +12 -4
- package/src/Chat/ChatLayout.spec.md +271 -0
- package/src/Chat/ChatLayout.test.tsx +28 -0
- package/src/Chat/ChatLayout.tsx +19 -0
- package/src/Chat/ChatLayoutScrollButton.spec.md +338 -0
- package/src/Chat/ChatLayoutScrollButton.test.tsx +31 -0
- package/src/Chat/ChatLayoutScrollButton.tsx +31 -14
- package/src/Chat/ChatMessageBubble.doc.mjs +28 -15
- package/src/Chat/ChatMessageBubble.spec.md +263 -0
- package/src/Chat/ChatMessageBubble.test.tsx +93 -0
- package/src/Chat/ChatMessageBubble.tsx +8 -3
- package/src/Chat/ChatMessageList.spec.md +222 -0
- package/src/Chat/ChatMessageList.test.tsx +64 -2
- package/src/Chat/ChatMessageList.tsx +4 -3
- package/src/Chat/ChatMessageMetadata.doc.mjs +57 -3
- package/src/Chat/ChatMessageMetadata.spec.md +220 -0
- package/src/Chat/ChatMessageMetadata.test.tsx +34 -0
- package/src/Chat/ChatMessageMetadata.tsx +13 -13
- package/src/Chat/ChatSendButton.test.tsx +1 -4
- package/src/Chat/ChatSendButton.tsx +1 -1
- package/src/Chat/__tests__/ChatLayoutScrollButton.a11y.chromium.spec.ts +514 -0
- package/src/Chat/__tests__/ChatLayoutScrollButtonSurface.a11y.chromium.spec.ts +740 -0
- package/src/Chat/__tests__/ChatMessageBubble.a11y.chromium.spec.ts +457 -0
- package/src/Chat/__tests__/ChatMessageBubble.auditProvenance.test.ts +91 -0
- package/src/Chat/__tests__/ChatMessageBubble.auditProvenance.ts +91 -0
- package/src/Chat/__tests__/ChatMessageList.a11y.chromium.spec.ts +456 -0
- package/src/Chat/__tests__/ChatMessageMetadata.a11y.chromium.spec.ts +413 -0
- package/src/Chat/index.ts +1 -0
- package/src/CheckboxInput/CheckboxInput.doc.mjs +11 -0
- package/src/CheckboxInput/CheckboxInput.test.tsx +34 -0
- package/src/CheckboxInput/CheckboxInput.tsx +21 -1
- package/src/Citation/Citation.doc.mjs +2 -2
- package/src/Citation/Citation.test.tsx +17 -0
- package/src/Citation/Citation.tsx +4 -2
- package/src/ClickableCard/ClickableCard.doc.mjs +2 -2
- package/src/ClickableCard/ClickableCard.test.tsx +102 -5
- package/src/CodeBlock/highlightRanges.test.ts +143 -1
- package/src/CodeBlock/highlightRanges.ts +15 -5
- package/src/Collapsible/Collapsible.doc.mjs +11 -0
- package/src/Collapsible/Collapsible.test.tsx +21 -0
- package/src/Collapsible/Collapsible.tsx +5 -0
- package/src/CommandPalette/CommandPaletteInput.tsx +6 -1
- package/src/ComplexSelector/ComplexSelector.tsx +6 -1
- package/src/DateInput/DateInput.tsx +6 -1
- package/src/DateInput/NativeDateField.tsx +24 -8
- package/src/DateInput/TouchDateField.tsx +6 -2
- package/src/DateRangeInput/DateRangeInput.tsx +6 -1
- package/src/DateTimeInput/DateTimeInput.tsx +6 -1
- package/src/DateTimeInput/TouchDateTimeField.tsx +6 -1
- package/src/DateTimeInput/nativePickerSegmentStyles.ts +24 -8
- package/src/Dialog/Dialog.doc.mjs +2 -0
- package/src/Dialog/DialogHeader.doc.mjs +22 -0
- package/src/Dialog/DialogHeader.test.tsx +167 -3
- package/src/Dialog/DialogHeader.tsx +37 -6
- package/src/Dialog/modules/DialogHeader.spec.md +101 -42
- package/src/DropdownMenu/DropdownMenu.test.tsx +5 -2
- package/src/Field/InputClearButton.test.tsx +17 -3
- package/src/Field/InputClearButton.tsx +5 -2
- package/src/Field/PanelSearchInput.tsx +5 -2
- package/src/FieldStatus/__tests__/StatusMessage.a11y.states.ts +1 -1
- package/src/FileInput/FileInput.tsx +11 -2
- package/src/Icon/Icon.doc.mjs +7 -5
- package/src/Icon/Icon.spec.md +81 -40
- package/src/Icon/Icon.test.tsx +6 -3
- package/src/Icon/Icon.tsx +20 -68
- package/src/Icon/IconDefaultSizeContext.ts +23 -0
- package/src/Icon/IconSize.stylex.ts +70 -0
- package/src/IconButton/IconButton.doc.mjs +3 -3
- package/src/IconButton/IconButton.test.tsx +40 -0
- package/src/Item/Item.doc.mjs +6 -2
- package/src/Item/Item.tsx +9 -3
- package/src/Kbd/Kbd.doc.mjs +3 -3
- package/src/Kbd/Kbd.test.tsx +57 -1
- package/src/Kbd/Kbd.tsx +57 -37
- package/src/Layout/LayoutFooter.doc.mjs +17 -0
- package/src/Link/Link.doc.mjs +17 -4
- package/src/Link/Link.test.tsx +24 -0
- package/src/Link/Link.tsx +5 -0
- package/src/Link/LinkProvider.doc.mjs +2 -2
- package/src/Link/__tests__/Link.navigation.a11y.chromium.spec.ts +261 -0
- package/src/Link/useLinkComponent.test.tsx +333 -0
- package/src/Link/useLinkComponent.ts +29 -57
- package/src/List/List.doc.mjs +6 -0
- package/src/List/List.spec.md +200 -0
- package/src/List/List.test.tsx +117 -1
- package/src/List/List.tsx +22 -2
- package/src/List/ListContext.tsx +3 -1
- package/src/List/ListItem.doc.mjs +2 -2
- package/src/List/ListItem.source-build.test.mjs +41 -0
- package/src/List/ListItem.tsx +26 -2
- package/src/Markdown/Markdown.doc.mjs +300 -2
- package/src/Markdown/Markdown.fr23.bench.ts +174 -0
- package/src/Markdown/Markdown.fr23.perf.test.ts +249 -0
- package/src/Markdown/Markdown.fr23.sampling.ts +49 -0
- package/src/Markdown/Markdown.helpers.perf.test.ts +489 -0
- package/src/Markdown/Markdown.public.test.ts +130 -3
- package/src/Markdown/Markdown.renderBoundary.test.tsx +72 -0
- package/src/Markdown/Markdown.spec.md +203 -45
- package/src/Markdown/Markdown.test.tsx +63 -0
- package/src/Markdown/Markdown.tsx +414 -135
- package/src/Markdown/ast.test.ts +307 -0
- package/src/Markdown/ast.ts +337 -0
- package/src/Markdown/incremental.test.ts +12 -0
- package/src/Markdown/index.ts +37 -1
- package/src/Markdown/modules/remark.spec.md +269 -0
- package/src/Markdown/modules/softBreaks.spec.md +137 -0
- package/src/Markdown/parser/index.ts +64 -0
- package/src/Markdown/parser.public.test.ts +103 -0
- package/src/Markdown/parser.test.ts +20 -0
- package/src/Markdown/parser.ts +1180 -235
- package/src/Markdown/plugins/frontmatter.test.tsx +125 -0
- package/src/Markdown/plugins/frontmatter.ts +198 -0
- package/src/Markdown/plugins/index.ts +54 -0
- package/src/Markdown/plugins/protocol.test.tsx +1022 -0
- package/src/Markdown/plugins/protocol.ts +1618 -0
- package/src/Markdown/plugins/semanticFence.test.tsx +546 -0
- package/src/Markdown/plugins/semanticFence.ts +424 -0
- package/src/Markdown/plugins/softBreaks.test.tsx +172 -0
- package/src/Markdown/plugins/softBreaks.ts +169 -0
- package/src/Markdown/plugins/sourceDecoration.test.tsx +799 -0
- package/src/Markdown/plugins/sourceDecoration.ts +539 -0
- package/src/Markdown/plugins/textTransform.test.ts +467 -0
- package/src/Markdown/plugins/textTransform.ts +554 -0
- package/src/Markdown/remark.test.tsx +1877 -0
- package/src/Markdown/remark.ts +1574 -0
- package/src/Markdown/url.ts +36 -0
- package/src/Markdown/utils.ts +13 -1
- package/src/MetadataList/MetadataList.test.tsx +4 -2
- package/src/MetadataList/MetadataList.tsx +3 -3
- package/src/MultiSelector/MultiSelector.test.tsx +1 -35
- package/src/MultiSelector/__tests__/MultiSelector.listbox.a11y.test.tsx +128 -0
- package/src/NumberInput/NumberInput.tsx +6 -1
- package/src/Outline/Outline.doc.mjs +7 -5
- package/src/Outline/Outline.spec.md +1 -1
- package/src/Outline/index.ts +1 -0
- package/src/Outline/modules/parseOutlineFromMarkdown.spec.md +142 -0
- package/src/Outline/parseOutlineFromMarkdown.ts +40 -10
- package/src/Outline/useOutlineFromMarkdown.ts +15 -3
- package/src/PowerSearch/PowerSearch.doc.mjs +2 -2
- package/src/PowerSearch/PowerSearchEditPopover.test.tsx +150 -1
- package/src/PowerSearch/PowerSearchEditPopover.tsx +51 -28
- package/src/PowerSearch/PowerSearchValueEditor.test.tsx +51 -0
- package/src/PowerSearch/PowerSearchValueEditor.tsx +4 -0
- package/src/ProgressBar/ProgressBar.test.tsx +29 -11
- package/src/ProgressBar/ProgressBar.tsx +59 -72
- package/src/RadioList/RadioList.doc.mjs +11 -0
- package/src/RadioList/RadioList.test.tsx +32 -0
- package/src/RadioList/RadioListItem.tsx +25 -1
- package/src/ScrollableArea/ScrollableArea.tsx +1 -0
- package/src/ScrollableArea/modules/useScrollableArea.spec.md +50 -22
- package/src/SegmentedControl/SegmentedControl.doc.mjs +2 -2
- package/src/SegmentedControl/SegmentedControl.test.tsx +31 -0
- package/src/SegmentedControl/SegmentedControlItem.tsx +6 -9
- package/src/Selector/Selector.doc.mjs +2 -2
- package/src/Selector/Selector.spec.md +51 -49
- package/src/Selector/Selector.test.tsx +61 -55
- package/src/Selector/Selector.tsx +34 -15
- package/src/Selector/__tests__/Listbox.a11y.chromium.spec.ts +130 -0
- package/src/Selector/__tests__/Listbox.a11y.dom.ts +38 -0
- package/src/Selector/__tests__/Listbox.a11y.inventory.test.ts +63 -0
- package/src/Selector/__tests__/Listbox.a11y.known-failures.ts +37 -0
- package/src/Selector/__tests__/Listbox.a11y.states.ts +232 -0
- package/src/Selector/__tests__/Selector.a11y.chromium.spec.ts +45 -0
- package/src/Selector/__tests__/Selector.listbox.a11y.test.tsx +121 -0
- package/src/SideNav/SideNav.doc.mjs +1 -1
- package/src/SideNav/SideNav.test.tsx +10 -0
- package/src/SideNav/SideNav.tsx +14 -2
- package/src/Slider/Slider.doc.mjs +17 -3
- package/src/Slider/Slider.test.tsx +221 -0
- package/src/Slider/Slider.tsx +40 -9
- package/src/Spinner/Spinner.test.tsx +4 -0
- package/src/Spinner/Spinner.tsx +31 -18
- package/src/Switch/Switch.doc.mjs +11 -0
- package/src/Switch/Switch.test.tsx +16 -0
- package/src/Switch/Switch.tsx +28 -0
- package/src/TabList/Tab.tsx +35 -7
- package/src/TabList/TabList.doc.mjs +11 -0
- package/src/TabList/TabList.spec.md +204 -0
- package/src/TabList/TabList.test.tsx +66 -0
- package/src/TabList/__tests__/Tabs.a11y.known-failures.ts +1 -34
- package/src/Table/Table.doc.mjs +1 -1
- package/src/Table/Table.spec.md +54 -40
- package/src/Table/Table.test.tsx +83 -10
- package/src/Table/Table.tsx +30 -22
- package/src/Table/__tests__/TableScroll.a11y.chromium.spec.ts +137 -0
- package/src/TextArea/TextArea.tsx +6 -1
- package/src/TextInput/TextInput.test.tsx +20 -1
- package/src/TextInput/TextInput.tsx +6 -1
- package/src/TimeInput/TimeInput.tsx +6 -1
- package/src/Timer/Timer.doc.mjs +183 -0
- package/src/Timer/Timer.spec.md +215 -0
- package/src/Timer/Timer.test.tsx +302 -0
- package/src/Timer/Timer.tsx +258 -0
- package/src/Timer/index.ts +11 -0
- package/src/ToggleButton/ToggleButton.doc.mjs +3 -3
- package/src/ToggleButton/ToggleButton.test.tsx +299 -4
- package/src/ToggleButton/ToggleButton.tsx +26 -33
- package/src/ToggleButton/ToggleButtonGroup.doc.mjs +3 -3
- package/src/ToggleButton/__tests__/ToggleButton.a11y.chromium.spec.ts +209 -0
- package/src/Token/Token.doc.mjs +2 -2
- package/src/Tokenizer/Tokenizer.spec.md +142 -75
- package/src/TopNav/TopNavMenu.doc.mjs +2 -2
- package/src/TopNav/TopNavMenu.test.tsx +30 -0
- package/src/TopNav/TopNavMenu.tsx +7 -2
- package/src/TreeList/TreeList.doc.mjs +3 -3
- package/src/TreeList/TreeList.spec.md +123 -75
- package/src/TreeList/TreeList.test.tsx +29 -0
- package/src/TreeList/TreeList.tsx +3 -0
- package/src/TreeList/TreeListItem.tsx +15 -1
- package/src/TreeList/TreeListTypes.ts +11 -1
- package/src/Typeahead/BaseTypeahead.spec.md +4 -3
- package/src/Typeahead/BaseTypeahead.tsx +21 -7
- package/src/Typeahead/Typeahead.test.tsx +53 -0
- package/src/__tests__/PressedState.a11y.chromium.spec.ts +813 -0
- package/src/__tests__/pressState.ts +93 -0
- package/src/hooks/scrollKeyboardDelegation.test.ts +155 -0
- package/src/hooks/scrollKeyboardDelegation.ts +233 -0
- package/src/hooks/useClickableContainer.test.tsx +236 -0
- package/src/hooks/useClickableContainer.ts +20 -4
- package/src/hooks/useScrollableArea.doc.mjs +15 -3
- package/src/hooks/useScrollableArea.test.tsx +59 -1
- package/src/hooks/useScrollableArea.ts +34 -10
- package/src/index.ts +3 -0
- package/src/inputFontFloor.test.ts +106 -0
- package/src/tailwind-theme.css +0 -6
- package/src/theme/Theme.test.tsx +32 -0
- package/src/theme/declarationBoundary.test.ts +126 -0
- package/src/theme/declarationBoundary.ts +347 -0
- package/src/theme/derivedVarRegistry.test.ts +7 -0
- package/src/theme/derivedVarRegistry.ts +1 -0
- package/src/theme/generateThemeRules.test.ts +526 -1
- package/src/theme/generateThemeRules.ts +228 -50
- package/src/theme/tokenValueCompat.test.ts +4 -1
- package/src/utils/interactionOverlay.stylex.ts +20 -0
- package/src/utils/safeUrl.test.ts +88 -0
- package/src/utils/safeUrl.ts +85 -0
|
@@ -4,89 +4,61 @@
|
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* @file useLinkComponent.ts
|
|
7
|
-
* @input
|
|
8
|
-
* @output
|
|
9
|
-
* @position
|
|
7
|
+
* @input Per-component override, LinkProvider context, and navigation props
|
|
8
|
+
* @output Stable link component with destination checks and custom-router `to` injection
|
|
9
|
+
* @position Shared native and custom link boundary; rejected destinations render
|
|
10
|
+
* as destination-less anchors without invoking a router.
|
|
10
11
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* When the resolved component is a custom component (not native `<a>`),
|
|
14
|
-
* wraps it to pass `to={href}` alongside `href`. This enables compatibility
|
|
15
|
-
* with routers that use `to` (React Router, TanStack Router)
|
|
16
|
-
* without requiring an adapter component.
|
|
17
|
-
*
|
|
18
|
-
* SYNC: When modified, update these files to stay in sync:
|
|
19
|
-
* - /packages/core/src/Link/index.ts
|
|
20
|
-
* - /packages/core/src/Link/Link.doc.mjs
|
|
12
|
+
* SYNC: Link.doc.mjs and LinkProvider.doc.mjs describe this boundary.
|
|
21
13
|
*/
|
|
22
14
|
|
|
23
15
|
import {use, useMemo, createElement} from 'react';
|
|
24
16
|
import {LinkContext} from './LinkContext';
|
|
17
|
+
import {isSafeDestination} from '../utils/safeUrl';
|
|
25
18
|
import type {LinkComponentType} from './types';
|
|
26
19
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
* to the underlying link component. This enables routers that use `to`
|
|
30
|
-
* (React Router, TanStack Router) to work without an adapter.
|
|
31
|
-
*
|
|
32
|
-
* The wrapper is transparent: it forwards refs and all other props unchanged.
|
|
33
|
-
* Native `<a>` elements ignore the unknown `to` prop harmlessly.
|
|
34
|
-
*/
|
|
35
|
-
function createLinkWithTo(
|
|
36
|
-
Component: LinkComponentType,
|
|
37
|
-
): LinkComponentType {
|
|
38
|
-
function LinkWithTo({
|
|
20
|
+
function createSafeLink(Component: LinkComponentType): LinkComponentType {
|
|
21
|
+
function SafeLink({
|
|
39
22
|
href,
|
|
23
|
+
to,
|
|
40
24
|
ref,
|
|
41
25
|
...rest
|
|
42
26
|
}: {
|
|
43
|
-
href?:
|
|
44
|
-
to?:
|
|
27
|
+
href?: unknown;
|
|
28
|
+
to?: unknown;
|
|
45
29
|
ref?: React.Ref<unknown>;
|
|
46
30
|
}) {
|
|
47
|
-
|
|
31
|
+
// An undefined destination can crash a router or resolve to the current
|
|
32
|
+
// route. Reject the whole handoff, not just one prop or a fallback value.
|
|
33
|
+
if (!isSafeDestination(href) || !isSafeDestination(to)) {
|
|
34
|
+
return createElement('a', {ref, ...rest});
|
|
35
|
+
}
|
|
36
|
+
if (Component === 'a') {
|
|
37
|
+
return createElement('a', {ref, ...rest, href});
|
|
38
|
+
}
|
|
39
|
+
return createElement(Component, {ref, ...rest, href, to: to ?? href});
|
|
48
40
|
}
|
|
49
|
-
|
|
41
|
+
SafeLink.displayName = `SafeLink(${
|
|
50
42
|
typeof Component === 'string'
|
|
51
43
|
? Component
|
|
52
44
|
: Component.displayName || Component.name || 'Component'
|
|
53
45
|
})`;
|
|
54
|
-
return
|
|
46
|
+
return SafeLink as LinkComponentType;
|
|
55
47
|
}
|
|
56
48
|
|
|
57
49
|
/**
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
* When the resolved component is a custom component (not the native `<a>`),
|
|
63
|
-
* it is wrapped to receive both `href` and `to` props set to the same value.
|
|
64
|
-
* This allows `to`-based routers (React Router, TanStack Router) to work
|
|
65
|
-
* out of the box without a manual adapter.
|
|
66
|
-
*
|
|
67
|
-
* @param as - Per-component override. If provided, takes highest priority.
|
|
68
|
-
* @returns The resolved link component (with `to` injection for custom components).
|
|
50
|
+
* Resolve `as`, then LinkProvider, then a native anchor. Both destination props
|
|
51
|
+
* are checked before rendering; accepted structured values retain identity.
|
|
52
|
+
* Custom components receive `to={href}` unless an explicit `to` is supplied.
|
|
69
53
|
*
|
|
70
54
|
* @example
|
|
71
55
|
* ```
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
* return <LinkComponent href="/foo">Click me</LinkComponent>;
|
|
75
|
-
* }
|
|
56
|
+
* const LinkComponent = useLinkComponent(as);
|
|
57
|
+
* return <LinkComponent href="/docs">Documentation</LinkComponent>;
|
|
76
58
|
* ```
|
|
77
59
|
*/
|
|
78
|
-
export function useLinkComponent(
|
|
79
|
-
as?: LinkComponentType,
|
|
80
|
-
): LinkComponentType {
|
|
60
|
+
export function useLinkComponent(as?: LinkComponentType): LinkComponentType {
|
|
81
61
|
const ctx = use(LinkContext);
|
|
82
62
|
const resolved = as ?? ctx?.component ?? 'a';
|
|
83
|
-
|
|
84
|
-
// Memoize the wrapper to maintain referential stability.
|
|
85
|
-
// The wrapper is only created for custom components (not native <a>).
|
|
86
|
-
return useMemo(() => {
|
|
87
|
-
if (resolved === 'a') {
|
|
88
|
-
return 'a';
|
|
89
|
-
}
|
|
90
|
-
return createLinkWithTo(resolved);
|
|
91
|
-
}, [resolved]);
|
|
63
|
+
return useMemo(() => createSafeLink(resolved), [resolved]);
|
|
92
64
|
}
|
package/src/List/List.doc.mjs
CHANGED
|
@@ -41,6 +41,12 @@ export const docs = {
|
|
|
41
41
|
description: 'Show dividers between items.',
|
|
42
42
|
default: 'false',
|
|
43
43
|
},
|
|
44
|
+
{
|
|
45
|
+
name: 'edgeCompensation',
|
|
46
|
+
type: "'inline'",
|
|
47
|
+
description:
|
|
48
|
+
"Compensate for item content inset on each inline edge by cancelling the smaller of each item's built-in horizontal inset and the container's published inline padding. The margin reads the same variable the items derive their inline padding from, so it tracks density and theme padding overrides automatically without pulling rows outside zero-padding or full-bleed surfaces. Use under a section heading to bring row text toward the heading text. Content aligns when container padding is at least the item inset; smaller padding leaves some inset uncompensated. Omit to leave item positions unchanged.",
|
|
49
|
+
},
|
|
44
50
|
{
|
|
45
51
|
name: 'header',
|
|
46
52
|
type: 'ReactNode',
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
---
|
|
2
|
+
schema_version: 3
|
|
3
|
+
template_version: 5
|
|
4
|
+
kind: component
|
|
5
|
+
id: component:List
|
|
6
|
+
authority: current
|
|
7
|
+
archive_reason: null
|
|
8
|
+
superseded_by: null
|
|
9
|
+
approved_by: cixzhang
|
|
10
|
+
approved_at: 2026-09-21
|
|
11
|
+
owners: [cixzhang]
|
|
12
|
+
review_triggers: [public-api, behavior, layout, theming]
|
|
13
|
+
verified_by:
|
|
14
|
+
[
|
|
15
|
+
packages/core/src/List/List.test.tsx,
|
|
16
|
+
packages/core/src/Item/Item.test.tsx,
|
|
17
|
+
packages/core/src/theme/derivedVarRegistry.test.ts,
|
|
18
|
+
packages/core/src/theme/generateThemeRules.test.ts,
|
|
19
|
+
apps/storybook/stories/List.stories.tsx,
|
|
20
|
+
scripts/check-knowledge.mjs,
|
|
21
|
+
]
|
|
22
|
+
modules: []
|
|
23
|
+
families: []
|
|
24
|
+
design_specs: []
|
|
25
|
+
architecture:
|
|
26
|
+
[architecture:container-padding, architecture:public-component-api]
|
|
27
|
+
contributing: [contributing:api-conventions]
|
|
28
|
+
system_specs: [spec:AST-002/DEC-1, spec:AST-002/DEC-2]
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
# List component contract
|
|
32
|
+
|
|
33
|
+
## Contract at a glance
|
|
34
|
+
|
|
35
|
+
| Area | Contract |
|
|
36
|
+
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
37
|
+
| Public contract | Add optional `edgeCompensation?: 'inline'`; omission preserves current layout. |
|
|
38
|
+
| Behavior | Every ListItem row moves its content toward a neutral host's inline content edges, reaching them when the host inset is at least the Item inset, while retaining Item-owned interaction padding. |
|
|
39
|
+
| End-user impact | List content can reduce or remove the extra inset beside its optional header and adjacent content without shrinking hover, press, selection, or focus paint. |
|
|
40
|
+
| Builder impact | Builders opt in only when the List is placed directly in an eligible container-padding host state. |
|
|
41
|
+
| Compatibility/readiness | Additive API; implementation and required real-browser evidence are pending. |
|
|
42
|
+
| Review checks | Reject header movement, unbounded negative margins, duplicated padding values, physical-edge logic, automatic opt-in, or a ListItem-level public prop. |
|
|
43
|
+
| Governing rules | `architecture:container-padding/INV9–INV11`; `spec:AST-002/DEC-1–DEC-2`. |
|
|
44
|
+
|
|
45
|
+
This table is a review projection; the body below is authoritative.
|
|
46
|
+
|
|
47
|
+
## Intent
|
|
48
|
+
|
|
49
|
+
List owns collection-wide edge alignment because its optional header is not
|
|
50
|
+
horizontally inset while every ListItem retains Item's inline padding. The caller
|
|
51
|
+
knows whether the List is placed in a neutral host where these content lines
|
|
52
|
+
should align; List cannot derive that design relationship from its children.
|
|
53
|
+
|
|
54
|
+
## Compatibility and migration
|
|
55
|
+
|
|
56
|
+
- Released default preserved: `yes`; omission keeps current List and ListItem geometry.
|
|
57
|
+
- Compatibility class: additive optional prop with no default behavior change.
|
|
58
|
+
- Controlled/uncontrolled behavior: not applicable.
|
|
59
|
+
- Migration decision: `component:List/DEC-1` and `component:List/DEC-2`.
|
|
60
|
+
|
|
61
|
+
Consumer migration instructions belong in consumer docs and release notes.
|
|
62
|
+
|
|
63
|
+
## Ownership boundary
|
|
64
|
+
|
|
65
|
+
**Owns**
|
|
66
|
+
|
|
67
|
+
- The collection-wide `edgeCompensation` opt-in and its propagation to every
|
|
68
|
+
ListItem row.
|
|
69
|
+
- Keeping the optional header in place while moving row content toward its line.
|
|
70
|
+
- The supported relationship between ListItem row geometry and Item's normal
|
|
71
|
+
density- and theme-aware inline inset.
|
|
72
|
+
|
|
73
|
+
**Does not own / non-goals**
|
|
74
|
+
|
|
75
|
+
- Container padding values, nesting, or overlay resets — owned by
|
|
76
|
+
`architecture:container-padding`.
|
|
77
|
+
- A ListItem-level compensation prop, automatic host detection, full bleed, or
|
|
78
|
+
arbitrary margin controls.
|
|
79
|
+
- TabList or Button projections.
|
|
80
|
+
|
|
81
|
+
## Public concepts
|
|
82
|
+
|
|
83
|
+
| Concept | Closed values or states | Meaning | Availability by variant/orientation/state | Default | Owner | Stability | Invalid-value behavior |
|
|
84
|
+
| ----------------- | ----------------------- | ----------------------------------------------------------------------------------------------------- | ------------------------------------------------------- | ------- | ---------------- | -------------------------------- | -------------------------------------------- |
|
|
85
|
+
| Edge compensation | omitted, `inline` | Move row content toward both logical host content edges while retaining row-local interaction padding | Every List density and marker style in an eligible host | omitted | `component:List` | accepted; implementation pending | Other values are rejected by the public type |
|
|
86
|
+
|
|
87
|
+
## Behavioral and layout contract
|
|
88
|
+
|
|
89
|
+
| ID | Invariant | Basis | Status |
|
|
90
|
+
| --- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------- | ----------------------------------------- |
|
|
91
|
+
| FR1 | List MUST own `edgeCompensation?: 'inline'`; ListItem MUST NOT expose a parallel public prop. | `component:List/DEC-1` | Accepted contract; implementation pending |
|
|
92
|
+
| FR2 | Omission MUST preserve current header, list, and row geometry. Enabling `inline` MUST leave the optional header in place and apply one compensation policy to every ListItem, including non-interactive rows. | Owner direction | Accepted contract; implementation pending |
|
|
93
|
+
| FR3 | Each row MUST resolve each logical margin independently as the negative smaller of its own component-owned inline inset and the matching available host inset. Missing host inset MUST resolve to zero. Content reaches the host line only when the host inset is at least the row inset; otherwise it moves toward that line without crossing the host boundary. | `architecture:container-padding/INV9–INV10`; `component:List/DEC-2` | Accepted contract; implementation pending |
|
|
94
|
+
| FR4 | The retained row inset MUST come from the same private density- and theme-derived source as Item's applied inline padding. A consumer override of row padding through `xstyle`, `className`, or `style` is outside the alignment guarantee; `edgeCompensation` MUST NOT mirror or infer that arbitrary CSS. | Shared two-owner geometry contract | Accepted contract; implementation pending |
|
|
95
|
+
| FR5 | Compensation MUST preserve Item's hover, press, selection, focus, content, semantics, and hit target. It MUST NOT make the row a full-bleed surface or pull it past either available host edge. | Owner direction | Accepted contract; implementation pending |
|
|
96
|
+
| FR6 | The opt-in is valid only under a host state whose published inline inset matches its applied padding and whose relevant boundary is visually neutral. Current automatic LayoutContent/LayoutPanel states with known publication mismatch are not eligible until that conformance gap is fixed. | `architecture:container-padding/INV8–INV11` | Accepted contract; implementation pending |
|
|
97
|
+
|
|
98
|
+
### Allowed variation
|
|
99
|
+
|
|
100
|
+
- Density and `item.paddingInline` theme overrides may change the retained inset;
|
|
101
|
+
compensation follows the same derived value.
|
|
102
|
+
- Container padding may be uniform, asymmetric, zero, or absent; each logical
|
|
103
|
+
edge resolves independently.
|
|
104
|
+
- Labels, descriptions, markers, start/end content, interactivity, and selection
|
|
105
|
+
may vary without changing the collection-wide alignment rule.
|
|
106
|
+
|
|
107
|
+
### Representative states
|
|
108
|
+
|
|
109
|
+
| State | Required invariant | Allowed variation |
|
|
110
|
+
| ------------------------------------ | --------------------------------------------------------------------------------------- | --------------------------------------------------------- |
|
|
111
|
+
| Default/omitted | Header and every row keep current geometry. | Any current List content and density. |
|
|
112
|
+
| Host inset at least Item inset | Row content reaches the host content line; row-local paint keeps Item padding. | Uniform or asymmetric inline padding. |
|
|
113
|
+
| Smaller, zero, or missing host inset | The row moves only by the available inset and is never pulled beyond the host boundary. | Either logical edge may independently be smaller or zero. |
|
|
114
|
+
| LTR or RTL | Logical start/end mapping follows direction without changing the `inline` meaning. | Writing direction. |
|
|
115
|
+
| Themed Item padding | Row padding and compensation move together. | Supported `item.paddingInline` value. |
|
|
116
|
+
|
|
117
|
+
### Transformation and precedence order
|
|
118
|
+
|
|
119
|
+
- **ORD1 — Host bound.** Read the matching published host inset for each selected
|
|
120
|
+
logical edge, with missing values falling back to zero.
|
|
121
|
+
- **ORD2 — Local inset.** Resolve Item's component-owned inline inset from density
|
|
122
|
+
and theme through the same private value that supplies applied padding.
|
|
123
|
+
- **ORD3 — Compensation.** Apply the negative minimum of those values to the row;
|
|
124
|
+
then compose supported consumer styles without claiming alignment for a consumer
|
|
125
|
+
override of row inline padding.
|
|
126
|
+
|
|
127
|
+
### Performance and resources
|
|
128
|
+
|
|
129
|
+
- **PR1 — CSS geometry only.** Edge compensation MUST add no measurement,
|
|
130
|
+
observer, listener, state, Effect, or render pass.
|
|
131
|
+
|
|
132
|
+
## Accessibility contract
|
|
133
|
+
|
|
134
|
+
- **AR1 — Interaction geometry remains local.** Compensation MUST NOT change the
|
|
135
|
+
interactive element, accessible role/name/state, focus target, tab order, or
|
|
136
|
+
hit target.
|
|
137
|
+
- **AR2 — Direction remains logical.** The same `inline` value MUST produce the
|
|
138
|
+
corresponding mirrored geometry under RTL without changing DOM order.
|
|
139
|
+
|
|
140
|
+
## Design relationships
|
|
141
|
+
|
|
142
|
+
| Anatomy or state | Design requirement | Representation authority | Hierarchy role | Component contract |
|
|
143
|
+
| ---------------- | ----------------------------------------------------------------------------------------------------------- | ------------------------ | ------------------ | ------------------ |
|
|
144
|
+
| Optional header | Stays on the host content line and is not moved by row compensation. | `component:List/DEC-1` | Context | FR2 |
|
|
145
|
+
| ListItem row | Retains local interaction paint and hit-target padding. | Item composition | Supporting surface | FR3–FR5 |
|
|
146
|
+
| Row content | Moves toward each host content line and reaches it only when the host inset can fully absorb the row inset. | `component:List/DEC-2` | Prominent | FR2–FR5 |
|
|
147
|
+
|
|
148
|
+
## Family and system relationships
|
|
149
|
+
|
|
150
|
+
- `architecture:container-padding` owns host publication, the shared
|
|
151
|
+
`edgeCompensation` vocabulary, eligibility, logical-edge semantics, and overlay
|
|
152
|
+
reset.
|
|
153
|
+
- `architecture:public-component-api` and `spec:AST-002` own public API admission.
|
|
154
|
+
- Item remains the implementation owner of row padding and interaction paint;
|
|
155
|
+
List owns only the collection-level opt-in and row application.
|
|
156
|
+
|
|
157
|
+
## Verification map
|
|
158
|
+
|
|
159
|
+
| Contract | Verification | Representative states | Mutation or failure expectation | Audit section |
|
|
160
|
+
| ---------------- | ------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- | ------------------- |
|
|
161
|
+
| FR1–FR4 | List, Item, and theme-registry tests | omitted/enabled; all densities; theme override; static/interactive rows | Removing context propagation, duplicating a padding value, or using one host edge for both margins fails | `audit:List/layout` |
|
|
162
|
+
| FR2–FR6, AR1–AR2 | Real-browser List geometry evidence | header; LTR/RTL; uniform/asymmetric/zero/missing host inset; host inset smaller/equal/larger than Item inset; hover/selection/focus | Header movement, over-pull, wrong bounded movement, or lost row-local paint fails | `audit:List/visual` |
|
|
163
|
+
| Documentation | `List.doc.mjs`, Storybook, and `scripts/check-knowledge.mjs` | public prop/default and representative composition | Missing or stale contract, docs, or example fails | `audit:List/docs` |
|
|
164
|
+
|
|
165
|
+
## Decision log
|
|
166
|
+
|
|
167
|
+
### DEC-1 — List owns the collection-wide opt-in
|
|
168
|
+
|
|
169
|
+
**Reference:** `component:List/DEC-1`
|
|
170
|
+
|
|
171
|
+
**Decider:** cixzhang, 2026-09-21
|
|
172
|
+
|
|
173
|
+
The List header and ListItem rows otherwise begin on different content lines.
|
|
174
|
+
Only the caller knows when the containing composition requires alignment, while
|
|
175
|
+
List can apply one consistent decision to every row without exposing Item-level
|
|
176
|
+
configuration.
|
|
177
|
+
|
|
178
|
+
### DEC-2 — Compensation is row-local and bounded
|
|
179
|
+
|
|
180
|
+
**Reference:** `component:List/DEC-2`
|
|
181
|
+
|
|
182
|
+
**Decider:** cixzhang, 2026-09-21
|
|
183
|
+
|
|
184
|
+
Each row cancels no more than its own component-owned inline inset or the
|
|
185
|
+
matching host inset. When the host inset is at least the row inset, content
|
|
186
|
+
reaches the host content line. When it is smaller, content moves toward that line
|
|
187
|
+
without pulling row paint beyond the host boundary.
|
|
188
|
+
|
|
189
|
+
Rejected: `isFullBleed`, because the row may stop inside the host padding and the
|
|
190
|
+
name would promise outer-edge bleed that the component does not provide.
|
|
191
|
+
|
|
192
|
+
## Open questions
|
|
193
|
+
|
|
194
|
+
None.
|
|
195
|
+
|
|
196
|
+
## Content boundary
|
|
197
|
+
|
|
198
|
+
This claim-scoped contract does not duplicate List's complete prop table,
|
|
199
|
+
container-padding internals, consumer examples, implementation steps, or future
|
|
200
|
+
TabList/Button projections. Those remain with their named owners.
|
package/src/List/List.test.tsx
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* @file List.test.tsx
|
|
5
5
|
* @input Uses vitest, @testing-library/react, List, ListItem
|
|
6
|
-
* @output Unit tests for List and ListItem
|
|
6
|
+
* @output Unit tests for List and ListItem, including bounded inline edge compensation
|
|
7
7
|
* @position Testing; validates List.tsx and ListItem.tsx implementation
|
|
8
8
|
*
|
|
9
9
|
* SYNC: When modified, update this header
|
|
@@ -255,6 +255,122 @@ describe('List', () => {
|
|
|
255
255
|
expect(item).toHaveAttribute('data-density', 'spacious');
|
|
256
256
|
});
|
|
257
257
|
|
|
258
|
+
// ===========================================================================
|
|
259
|
+
// Inline edge compensation
|
|
260
|
+
// ===========================================================================
|
|
261
|
+
|
|
262
|
+
it('does not apply inline edge compensation styles by default', () => {
|
|
263
|
+
const {container} = render(
|
|
264
|
+
<List>
|
|
265
|
+
<ListItem label="Item" />
|
|
266
|
+
</List>,
|
|
267
|
+
);
|
|
268
|
+
const item = container.querySelector('li')!;
|
|
269
|
+
expect(item.className).not.toContain('inlineEdgeCompensation');
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
it('applies the clamped cancelling margin to each item when edgeCompensation is inline', () => {
|
|
273
|
+
// The margin lives on the row element itself — the only place that can
|
|
274
|
+
// read --_item-inset-inline, which Item sets on the same element (custom
|
|
275
|
+
// properties cascade downward, so the <ul> cannot read it).
|
|
276
|
+
const {container} = render(
|
|
277
|
+
<List edgeCompensation="inline">
|
|
278
|
+
<ListItem label="Item 1" />
|
|
279
|
+
<ListItem label="Item 2" />
|
|
280
|
+
</List>,
|
|
281
|
+
);
|
|
282
|
+
const items = container.querySelectorAll('li');
|
|
283
|
+
expect(items).toHaveLength(2);
|
|
284
|
+
for (const item of items) {
|
|
285
|
+
expect(item.className).toContain('inlineEdgeCompensation');
|
|
286
|
+
}
|
|
287
|
+
expect(container.querySelector('ul')!.className).not.toContain(
|
|
288
|
+
'inlineEdgeCompensation',
|
|
289
|
+
);
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
it('uses the same var-derived cancel for every density', () => {
|
|
293
|
+
// The cancelling margin reads --_item-inset-inline instead of hardcoding
|
|
294
|
+
// per-density values, so all densities (and theme paddingInline
|
|
295
|
+
// overrides on `item`) share one style.
|
|
296
|
+
for (const density of ['compact', 'balanced', 'spacious'] as const) {
|
|
297
|
+
const {container, unmount} = render(
|
|
298
|
+
<List edgeCompensation="inline" density={density}>
|
|
299
|
+
<ListItem label="Item" />
|
|
300
|
+
</List>,
|
|
301
|
+
);
|
|
302
|
+
expect(container.querySelector('li')!.className).toContain(
|
|
303
|
+
'inlineEdgeCompensation',
|
|
304
|
+
);
|
|
305
|
+
unmount();
|
|
306
|
+
}
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
it('does not pull the header when edgeCompensation is inline', () => {
|
|
310
|
+
// The negative margin lives on the row elements, so the header keeps its
|
|
311
|
+
// position and the row text aligns up to it.
|
|
312
|
+
const {container} = render(
|
|
313
|
+
<List edgeCompensation="inline" header={<span>Items</span>}>
|
|
314
|
+
<ListItem label="Item" />
|
|
315
|
+
</List>,
|
|
316
|
+
);
|
|
317
|
+
expect(container.querySelector('li')!.className).toContain(
|
|
318
|
+
'inlineEdgeCompensation',
|
|
319
|
+
);
|
|
320
|
+
const ul = container.querySelector('ul')!;
|
|
321
|
+
expect(ul.className).not.toContain('inlineEdgeCompensation');
|
|
322
|
+
expect(ul.parentElement?.className).not.toContain('inlineEdgeCompensation');
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
it('clamps each inline edge against its own container padding var', () => {
|
|
326
|
+
// Regression for asymmetric container padding (16px start / 4px end):
|
|
327
|
+
// clamping BOTH margins against --container-padding-inline-start
|
|
328
|
+
// over-cancels the end edge by the difference, and the selected row
|
|
329
|
+
// paints past the outer border (RTL mirrors the spill). Each edge must
|
|
330
|
+
// clamp against its matching var; logical properties keep RTL correct.
|
|
331
|
+
// jsdom does no real layout, so assert on the injected StyleX CSS
|
|
332
|
+
// (runtimeInjection is on in the test config).
|
|
333
|
+
const {container} = render(
|
|
334
|
+
<List edgeCompensation="inline">
|
|
335
|
+
<ListItem label="Item" />
|
|
336
|
+
</List>,
|
|
337
|
+
);
|
|
338
|
+
expect(container.querySelector('li')!.className).toContain(
|
|
339
|
+
'inlineEdgeCompensation',
|
|
340
|
+
);
|
|
341
|
+
let out = '';
|
|
342
|
+
for (const sheet of Array.from(document.styleSheets)) {
|
|
343
|
+
try {
|
|
344
|
+
for (const rule of Array.from(sheet.cssRules)) {
|
|
345
|
+
out += rule.cssText + '\n';
|
|
346
|
+
}
|
|
347
|
+
} catch {
|
|
348
|
+
// ignore cross-origin sheets
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
out += Array.from(document.querySelectorAll('style'))
|
|
352
|
+
.map(s => s.textContent || '')
|
|
353
|
+
.join('\n');
|
|
354
|
+
const css = out.replace(/\s+/g, '');
|
|
355
|
+
expect(css).toContain(
|
|
356
|
+
'margin-inline-start:calc(-1*min(var(--_item-inset-inline),var(--container-padding-inline-start,0px)))',
|
|
357
|
+
);
|
|
358
|
+
expect(css).toContain(
|
|
359
|
+
'margin-inline-end:calc(-1*min(var(--_item-inset-inline),var(--container-padding-inline-end,0px)))',
|
|
360
|
+
);
|
|
361
|
+
// The end edge must not clamp against the start var (the over-cancel).
|
|
362
|
+
expect(css).not.toContain(
|
|
363
|
+
'margin-inline-end:calc(-1*min(var(--_item-inset-inline),var(--container-padding-inline-start,0px)))',
|
|
364
|
+
);
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
it('does not apply the cancelling margin to a ListItem outside a List', () => {
|
|
368
|
+
const {container} = render(<ListItem label="Standalone" />);
|
|
369
|
+
expect(container.querySelector('li')!.className).not.toContain(
|
|
370
|
+
'inlineEdgeCompensation',
|
|
371
|
+
);
|
|
372
|
+
});
|
|
373
|
+
|
|
258
374
|
// ===========================================================================
|
|
259
375
|
// Dividers
|
|
260
376
|
// ===========================================================================
|
package/src/List/List.tsx
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
/**
|
|
6
6
|
* @file List.tsx
|
|
7
7
|
* @input Uses React, ReactNode, StyleXStyles, theme tokens, ListContext
|
|
8
|
+
* with optional inline edge compensation
|
|
8
9
|
* @output Exports List component, ListProps, ListDensity, ListStyle types
|
|
9
10
|
* @position Core implementation; consumed by index.ts, tested by List.test.tsx
|
|
10
11
|
*
|
|
@@ -58,6 +59,20 @@ export interface ListProps extends BaseProps<
|
|
|
58
59
|
*/
|
|
59
60
|
hasDividers?: boolean;
|
|
60
61
|
|
|
62
|
+
/**
|
|
63
|
+
* Compensates for each item's built-in inline inset, up to the container
|
|
64
|
+
* padding available on each edge. Use "inline" to bring row content toward
|
|
65
|
+
* sibling content such as a section heading. Content aligns when the
|
|
66
|
+
* container padding is at least the item inset; smaller padding leaves
|
|
67
|
+
* some inset uncompensated.
|
|
68
|
+
* The cancelling margin reads the same variable the items derive their
|
|
69
|
+
* inline padding from, so it tracks density and theme padding overrides
|
|
70
|
+
* automatically. Hover and selection backgrounds still extend past the
|
|
71
|
+
* text by the inset.
|
|
72
|
+
* Omit to leave item positions unchanged.
|
|
73
|
+
*/
|
|
74
|
+
edgeCompensation?: 'inline';
|
|
75
|
+
|
|
61
76
|
/**
|
|
62
77
|
* Header content rendered above the list.
|
|
63
78
|
* Semantically associated via aria-labelledby.
|
|
@@ -128,6 +143,10 @@ const dynamicStyles = stylex.create({
|
|
|
128
143
|
* Renders semantic `<ul>` or `<ol>` elements with configurable density,
|
|
129
144
|
* dividers, marker styles, and an optional header.
|
|
130
145
|
*
|
|
146
|
+
* Set `edgeCompensation="inline"` to compensate for the items' inline inset
|
|
147
|
+
* up to the container padding available on each edge, for alignment with
|
|
148
|
+
* sibling content such as a section heading.
|
|
149
|
+
*
|
|
131
150
|
* @example
|
|
132
151
|
* ```
|
|
133
152
|
* <List>
|
|
@@ -144,6 +163,7 @@ export function List({
|
|
|
144
163
|
children,
|
|
145
164
|
density = 'balanced',
|
|
146
165
|
hasDividers = false,
|
|
166
|
+
edgeCompensation,
|
|
147
167
|
header,
|
|
148
168
|
listStyle = 'none',
|
|
149
169
|
start,
|
|
@@ -159,8 +179,8 @@ export function List({
|
|
|
159
179
|
const Tag = isOrdered ? 'ol' : 'ul';
|
|
160
180
|
|
|
161
181
|
const contextValue = useMemo(
|
|
162
|
-
() => ({density, hasDividers, listStyle}),
|
|
163
|
-
[density, hasDividers, listStyle],
|
|
182
|
+
() => ({density, hasDividers, listStyle, edgeCompensation}),
|
|
183
|
+
[density, hasDividers, listStyle, edgeCompensation],
|
|
164
184
|
);
|
|
165
185
|
|
|
166
186
|
const listElement = (
|
package/src/List/ListContext.tsx
CHANGED
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
/**
|
|
6
6
|
* @file ListContext.tsx
|
|
7
7
|
* @input Uses React createContext
|
|
8
|
-
* @output Exports ListContext for sharing density
|
|
8
|
+
* @output Exports ListContext for sharing density, dividers, marker style,
|
|
9
|
+
* and optional inline edge compensation between List and ListItem
|
|
9
10
|
* @position Internal context; consumed by List.tsx and ListItem.tsx
|
|
10
11
|
*/
|
|
11
12
|
|
|
@@ -18,6 +19,7 @@ export interface ListContextValue {
|
|
|
18
19
|
density: ListDensity;
|
|
19
20
|
hasDividers: boolean;
|
|
20
21
|
listStyle: ListMarkerStyle;
|
|
22
|
+
edgeCompensation?: 'inline';
|
|
21
23
|
}
|
|
22
24
|
|
|
23
25
|
export const ListContext = createContext<ListContextValue | null>(null);
|
|
@@ -67,7 +67,7 @@ export const docs = {
|
|
|
67
67
|
{
|
|
68
68
|
name: 'href',
|
|
69
69
|
type: 'string',
|
|
70
|
-
description: 'Link URL; enables the invisible anchor pattern.',
|
|
70
|
+
description: 'Link URL; enables the invisible anchor pattern. The destination follows the shared navigation rule described on the Link `href` prop.',
|
|
71
71
|
},
|
|
72
72
|
{
|
|
73
73
|
name: 'target',
|
|
@@ -174,7 +174,7 @@ export const docsDense = {
|
|
|
174
174
|
onClick: 'Click handler; enables invisible button pattern.',
|
|
175
175
|
interactiveRef:
|
|
176
176
|
'Ref to a nested control that owns the item\'s keyboard access/action; row delegates surface clicks to it (useClickableContainer), no invisible button/anchor, no second tab stop (WCAG 4.1.2). Mutually exclusive with onClick/href.',
|
|
177
|
-
href: 'Link URL; enables invisible anchor pattern.',
|
|
177
|
+
href: 'Link URL; enables invisible anchor pattern. Follows the shared navigation rule (see Link href).',
|
|
178
178
|
target: 'Link target attribute, only when href provided. target="_blank" auto-adds noopener noreferrer.',
|
|
179
179
|
rel: 'Link relationship tokens. noopener noreferrer are merged for target="_blank".',
|
|
180
180
|
isDisabled: 'Disabled state; sets aria-disabled.',
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file ListItem.source-build.test.mjs
|
|
5
|
+
* @input Uses Babel, the core StyleX build config, and ListItem source
|
|
6
|
+
* @output Verifies the divider reset for the last item survives compilation
|
|
7
|
+
* @position Regression test for the shipped ListItem stylesheet
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import fs from 'node:fs/promises';
|
|
11
|
+
import path from 'node:path';
|
|
12
|
+
import {fileURLToPath} from 'node:url';
|
|
13
|
+
import {transformAsync} from '@babel/core';
|
|
14
|
+
import {describe, expect, it} from 'vitest';
|
|
15
|
+
|
|
16
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
17
|
+
const CORE_ROOT = path.resolve(__dirname, '../..');
|
|
18
|
+
const LIST_ITEM_SOURCE = path.join(__dirname, 'ListItem.tsx');
|
|
19
|
+
|
|
20
|
+
async function compileListItemRules() {
|
|
21
|
+
const source = await fs.readFile(LIST_ITEM_SOURCE, 'utf8');
|
|
22
|
+
const result = await transformAsync(source, {
|
|
23
|
+
babelrc: false,
|
|
24
|
+
configFile: path.join(CORE_ROOT, 'babel.config.json'),
|
|
25
|
+
filename: LIST_ITEM_SOURCE,
|
|
26
|
+
});
|
|
27
|
+
return (result?.metadata?.stylex ?? []).map(([, rule]) => rule.ltr);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
describe('ListItem divider stylesheet', () => {
|
|
31
|
+
it('emits a :last-child rule that removes the divider after the last item', async () => {
|
|
32
|
+
const rules = await compileListItemRules();
|
|
33
|
+
|
|
34
|
+
// The divider is the item's block-end border. StyleX's default
|
|
35
|
+
// property-specificity mode silently drops a `borderBlockEnd` shorthand,
|
|
36
|
+
// so the last-item reset must be a longhand to reach the shipped CSS.
|
|
37
|
+
expect(rules).toContainEqual(
|
|
38
|
+
expect.stringMatching(/:last-child\{border-bottom-width:0\}$/),
|
|
39
|
+
);
|
|
40
|
+
});
|
|
41
|
+
});
|