@astryxdesign/core 0.6.2 → 0.6.3-canary.0788c49
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.d.ts +2 -1
- package/dist/Chat/ChatSendButton.d.ts.map +1 -1
- package/dist/Chat/ChatSendButton.js +6 -4
- 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 +726 -177
- 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/SegmentedControl.d.ts +1 -1
- package/dist/SegmentedControl/SegmentedControl.d.ts.map +1 -1
- package/dist/SegmentedControl/SegmentedControl.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.doc.mjs +32 -0
- package/src/Chat/ChatSendButton.spec.md +227 -0
- package/src/Chat/ChatSendButton.test.tsx +128 -8
- package/src/Chat/ChatSendButton.tsx +6 -4
- 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/__tests__/ChatSendButton.a11y.chromium.spec.ts +1522 -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 +210 -45
- package/src/Markdown/Markdown.test.tsx +87 -3
- 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 +43 -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 +38 -6
- package/src/Markdown/parser.ts +1221 -242
- 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 +61 -0
- package/src/SegmentedControl/SegmentedControl.tsx +2 -1
- package/src/SegmentedControl/SegmentedControlItem.tsx +6 -9
- package/src/Selector/Selector.doc.mjs +3 -3
- 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
|
@@ -208,6 +208,339 @@ describe('useLinkComponent — to prop', () => {
|
|
|
208
208
|
});
|
|
209
209
|
});
|
|
210
210
|
|
|
211
|
+
// =============================================================================
|
|
212
|
+
// href scheme rule
|
|
213
|
+
// =============================================================================
|
|
214
|
+
|
|
215
|
+
describe('useLinkComponent — href scheme rule', () => {
|
|
216
|
+
// Kept in a const so the JSX stays lint-clean — the test is ABOUT this URL.
|
|
217
|
+
const scriptUrl = 'javascript:alert(1)';
|
|
218
|
+
|
|
219
|
+
function HrefConsumer({href}: {href: string}) {
|
|
220
|
+
const LinkComponent = useLinkComponent();
|
|
221
|
+
return (
|
|
222
|
+
<LinkComponent href={href} data-testid="resolved-link">
|
|
223
|
+
Link
|
|
224
|
+
</LinkComponent>
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function propSpy() {
|
|
229
|
+
return vi.fn(
|
|
230
|
+
({
|
|
231
|
+
children,
|
|
232
|
+
...props
|
|
233
|
+
}: {
|
|
234
|
+
children?: React.ReactNode;
|
|
235
|
+
[key: string]: unknown;
|
|
236
|
+
}) => (
|
|
237
|
+
<a data-testid="spy-link" href={props.href as string}>
|
|
238
|
+
{children}
|
|
239
|
+
</a>
|
|
240
|
+
),
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
it('forwards ordinary hrefs to a custom component untouched', () => {
|
|
245
|
+
const spy = propSpy();
|
|
246
|
+
function SpyLink(props: {[key: string]: unknown}) {
|
|
247
|
+
return spy(props);
|
|
248
|
+
}
|
|
249
|
+
render(
|
|
250
|
+
<LinkProvider component={SpyLink}>
|
|
251
|
+
<HrefConsumer href="/docs/button" />
|
|
252
|
+
</LinkProvider>,
|
|
253
|
+
);
|
|
254
|
+
expect(spy).toHaveBeenCalledWith(
|
|
255
|
+
expect.objectContaining({href: '/docs/button', to: '/docs/button'}),
|
|
256
|
+
);
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
it('withholds a script-scheme href from a custom component (href and to alike)', () => {
|
|
260
|
+
const spy = propSpy();
|
|
261
|
+
function SpyLink(props: {[key: string]: unknown}) {
|
|
262
|
+
return spy(props);
|
|
263
|
+
}
|
|
264
|
+
render(
|
|
265
|
+
<LinkProvider component={SpyLink}>
|
|
266
|
+
{/* eslint-disable-next-line @eslint-react/dom-no-script-url -- the test proves this URL never navigates */}
|
|
267
|
+
<HrefConsumer href={scriptUrl} />
|
|
268
|
+
</LinkProvider>,
|
|
269
|
+
);
|
|
270
|
+
expect(spy).not.toHaveBeenCalled();
|
|
271
|
+
expect(screen.getByTestId('resolved-link')).not.toHaveAttribute('href');
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
it('an explicit `to` passes the same check instead of riding through ...rest', () => {
|
|
275
|
+
const spy = propSpy();
|
|
276
|
+
function SpyLink(props: {[key: string]: unknown}) {
|
|
277
|
+
return spy(props);
|
|
278
|
+
}
|
|
279
|
+
function ToConsumer({to}: {to: string}) {
|
|
280
|
+
const LinkComponent = useLinkComponent();
|
|
281
|
+
return (
|
|
282
|
+
<LinkComponent href="/fallback" to={to} data-testid="resolved-link">
|
|
283
|
+
Link
|
|
284
|
+
</LinkComponent>
|
|
285
|
+
);
|
|
286
|
+
}
|
|
287
|
+
render(
|
|
288
|
+
<LinkProvider component={SpyLink}>
|
|
289
|
+
{/* eslint-disable-next-line @eslint-react/dom-no-script-url -- the test proves this URL never reaches the router */}
|
|
290
|
+
<ToConsumer to={scriptUrl} />
|
|
291
|
+
</LinkProvider>,
|
|
292
|
+
);
|
|
293
|
+
expect(spy).not.toHaveBeenCalled();
|
|
294
|
+
expect(screen.getByTestId('resolved-link')).not.toHaveAttribute('href');
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
it('an ordinary explicit `to` keeps its precedence over href', () => {
|
|
298
|
+
const spy = propSpy();
|
|
299
|
+
function SpyLink(props: {[key: string]: unknown}) {
|
|
300
|
+
return spy(props);
|
|
301
|
+
}
|
|
302
|
+
function ToConsumer() {
|
|
303
|
+
const LinkComponent = useLinkComponent();
|
|
304
|
+
return (
|
|
305
|
+
<LinkComponent
|
|
306
|
+
href="/fallback"
|
|
307
|
+
to="/routed"
|
|
308
|
+
data-testid="resolved-link">
|
|
309
|
+
Link
|
|
310
|
+
</LinkComponent>
|
|
311
|
+
);
|
|
312
|
+
}
|
|
313
|
+
render(
|
|
314
|
+
<LinkProvider component={SpyLink}>
|
|
315
|
+
<ToConsumer />
|
|
316
|
+
</LinkProvider>,
|
|
317
|
+
);
|
|
318
|
+
expect(spy).toHaveBeenCalledWith(
|
|
319
|
+
expect.objectContaining({href: '/fallback', to: '/routed'}),
|
|
320
|
+
);
|
|
321
|
+
});
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
// =============================================================================
|
|
325
|
+
// structured router `to`
|
|
326
|
+
// =============================================================================
|
|
327
|
+
|
|
328
|
+
describe('useLinkComponent — structured router `to`', () => {
|
|
329
|
+
const scriptUrl = 'javascript:alert(1)';
|
|
330
|
+
|
|
331
|
+
function propSpy() {
|
|
332
|
+
return vi.fn(
|
|
333
|
+
({
|
|
334
|
+
children,
|
|
335
|
+
...props
|
|
336
|
+
}: {
|
|
337
|
+
children?: React.ReactNode;
|
|
338
|
+
[key: string]: unknown;
|
|
339
|
+
}) => (
|
|
340
|
+
<a data-testid="spy-link" href={props.href as string}>
|
|
341
|
+
{children}
|
|
342
|
+
</a>
|
|
343
|
+
),
|
|
344
|
+
);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
function ToConsumer({to}: {to: unknown}) {
|
|
348
|
+
const LinkComponent = useLinkComponent();
|
|
349
|
+
return (
|
|
350
|
+
<LinkComponent href="/fallback" to={to} data-testid="resolved-link">
|
|
351
|
+
Link
|
|
352
|
+
</LinkComponent>
|
|
353
|
+
);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
it('preserves a structured destination for the router (no crash, same object)', () => {
|
|
357
|
+
const spy = propSpy();
|
|
358
|
+
function SpyLink(props: {[key: string]: unknown}) {
|
|
359
|
+
return spy(props);
|
|
360
|
+
}
|
|
361
|
+
const to = {pathname: '/docs', search: '?q=1', hash: '#top'};
|
|
362
|
+
render(
|
|
363
|
+
<LinkProvider component={SpyLink}>
|
|
364
|
+
<ToConsumer to={to} />
|
|
365
|
+
</LinkProvider>,
|
|
366
|
+
);
|
|
367
|
+
expect(spy).toHaveBeenCalledWith(
|
|
368
|
+
expect.objectContaining({href: '/fallback', to}),
|
|
369
|
+
);
|
|
370
|
+
expect(spy.mock.calls[0][0].to).toBe(to);
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
it('preserves a structured destination that carries no pathname', () => {
|
|
374
|
+
const spy = propSpy();
|
|
375
|
+
function SpyLink(props: {[key: string]: unknown}) {
|
|
376
|
+
return spy(props);
|
|
377
|
+
}
|
|
378
|
+
const to = {search: '?page=2'};
|
|
379
|
+
render(
|
|
380
|
+
<LinkProvider component={SpyLink}>
|
|
381
|
+
<ToConsumer to={to} />
|
|
382
|
+
</LinkProvider>,
|
|
383
|
+
);
|
|
384
|
+
expect(spy).toHaveBeenCalledWith(expect.objectContaining({to}));
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
it('withholds a structured destination whose pathname is a script scheme', () => {
|
|
388
|
+
const spy = propSpy();
|
|
389
|
+
function SpyLink(props: {[key: string]: unknown}) {
|
|
390
|
+
return spy(props);
|
|
391
|
+
}
|
|
392
|
+
render(
|
|
393
|
+
<LinkProvider component={SpyLink}>
|
|
394
|
+
<ToConsumer to={{pathname: scriptUrl}} />
|
|
395
|
+
</LinkProvider>,
|
|
396
|
+
);
|
|
397
|
+
expect(spy).not.toHaveBeenCalled();
|
|
398
|
+
expect(screen.getByTestId('resolved-link')).not.toHaveAttribute('href');
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
it('withholds a structured destination whose href is a script scheme', () => {
|
|
402
|
+
const spy = propSpy();
|
|
403
|
+
function SpyLink(props: {[key: string]: unknown}) {
|
|
404
|
+
return spy(props);
|
|
405
|
+
}
|
|
406
|
+
render(
|
|
407
|
+
<LinkProvider component={SpyLink}>
|
|
408
|
+
<ToConsumer to={{href: scriptUrl}} />
|
|
409
|
+
</LinkProvider>,
|
|
410
|
+
);
|
|
411
|
+
expect(spy).not.toHaveBeenCalled();
|
|
412
|
+
expect(screen.getByTestId('resolved-link')).not.toHaveAttribute('href');
|
|
413
|
+
});
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
// =============================================================================
|
|
417
|
+
// Shared native and router boundary
|
|
418
|
+
// =============================================================================
|
|
419
|
+
|
|
420
|
+
describe('useLinkComponent — rejected destinations are inert', () => {
|
|
421
|
+
function Consumer({
|
|
422
|
+
href,
|
|
423
|
+
to,
|
|
424
|
+
as,
|
|
425
|
+
onClick,
|
|
426
|
+
linkRef,
|
|
427
|
+
}: {
|
|
428
|
+
href?: unknown;
|
|
429
|
+
to?: unknown;
|
|
430
|
+
as?: LinkComponentType;
|
|
431
|
+
onClick?: () => void;
|
|
432
|
+
linkRef?: React.Ref<HTMLAnchorElement>;
|
|
433
|
+
}) {
|
|
434
|
+
const Component = useLinkComponent(as);
|
|
435
|
+
return (
|
|
436
|
+
<Component
|
|
437
|
+
href={href}
|
|
438
|
+
to={to}
|
|
439
|
+
ref={linkRef}
|
|
440
|
+
onClick={onClick}
|
|
441
|
+
className="consumer-class"
|
|
442
|
+
aria-label="Destination"
|
|
443
|
+
data-testid="destination">
|
|
444
|
+
Go
|
|
445
|
+
</Component>
|
|
446
|
+
);
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
for (const mode of ['native', 'provider', 'as'] as const) {
|
|
450
|
+
it.each([
|
|
451
|
+
{href: 'javascript:alert(1)'},
|
|
452
|
+
{href: 'vbscript:MsgBox(1)'},
|
|
453
|
+
{href: 'data:text/html,<b>x</b>'},
|
|
454
|
+
{href: 'java\nscript:alert(1)'},
|
|
455
|
+
{href: '/safe', to: 'javascript:alert(1)'},
|
|
456
|
+
{href: 'vbscript:MsgBox(1)', to: '/safe'},
|
|
457
|
+
{href: {protocol: 'javascript:', pathname: 'alert(1)'}},
|
|
458
|
+
{href: '/safe', to: {protocol: 'data:', pathname: 'text/html,x'}},
|
|
459
|
+
])(`${mode}: rejects %j without calling a router`, props => {
|
|
460
|
+
const router = vi.fn(() => {
|
|
461
|
+
throw new Error('rejected router invoked');
|
|
462
|
+
});
|
|
463
|
+
const onClick = vi.fn();
|
|
464
|
+
const linkRef = vi.fn();
|
|
465
|
+
render(
|
|
466
|
+
<LinkProvider component={mode === 'provider' ? router : 'a'}>
|
|
467
|
+
<Consumer
|
|
468
|
+
{...props}
|
|
469
|
+
as={mode === 'as' ? router : undefined}
|
|
470
|
+
onClick={onClick}
|
|
471
|
+
linkRef={linkRef}
|
|
472
|
+
/>
|
|
473
|
+
</LinkProvider>,
|
|
474
|
+
);
|
|
475
|
+
const node = screen.getByTestId('destination');
|
|
476
|
+
expect(router).not.toHaveBeenCalled();
|
|
477
|
+
expect(node.tagName).toBe('A');
|
|
478
|
+
expect(node).not.toHaveAttribute('href');
|
|
479
|
+
expect(node).not.toHaveAttribute('to');
|
|
480
|
+
expect(node).not.toHaveAttribute('aria-disabled');
|
|
481
|
+
expect(node).toHaveAttribute('aria-label', 'Destination');
|
|
482
|
+
expect(node).toHaveClass('consumer-class');
|
|
483
|
+
expect(linkRef.mock.calls[0][0]).toBe(node);
|
|
484
|
+
node.click();
|
|
485
|
+
expect(onClick).toHaveBeenCalledOnce();
|
|
486
|
+
});
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
it.each([
|
|
490
|
+
'/relative',
|
|
491
|
+
'#fragment',
|
|
492
|
+
'https://example.com',
|
|
493
|
+
'http://example.com',
|
|
494
|
+
'mailto:a@example.com',
|
|
495
|
+
'tel:+1234567890',
|
|
496
|
+
'custom:document',
|
|
497
|
+
'data:text/plain,download',
|
|
498
|
+
])('preserves native href and download affordances for %s', href => {
|
|
499
|
+
function Download() {
|
|
500
|
+
const Component = useLinkComponent();
|
|
501
|
+
return (
|
|
502
|
+
<Component
|
|
503
|
+
href={href}
|
|
504
|
+
download="note.txt"
|
|
505
|
+
target="_blank"
|
|
506
|
+
rel="noopener"
|
|
507
|
+
data-testid="download">
|
|
508
|
+
Download
|
|
509
|
+
</Component>
|
|
510
|
+
);
|
|
511
|
+
}
|
|
512
|
+
render(<Download />);
|
|
513
|
+
const node = screen.getByTestId('download');
|
|
514
|
+
expect(node).toHaveAttribute('href', href);
|
|
515
|
+
expect(node).toHaveAttribute('download', 'note.txt');
|
|
516
|
+
expect(node).toHaveAttribute('target', '_blank');
|
|
517
|
+
expect(node).toHaveAttribute('rel', 'noopener');
|
|
518
|
+
});
|
|
519
|
+
|
|
520
|
+
it('preserves both structured prop identities and safe explicit precedence', () => {
|
|
521
|
+
const router = vi.fn<(props: {href?: unknown; to?: unknown}) => null>(
|
|
522
|
+
() => null,
|
|
523
|
+
);
|
|
524
|
+
const href = {
|
|
525
|
+
protocol: 'https:',
|
|
526
|
+
hostname: 'example.com',
|
|
527
|
+
pathname: '/docs',
|
|
528
|
+
};
|
|
529
|
+
const to = {pathname: '/next', search: '?q=1', hash: '#top'};
|
|
530
|
+
render(<Consumer as={router} href={href} to={to} />);
|
|
531
|
+
expect(router.mock.calls[0][0].href).toBe(href);
|
|
532
|
+
expect(router.mock.calls[0][0].to).toBe(to);
|
|
533
|
+
});
|
|
534
|
+
|
|
535
|
+
it('keeps the native node stable while accepted props update', () => {
|
|
536
|
+
const {rerender} = render(<Consumer href="/first" />);
|
|
537
|
+
const node = screen.getByTestId('destination');
|
|
538
|
+
rerender(<Consumer href="/second" />);
|
|
539
|
+
expect(screen.getByTestId('destination')).toBe(node);
|
|
540
|
+
expect(node).toHaveAttribute('href', '/second');
|
|
541
|
+
});
|
|
542
|
+
});
|
|
543
|
+
|
|
211
544
|
// =============================================================================
|
|
212
545
|
// LinkProvider
|
|
213
546
|
// =============================================================================
|
|
@@ -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.
|