@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
package/src/Kbd/Kbd.test.tsx
CHANGED
|
@@ -10,13 +10,14 @@
|
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import {render, screen} from '@testing-library/react';
|
|
13
|
-
import {describe, it, expect, afterEach} from 'vitest';
|
|
13
|
+
import {describe, it, expect, afterEach, vi} from 'vitest';
|
|
14
14
|
import {Kbd} from './Kbd';
|
|
15
15
|
|
|
16
16
|
describe('Kbd', () => {
|
|
17
17
|
const originalPlatform = navigator.platform;
|
|
18
18
|
|
|
19
19
|
afterEach(() => {
|
|
20
|
+
vi.restoreAllMocks();
|
|
20
21
|
// Restore platform after any test that spoofs it \u2014 ensures no
|
|
21
22
|
// test pollution even if an assertion fails mid-test.
|
|
22
23
|
Object.defineProperty(navigator, 'platform', {
|
|
@@ -72,6 +73,61 @@ describe('Kbd', () => {
|
|
|
72
73
|
expect(screen.getByText('Esc')).toBeInTheDocument();
|
|
73
74
|
});
|
|
74
75
|
|
|
76
|
+
it.each([
|
|
77
|
+
{alias: 'esc', display: 'Esc', label: 'Escape'},
|
|
78
|
+
{alias: 'return', display: '\u21B5', label: 'Enter'},
|
|
79
|
+
])('normalizes the $alias key alias', ({alias, display, label}) => {
|
|
80
|
+
render(<Kbd keys={alias} />);
|
|
81
|
+
expect(screen.getByText(display)).toBeInTheDocument();
|
|
82
|
+
expect(screen.getByRole('img')).toHaveAttribute('aria-label', label);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it.each([
|
|
86
|
+
{alias: 'esc', canonical: 'escape'},
|
|
87
|
+
{alias: 'return', canonical: 'enter'},
|
|
88
|
+
])('$alias renders identically to $canonical', ({alias, canonical}) => {
|
|
89
|
+
const {container, rerender} = render(<Kbd keys={canonical} />);
|
|
90
|
+
const text = container.textContent;
|
|
91
|
+
const label = screen.getByRole('img').getAttribute('aria-label');
|
|
92
|
+
rerender(<Kbd keys={alias} />);
|
|
93
|
+
expect(container.textContent).toBe(text);
|
|
94
|
+
expect(screen.getByRole('img').getAttribute('aria-label')).toBe(label);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it.each([
|
|
98
|
+
{keys: 'ctrl + ESC', display: 'Esc', label: 'Control + Escape'},
|
|
99
|
+
{keys: 'shift + RETURN', display: '\u21B5', label: 'Shift + Enter'},
|
|
100
|
+
])('normalizes aliases inside the $keys combo', ({keys, display, label}) => {
|
|
101
|
+
render(<Kbd keys={keys} />);
|
|
102
|
+
expect(screen.getByText(display)).toBeInTheDocument();
|
|
103
|
+
expect(screen.getByRole('img')).toHaveAttribute('aria-label', label);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it('keeps child keys unique after alias normalization', () => {
|
|
107
|
+
const consoleError = vi
|
|
108
|
+
.spyOn(console, 'error')
|
|
109
|
+
.mockImplementation(() => {});
|
|
110
|
+
const {container} = render(
|
|
111
|
+
<Kbd keys="escape+esc+ESC+enter+return+return" />,
|
|
112
|
+
);
|
|
113
|
+
expect(consoleError).not.toHaveBeenCalled();
|
|
114
|
+
expect(
|
|
115
|
+
Array.from(container.querySelectorAll('kbd'), key => key.textContent),
|
|
116
|
+
).toEqual(['Esc', 'Esc', 'Esc', '↵', '↵', '↵']);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it.each(['constructor', '__proto__'])(
|
|
120
|
+
'renders the unknown %s key without consulting object prototypes',
|
|
121
|
+
key => {
|
|
122
|
+
render(<Kbd keys={key} />);
|
|
123
|
+
expect(screen.getByText(key.toUpperCase())).toBeInTheDocument();
|
|
124
|
+
expect(screen.getByRole('img')).toHaveAttribute(
|
|
125
|
+
'aria-label',
|
|
126
|
+
key.toUpperCase(),
|
|
127
|
+
);
|
|
128
|
+
},
|
|
129
|
+
);
|
|
130
|
+
|
|
75
131
|
it('exposes a spoken accessible name and hides the glyphs (obs-1)', () => {
|
|
76
132
|
render(<Kbd keys="mod+shift+k" />);
|
|
77
133
|
// The wrapper carries a screen-reader name built from spoken key labels
|
package/src/Kbd/Kbd.tsx
CHANGED
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* @file Kbd.tsx
|
|
7
|
-
* @input Uses React, StyleX, theme tokens
|
|
8
|
-
* @output Exports Kbd component and KbdProps
|
|
7
|
+
* @input Uses React, StyleX, theme tokens, shortcut key names and aliases
|
|
8
|
+
* @output Exports Kbd component and KbdProps with canonical shortcut labels
|
|
9
9
|
* @position Core implementation; renders styled keyboard shortcut indicators
|
|
10
10
|
*
|
|
11
11
|
* SYNC: When modified, update:
|
|
@@ -61,20 +61,25 @@ const styles = stylex.create({
|
|
|
61
61
|
* Note: `mod` is not in this map — it resolves dynamically via platform
|
|
62
62
|
* detection inside the component.
|
|
63
63
|
*/
|
|
64
|
-
const KEY_DISPLAY
|
|
65
|
-
ctrl
|
|
66
|
-
alt
|
|
67
|
-
shift
|
|
68
|
-
enter
|
|
69
|
-
backspace
|
|
70
|
-
escape
|
|
71
|
-
tab
|
|
72
|
-
up
|
|
73
|
-
down
|
|
74
|
-
left
|
|
75
|
-
right
|
|
76
|
-
plus
|
|
77
|
-
|
|
64
|
+
const KEY_DISPLAY = new Map<string, string>([
|
|
65
|
+
['ctrl', '\u2303'], // ⌃
|
|
66
|
+
['alt', '\u2325'], // ⌥
|
|
67
|
+
['shift', '\u21E7'], // ⇧
|
|
68
|
+
['enter', '\u21B5'], // ↵
|
|
69
|
+
['backspace', '\u232B'], // ⌫
|
|
70
|
+
['escape', 'Esc'],
|
|
71
|
+
['tab', '\u21E5'], // ⇥
|
|
72
|
+
['up', '\u2191'],
|
|
73
|
+
['down', '\u2193'],
|
|
74
|
+
['left', '\u2190'],
|
|
75
|
+
['right', '\u2192'],
|
|
76
|
+
['plus', '+'],
|
|
77
|
+
]);
|
|
78
|
+
|
|
79
|
+
const KBD_KEY_ALIASES = new Map<string, string>([
|
|
80
|
+
['esc', 'escape'],
|
|
81
|
+
['return', 'enter'],
|
|
82
|
+
]);
|
|
78
83
|
|
|
79
84
|
/**
|
|
80
85
|
* Resolves a key name to its display string. Handles the platform-aware
|
|
@@ -85,7 +90,7 @@ function getKeyDisplay(key: string, isMac: boolean): string {
|
|
|
85
90
|
if (key === 'mod') {
|
|
86
91
|
return isMac ? '\u2318' : 'Ctrl';
|
|
87
92
|
}
|
|
88
|
-
return KEY_DISPLAY
|
|
93
|
+
return KEY_DISPLAY.get(key) ?? key.toUpperCase();
|
|
89
94
|
}
|
|
90
95
|
|
|
91
96
|
/**
|
|
@@ -93,26 +98,26 @@ function getKeyDisplay(key: string, isMac: boolean): string {
|
|
|
93
98
|
* (⌘, ⇧, ↵, …) that assistive tech cannot announce meaningfully, so the
|
|
94
99
|
* accessible name for the shortcut is built from these words instead.
|
|
95
100
|
*/
|
|
96
|
-
const KEY_LABEL
|
|
97
|
-
ctrl
|
|
98
|
-
alt
|
|
99
|
-
shift
|
|
100
|
-
enter
|
|
101
|
-
backspace
|
|
102
|
-
escape
|
|
103
|
-
tab
|
|
104
|
-
up
|
|
105
|
-
down
|
|
106
|
-
left
|
|
107
|
-
right
|
|
108
|
-
plus
|
|
109
|
-
|
|
101
|
+
const KEY_LABEL = new Map<string, string>([
|
|
102
|
+
['ctrl', 'Control'],
|
|
103
|
+
['alt', 'Alt'],
|
|
104
|
+
['shift', 'Shift'],
|
|
105
|
+
['enter', 'Enter'],
|
|
106
|
+
['backspace', 'Backspace'],
|
|
107
|
+
['escape', 'Escape'],
|
|
108
|
+
['tab', 'Tab'],
|
|
109
|
+
['up', 'Up arrow'],
|
|
110
|
+
['down', 'Down arrow'],
|
|
111
|
+
['left', 'Left arrow'],
|
|
112
|
+
['right', 'Right arrow'],
|
|
113
|
+
['plus', 'Plus'],
|
|
114
|
+
]);
|
|
110
115
|
|
|
111
116
|
function getKeyLabel(key: string, isMac: boolean): string {
|
|
112
117
|
if (key === 'mod') {
|
|
113
118
|
return isMac ? 'Command' : 'Control';
|
|
114
119
|
}
|
|
115
|
-
return KEY_LABEL
|
|
120
|
+
return KEY_LABEL.get(key) ?? key.toUpperCase();
|
|
116
121
|
}
|
|
117
122
|
|
|
118
123
|
function subscribeToPlatformChanges(): () => void {
|
|
@@ -127,7 +132,9 @@ export interface KbdProps extends BaseProps<HTMLSpanElement> {
|
|
|
127
132
|
ref?: React.Ref<HTMLSpanElement>;
|
|
128
133
|
/**
|
|
129
134
|
* Keyboard shortcut string. Use "+" to separate keys.
|
|
130
|
-
* Special keys: mod (Cmd on Mac), ctrl, alt, shift, enter, backspace, escape
|
|
135
|
+
* Special keys: mod (Cmd on Mac), ctrl, alt, shift, enter, backspace, escape,
|
|
136
|
+
* tab, up, down, left, and right.
|
|
137
|
+
* Aliases: "esc" for "escape" and "return" for "enter".
|
|
131
138
|
* Use "plus" to render a literal "+" key (e.g. "shift+plus").
|
|
132
139
|
*
|
|
133
140
|
* @example
|
|
@@ -163,11 +170,24 @@ export function Kbd({keys, ref, xstyle, className, style, ...rest}: KbdProps) {
|
|
|
163
170
|
getServerPlatformSnapshot,
|
|
164
171
|
);
|
|
165
172
|
|
|
166
|
-
const
|
|
173
|
+
const keyOccurrences = new Map<string, number>();
|
|
174
|
+
const parts = keys
|
|
175
|
+
.split('+')
|
|
176
|
+
.map(key => key.trim().toLowerCase())
|
|
177
|
+
.map(sourceKey => {
|
|
178
|
+
const occurrence = keyOccurrences.get(sourceKey) ?? 0;
|
|
179
|
+
keyOccurrences.set(sourceKey, occurrence + 1);
|
|
180
|
+
return {
|
|
181
|
+
key: KBD_KEY_ALIASES.get(sourceKey) ?? sourceKey,
|
|
182
|
+
reactKey: `${sourceKey}:${occurrence}`,
|
|
183
|
+
};
|
|
184
|
+
});
|
|
167
185
|
|
|
168
186
|
// Screen-reader name: the joined spoken labels (e.g. "Command + K"), since
|
|
169
187
|
// the visual glyphs below are announced meaninglessly by assistive tech.
|
|
170
|
-
const accessibleName = parts
|
|
188
|
+
const accessibleName = parts
|
|
189
|
+
.map(({key}) => getKeyLabel(key, isMac))
|
|
190
|
+
.join(' + ');
|
|
171
191
|
|
|
172
192
|
return (
|
|
173
193
|
<span
|
|
@@ -181,8 +201,8 @@ export function Kbd({keys, ref, xstyle, className, style, ...rest}: KbdProps) {
|
|
|
181
201
|
className,
|
|
182
202
|
style,
|
|
183
203
|
)}>
|
|
184
|
-
{parts.map(key => (
|
|
185
|
-
<kbd key={
|
|
204
|
+
{parts.map(({key, reactKey}) => (
|
|
205
|
+
<kbd key={reactKey} aria-hidden="true" {...stylex.props(styles.kbd)}>
|
|
186
206
|
{getKeyDisplay(key, isMac)}
|
|
187
207
|
</kbd>
|
|
188
208
|
))}
|
|
@@ -8,6 +8,23 @@ export const docs = {
|
|
|
8
8
|
displayName: 'Layout Footer',
|
|
9
9
|
isHiddenFromOverview: true,
|
|
10
10
|
description: 'Bottom bar for action bars, pagination, and status bars.',
|
|
11
|
+
playground: {
|
|
12
|
+
defaults: {
|
|
13
|
+
children: 'Footer content: status bar or actions',
|
|
14
|
+
hasDivider: true,
|
|
15
|
+
},
|
|
16
|
+
wrapper: {
|
|
17
|
+
component: 'Layout',
|
|
18
|
+
slotProp: 'footer',
|
|
19
|
+
props: {
|
|
20
|
+
content: {
|
|
21
|
+
__element: 'LayoutContent',
|
|
22
|
+
props: {},
|
|
23
|
+
children: 'Main content area',
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
},
|
|
11
28
|
props: [
|
|
12
29
|
{
|
|
13
30
|
name: 'children',
|
package/src/Link/Link.doc.mjs
CHANGED
|
@@ -38,7 +38,8 @@ export const docs = {
|
|
|
38
38
|
{
|
|
39
39
|
name: 'href',
|
|
40
40
|
type: 'string',
|
|
41
|
-
description:
|
|
41
|
+
description:
|
|
42
|
+
'Link destination URL. Astryx-owned links and clickable surfaces block javascript:, vbscript:, and data:text/html after control-character removal, outer-whitespace trimming, and case-insensitive scheme inspection. The same rule covers native anchors, custom routers, Markdown links, and every activation method. Relative paths, fragments, protocol-relative URLs, HTTP(S), mailto, tel, safe custom schemes, and accepted downloads keep their existing behavior. A rejected destination renders inertly without invoking a custom router. There is no bypass prop; exceptional behavior requires caller-owned custom rendering outside this guarantee. Images and other resource URLs have separate policies.',
|
|
42
43
|
},
|
|
43
44
|
{
|
|
44
45
|
name: 'hasUnderline',
|
|
@@ -151,7 +152,8 @@ export const docs = {
|
|
|
151
152
|
{
|
|
152
153
|
name: 'component',
|
|
153
154
|
type: 'LinkComponentType',
|
|
154
|
-
description:
|
|
155
|
+
description:
|
|
156
|
+
'Component to use for all link elements. It receives accepted `href` and `to` values under the shared navigation rule described on the Link `href` prop. Supported structured destinations, including their `protocol`, are checked without changing object identity. If either supplied destination is rejected, Astryx renders inert content without invoking this component; it does not pass undefined or fall back to the other destination.',
|
|
155
157
|
required: true,
|
|
156
158
|
},
|
|
157
159
|
{
|
|
@@ -164,6 +166,17 @@ export const docs = {
|
|
|
164
166
|
},
|
|
165
167
|
],
|
|
166
168
|
usage: {
|
|
169
|
+
accessibility: [
|
|
170
|
+
{
|
|
171
|
+
name: 'Link text',
|
|
172
|
+
category: 'Color contrast',
|
|
173
|
+
criterion: '1.4.3 Contrast (Minimum)',
|
|
174
|
+
requirement: '4.5:1',
|
|
175
|
+
states: ['Rest', 'Hover', 'Pointer down'],
|
|
176
|
+
description:
|
|
177
|
+
'Link text must have at least 4.5:1 contrast with the background behind it. For Pointer down, measure against the pressed overlay the link paints behind its text.',
|
|
178
|
+
},
|
|
179
|
+
],
|
|
167
180
|
description:
|
|
168
181
|
'A styled anchor for inline and standalone text navigation. Supports external links, underline variants, tooltips, and custom link components for router integration. Use it for navigating between pages or to external URLs.',
|
|
169
182
|
bestPractices: [
|
|
@@ -344,7 +357,7 @@ export const docsDense = {
|
|
|
344
357
|
propDescriptions: {
|
|
345
358
|
as: 'Custom component to render instead of <a>',
|
|
346
359
|
label: 'Accessible label (aria-label). Only for non-text content like icon-only links.',
|
|
347
|
-
href: 'Link destination URL',
|
|
360
|
+
href: 'Link destination URL. Shared rule blocks javascript:, vbscript:, data:text/html after scheme normalization on every Astryx-owned navigation path. Ordinary URLs, fragments, safe custom schemes, and accepted downloads are preserved. Rejected => inert, no custom router. No bypass prop; exceptional caller-owned rendering is outside the guarantee. Image/resource policy is separate.',
|
|
348
361
|
hasUnderline: 'Always show underline',
|
|
349
362
|
isDisabled: 'Disables link',
|
|
350
363
|
isExternalLink: 'Opens new tab w/ external icon and safe rel tokens',
|
|
@@ -365,7 +378,7 @@ export const docsDense = {
|
|
|
365
378
|
description:
|
|
366
379
|
'Provider setting default link component for all Astryx links in subtree.',
|
|
367
380
|
propDescriptions: {
|
|
368
|
-
component: 'Component for all link elements',
|
|
381
|
+
component: 'Component for all link elements. Accepted href/to follow the Link href rule; structured fields including protocol are checked, preserving identity. Either rejected => inert content, no router invocation or fallback.',
|
|
369
382
|
children: 'Subtree',
|
|
370
383
|
},
|
|
371
384
|
},
|
package/src/Link/Link.test.tsx
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
import {describe, it, expect, vi} from 'vitest';
|
|
13
13
|
import {fireEvent, render, screen} from '@testing-library/react';
|
|
14
14
|
import userEvent from '@testing-library/user-event';
|
|
15
|
+
import {hasPressedArm} from '../__tests__/pressState';
|
|
15
16
|
import {Link} from './Link';
|
|
16
17
|
import {LinkProvider} from './LinkProvider';
|
|
17
18
|
|
|
@@ -417,3 +418,26 @@ describe('Link', () => {
|
|
|
417
418
|
expect(link).toHaveAttribute('data-color', 'secondary');
|
|
418
419
|
});
|
|
419
420
|
});
|
|
421
|
+
|
|
422
|
+
describe('pressed state', () => {
|
|
423
|
+
it('paints the pressed overlay behind a link while it is pressed', () => {
|
|
424
|
+
render(<Link href="/docs">Docs</Link>);
|
|
425
|
+
expect(hasPressedArm(screen.getByRole('link', {name: 'Docs'}))).toBe(true);
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
it('paints it on the button form too', () => {
|
|
429
|
+
render(<Link onClick={() => {}}>Open</Link>);
|
|
430
|
+
expect(hasPressedArm(screen.getByRole('button', {name: 'Open'}))).toBe(
|
|
431
|
+
true,
|
|
432
|
+
);
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
it('does not press a disabled link', () => {
|
|
436
|
+
render(
|
|
437
|
+
<Link href="/docs" isDisabled>
|
|
438
|
+
Docs
|
|
439
|
+
</Link>,
|
|
440
|
+
);
|
|
441
|
+
expect(hasPressedArm(screen.getByText('Docs').closest('a')!)).toBe(false);
|
|
442
|
+
});
|
|
443
|
+
});
|
package/src/Link/Link.tsx
CHANGED
|
@@ -45,6 +45,7 @@ import {computeTargetAndRel} from './computeTargetAndRel';
|
|
|
45
45
|
import {useInteractiveRole} from '../hooks/useInteractiveRole';
|
|
46
46
|
import {themeProps} from '../utils/themeProps';
|
|
47
47
|
import {focusOutlineProps} from '../utils/focusOutline.stylex';
|
|
48
|
+
import {interactionOverlayStyles} from '../utils/interactionOverlay.stylex';
|
|
48
49
|
import {useTranslator} from '../i18n';
|
|
49
50
|
|
|
50
51
|
/**
|
|
@@ -368,6 +369,9 @@ export function Link({
|
|
|
368
369
|
styles.base,
|
|
369
370
|
styles.buttonReset,
|
|
370
371
|
linkColorStyles[color],
|
|
372
|
+
// The system's pressed overlay behind the text; the hover stays the
|
|
373
|
+
// colour change above, so a press is the one background it paints.
|
|
374
|
+
!isDisabled && interactionOverlayStyles.pressedBackgroundColor,
|
|
371
375
|
hasUnderline && styles.hasUnderline,
|
|
372
376
|
isStandalone && styles.standalone,
|
|
373
377
|
isDisabled && styles.disabled,
|
|
@@ -427,6 +431,7 @@ export function Link({
|
|
|
427
431
|
focusOutlineProps.focusVisible(
|
|
428
432
|
styles.base,
|
|
429
433
|
linkColorStyles[color],
|
|
434
|
+
!isDisabled && interactionOverlayStyles.pressedBackgroundColor,
|
|
430
435
|
hasUnderline && styles.hasUnderline,
|
|
431
436
|
isStandalone && styles.standalone,
|
|
432
437
|
isDisabled && styles.disabled,
|
|
@@ -13,7 +13,7 @@ export const docs = {
|
|
|
13
13
|
description: 'Wraps your app to replace the default <a> tag with a framework-specific link component (e.g. Next.js Link) for client-side routing across all Astryx components.',
|
|
14
14
|
},
|
|
15
15
|
props: [
|
|
16
|
-
{name: 'component', type: 'LinkComponentType', required: true, description: 'Link component to use for all link elements in the subtree (e.g. Next.js Link).'},
|
|
16
|
+
{name: 'component', type: 'LinkComponentType', required: true, description: 'Link component to use for all link elements in the subtree (e.g. Next.js Link). It receives accepted `href` and `to` values under the shared navigation rule described on the Link `href` prop. Supported structured destinations, including their `protocol`, are checked without changing object identity. If either supplied destination is rejected, Astryx renders inert content without invoking this component; it does not pass undefined or fall back to the other destination.'},
|
|
17
17
|
{name: 'children', type: 'ReactNode', required: true, description: 'Content to render with the link provider.'},
|
|
18
18
|
],
|
|
19
19
|
};
|
|
@@ -25,6 +25,6 @@ export const docsDense = {
|
|
|
25
25
|
description: 'Wraps app to replace default <a> tag w/ framework-specific link component (e.g. Next.js Link) for client-side routing across all Astryx components.',
|
|
26
26
|
},
|
|
27
27
|
propDescriptions: {
|
|
28
|
-
component: 'link component for all link elements in subtree (e.g. Next.js Link)',
|
|
28
|
+
component: 'link component for all link elements in subtree (e.g. Next.js Link). Accepted href/to follow the Link href rule; structured fields including protocol are checked, preserving identity. Either rejected => inert content, no router invocation or fallback.',
|
|
29
29
|
},
|
|
30
30
|
};
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file Link.navigation.a11y.chromium.spec.ts
|
|
5
|
+
* @input Built Storybook navigation fixture and real Chromium activation
|
|
6
|
+
* @output Blocked-scheme and accepted-navigation evidence across every sink
|
|
7
|
+
* @position Browser proof for the shared navigation-destination contract
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import {expect, test, type Page} from '@playwright/test';
|
|
11
|
+
import {
|
|
12
|
+
DEFAULT_STORYBOOK_DIR,
|
|
13
|
+
serveStorybook,
|
|
14
|
+
type StaticServer,
|
|
15
|
+
} from '@astryxdesign/a11y-spec/storybook';
|
|
16
|
+
|
|
17
|
+
let storybook: StaticServer;
|
|
18
|
+
const storyId = 'core-link-navigation--destinations';
|
|
19
|
+
|
|
20
|
+
test.beforeAll(async () => {
|
|
21
|
+
storybook = await serveStorybook(
|
|
22
|
+
process.env.ASTRYX_STORYBOOK_DIR ?? DEFAULT_STORYBOOK_DIR,
|
|
23
|
+
);
|
|
24
|
+
});
|
|
25
|
+
test.afterAll(async () => {
|
|
26
|
+
await storybook?.close();
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
async function mount(page: Page, href: string) {
|
|
30
|
+
await page.goto(
|
|
31
|
+
`${storybook.origin}/iframe.html?id=${storyId}&viewMode=story`,
|
|
32
|
+
);
|
|
33
|
+
await expect(page.getByTestId('navigation-fixture')).toBeVisible();
|
|
34
|
+
await page.evaluate(
|
|
35
|
+
({storyId, href}) => {
|
|
36
|
+
const channel = (
|
|
37
|
+
window as unknown as {
|
|
38
|
+
__STORYBOOK_ADDONS_CHANNEL__: {
|
|
39
|
+
emit: (event: string, payload: unknown) => void;
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
).__STORYBOOK_ADDONS_CHANNEL__;
|
|
43
|
+
channel.emit('updateStoryArgs', {storyId, updatedArgs: {href}});
|
|
44
|
+
},
|
|
45
|
+
{storyId, href},
|
|
46
|
+
);
|
|
47
|
+
await expect(page.getByTestId('navigation-fixture')).toHaveAttribute(
|
|
48
|
+
'data-destination',
|
|
49
|
+
href,
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const blocked = [
|
|
54
|
+
'javascript:window.__navigationExecuted=true',
|
|
55
|
+
'vbscript:MsgBox(1)',
|
|
56
|
+
'data:text/html,<script>window.__navigationExecuted=true</script>',
|
|
57
|
+
' \u0000JaVa\tsCrIpT:window.__navigationExecuted=true',
|
|
58
|
+
];
|
|
59
|
+
|
|
60
|
+
for (const href of blocked) {
|
|
61
|
+
test(`rejected ${JSON.stringify(href)} is inert across native, router, Markdown, and imperative paths`, async ({
|
|
62
|
+
page,
|
|
63
|
+
context,
|
|
64
|
+
}) => {
|
|
65
|
+
await mount(page, href);
|
|
66
|
+
const originalUrl = page.url();
|
|
67
|
+
await page.evaluate(() => {
|
|
68
|
+
(
|
|
69
|
+
window as unknown as {__navigationExecuted: boolean}
|
|
70
|
+
).__navigationExecuted = false;
|
|
71
|
+
});
|
|
72
|
+
const pagesBefore = context.pages().length;
|
|
73
|
+
for (const id of [
|
|
74
|
+
'native',
|
|
75
|
+
'provider',
|
|
76
|
+
'as',
|
|
77
|
+
'structured',
|
|
78
|
+
'citation',
|
|
79
|
+
'download',
|
|
80
|
+
]) {
|
|
81
|
+
const target = page.getByTestId(id);
|
|
82
|
+
await expect(target).not.toHaveAttribute('href');
|
|
83
|
+
await expect(target).not.toHaveAttribute('data-router');
|
|
84
|
+
await target.click();
|
|
85
|
+
await target.click({button: 'middle'});
|
|
86
|
+
}
|
|
87
|
+
await expect(page.getByTestId('markdown').locator('a')).toHaveCount(0);
|
|
88
|
+
await expect(page.getByTestId('delegated').locator('a[href]')).toHaveCount(
|
|
89
|
+
0,
|
|
90
|
+
);
|
|
91
|
+
for (const id of ['imperative', 'imperative-blank', 'delegated']) {
|
|
92
|
+
const target =
|
|
93
|
+
id === 'delegated'
|
|
94
|
+
? page.getByText('Delegated card surface', {exact: true})
|
|
95
|
+
: page.getByTestId(id);
|
|
96
|
+
await target.click();
|
|
97
|
+
await target.click({modifiers: ['Control']});
|
|
98
|
+
await target.click({modifiers: ['Meta']});
|
|
99
|
+
await target.click({button: 'middle'});
|
|
100
|
+
}
|
|
101
|
+
// Drain browser activation tasks before checking that no sink fired.
|
|
102
|
+
await page.evaluate(
|
|
103
|
+
async () =>
|
|
104
|
+
new Promise<void>(resolve =>
|
|
105
|
+
requestAnimationFrame(() => requestAnimationFrame(() => resolve())),
|
|
106
|
+
),
|
|
107
|
+
);
|
|
108
|
+
expect(page.url()).toBe(originalUrl);
|
|
109
|
+
expect(context.pages()).toHaveLength(pagesBefore);
|
|
110
|
+
expect(
|
|
111
|
+
await page.evaluate(
|
|
112
|
+
() =>
|
|
113
|
+
(window as unknown as {__navigationExecuted: boolean})
|
|
114
|
+
.__navigationExecuted,
|
|
115
|
+
),
|
|
116
|
+
).toBe(false);
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
test('ordinary schemes keep native attributes, custom routing, and downloads', async ({
|
|
121
|
+
page,
|
|
122
|
+
}) => {
|
|
123
|
+
for (const href of [
|
|
124
|
+
'#fragment',
|
|
125
|
+
'/relative',
|
|
126
|
+
'https://example.com/docs',
|
|
127
|
+
'http://example.com/docs',
|
|
128
|
+
'mailto:a@example.com',
|
|
129
|
+
'tel:+1234567890',
|
|
130
|
+
'custom:document',
|
|
131
|
+
'data:text/plain,download',
|
|
132
|
+
]) {
|
|
133
|
+
await mount(page, href);
|
|
134
|
+
for (const id of [
|
|
135
|
+
'native',
|
|
136
|
+
'provider',
|
|
137
|
+
'as',
|
|
138
|
+
'structured',
|
|
139
|
+
'citation',
|
|
140
|
+
'download',
|
|
141
|
+
]) {
|
|
142
|
+
await expect(page.getByTestId(id)).toHaveAttribute('href', href);
|
|
143
|
+
}
|
|
144
|
+
for (const id of ['provider', 'as', 'structured']) {
|
|
145
|
+
await expect(page.getByTestId(id)).toHaveAttribute('data-router', 'true');
|
|
146
|
+
}
|
|
147
|
+
await expect(page.getByTestId('download')).toHaveAttribute(
|
|
148
|
+
'download',
|
|
149
|
+
'note.txt',
|
|
150
|
+
);
|
|
151
|
+
await expect(page.getByTestId('markdown').locator('a')).toHaveAttribute(
|
|
152
|
+
'href',
|
|
153
|
+
href,
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
for (const id of [
|
|
159
|
+
'native',
|
|
160
|
+
'provider',
|
|
161
|
+
'as',
|
|
162
|
+
'structured',
|
|
163
|
+
'imperative',
|
|
164
|
+
'delegated',
|
|
165
|
+
]) {
|
|
166
|
+
test(`${id} retains real same-tab navigation`, async ({page}) => {
|
|
167
|
+
const href = '/navigation-destination?source=' + id;
|
|
168
|
+
await page.route('**/navigation-destination?*', async route =>
|
|
169
|
+
route.fulfill({
|
|
170
|
+
contentType: 'text/html',
|
|
171
|
+
body: '<h1>Destination reached</h1>',
|
|
172
|
+
}),
|
|
173
|
+
);
|
|
174
|
+
await mount(page, href);
|
|
175
|
+
const target =
|
|
176
|
+
id === 'delegated'
|
|
177
|
+
? page.getByText('Delegated card surface', {exact: true})
|
|
178
|
+
: page.getByTestId(id);
|
|
179
|
+
await target.click();
|
|
180
|
+
await expect(page).toHaveURL(`${storybook.origin}${href}`);
|
|
181
|
+
await expect(
|
|
182
|
+
page.getByRole('heading', {name: 'Destination reached'}),
|
|
183
|
+
).toBeVisible();
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
for (const activation of ['blank', 'control', 'meta', 'middle'] as const) {
|
|
188
|
+
test(`imperative ${activation} retains real new-tab navigation`, async ({
|
|
189
|
+
page,
|
|
190
|
+
context,
|
|
191
|
+
}) => {
|
|
192
|
+
const href = `${storybook.origin}/navigation-destination?source=${activation}`;
|
|
193
|
+
await context.route('**/navigation-destination?*', async route =>
|
|
194
|
+
route.fulfill({
|
|
195
|
+
contentType: 'text/html',
|
|
196
|
+
body: '<h1>Destination reached</h1>',
|
|
197
|
+
}),
|
|
198
|
+
);
|
|
199
|
+
await mount(page, href);
|
|
200
|
+
const originalUrl = page.url();
|
|
201
|
+
const opened = context.waitForEvent('page');
|
|
202
|
+
await page
|
|
203
|
+
.getByTestId(activation === 'blank' ? 'imperative-blank' : 'imperative')
|
|
204
|
+
.click({
|
|
205
|
+
button: activation === 'middle' ? 'middle' : 'left',
|
|
206
|
+
modifiers:
|
|
207
|
+
activation === 'control'
|
|
208
|
+
? ['Control']
|
|
209
|
+
: activation === 'meta'
|
|
210
|
+
? ['Meta']
|
|
211
|
+
: [],
|
|
212
|
+
});
|
|
213
|
+
const popup = await opened;
|
|
214
|
+
await expect(popup).toHaveURL(href);
|
|
215
|
+
await expect(
|
|
216
|
+
popup.getByRole('heading', {name: 'Destination reached'}),
|
|
217
|
+
).toBeVisible();
|
|
218
|
+
expect(page.url()).toBe(originalUrl);
|
|
219
|
+
await popup.close();
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
test('native anchors retain real downloads and same-document fragments', async ({
|
|
224
|
+
page,
|
|
225
|
+
}) => {
|
|
226
|
+
await mount(page, 'data:text/plain,download');
|
|
227
|
+
const downloadEvent = page.waitForEvent('download');
|
|
228
|
+
await page.getByTestId('download').click();
|
|
229
|
+
const download = await downloadEvent;
|
|
230
|
+
expect(download.suggestedFilename()).toBe('note.txt');
|
|
231
|
+
expect(await download.failure()).toBeNull();
|
|
232
|
+
await mount(page, '#destination');
|
|
233
|
+
await page.getByTestId('native').click();
|
|
234
|
+
await expect(page).toHaveURL(/#destination$/);
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
test('native anchors retain real middle-click and external target navigation', async ({
|
|
238
|
+
page,
|
|
239
|
+
context,
|
|
240
|
+
}) => {
|
|
241
|
+
const href = 'https://example.com/navigation-destination';
|
|
242
|
+
await context.route(href, async route =>
|
|
243
|
+
route.fulfill({
|
|
244
|
+
contentType: 'text/html',
|
|
245
|
+
body: '<h1>External destination</h1>',
|
|
246
|
+
}),
|
|
247
|
+
);
|
|
248
|
+
for (const id of ['native', 'citation']) {
|
|
249
|
+
await mount(page, href);
|
|
250
|
+
const opened = context.waitForEvent('page');
|
|
251
|
+
await page
|
|
252
|
+
.getByTestId(id)
|
|
253
|
+
.click({button: id === 'native' ? 'middle' : 'left'});
|
|
254
|
+
const popup = await opened;
|
|
255
|
+
await expect(popup).toHaveURL(href);
|
|
256
|
+
await expect(
|
|
257
|
+
popup.getByRole('heading', {name: 'External destination'}),
|
|
258
|
+
).toBeVisible();
|
|
259
|
+
await popup.close();
|
|
260
|
+
}
|
|
261
|
+
});
|