@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
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file useClickableContainer.test.tsx
|
|
5
|
+
* @input Uses vitest, @testing-library/react, useClickableContainer
|
|
6
|
+
* @output Unit tests for the hook's navigation paths
|
|
7
|
+
* @position Testing; pins the shared navigation-destination rule on every
|
|
8
|
+
* activation method the hook owns — plain click (location.href), new tab
|
|
9
|
+
* (target="_blank"), Cmd/Ctrl-click, middle-click, and the delegated click
|
|
10
|
+
* on the interactive ref. None of these pass through React DOM's own href
|
|
11
|
+
* vetting, so each one has a positive and a blocked case here.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import {afterEach, beforeEach, describe, expect, it, vi} from 'vitest';
|
|
15
|
+
import {useRef} from 'react';
|
|
16
|
+
import {fireEvent, render, screen} from '@testing-library/react';
|
|
17
|
+
import {useClickableContainer} from './useClickableContainer';
|
|
18
|
+
|
|
19
|
+
// The scheme the rule exists to stop (kept in consts so the JSX below stays
|
|
20
|
+
// lint-clean — the tests are ABOUT these URLs never navigating).
|
|
21
|
+
const scriptUrl = 'javascript:alert(1)';
|
|
22
|
+
// The same scheme hidden behind a control character a browser ignores.
|
|
23
|
+
const hiddenScriptUrl = 'java\nscript:alert(1)';
|
|
24
|
+
|
|
25
|
+
function Card({href, target}: {href: string; target?: string}) {
|
|
26
|
+
const containerRef = useRef<HTMLDivElement>(null);
|
|
27
|
+
const {onClick, onMouseUp} = useClickableContainer({
|
|
28
|
+
containerRef,
|
|
29
|
+
href,
|
|
30
|
+
target,
|
|
31
|
+
});
|
|
32
|
+
return (
|
|
33
|
+
<div
|
|
34
|
+
ref={containerRef}
|
|
35
|
+
data-testid="card"
|
|
36
|
+
onClick={onClick}
|
|
37
|
+
onMouseUp={onMouseUp}>
|
|
38
|
+
content
|
|
39
|
+
</div>
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Card that delegates plain clicks to an inner link (the ClickableCard /
|
|
45
|
+
* Item shape). The inner anchor's own click is spied on so the test observes
|
|
46
|
+
* whether the hook activated it.
|
|
47
|
+
*/
|
|
48
|
+
function DelegatingCard({
|
|
49
|
+
href,
|
|
50
|
+
onAnchorClick,
|
|
51
|
+
}: {
|
|
52
|
+
href: string;
|
|
53
|
+
onAnchorClick: () => void;
|
|
54
|
+
}) {
|
|
55
|
+
const containerRef = useRef<HTMLDivElement>(null);
|
|
56
|
+
const interactiveRef = useRef<HTMLAnchorElement>(null);
|
|
57
|
+
const {onClick, onMouseUp} = useClickableContainer({
|
|
58
|
+
containerRef,
|
|
59
|
+
interactiveRef,
|
|
60
|
+
href,
|
|
61
|
+
});
|
|
62
|
+
return (
|
|
63
|
+
<div
|
|
64
|
+
ref={containerRef}
|
|
65
|
+
data-testid="card"
|
|
66
|
+
onClick={onClick}
|
|
67
|
+
onMouseUp={onMouseUp}>
|
|
68
|
+
content
|
|
69
|
+
<a
|
|
70
|
+
ref={interactiveRef}
|
|
71
|
+
// The anchor is the delegate, not the subject: the tests below use a
|
|
72
|
+
// fixed inert href here so that only the hook's decision is observed.
|
|
73
|
+
href="#inner"
|
|
74
|
+
data-testid="inner-link"
|
|
75
|
+
onClick={e => {
|
|
76
|
+
e.preventDefault();
|
|
77
|
+
onAnchorClick();
|
|
78
|
+
}}>
|
|
79
|
+
inner
|
|
80
|
+
</a>
|
|
81
|
+
</div>
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
afterEach(() => {
|
|
86
|
+
vi.restoreAllMocks();
|
|
87
|
+
vi.unstubAllGlobals();
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
describe('useClickableContainer navigation — new tab (target="_blank")', () => {
|
|
91
|
+
it('opens ordinary hrefs in a new tab', () => {
|
|
92
|
+
const open = vi.spyOn(window, 'open').mockImplementation(() => null);
|
|
93
|
+
render(<Card href="https://example.com/docs" target="_blank" />);
|
|
94
|
+
fireEvent.click(screen.getByTestId('card'));
|
|
95
|
+
expect(open).toHaveBeenCalledWith(
|
|
96
|
+
'https://example.com/docs',
|
|
97
|
+
'_blank',
|
|
98
|
+
'noopener',
|
|
99
|
+
);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('does not open a script-scheme href in a new tab', () => {
|
|
103
|
+
const open = vi.spyOn(window, 'open').mockImplementation(() => null);
|
|
104
|
+
// eslint-disable-next-line @eslint-react/dom-no-script-url -- the test proves this URL never navigates
|
|
105
|
+
render(<Card href={scriptUrl} target="_blank" />);
|
|
106
|
+
fireEvent.click(screen.getByTestId('card'));
|
|
107
|
+
expect(open).not.toHaveBeenCalled();
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
describe('useClickableContainer navigation — Cmd/Ctrl-click', () => {
|
|
112
|
+
it('opens ordinary hrefs in a new tab on Cmd-click', () => {
|
|
113
|
+
const open = vi.spyOn(window, 'open').mockImplementation(() => null);
|
|
114
|
+
render(<Card href="/docs" />);
|
|
115
|
+
fireEvent.click(screen.getByTestId('card'), {metaKey: true});
|
|
116
|
+
expect(open).toHaveBeenCalledWith('/docs', '_blank', 'noopener');
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it('opens ordinary hrefs in a new tab on Ctrl-click', () => {
|
|
120
|
+
const open = vi.spyOn(window, 'open').mockImplementation(() => null);
|
|
121
|
+
render(<Card href="/docs" />);
|
|
122
|
+
fireEvent.click(screen.getByTestId('card'), {ctrlKey: true});
|
|
123
|
+
expect(open).toHaveBeenCalledWith('/docs', '_blank', 'noopener');
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it('does not open a script-scheme href on Cmd-click', () => {
|
|
127
|
+
const open = vi.spyOn(window, 'open').mockImplementation(() => null);
|
|
128
|
+
// eslint-disable-next-line @eslint-react/dom-no-script-url -- the test proves this URL never navigates
|
|
129
|
+
render(<Card href={scriptUrl} />);
|
|
130
|
+
fireEvent.click(screen.getByTestId('card'), {metaKey: true});
|
|
131
|
+
expect(open).not.toHaveBeenCalled();
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it('does not open a script-scheme href on Ctrl-click', () => {
|
|
135
|
+
const open = vi.spyOn(window, 'open').mockImplementation(() => null);
|
|
136
|
+
// eslint-disable-next-line @eslint-react/dom-no-script-url -- the test proves this URL never navigates
|
|
137
|
+
render(<Card href={scriptUrl} />);
|
|
138
|
+
fireEvent.click(screen.getByTestId('card'), {ctrlKey: true});
|
|
139
|
+
expect(open).not.toHaveBeenCalled();
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
describe('useClickableContainer navigation — middle-click', () => {
|
|
144
|
+
it('opens ordinary hrefs on middle-click', () => {
|
|
145
|
+
const open = vi.spyOn(window, 'open').mockImplementation(() => null);
|
|
146
|
+
render(<Card href="/docs" />);
|
|
147
|
+
fireEvent.mouseUp(screen.getByTestId('card'), {button: 1});
|
|
148
|
+
expect(open).toHaveBeenCalledWith('/docs', '_blank', 'noopener');
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
it('does not open a script-scheme href on middle-click', () => {
|
|
152
|
+
const open = vi.spyOn(window, 'open').mockImplementation(() => null);
|
|
153
|
+
// eslint-disable-next-line @eslint-react/dom-no-script-url -- the test proves this URL never navigates
|
|
154
|
+
render(<Card href={scriptUrl} />);
|
|
155
|
+
fireEvent.mouseUp(screen.getByTestId('card'), {button: 1});
|
|
156
|
+
expect(open).not.toHaveBeenCalled();
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
describe('useClickableContainer navigation — plain click (location.href)', () => {
|
|
161
|
+
// jsdom does not navigate, so assigning `location.href` is a no-op there
|
|
162
|
+
// and would let a missing guard pass unnoticed. Stand in a plain object so
|
|
163
|
+
// the assignment is observable.
|
|
164
|
+
let location: {href: string};
|
|
165
|
+
|
|
166
|
+
beforeEach(() => {
|
|
167
|
+
location = {href: 'https://app.example/start'};
|
|
168
|
+
vi.stubGlobal('location', location);
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
it('assigns ordinary hrefs to location', () => {
|
|
172
|
+
render(<Card href="/docs" />);
|
|
173
|
+
fireEvent.click(screen.getByTestId('card'));
|
|
174
|
+
expect(location.href).toBe('/docs');
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
it('does not assign a script-scheme href to location', () => {
|
|
178
|
+
// eslint-disable-next-line @eslint-react/dom-no-script-url -- the test proves this URL never navigates
|
|
179
|
+
render(<Card href={scriptUrl} />);
|
|
180
|
+
fireEvent.click(screen.getByTestId('card'));
|
|
181
|
+
expect(location.href).toBe('https://app.example/start');
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
it('does not assign a control-character-hidden script scheme to location', () => {
|
|
185
|
+
// eslint-disable-next-line @eslint-react/dom-no-script-url -- the test proves this URL never navigates
|
|
186
|
+
render(<Card href={hiddenScriptUrl} />);
|
|
187
|
+
fireEvent.click(screen.getByTestId('card'));
|
|
188
|
+
expect(location.href).toBe('https://app.example/start');
|
|
189
|
+
});
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
describe('useClickableContainer navigation — delegated interactive ref', () => {
|
|
193
|
+
it('delegates ordinary hrefs to the inner link', () => {
|
|
194
|
+
const onAnchorClick = vi.fn();
|
|
195
|
+
render(<DelegatingCard href="/docs" onAnchorClick={onAnchorClick} />);
|
|
196
|
+
fireEvent.click(screen.getByTestId('card'));
|
|
197
|
+
expect(onAnchorClick).toHaveBeenCalledTimes(1);
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
it('does not delegate a script-scheme href to the inner link', () => {
|
|
201
|
+
const onAnchorClick = vi.fn();
|
|
202
|
+
// eslint-disable-next-line @eslint-react/dom-no-script-url -- the test proves this URL never navigates
|
|
203
|
+
render(<DelegatingCard href={scriptUrl} onAnchorClick={onAnchorClick} />);
|
|
204
|
+
fireEvent.click(screen.getByTestId('card'));
|
|
205
|
+
expect(onAnchorClick).not.toHaveBeenCalled();
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
it('a click on the inner link itself is left to the link', () => {
|
|
209
|
+
const onAnchorClick = vi.fn();
|
|
210
|
+
render(<DelegatingCard href="/docs" onAnchorClick={onAnchorClick} />);
|
|
211
|
+
fireEvent.click(screen.getByTestId('inner-link'));
|
|
212
|
+
// The anchor's own handler ran once; the hook did not proxy a second one.
|
|
213
|
+
expect(onAnchorClick).toHaveBeenCalledTimes(1);
|
|
214
|
+
});
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
describe('useClickableContainer navigation — blocked href keeps the rest', () => {
|
|
218
|
+
it('still fires the onClick handler for a blocked href', () => {
|
|
219
|
+
const onClick = vi.fn();
|
|
220
|
+
const open = vi.spyOn(window, 'open').mockImplementation(() => null);
|
|
221
|
+
function Surface() {
|
|
222
|
+
const containerRef = useRef<HTMLDivElement>(null);
|
|
223
|
+
const handlers = useClickableContainer({
|
|
224
|
+
containerRef,
|
|
225
|
+
href: scriptUrl,
|
|
226
|
+
target: '_blank',
|
|
227
|
+
onClick,
|
|
228
|
+
});
|
|
229
|
+
return <div ref={containerRef} data-testid="card" {...handlers} />;
|
|
230
|
+
}
|
|
231
|
+
render(<Surface />);
|
|
232
|
+
fireEvent.click(screen.getByTestId('card'));
|
|
233
|
+
expect(onClick).toHaveBeenCalledTimes(1);
|
|
234
|
+
expect(open).not.toHaveBeenCalled();
|
|
235
|
+
});
|
|
236
|
+
});
|
|
@@ -11,11 +11,17 @@
|
|
|
11
11
|
* Solves the "nested interactive elements" problem: when a card is clickable
|
|
12
12
|
* but contains buttons/links, clicking those should NOT trigger the card's action.
|
|
13
13
|
*
|
|
14
|
+
* Every navigation exit here (window.open, location.href, and the delegated
|
|
15
|
+
* click on the interactive ref) runs after the shared navigation-destination
|
|
16
|
+
* rule in utils/safeUrl.ts; a blocked href does not navigate by any
|
|
17
|
+
* activation method.
|
|
18
|
+
*
|
|
14
19
|
* SYNC: When modified, update:
|
|
15
20
|
* - /packages/core/src/hooks/index.ts (export)
|
|
16
21
|
*/
|
|
17
22
|
|
|
18
23
|
import {useCallback, useEffect, type RefObject, type MouseEvent} from 'react';
|
|
24
|
+
import {isSafeUrl} from '../utils/safeUrl';
|
|
19
25
|
|
|
20
26
|
/**
|
|
21
27
|
* Canonical list of interactive element selectors — native controls plus
|
|
@@ -89,7 +95,11 @@ export interface UseClickableContainerOptions {
|
|
|
89
95
|
interactiveRef?: RefObject<HTMLElement | null>;
|
|
90
96
|
/** Click handler */
|
|
91
97
|
onClick?: (event: MouseEvent<HTMLElement>) => void;
|
|
92
|
-
/**
|
|
98
|
+
/**
|
|
99
|
+
* Navigation URL — when provided, clicking the container navigates. Checked
|
|
100
|
+
* with the shared navigation rule (utils/safeUrl.ts) before any activation
|
|
101
|
+
* method (plain, new-tab, Cmd/Ctrl-click, middle-click, delegated click).
|
|
102
|
+
*/
|
|
93
103
|
href?: string;
|
|
94
104
|
/** Link target */
|
|
95
105
|
target?: string;
|
|
@@ -180,8 +190,12 @@ export function useClickableContainer({
|
|
|
180
190
|
return;
|
|
181
191
|
}
|
|
182
192
|
|
|
183
|
-
// Navigate if href is provided
|
|
184
|
-
|
|
193
|
+
// Navigate if href is provided. React DOM vets the hrefs it writes as
|
|
194
|
+
// attributes; nothing below passes through React, so the shared
|
|
195
|
+
// navigation rule decides once, before target, modifier key, or the
|
|
196
|
+
// delegated anchor can select a sink. A blocked href simply does not
|
|
197
|
+
// navigate — the click handler above still ran.
|
|
198
|
+
if (href != null && isSafeUrl(href)) {
|
|
185
199
|
const shouldOpenNewTab =
|
|
186
200
|
target === '_blank' || event.ctrlKey || event.metaKey;
|
|
187
201
|
if (shouldOpenNewTab) {
|
|
@@ -230,11 +244,13 @@ export function useClickableContainer({
|
|
|
230
244
|
return;
|
|
231
245
|
}
|
|
232
246
|
|
|
233
|
-
// Middle-click on href opens in new tab
|
|
247
|
+
// Middle-click on href opens in new tab (same scheme rule as onClick —
|
|
248
|
+
// this navigation never passes through React either).
|
|
234
249
|
const isMiddleClick = event.button === 1;
|
|
235
250
|
if (
|
|
236
251
|
isMiddleClick &&
|
|
237
252
|
href != null &&
|
|
253
|
+
isSafeUrl(href) &&
|
|
238
254
|
(eventTarget === event.currentTarget ||
|
|
239
255
|
!hasInteractiveAncestor(eventTarget, containerEl))
|
|
240
256
|
) {
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* @file useScrollableArea.doc.mjs
|
|
5
|
+
* @input Shared scroll hook's fixed and entry-time keyboard policies
|
|
6
|
+
* @output Consumer guidance for named viewports and safe focus delegation
|
|
7
|
+
* @position Hook documentation consumed by the CLI and docsite
|
|
8
|
+
*/
|
|
9
|
+
|
|
3
10
|
/** @type {import('@astryxdesign/cli/authoring').HookDoc} */
|
|
4
11
|
export const docs = {
|
|
5
12
|
name: 'useScrollableArea',
|
|
@@ -18,7 +25,7 @@ export const docs = {
|
|
|
18
25
|
name: 'options',
|
|
19
26
|
type: 'UseScrollableAreaOptions',
|
|
20
27
|
description:
|
|
21
|
-
'Logical scroll intent, keyboard owner, overscroll policy, and fitting Sticky containment.',
|
|
28
|
+
'Logical scroll intent, fixed or automatic keyboard owner, overscroll policy, and fitting Sticky containment.',
|
|
22
29
|
required: true,
|
|
23
30
|
},
|
|
24
31
|
],
|
|
@@ -59,7 +66,12 @@ export const docs = {
|
|
|
59
66
|
{
|
|
60
67
|
guidance: true,
|
|
61
68
|
description:
|
|
62
|
-
'Use
|
|
69
|
+
'Use contentOrViewport to delegate forward Tab entry to the first sequential native link or button when it preserves native scroll keys. Inputs, composite widgets, and nested scroll areas retain the named viewport stop. Shift+Tab from the delegated first child skips the viewport; pointer and programmatic focus stay on it.',
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
guidance: true,
|
|
73
|
+
description:
|
|
74
|
+
'Use content keyboard ownership when your integration already supplies keyboard access to the full scroll range. Automatic delegation checks current content at each keyboard entry without continuously tracking its focusability.',
|
|
63
75
|
},
|
|
64
76
|
{
|
|
65
77
|
guidance: true,
|
|
@@ -85,7 +97,7 @@ export const docsDense = {
|
|
|
85
97
|
'Composes logical-axis scrolling into caller-owned viewport/content elements with stable effective-axis and edge state.',
|
|
86
98
|
paramDescriptions: {
|
|
87
99
|
options:
|
|
88
|
-
'axis, keyboard owner, allow/contain overscroll policy, and fitting Sticky containment.',
|
|
100
|
+
'axis, fixed/automatic keyboard owner, allow/contain overscroll policy, and fitting Sticky containment.',
|
|
89
101
|
},
|
|
90
102
|
returnDescriptions: {
|
|
91
103
|
getViewportProps:
|
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* @file useScrollableArea.test.tsx
|
|
5
|
+
* @input Shared scroll hook, mocked geometry, and observed DOM changes
|
|
6
|
+
* @output Regression coverage for measurement, composition, and stable viewport access
|
|
7
|
+
* @position DOM-level hook contract; native traversal is verified in browser tests
|
|
8
|
+
*/
|
|
9
|
+
|
|
3
10
|
import {act, render, screen} from '@testing-library/react';
|
|
4
11
|
import * as stylex from '@stylexjs/stylex';
|
|
5
|
-
import {useRef, type Ref} from 'react';
|
|
12
|
+
import {useRef, type ReactNode, type Ref} from 'react';
|
|
6
13
|
import {afterEach, beforeEach, describe, expect, it, vi} from 'vitest';
|
|
7
14
|
import {
|
|
8
15
|
useScrollableArea,
|
|
@@ -194,6 +201,57 @@ describe('useScrollableArea', () => {
|
|
|
194
201
|
expect(viewport.getAttribute('style')).toContain('--x-overflowY: hidden');
|
|
195
202
|
});
|
|
196
203
|
|
|
204
|
+
it('keeps the named viewport available while content eligibility changes', () => {
|
|
205
|
+
function AdaptiveFixture({children}: {children?: ReactNode}) {
|
|
206
|
+
const area = useScrollableArea({
|
|
207
|
+
axis: 'block',
|
|
208
|
+
keyboardAccess: {
|
|
209
|
+
owner: 'contentOrViewport',
|
|
210
|
+
label: 'Adaptive results',
|
|
211
|
+
role: 'region',
|
|
212
|
+
},
|
|
213
|
+
});
|
|
214
|
+
return (
|
|
215
|
+
<div data-testid="adaptive-viewport" {...area.getViewportProps()}>
|
|
216
|
+
<div data-testid="adaptive-content" {...area.getContentProps()}>
|
|
217
|
+
{children}
|
|
218
|
+
</div>
|
|
219
|
+
</div>
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
vi.spyOn(HTMLElement.prototype, 'getClientRects').mockReturnValue([
|
|
224
|
+
new DOMRect(0, 0, 20, 20),
|
|
225
|
+
] as unknown as DOMRectList);
|
|
226
|
+
const {rerender} = render(<AdaptiveFixture>Plain text</AdaptiveFixture>);
|
|
227
|
+
const viewport = screen.getByTestId('adaptive-viewport');
|
|
228
|
+
const content = screen.getByTestId('adaptive-content');
|
|
229
|
+
makeMeasurable(viewport);
|
|
230
|
+
setGeometry(viewport, {scrollHeight: 180});
|
|
231
|
+
|
|
232
|
+
void act(() => viewport.dispatchEvent(new Event('scroll')));
|
|
233
|
+
flushFrame();
|
|
234
|
+
expect(viewport).toHaveAttribute('role', 'region');
|
|
235
|
+
expect(viewport).toHaveAccessibleName('Adaptive results');
|
|
236
|
+
expect(viewport).toHaveAttribute('tabindex', '0');
|
|
237
|
+
|
|
238
|
+
viewport.focus();
|
|
239
|
+
rerender(
|
|
240
|
+
<AdaptiveFixture>
|
|
241
|
+
<button type="button">Use existing action</button>
|
|
242
|
+
</AdaptiveFixture>,
|
|
243
|
+
);
|
|
244
|
+
void act(() => content.dispatchEvent(new Event('transitionend')));
|
|
245
|
+
flushFrame();
|
|
246
|
+
expect(viewport).toHaveAttribute('tabindex', '0');
|
|
247
|
+
expect(viewport).toHaveFocus();
|
|
248
|
+
|
|
249
|
+
rerender(<AdaptiveFixture>Plain text again</AdaptiveFixture>);
|
|
250
|
+
void act(() => content.dispatchEvent(new Event('transitionend')));
|
|
251
|
+
flushFrame();
|
|
252
|
+
expect(viewport).toHaveAttribute('tabindex', '0');
|
|
253
|
+
});
|
|
254
|
+
|
|
197
255
|
it('requires scroll-capable computed overflow and more than 1px excess geometry', () => {
|
|
198
256
|
render(<Fixture />);
|
|
199
257
|
const viewport = screen.getByTestId('viewport');
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* @file useScrollableArea.ts
|
|
7
|
-
* @input Logical scroll intent, keyboard
|
|
8
|
-
* @output Safe prop getters and stable per-axis effective scroll state
|
|
7
|
+
* @input Logical scroll intent, focus-time keyboard delegation, caller-owned viewport/content props
|
|
8
|
+
* @output Safe prop getters, native keyboard entry, and stable per-axis effective scroll state
|
|
9
9
|
* @position Canonical behavior core for ScrollableArea and structure-owning adopters
|
|
10
10
|
*
|
|
11
11
|
* SYNC: When modified, update:
|
|
@@ -26,6 +26,7 @@ import {
|
|
|
26
26
|
} from 'react';
|
|
27
27
|
import * as stylex from '@stylexjs/stylex';
|
|
28
28
|
import type {BaseProps} from '../BaseProps';
|
|
29
|
+
import {attachScrollKeyboardDelegation} from './scrollKeyboardDelegation';
|
|
29
30
|
import {useIsomorphicLayoutEffect} from './useIsomorphicLayoutEffect';
|
|
30
31
|
import {mergeProps} from '../utils/mergeProps';
|
|
31
32
|
import {mergeRefs} from '../utils/mergeRefs';
|
|
@@ -48,7 +49,12 @@ export type ScrollStickyContainment = 'whenScrollable' | 'always';
|
|
|
48
49
|
|
|
49
50
|
export type ScrollKeyboardAccess =
|
|
50
51
|
| {owner: 'content'}
|
|
51
|
-
| {owner: 'viewport'; label: string; role?: 'group' | 'region'}
|
|
52
|
+
| {owner: 'viewport'; label: string; role?: 'group' | 'region'}
|
|
53
|
+
| {
|
|
54
|
+
owner: 'contentOrViewport';
|
|
55
|
+
label: string;
|
|
56
|
+
role?: 'group' | 'region';
|
|
57
|
+
};
|
|
52
58
|
|
|
53
59
|
export interface ScrollAxisState {
|
|
54
60
|
isScrollable: boolean;
|
|
@@ -285,6 +291,8 @@ export function useScrollableArea({
|
|
|
285
291
|
content.addEventListener('transitionend', scheduleMeasure);
|
|
286
292
|
content.addEventListener('animationend', scheduleMeasure);
|
|
287
293
|
|
|
294
|
+
// Geometry invalidation is independent of keyboard eligibility. The latter
|
|
295
|
+
// is inspected only on native keyboard entry, never by these observers.
|
|
288
296
|
const mutationObserver =
|
|
289
297
|
typeof MutationObserver === 'undefined'
|
|
290
298
|
? null
|
|
@@ -346,6 +354,20 @@ export function useScrollableArea({
|
|
|
346
354
|
[axis, measured],
|
|
347
355
|
);
|
|
348
356
|
|
|
357
|
+
const hasEffectiveAxis =
|
|
358
|
+
state.inline.isScrollable || state.block.isScrollable;
|
|
359
|
+
useIsomorphicLayoutEffect(() => {
|
|
360
|
+
if (
|
|
361
|
+
keyboardAccess.owner !== 'contentOrViewport' ||
|
|
362
|
+
!hasEffectiveAxis ||
|
|
363
|
+
viewport == null ||
|
|
364
|
+
content == null
|
|
365
|
+
) {
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
return attachScrollKeyboardDelegation(viewport, content);
|
|
369
|
+
}, [content, hasEffectiveAxis, keyboardAccess.owner, viewport]);
|
|
370
|
+
|
|
349
371
|
const getViewportProps = useCallback(
|
|
350
372
|
<E extends HTMLElement>(
|
|
351
373
|
props: ScrollableElementProps<E> = {},
|
|
@@ -385,8 +407,9 @@ export function useScrollableArea({
|
|
|
385
407
|
),
|
|
386
408
|
),
|
|
387
409
|
) as ScrollableElementProps<E>;
|
|
410
|
+
const viewportIsKeyboardOwner = keyboardAccess.owner !== 'content';
|
|
388
411
|
const keepsProgrammaticFocus =
|
|
389
|
-
!hasEffectiveAxis &&
|
|
412
|
+
(!viewportIsKeyboardOwner || !hasEffectiveAxis) &&
|
|
390
413
|
viewport != null &&
|
|
391
414
|
typeof document !== 'undefined' &&
|
|
392
415
|
document.activeElement === viewport;
|
|
@@ -409,15 +432,16 @@ export function useScrollableArea({
|
|
|
409
432
|
}
|
|
410
433
|
|
|
411
434
|
const keyboardProps =
|
|
412
|
-
keyboardAccess.owner
|
|
435
|
+
keyboardAccess.owner !== 'content'
|
|
413
436
|
? {
|
|
414
437
|
role: keyboardAccess.role ?? 'group',
|
|
415
438
|
'aria-label': keyboardAccess.label,
|
|
416
|
-
tabIndex:
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
439
|
+
tabIndex:
|
|
440
|
+
viewportIsKeyboardOwner && hasEffectiveAxis
|
|
441
|
+
? 0
|
|
442
|
+
: keepsProgrammaticFocus
|
|
443
|
+
? -1
|
|
444
|
+
: undefined,
|
|
421
445
|
}
|
|
422
446
|
: {};
|
|
423
447
|
|
package/src/index.ts
CHANGED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file inputFontFloor.test.ts
|
|
5
|
+
* @input Uses node:fs and node:path to read the text-control sources
|
|
6
|
+
* @output Invariants for the 16px input floor shared by every text control
|
|
7
|
+
* @position Family-wide source test, in the source-reading style of
|
|
8
|
+
* reset.test.ts. jsdom's CSSOM drops declarations nested inside @supports,
|
|
9
|
+
* so the iOS branch cannot be asserted against injected rules — the
|
|
10
|
+
* runtime half (no bare coarse-pointer floor) lives in TextInput.test.tsx.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import {readFileSync, readdirSync} from 'node:fs';
|
|
14
|
+
import path from 'node:path';
|
|
15
|
+
|
|
16
|
+
import {describe, expect, it} from 'vitest';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Every control that floors its text to 16px. iOS Safari zooms the page when
|
|
20
|
+
* a focused control renders under 16px; no other coarse-pointer browser does.
|
|
21
|
+
* `-webkit-touch-callout` is implemented by iOS WebKit alone, so each floor
|
|
22
|
+
* nests `@supports (-webkit-touch-callout: none)` inside the coarse-pointer
|
|
23
|
+
* query: iPhones and iPads keep the zoom guard, while Android and touch
|
|
24
|
+
* laptops keep the theme's own type scale instead of an inflated 16px.
|
|
25
|
+
*/
|
|
26
|
+
const FLOORED_CONTROLS = [
|
|
27
|
+
'Chat/ChatComposerInput.tsx',
|
|
28
|
+
'CommandPalette/CommandPaletteInput.tsx',
|
|
29
|
+
'ComplexSelector/ComplexSelector.tsx',
|
|
30
|
+
'DateInput/DateInput.tsx',
|
|
31
|
+
'DateInput/NativeDateField.tsx',
|
|
32
|
+
'DateInput/TouchDateField.tsx',
|
|
33
|
+
'DateRangeInput/DateRangeInput.tsx',
|
|
34
|
+
'DateTimeInput/DateTimeInput.tsx',
|
|
35
|
+
'DateTimeInput/TouchDateTimeField.tsx',
|
|
36
|
+
'DateTimeInput/nativePickerSegmentStyles.ts',
|
|
37
|
+
'Field/PanelSearchInput.tsx',
|
|
38
|
+
'FileInput/FileInput.tsx',
|
|
39
|
+
'NumberInput/NumberInput.tsx',
|
|
40
|
+
'Selector/Selector.tsx',
|
|
41
|
+
'TextArea/TextArea.tsx',
|
|
42
|
+
'TextInput/TextInput.tsx',
|
|
43
|
+
'TimeInput/TimeInput.tsx',
|
|
44
|
+
'Typeahead/BaseTypeahead.tsx',
|
|
45
|
+
// The rich-text package carries the same floor; its path is relative to
|
|
46
|
+
// packages/core/src.
|
|
47
|
+
'../../richtext/src/RichTextEditor.tsx',
|
|
48
|
+
];
|
|
49
|
+
|
|
50
|
+
/** A font floor applied to every coarse pointer, iOS or not. */
|
|
51
|
+
const BARE_COARSE_FLOOR = `'@media (pointer: coarse)': \`max(1rem`;
|
|
52
|
+
|
|
53
|
+
/** The same floor, gated to iOS WebKit inside the coarse-pointer query. */
|
|
54
|
+
const IOS_GATED_FLOOR =
|
|
55
|
+
/'@media \(pointer: coarse\)': \{\s*'@supports \(-webkit-touch-callout: none\)': `max\(1rem, \$\{typeScaleVars\['--text-(?:body|label)-size'\]\}\)`/;
|
|
56
|
+
|
|
57
|
+
/** Collect every non-test component source under `dir`, recursively. */
|
|
58
|
+
function componentSources(dir: string): string[] {
|
|
59
|
+
const SOURCE_EXTENSIONS = ['.ts', '.tsx', '.js', '.jsx'];
|
|
60
|
+
const out: string[] = [];
|
|
61
|
+
for (const entry of readdirSync(dir, {withFileTypes: true})) {
|
|
62
|
+
const full = path.join(dir, entry.name);
|
|
63
|
+
if (entry.isDirectory()) {
|
|
64
|
+
out.push(...componentSources(full));
|
|
65
|
+
} else if (
|
|
66
|
+
SOURCE_EXTENSIONS.some(ext => entry.name.endsWith(ext)) &&
|
|
67
|
+
!entry.name.endsWith('.test.ts') &&
|
|
68
|
+
!entry.name.endsWith('.test.tsx') &&
|
|
69
|
+
!entry.name.endsWith('.doc.mjs')
|
|
70
|
+
) {
|
|
71
|
+
out.push(full);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return out;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
describe('the 16px input floor', () => {
|
|
78
|
+
it('gates every floored text control to iOS alone', () => {
|
|
79
|
+
// Guards the loop below against the list silently emptying out.
|
|
80
|
+
expect(FLOORED_CONTROLS.length).toBeGreaterThanOrEqual(18);
|
|
81
|
+
for (const file of FLOORED_CONTROLS) {
|
|
82
|
+
const source = readFileSync(path.resolve(__dirname, file), 'utf8');
|
|
83
|
+
expect(source, file).toMatch(IOS_GATED_FLOOR);
|
|
84
|
+
expect(source, file).not.toContain(BARE_COARSE_FLOOR);
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('leaves no bare coarse-pointer font floor anywhere in core or richtext', () => {
|
|
89
|
+
// Catches a new (or renamed) control reintroducing the ungated floor,
|
|
90
|
+
// wherever it lands — the list above only knows the current family. The
|
|
91
|
+
// rich-text package carries the same floor, so it is scanned too.
|
|
92
|
+
const scanRoots = [
|
|
93
|
+
__dirname,
|
|
94
|
+
path.resolve(__dirname, '../../richtext/src'),
|
|
95
|
+
];
|
|
96
|
+
const offenders = scanRoots
|
|
97
|
+
.flatMap(root => componentSources(root))
|
|
98
|
+
.map(file => ({
|
|
99
|
+
file,
|
|
100
|
+
source: readFileSync(file, 'utf8'),
|
|
101
|
+
}))
|
|
102
|
+
.filter(({source}) => source.includes(BARE_COARSE_FLOOR))
|
|
103
|
+
.map(({file}) => path.relative(__dirname, file));
|
|
104
|
+
expect(offenders).toEqual([]);
|
|
105
|
+
});
|
|
106
|
+
});
|
package/src/tailwind-theme.css
CHANGED
|
@@ -182,12 +182,6 @@
|
|
|
182
182
|
--text-4xl: var(--font-size-4xl);
|
|
183
183
|
--text-5xl: var(--font-size-5xl);
|
|
184
184
|
|
|
185
|
-
/* --- Font weights --- */
|
|
186
|
-
--font-weight-normal: var(--font-weight-normal);
|
|
187
|
-
--font-weight-medium: var(--font-weight-medium);
|
|
188
|
-
--font-weight-semibold: var(--font-weight-semibold);
|
|
189
|
-
--font-weight-bold: var(--font-weight-bold);
|
|
190
|
-
|
|
191
185
|
/* =========================================================================
|
|
192
186
|
* Border radius
|
|
193
187
|
*
|
package/src/theme/Theme.test.tsx
CHANGED
|
@@ -55,6 +55,38 @@ describe('Theme', () => {
|
|
|
55
55
|
expect(getByText('hello')).toBeTruthy();
|
|
56
56
|
});
|
|
57
57
|
|
|
58
|
+
it('mounts the remaining CSS when one authored declaration is dropped', () => {
|
|
59
|
+
const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
60
|
+
try {
|
|
61
|
+
const theme = defineTheme({
|
|
62
|
+
name: 'declaration-boundary',
|
|
63
|
+
tokens: {'--color-accent': 'red; } body { color: red'},
|
|
64
|
+
components: {button: {base: {borderRadius: '4px'}}},
|
|
65
|
+
});
|
|
66
|
+
render(
|
|
67
|
+
<Theme theme={theme}>
|
|
68
|
+
<span>child</span>
|
|
69
|
+
</Theme>,
|
|
70
|
+
);
|
|
71
|
+
const css = Array.from(document.querySelectorAll('style'))
|
|
72
|
+
.map(tag => tag.textContent ?? '')
|
|
73
|
+
.join('\n');
|
|
74
|
+
expect(css).not.toContain('body { color: red');
|
|
75
|
+
expect(css).toContain('border-radius: 4px;');
|
|
76
|
+
expect(css).toContain(':where(p)');
|
|
77
|
+
expect(
|
|
78
|
+
warn.mock.calls.filter(([message]) =>
|
|
79
|
+
String(message).startsWith('[astryx theme] dropped'),
|
|
80
|
+
),
|
|
81
|
+
).toHaveLength(1);
|
|
82
|
+
expect(warn).toHaveBeenCalledWith(
|
|
83
|
+
expect.stringContaining('dropped "--color-accent" in tokens'),
|
|
84
|
+
);
|
|
85
|
+
} finally {
|
|
86
|
+
warn.mockRestore();
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
|
|
58
90
|
it('sets data-astryx-theme on wrapper div', () => {
|
|
59
91
|
const {container} = render(
|
|
60
92
|
<Theme theme={testTheme}>
|