@astryxdesign/core 0.1.2 → 0.1.3-canary.6d57c8d
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 +165 -0
- package/README.md +12 -12
- package/dist/Avatar/Avatar.d.ts.map +1 -1
- package/dist/Avatar/Avatar.js +11 -5
- package/dist/Breadcrumbs/BreadcrumbItem.d.ts +1 -1
- package/dist/Breadcrumbs/BreadcrumbItem.d.ts.map +1 -1
- package/dist/Breadcrumbs/BreadcrumbItem.js +24 -5
- package/dist/Button/Button.d.ts.map +1 -1
- package/dist/Button/Button.js +2 -4
- package/dist/ButtonGroup/ButtonGroup.d.ts.map +1 -1
- package/dist/ButtonGroup/ButtonGroup.js +0 -1
- package/dist/Calendar/Calendar.d.ts +5 -4
- package/dist/Calendar/Calendar.d.ts.map +1 -1
- package/dist/Calendar/Calendar.js +82 -43
- package/dist/Calendar/hooks/index.d.ts +0 -2
- package/dist/Calendar/hooks/index.d.ts.map +1 -1
- package/dist/Calendar/hooks/index.js +1 -2
- package/dist/Calendar/index.d.ts +3 -3
- package/dist/Calendar/index.d.ts.map +1 -1
- package/dist/Calendar/index.js +1 -1
- package/dist/Calendar/styles.d.ts +3 -9
- package/dist/Calendar/styles.d.ts.map +1 -1
- package/dist/Calendar/styles.js +3 -11
- package/dist/Carousel/Carousel.d.ts.map +1 -1
- package/dist/Carousel/Carousel.js +17 -3
- package/dist/Center/Center.d.ts +11 -1
- package/dist/Center/Center.d.ts.map +1 -1
- package/dist/Center/Center.js +9 -3
- package/dist/Chat/ChatComposerInput.d.ts.map +1 -1
- package/dist/Chat/ChatComposerInput.js +0 -1
- package/dist/Chat/ChatMessageList.d.ts +14 -1
- package/dist/Chat/ChatMessageList.d.ts.map +1 -1
- package/dist/Chat/ChatMessageList.js +2 -0
- package/dist/Chat/useTriggerMenu.d.ts +8 -1
- package/dist/Chat/useTriggerMenu.d.ts.map +1 -1
- package/dist/Chat/useTriggerMenu.js +15 -3
- package/dist/CheckboxInput/CheckboxInput.d.ts +23 -2
- package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
- package/dist/CheckboxInput/CheckboxInput.js +51 -9
- package/dist/CheckboxList/CheckboxList.d.ts +13 -1
- package/dist/CheckboxList/CheckboxList.d.ts.map +1 -1
- package/dist/CheckboxList/CheckboxList.js +39 -9
- package/dist/CheckboxList/CheckboxListContext.d.ts +7 -0
- package/dist/CheckboxList/CheckboxListContext.d.ts.map +1 -1
- package/dist/CheckboxList/CheckboxListItem.d.ts.map +1 -1
- package/dist/CheckboxList/CheckboxListItem.js +0 -1
- package/dist/Citation/Citation.d.ts.map +1 -1
- package/dist/Citation/Citation.js +9 -5
- package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
- package/dist/CodeBlock/CodeBlock.js +51 -37
- package/dist/CommandPalette/CommandPalette.d.ts.map +1 -1
- package/dist/CommandPalette/CommandPalette.js +5 -5
- package/dist/ContextMenu/ContextMenu.d.ts +4 -5
- package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
- package/dist/ContextMenu/ContextMenu.js +93 -11
- package/dist/DateInput/DateInput.d.ts +23 -1
- package/dist/DateInput/DateInput.d.ts.map +1 -1
- package/dist/DateInput/DateInput.js +167 -99
- package/dist/DateRangeInput/DateRangeInput.d.ts +23 -1
- package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
- package/dist/DateRangeInput/DateRangeInput.js +42 -9
- package/dist/DateTimeInput/DateTimeInput.d.ts +35 -2
- package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
- package/dist/DateTimeInput/DateTimeInput.js +78 -15
- package/dist/Dialog/Dialog.d.ts.map +1 -1
- package/dist/Dialog/Dialog.js +12 -0
- package/dist/DropdownMenu/DropdownMenu.d.ts +1 -8
- package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenu.js +42 -14
- package/dist/Field/Field.d.ts +19 -2
- package/dist/Field/Field.d.ts.map +1 -1
- package/dist/Field/Field.js +4 -0
- package/dist/Field/FieldLabel.d.ts +21 -2
- package/dist/Field/FieldLabel.d.ts.map +1 -1
- package/dist/Field/FieldLabel.js +41 -26
- package/dist/FileInput/FileInput.d.ts +22 -1
- package/dist/FileInput/FileInput.d.ts.map +1 -1
- package/dist/FileInput/FileInput.js +56 -10
- package/dist/Grid/Grid.d.ts +11 -1
- package/dist/Grid/Grid.d.ts.map +1 -1
- package/dist/Grid/Grid.js +32 -6
- package/dist/InputGroup/InputGroup.d.ts +4 -3
- package/dist/InputGroup/InputGroup.d.ts.map +1 -1
- package/dist/InputGroup/InputGroup.js +16 -6
- package/dist/InputGroup/InputGroupContext.d.ts +4 -0
- package/dist/InputGroup/InputGroupContext.d.ts.map +1 -1
- package/dist/InputGroup/InputGroupContext.js +2 -2
- package/dist/InputGroup/groupStyles.d.ts +1 -1
- package/dist/InputGroup/groupStyles.d.ts.map +1 -1
- package/dist/InputGroup/groupStyles.js +3 -3
- package/dist/InputGroup/index.d.ts +1 -0
- package/dist/InputGroup/index.d.ts.map +1 -1
- package/dist/Kbd/Kbd.d.ts.map +1 -1
- package/dist/Kbd/Kbd.js +33 -1
- package/dist/Layer/layerAnimations.stylex.d.ts +4 -0
- package/dist/Layer/layerAnimations.stylex.d.ts.map +1 -1
- package/dist/Layer/layerAnimations.stylex.js +8 -4
- package/dist/Layer/useLayer.d.ts +10 -0
- package/dist/Layer/useLayer.d.ts.map +1 -1
- package/dist/Layer/useLayer.js +64 -9
- package/dist/Layout/Layout.d.ts +4 -11
- package/dist/Layout/Layout.d.ts.map +1 -1
- package/dist/Layout/Layout.js +10 -11
- package/dist/Layout/LayoutFooter.d.ts +2 -2
- package/dist/Layout/LayoutFooter.d.ts.map +1 -1
- package/dist/Layout/LayoutHeader.d.ts +2 -2
- package/dist/Layout/LayoutHeader.d.ts.map +1 -1
- package/dist/Layout/LayoutPanel.d.ts +2 -2
- package/dist/Layout/LayoutPanel.d.ts.map +1 -1
- package/dist/Layout/padding.stylex.d.ts +51 -0
- package/dist/Layout/padding.stylex.d.ts.map +1 -1
- package/dist/Layout/padding.stylex.js +63 -0
- package/dist/Lightbox/Lightbox.d.ts.map +1 -1
- package/dist/Lightbox/Lightbox.js +4 -2
- package/dist/Link/Link.d.ts +16 -1
- package/dist/Link/Link.d.ts.map +1 -1
- package/dist/Link/Link.js +18 -7
- package/dist/Markdown/Markdown.d.ts.map +1 -1
- package/dist/Markdown/Markdown.js +7 -0
- package/dist/MoreMenu/MoreMenu.d.ts +1 -7
- package/dist/MoreMenu/MoreMenu.d.ts.map +1 -1
- package/dist/MoreMenu/MoreMenu.js +0 -2
- package/dist/MultiSelector/MultiSelector.d.ts +27 -2
- package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
- package/dist/MultiSelector/MultiSelector.js +141 -42
- package/dist/MultiSelector/hooks.d.ts +12 -1
- package/dist/MultiSelector/hooks.d.ts.map +1 -1
- package/dist/MultiSelector/hooks.js +14 -1
- package/dist/NavMenu/NavHeadingMenu.d.ts.map +1 -1
- package/dist/NavMenu/NavHeadingMenu.js +34 -3
- package/dist/NumberInput/NumberInput.d.ts +23 -2
- package/dist/NumberInput/NumberInput.d.ts.map +1 -1
- package/dist/NumberInput/NumberInput.js +62 -12
- package/dist/Pagination/Pagination.d.ts +1 -1
- package/dist/Pagination/Pagination.d.ts.map +1 -1
- package/dist/Pagination/Pagination.js +15 -1
- package/dist/Popover/Popover.d.ts +18 -2
- package/dist/Popover/Popover.d.ts.map +1 -1
- package/dist/Popover/Popover.js +5 -2
- package/dist/Popover/usePopover.d.ts +38 -3
- package/dist/Popover/usePopover.d.ts.map +1 -1
- package/dist/Popover/usePopover.js +24 -9
- package/dist/PowerSearch/PowerSearch.d.ts +11 -1
- package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
- package/dist/PowerSearch/PowerSearch.js +6 -1
- package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
- package/dist/ProgressBar/ProgressBar.js +3 -5
- package/dist/RadioList/RadioList.d.ts +20 -2
- package/dist/RadioList/RadioList.d.ts.map +1 -1
- package/dist/RadioList/RadioList.js +105 -9
- package/dist/RadioList/RadioListItem.d.ts.map +1 -1
- package/dist/RadioList/RadioListItem.js +13 -2
- package/dist/SegmentedControl/SegmentedControl.d.ts +13 -2
- package/dist/SegmentedControl/SegmentedControl.d.ts.map +1 -1
- package/dist/SegmentedControl/SegmentedControl.js +78 -44
- package/dist/SegmentedControl/SegmentedControlContext.d.ts +7 -0
- package/dist/SegmentedControl/SegmentedControlContext.d.ts.map +1 -1
- package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
- package/dist/SegmentedControl/SegmentedControlItem.js +13 -2
- package/dist/Selector/Selector.d.ts +24 -1
- package/dist/Selector/Selector.d.ts.map +1 -1
- package/dist/Selector/Selector.js +122 -43
- package/dist/Selector/hooks.d.ts +8 -1
- package/dist/Selector/hooks.d.ts.map +1 -1
- package/dist/Selector/hooks.js +14 -1
- package/dist/SideNav/SideNavHeading.d.ts.map +1 -1
- package/dist/SideNav/SideNavHeading.js +2 -0
- package/dist/SideNav/SideNavItem.d.ts.map +1 -1
- package/dist/SideNav/SideNavItem.js +1 -1
- package/dist/Skeleton/Skeleton.d.ts.map +1 -1
- package/dist/Skeleton/Skeleton.js +8 -2
- package/dist/Slider/Slider.d.ts +21 -0
- package/dist/Slider/Slider.d.ts.map +1 -1
- package/dist/Slider/Slider.js +36 -9
- package/dist/Spinner/Spinner.d.ts.map +1 -1
- package/dist/Spinner/Spinner.js +1 -1
- package/dist/Stack/Stack.d.ts +38 -23
- package/dist/Stack/Stack.d.ts.map +1 -1
- package/dist/Stack/Stack.js +25 -1
- package/dist/Stack/StackItem.d.ts +12 -23
- package/dist/Stack/StackItem.d.ts.map +1 -1
- package/dist/Stack/StackItem.js +8 -1
- package/dist/Switch/Switch.d.ts +23 -2
- package/dist/Switch/Switch.d.ts.map +1 -1
- package/dist/Switch/Switch.js +41 -11
- package/dist/TabList/Tab.d.ts.map +1 -1
- package/dist/TabList/Tab.js +6 -0
- package/dist/TabList/TabList.d.ts +16 -4
- package/dist/TabList/TabList.d.ts.map +1 -1
- package/dist/TabList/TabList.js +87 -10
- package/dist/TabList/TabListContext.d.ts +5 -0
- package/dist/TabList/TabListContext.d.ts.map +1 -1
- package/dist/TabList/TabListContext.js +5 -0
- package/dist/TabList/TabMenu.d.ts.map +1 -1
- package/dist/TabList/TabMenu.js +6 -1
- package/dist/Table/BaseTable.d.ts.map +1 -1
- package/dist/Table/BaseTable.js +11 -8
- package/dist/Table/Table.d.ts.map +1 -1
- package/dist/Table/Table.js +10 -1
- package/dist/Table/TableCell.d.ts +8 -1
- package/dist/Table/TableCell.d.ts.map +1 -1
- package/dist/Table/TableCell.js +10 -10
- package/dist/Table/TableHeaderCell.d.ts +8 -1
- package/dist/Table/TableHeaderCell.d.ts.map +1 -1
- package/dist/Table/TableHeaderCell.js +16 -16
- package/dist/Table/index.d.ts +2 -1
- package/dist/Table/index.d.ts.map +1 -1
- package/dist/Table/index.js +2 -2
- package/dist/Table/plugins/pagination/paginateData.d.ts +2 -1
- package/dist/Table/plugins/pagination/paginateData.d.ts.map +1 -1
- package/dist/Table/plugins/pagination/paginateData.js +8 -3
- package/dist/Table/plugins/pagination/useTablePagination.d.ts.map +1 -1
- package/dist/Table/plugins/pagination/useTablePagination.js +6 -1
- package/dist/Table/plugins/sortable/useTableSortable.d.ts.map +1 -1
- package/dist/Table/plugins/sortable/useTableSortable.js +59 -3
- package/dist/Table/tableContextMenu.d.ts +33 -0
- package/dist/Table/tableContextMenu.d.ts.map +1 -0
- package/dist/Table/tableContextMenu.js +133 -0
- package/dist/Table/types.d.ts +66 -0
- package/dist/Table/types.d.ts.map +1 -1
- package/dist/TextArea/TextArea.d.ts +22 -1
- package/dist/TextArea/TextArea.d.ts.map +1 -1
- package/dist/TextArea/TextArea.js +41 -9
- package/dist/TextInput/TextInput.d.ts +23 -2
- package/dist/TextInput/TextInput.d.ts.map +1 -1
- package/dist/TextInput/TextInput.js +55 -10
- package/dist/TimeInput/TimeInput.d.ts +23 -1
- package/dist/TimeInput/TimeInput.d.ts.map +1 -1
- package/dist/TimeInput/TimeInput.js +147 -76
- package/dist/Toast/Toast.d.ts.map +1 -1
- package/dist/Toast/Toast.js +14 -0
- package/dist/Toast/ToastViewport.d.ts.map +1 -1
- package/dist/Toast/ToastViewport.js +131 -3
- package/dist/Tokenizer/Tokenizer.d.ts +12 -1
- package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
- package/dist/Tokenizer/Tokenizer.js +26 -4
- package/dist/Toolbar/Toolbar.d.ts +3 -3
- package/dist/Toolbar/Toolbar.d.ts.map +1 -1
- package/dist/Toolbar/Toolbar.js +51 -7
- package/dist/Tooltip/useTooltip.d.ts.map +1 -1
- package/dist/Tooltip/useTooltip.js +56 -9
- package/dist/TopNav/TopNavHeading.d.ts +7 -1
- package/dist/TopNav/TopNavHeading.d.ts.map +1 -1
- package/dist/TopNav/TopNavHeading.js +8 -0
- package/dist/TreeList/TreeList.d.ts +1 -1
- package/dist/TreeList/TreeList.d.ts.map +1 -1
- package/dist/TreeList/TreeList.js +72 -2
- package/dist/TreeList/TreeListItem.d.ts +11 -1
- package/dist/TreeList/TreeListItem.d.ts.map +1 -1
- package/dist/TreeList/TreeListItem.js +51 -12
- package/dist/TreeList/treeListItem.markers.stylex.d.ts +16 -0
- package/dist/TreeList/treeListItem.markers.stylex.d.ts.map +1 -0
- package/dist/TreeList/treeListItem.markers.stylex.js +22 -0
- package/dist/Typeahead/BaseTypeahead.d.ts +13 -0
- package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
- package/dist/Typeahead/BaseTypeahead.js +69 -6
- package/dist/Typeahead/Typeahead.d.ts +26 -3
- package/dist/Typeahead/Typeahead.d.ts.map +1 -1
- package/dist/Typeahead/Typeahead.js +65 -29
- package/dist/VisuallyHidden/VisuallyHidden.d.ts +61 -0
- package/dist/VisuallyHidden/VisuallyHidden.d.ts.map +1 -0
- package/dist/VisuallyHidden/VisuallyHidden.js +65 -0
- package/dist/VisuallyHidden/index.d.ts +11 -0
- package/dist/VisuallyHidden/index.d.ts.map +1 -0
- package/dist/VisuallyHidden/index.js +13 -0
- package/dist/astryx.css +18 -0
- package/dist/astryx.umd.js +55 -55
- package/dist/astryx.umd.js.map +4 -4
- package/dist/hooks/focusableSelector.d.ts +22 -0
- package/dist/hooks/focusableSelector.d.ts.map +1 -0
- package/dist/hooks/focusableSelector.js +24 -0
- package/dist/hooks/index.d.ts +12 -2
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +7 -2
- package/dist/hooks/useAnnounce.d.ts +40 -0
- package/dist/hooks/useAnnounce.d.ts.map +1 -0
- package/dist/hooks/useAnnounce.js +142 -0
- package/dist/hooks/useClickableContainer.d.ts +16 -1
- package/dist/hooks/useClickableContainer.d.ts.map +1 -1
- package/dist/hooks/useClickableContainer.js +16 -4
- package/dist/hooks/useEntryAnimation.d.ts.map +1 -1
- package/dist/hooks/useEntryAnimation.js +4 -4
- package/dist/hooks/useFocusTrap.d.ts +18 -0
- package/dist/hooks/useFocusTrap.d.ts.map +1 -1
- package/dist/hooks/useFocusTrap.js +87 -3
- package/dist/hooks/useGridFocus.d.ts +88 -1
- package/dist/hooks/useGridFocus.d.ts.map +1 -1
- package/dist/hooks/useGridFocus.js +261 -62
- package/dist/hooks/useKeyboardHint.d.ts +82 -0
- package/dist/hooks/useKeyboardHint.d.ts.map +1 -0
- package/dist/hooks/useKeyboardHint.js +220 -0
- package/dist/hooks/useListFocus.d.ts +78 -7
- package/dist/hooks/useListFocus.d.ts.map +1 -1
- package/dist/hooks/useListFocus.js +305 -52
- package/dist/hooks/useLongPress.d.ts +21 -0
- package/dist/hooks/useLongPress.d.ts.map +1 -0
- package/dist/hooks/useLongPress.js +89 -0
- package/dist/hooks/useTreeFocus.d.ts +157 -0
- package/dist/hooks/useTreeFocus.d.ts.map +1 -0
- package/dist/hooks/useTreeFocus.js +337 -0
- package/dist/hooks/useTypeahead.d.ts +63 -0
- package/dist/hooks/useTypeahead.d.ts.map +1 -0
- package/dist/hooks/useTypeahead.js +117 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/utils/dateTypes.d.ts +12 -0
- package/dist/utils/dateTypes.d.ts.map +1 -1
- package/dist/utils/dateTypes.js +41 -1
- package/dist/utils/index.d.ts +4 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +3 -1
- package/dist/utils/inputAria.d.ts +29 -0
- package/dist/utils/inputAria.d.ts.map +1 -0
- package/dist/utils/inputAria.js +30 -0
- package/dist/utils/timeParser.d.ts.map +1 -1
- package/dist/utils/timeParser.js +5 -2
- package/docs.mjs +2 -2
- package/package.json +12 -8
- package/src/Avatar/Avatar.test.tsx +37 -0
- package/src/Avatar/Avatar.tsx +10 -5
- package/src/Breadcrumbs/BreadcrumbItem.tsx +25 -7
- package/src/Breadcrumbs/Breadcrumbs.test.tsx +31 -10
- package/src/Button/Button.tsx +3 -16
- package/src/ButtonGroup/ButtonGroup.test.tsx +9 -9
- package/src/ButtonGroup/ButtonGroup.tsx +0 -1
- package/src/Calendar/Calendar.doc.mjs +4 -4
- package/src/Calendar/Calendar.test.tsx +168 -30
- package/src/Calendar/Calendar.tsx +121 -66
- package/src/Calendar/hooks/index.ts +0 -6
- package/src/Calendar/index.ts +1 -3
- package/src/Calendar/styles.ts +4 -9
- package/src/Carousel/Carousel.doc.mjs +7 -1
- package/src/Carousel/Carousel.test.tsx +20 -0
- package/src/Carousel/Carousel.tsx +16 -1
- package/src/Center/Center.doc.mjs +12 -2
- package/src/Center/Center.tsx +28 -3
- package/src/Chat/ChatComposerInput.test.tsx +26 -7
- package/src/Chat/ChatComposerInput.tsx +0 -1
- package/src/Chat/ChatDictationButton.doc.mjs +1 -1
- package/src/Chat/ChatMessageList.doc.mjs +8 -0
- package/src/Chat/ChatMessageList.test.tsx +18 -0
- package/src/Chat/ChatMessageList.tsx +16 -0
- package/src/Chat/useTriggerMenu.tsx +22 -4
- package/src/CheckboxInput/CheckboxInput.doc.mjs +10 -0
- package/src/CheckboxInput/CheckboxInput.test.tsx +172 -27
- package/src/CheckboxInput/CheckboxInput.tsx +78 -6
- package/src/CheckboxList/CheckboxList.doc.mjs +9 -0
- package/src/CheckboxList/CheckboxList.test.tsx +192 -26
- package/src/CheckboxList/CheckboxList.tsx +56 -3
- package/src/CheckboxList/CheckboxListContext.tsx +7 -0
- package/src/CheckboxList/CheckboxListItem.tsx +0 -3
- package/src/Citation/Citation.doc.mjs +10 -0
- package/src/Citation/Citation.test.tsx +121 -0
- package/src/Citation/Citation.tsx +15 -3
- package/src/CodeBlock/CodeBlock.test.tsx +115 -0
- package/src/CodeBlock/CodeBlock.tsx +63 -33
- package/src/CommandPalette/CommandPalette.test.tsx +47 -1
- package/src/CommandPalette/CommandPalette.tsx +9 -21
- package/src/ContextMenu/ContextMenu.doc.mjs +4 -4
- package/src/ContextMenu/ContextMenu.test.tsx +155 -1
- package/src/ContextMenu/ContextMenu.tsx +113 -16
- package/src/DateInput/DateInput.doc.mjs +286 -56
- package/src/DateInput/DateInput.test.tsx +248 -2
- package/src/DateInput/DateInput.tsx +196 -100
- package/src/DateRangeInput/DateRangeInput.doc.mjs +251 -46
- package/src/DateRangeInput/DateRangeInput.test.tsx +182 -20
- package/src/DateRangeInput/DateRangeInput.tsx +63 -5
- package/src/DateTimeInput/DateTimeInput.doc.mjs +368 -67
- package/src/DateTimeInput/DateTimeInput.test.tsx +279 -11
- package/src/DateTimeInput/DateTimeInput.tsx +125 -17
- package/src/Dialog/Dialog.doc.mjs +2 -2
- package/src/Dialog/Dialog.tsx +12 -0
- package/src/DropdownMenu/DropdownMenu.doc.mjs +1 -8
- package/src/DropdownMenu/DropdownMenu.test.tsx +56 -42
- package/src/DropdownMenu/DropdownMenu.tsx +52 -18
- package/src/Field/Field.doc.mjs +59 -18
- package/src/Field/Field.test.tsx +32 -0
- package/src/Field/Field.tsx +23 -5
- package/src/Field/FieldLabel.tsx +55 -17
- package/src/FileInput/FileInput.doc.mjs +17 -0
- package/src/FileInput/FileInput.test.tsx +283 -49
- package/src/FileInput/FileInput.tsx +76 -4
- package/src/Grid/Grid.doc.mjs +17 -4
- package/src/Grid/Grid.test.tsx +43 -30
- package/src/Grid/Grid.tsx +38 -3
- package/src/Icon/Icon.doc.mjs +3 -3
- package/src/InputGroup/InputGroup.doc.mjs +111 -23
- package/src/InputGroup/InputGroup.test.tsx +257 -60
- package/src/InputGroup/InputGroup.tsx +23 -5
- package/src/InputGroup/InputGroupContext.ts +9 -4
- package/src/InputGroup/groupStyles.ts +7 -1
- package/src/InputGroup/index.ts +1 -0
- package/src/Kbd/Kbd.test.tsx +22 -4
- package/src/Kbd/Kbd.tsx +35 -3
- package/src/Layer/layerAnimations.stylex.ts +20 -4
- package/src/Layer/useLayer.doc.mjs +5 -0
- package/src/Layer/useLayer.test.tsx +111 -0
- package/src/Layer/useLayer.tsx +90 -9
- package/src/Layout/Layout.tsx +8 -16
- package/src/Layout/LayoutFooter.doc.mjs +4 -4
- package/src/Layout/LayoutFooter.tsx +3 -3
- package/src/Layout/LayoutHeader.doc.mjs +4 -4
- package/src/Layout/LayoutHeader.tsx +3 -3
- package/src/Layout/LayoutPanel.tsx +3 -3
- package/src/Layout/padding.stylex.ts +52 -0
- package/src/Lightbox/Lightbox.doc.mjs +9 -0
- package/src/Lightbox/Lightbox.test.tsx +53 -5
- package/src/Lightbox/Lightbox.tsx +9 -4
- package/src/Link/Link.doc.mjs +14 -0
- package/src/Link/Link.test.tsx +50 -0
- package/src/Link/Link.tsx +39 -4
- package/src/Markdown/Markdown.test.tsx +11 -0
- package/src/Markdown/Markdown.tsx +7 -0
- package/src/MetadataList/MetadataListItem.doc.mjs +4 -0
- package/src/MoreMenu/MoreMenu.doc.mjs +2 -17
- package/src/MoreMenu/MoreMenu.tsx +0 -9
- package/src/MultiSelector/MultiSelector.doc.mjs +30 -0
- package/src/MultiSelector/MultiSelector.test.tsx +375 -8
- package/src/MultiSelector/MultiSelector.tsx +194 -48
- package/src/MultiSelector/hooks.ts +27 -0
- package/src/NavMenu/NavHeadingMenu.test.tsx +77 -1
- package/src/NavMenu/NavHeadingMenu.tsx +50 -3
- package/src/NumberInput/NumberInput.doc.mjs +17 -0
- package/src/NumberInput/NumberInput.test.tsx +242 -40
- package/src/NumberInput/NumberInput.tsx +89 -11
- package/src/Pagination/Pagination.doc.mjs +3 -3
- package/src/Pagination/Pagination.test.tsx +117 -5
- package/src/Pagination/Pagination.tsx +21 -1
- package/src/Popover/Popover.doc.mjs +26 -0
- package/src/Popover/Popover.test.tsx +121 -0
- package/src/Popover/Popover.tsx +23 -2
- package/src/Popover/usePopover.doc.mjs +9 -3
- package/src/Popover/usePopover.tsx +70 -12
- package/src/PowerSearch/PowerSearch.doc.mjs +15 -0
- package/src/PowerSearch/PowerSearch.test.tsx +97 -1
- package/src/PowerSearch/PowerSearch.tsx +16 -3
- package/src/ProgressBar/ProgressBar.test.tsx +34 -26
- package/src/ProgressBar/ProgressBar.tsx +3 -4
- package/src/RadioList/RadioList.doc.mjs +9 -0
- package/src/RadioList/RadioList.test.tsx +252 -16
- package/src/RadioList/RadioList.tsx +166 -10
- package/src/RadioList/RadioListItem.tsx +14 -2
- package/src/SegmentedControl/SegmentedControl.doc.mjs +9 -0
- package/src/SegmentedControl/SegmentedControl.test.tsx +214 -42
- package/src/SegmentedControl/SegmentedControl.tsx +102 -54
- package/src/SegmentedControl/SegmentedControlContext.ts +7 -0
- package/src/SegmentedControl/SegmentedControlItem.tsx +14 -1
- package/src/SelectableCard/SelectableCard.doc.mjs +1 -1
- package/src/Selector/Selector.doc.mjs +26 -0
- package/src/Selector/Selector.test.tsx +387 -15
- package/src/Selector/Selector.tsx +175 -67
- package/src/Selector/hooks.ts +23 -0
- package/src/SideNav/SideNav.test.tsx +48 -0
- package/src/SideNav/SideNavHeading.tsx +8 -2
- package/src/SideNav/SideNavItem.tsx +2 -4
- package/src/Skeleton/Skeleton.test.tsx +25 -0
- package/src/Skeleton/Skeleton.tsx +11 -1
- package/src/Slider/Slider.doc.mjs +15 -0
- package/src/Slider/Slider.test.tsx +155 -2
- package/src/Slider/Slider.tsx +53 -5
- package/src/Spinner/Spinner.tsx +7 -1
- package/src/Stack/Stack.doc.mjs +77 -0
- package/src/Stack/Stack.test.tsx +55 -5
- package/src/Stack/Stack.tsx +73 -24
- package/src/Stack/StackItem.test.tsx +23 -0
- package/src/Stack/StackItem.tsx +20 -23
- package/src/Switch/Switch.doc.mjs +15 -0
- package/src/Switch/Switch.test.tsx +162 -27
- package/src/Switch/Switch.tsx +61 -21
- package/src/TabList/Tab.tsx +6 -0
- package/src/TabList/TabList.doc.mjs +6 -0
- package/src/TabList/TabList.test.tsx +251 -0
- package/src/TabList/TabList.tsx +103 -12
- package/src/TabList/TabListContext.ts +7 -3
- package/src/TabList/TabMenu.tsx +6 -3
- package/src/Table/BaseTable.tsx +21 -17
- package/src/Table/Table.doc.mjs +2 -0
- package/src/Table/Table.test.tsx +10 -0
- package/src/Table/Table.tsx +8 -0
- package/src/Table/TableCell.tsx +25 -23
- package/src/Table/TableHeaderCell.tsx +28 -29
- package/src/Table/index.ts +3 -2
- package/src/Table/plugins/pagination/paginateData.ts +10 -3
- package/src/Table/plugins/pagination/useTablePagination.test.tsx +16 -0
- package/src/Table/plugins/pagination/useTablePagination.tsx +8 -1
- package/src/Table/plugins/sortable/useTableSortable.test.tsx +91 -2
- package/src/Table/plugins/sortable/useTableSortable.tsx +53 -1
- package/src/Table/tableContextMenu.test.tsx +157 -0
- package/src/Table/tableContextMenu.tsx +142 -0
- package/src/Table/types.ts +74 -0
- package/src/Table/useTableColumnResize.doc.mjs +3 -3
- package/src/Table/useTableStickyColumns.doc.mjs +2 -2
- package/src/Text/Text.doc.mjs +18 -18
- package/src/TextArea/TextArea.doc.mjs +17 -0
- package/src/TextArea/TextArea.test.tsx +167 -35
- package/src/TextArea/TextArea.tsx +62 -15
- package/src/TextInput/TextInput.doc.mjs +17 -0
- package/src/TextInput/TextInput.test.tsx +168 -34
- package/src/TextInput/TextInput.tsx +77 -9
- package/src/Thumbnail/Thumbnail.doc.mjs +4 -4
- package/src/TimeInput/TimeInput.doc.mjs +340 -60
- package/src/TimeInput/TimeInput.test.tsx +255 -7
- package/src/TimeInput/TimeInput.tsx +176 -70
- package/src/Timestamp/Timestamp.doc.mjs +19 -19
- package/src/Toast/Toast.tsx +14 -0
- package/src/Toast/ToastViewport.test.tsx +214 -0
- package/src/Toast/ToastViewport.tsx +167 -21
- package/src/Tokenizer/Tokenizer.doc.mjs +29 -14
- package/src/Tokenizer/Tokenizer.test.tsx +105 -1
- package/src/Tokenizer/Tokenizer.tsx +37 -1
- package/src/Toolbar/Toolbar.test.tsx +109 -6
- package/src/Toolbar/Toolbar.tsx +59 -5
- package/src/Tooltip/Tooltip.test.tsx +68 -0
- package/src/Tooltip/useTooltip.tsx +55 -8
- package/src/TopNav/TopNav.test.tsx +50 -0
- package/src/TopNav/TopNavHeading.tsx +17 -1
- package/src/TreeList/TreeList.test.tsx +295 -6
- package/src/TreeList/TreeList.tsx +73 -1
- package/src/TreeList/TreeListItem.tsx +51 -5
- package/src/TreeList/treeListItem.markers.stylex.ts +20 -0
- package/src/Typeahead/BaseTypeahead.tsx +93 -9
- package/src/Typeahead/Typeahead.doc.mjs +141 -30
- package/src/Typeahead/Typeahead.test.tsx +331 -1
- package/src/Typeahead/Typeahead.tsx +105 -33
- package/src/VisuallyHidden/VisuallyHidden.doc.mjs +80 -0
- package/src/VisuallyHidden/VisuallyHidden.test.tsx +76 -0
- package/src/VisuallyHidden/VisuallyHidden.tsx +113 -0
- package/src/VisuallyHidden/index.ts +15 -0
- package/src/docs-script.test.mjs +50 -0
- package/src/hooks/focusableSelector.ts +25 -0
- package/src/hooks/index.ts +28 -2
- package/src/hooks/useAnnounce.test.tsx +101 -0
- package/src/hooks/useAnnounce.ts +168 -0
- package/src/hooks/useClickableContainer.ts +16 -4
- package/src/hooks/useEntryAnimation.ts +16 -4
- package/src/hooks/useFocusTrap.test.tsx +132 -0
- package/src/hooks/useFocusTrap.ts +98 -4
- package/src/hooks/useGridFocus.doc.mjs +40 -2
- package/src/hooks/useGridFocus.test.tsx +132 -0
- package/src/hooks/useGridFocus.ts +343 -67
- package/src/hooks/useKeyboardHint.doc.mjs +101 -0
- package/src/hooks/useKeyboardHint.test.tsx +70 -0
- package/src/hooks/useKeyboardHint.tsx +332 -0
- package/src/hooks/useListFocus.doc.mjs +50 -10
- package/src/hooks/useListFocus.test.tsx +375 -0
- package/src/hooks/useListFocus.ts +395 -57
- package/src/hooks/useLongPress.test.tsx +134 -0
- package/src/hooks/useLongPress.ts +119 -0
- package/src/hooks/useTreeFocus.doc.mjs +140 -0
- package/src/hooks/useTreeFocus.test.tsx +214 -0
- package/src/hooks/useTreeFocus.ts +559 -0
- package/src/hooks/useTypeahead.test.tsx +109 -0
- package/src/hooks/useTypeahead.ts +183 -0
- package/src/index.ts +1 -0
- package/src/theme/derivedVarRegistry.test.ts +10 -5
- package/src/utils/dateTypes.test.ts +40 -0
- package/src/utils/dateTypes.ts +40 -0
- package/src/utils/index.ts +9 -0
- package/src/utils/inputAria.test.ts +39 -0
- package/src/utils/inputAria.ts +58 -0
- package/src/utils/timeParser.test.ts +10 -0
- package/src/utils/timeParser.ts +5 -2
- package/dist/Calendar/hooks/useCalendarRovingTabindex.d.ts +0 -57
- package/dist/Calendar/hooks/useCalendarRovingTabindex.d.ts.map +0 -1
- package/dist/Calendar/hooks/useCalendarRovingTabindex.js +0 -96
- package/src/Calendar/hooks/useCalendarRovingTabindex.ts +0 -118
|
@@ -61,14 +61,7 @@ export const docs = {
|
|
|
61
61
|
type: 'boolean',
|
|
62
62
|
description: 'Whether to show a chevron icon on the trigger button. Set to false for icon-only triggers.',
|
|
63
63
|
default: 'true',
|
|
64
|
-
},
|
|
65
|
-
{
|
|
66
|
-
name: 'hasAutoFocus',
|
|
67
|
-
type: 'boolean',
|
|
68
|
-
description: 'Whether to auto-focus the first menu item when the menu opens. Set to false for inline showcases or documentation previews.',
|
|
69
|
-
default: 'true',
|
|
70
|
-
},
|
|
71
|
-
{
|
|
64
|
+
}, {
|
|
72
65
|
name: 'children',
|
|
73
66
|
type: '(item: DropdownMenuItemData) => ReactNode',
|
|
74
67
|
description: 'Custom render function for each item in the list.',
|
|
@@ -45,30 +45,43 @@ beforeEach(() => {
|
|
|
45
45
|
describe('DropdownMenu', () => {
|
|
46
46
|
it('renders trigger button with label', () => {
|
|
47
47
|
render(
|
|
48
|
-
<DropdownMenu
|
|
49
|
-
button={{label: 'Actions'}}
|
|
50
|
-
items={[{label: 'Item 1'}]}
|
|
51
|
-
/>,
|
|
48
|
+
<DropdownMenu button={{label: 'Actions'}} items={[{label: 'Item 1'}]} />,
|
|
52
49
|
);
|
|
53
50
|
expect(screen.getByRole('button', {name: /Actions/})).toBeInTheDocument();
|
|
54
51
|
});
|
|
55
52
|
|
|
56
53
|
it('renders menu with role="menu"', () => {
|
|
57
54
|
render(
|
|
58
|
-
<DropdownMenu
|
|
59
|
-
button={{label: 'Actions'}}
|
|
60
|
-
items={[{label: 'Item 1'}]}
|
|
61
|
-
/>,
|
|
55
|
+
<DropdownMenu button={{label: 'Actions'}} items={[{label: 'Item 1'}]} />,
|
|
62
56
|
);
|
|
63
57
|
expect(screen.getByRole('menu', {hidden: true})).toBeInTheDocument();
|
|
64
58
|
});
|
|
65
59
|
|
|
60
|
+
it('names the menu from the trigger label (menus-13)', () => {
|
|
61
|
+
render(
|
|
62
|
+
<DropdownMenu button={{label: 'Actions'}} items={[{label: 'Item 1'}]} />,
|
|
63
|
+
);
|
|
64
|
+
expect(
|
|
65
|
+
screen.getByRole('menu', {name: 'Actions', hidden: true}),
|
|
66
|
+
).toBeInTheDocument();
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('does not wrap the menu in a role="dialog" aria-modal element', () => {
|
|
70
|
+
render(
|
|
71
|
+
<DropdownMenu button={{label: 'Actions'}} items={[{label: 'Item 1'}]} />,
|
|
72
|
+
);
|
|
73
|
+
// The popup exposes its own role="menu"; it must not be nested inside a
|
|
74
|
+
// modal dialog, which would announce an unnamed dialog around the menu
|
|
75
|
+
// while focus stays on the trigger.
|
|
76
|
+
expect(screen.queryByRole('dialog', {hidden: true})).not.toBeInTheDocument();
|
|
77
|
+
expect(
|
|
78
|
+
document.querySelector('[aria-modal="true"]'),
|
|
79
|
+
).not.toBeInTheDocument();
|
|
80
|
+
});
|
|
81
|
+
|
|
66
82
|
it('defaults menu placement below', () => {
|
|
67
83
|
render(
|
|
68
|
-
<DropdownMenu
|
|
69
|
-
button={{label: 'Actions'}}
|
|
70
|
-
items={[{label: 'Item 1'}]}
|
|
71
|
-
/>,
|
|
84
|
+
<DropdownMenu button={{label: 'Actions'}} items={[{label: 'Item 1'}]} />,
|
|
72
85
|
);
|
|
73
86
|
const popover = screen
|
|
74
87
|
.getByRole('menu', {hidden: true})
|
|
@@ -96,10 +109,7 @@ describe('DropdownMenu', () => {
|
|
|
96
109
|
|
|
97
110
|
it('has aria-haspopup and aria-expanded attributes', () => {
|
|
98
111
|
render(
|
|
99
|
-
<DropdownMenu
|
|
100
|
-
button={{label: 'Actions'}}
|
|
101
|
-
items={[{label: 'Item 1'}]}
|
|
102
|
-
/>,
|
|
112
|
+
<DropdownMenu button={{label: 'Actions'}} items={[{label: 'Item 1'}]} />,
|
|
103
113
|
);
|
|
104
114
|
const button = screen.getByRole('button', {name: /Actions/});
|
|
105
115
|
expect(button).toHaveAttribute('aria-haspopup', 'menu');
|
|
@@ -107,16 +117,43 @@ describe('DropdownMenu', () => {
|
|
|
107
117
|
});
|
|
108
118
|
|
|
109
119
|
it('opens menu when button is clicked', async () => {
|
|
120
|
+
const user = userEvent.setup();
|
|
121
|
+
render(
|
|
122
|
+
<DropdownMenu button={{label: 'Actions'}} items={[{label: 'Item 1'}]} />,
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
await user.click(screen.getByRole('button', {name: /Actions/}));
|
|
126
|
+
expect(HTMLElement.prototype.showPopover).toHaveBeenCalled();
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it('closes the menu when Tab is pressed inside it (APG menu-button)', async () => {
|
|
130
|
+
const user = userEvent.setup();
|
|
131
|
+
render(
|
|
132
|
+
<DropdownMenu button={{label: 'Actions'}} items={[{label: 'Item 1'}]} />,
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
await user.click(screen.getByRole('button', {name: /Actions/}));
|
|
136
|
+
expect(HTMLElement.prototype.showPopover).toHaveBeenCalled();
|
|
137
|
+
|
|
138
|
+
const menu = screen.getByRole('menu', {hidden: true});
|
|
139
|
+
fireEvent.keyDown(menu, {key: 'Tab'});
|
|
140
|
+
expect(HTMLElement.prototype.hidePopover).toHaveBeenCalled();
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it('typeahead focuses the item matching the typed character (menus-11)', async () => {
|
|
110
144
|
const user = userEvent.setup();
|
|
111
145
|
render(
|
|
112
146
|
<DropdownMenu
|
|
113
147
|
button={{label: 'Actions'}}
|
|
114
|
-
items={[{label: '
|
|
148
|
+
items={[{label: 'Cut'}, {label: 'Copy'}, {label: 'Delete'}]}
|
|
115
149
|
/>,
|
|
116
150
|
);
|
|
117
|
-
|
|
118
151
|
await user.click(screen.getByRole('button', {name: /Actions/}));
|
|
119
|
-
|
|
152
|
+
const menu = screen.getByRole('menu', {hidden: true});
|
|
153
|
+
fireEvent.keyDown(menu, {key: 'd'});
|
|
154
|
+
expect(
|
|
155
|
+
screen.getByRole('menuitem', {name: 'Delete', hidden: true}),
|
|
156
|
+
).toHaveFocus();
|
|
120
157
|
});
|
|
121
158
|
|
|
122
159
|
it('calls onClick callback when button is clicked', async () => {
|
|
@@ -212,29 +249,6 @@ describe('DropdownMenu controlled mode', () => {
|
|
|
212
249
|
});
|
|
213
250
|
});
|
|
214
251
|
|
|
215
|
-
describe('DropdownMenu hasAutoFocus', () => {
|
|
216
|
-
it('does not focus menu items when hasAutoFocus is false and isMenuOpen is true', () => {
|
|
217
|
-
const focusSpy = vi.spyOn(HTMLElement.prototype, 'focus');
|
|
218
|
-
render(
|
|
219
|
-
<DropdownMenu
|
|
220
|
-
button={{label: 'Actions'}}
|
|
221
|
-
items={[{label: 'Edit'}, {label: 'Delete'}]}
|
|
222
|
-
isMenuOpen={true}
|
|
223
|
-
hasAutoFocus={false}
|
|
224
|
-
onOpenChange={() => {}}
|
|
225
|
-
/>,
|
|
226
|
-
);
|
|
227
|
-
|
|
228
|
-
const menuItems = screen.getAllByRole('menuitem', {hidden: true});
|
|
229
|
-
const menuItemFocusCalls = focusSpy.mock.calls.filter((_, i) => {
|
|
230
|
-
const ctx = focusSpy.mock.contexts[i];
|
|
231
|
-
return menuItems.includes(ctx as HTMLElement);
|
|
232
|
-
});
|
|
233
|
-
expect(menuItemFocusCalls).toHaveLength(0);
|
|
234
|
-
focusSpy.mockRestore();
|
|
235
|
-
});
|
|
236
|
-
});
|
|
237
|
-
|
|
238
252
|
describe('DropdownMenu items', () => {
|
|
239
253
|
it('renders items with labels', () => {
|
|
240
254
|
render(
|
|
@@ -43,6 +43,7 @@ import {
|
|
|
43
43
|
type DropdownMenuContextValue,
|
|
44
44
|
} from './DropdownMenuContext';
|
|
45
45
|
import {useListFocus} from '../hooks/useListFocus';
|
|
46
|
+
import {useTypeahead} from '../hooks/useTypeahead';
|
|
46
47
|
import {layerAnimations} from '../Layer/layerAnimations.stylex';
|
|
47
48
|
import type {LayerPlacement} from '../Layer/useLayer';
|
|
48
49
|
import {
|
|
@@ -134,13 +135,6 @@ interface DropdownMenuBaseProps extends BaseProps {
|
|
|
134
135
|
*/
|
|
135
136
|
placement?: LayerPlacement;
|
|
136
137
|
|
|
137
|
-
/**
|
|
138
|
-
* Whether to auto-focus the first menu item when the menu opens.
|
|
139
|
-
* Set to `false` for inline showcases or documentation previews
|
|
140
|
-
* where stealing focus is undesirable.
|
|
141
|
-
* @default true
|
|
142
|
-
*/
|
|
143
|
-
hasAutoFocus?: boolean;
|
|
144
138
|
'data-testid'?: string;
|
|
145
139
|
}
|
|
146
140
|
|
|
@@ -192,7 +186,6 @@ export function DropdownMenu({
|
|
|
192
186
|
onClick,
|
|
193
187
|
hasChevron = true,
|
|
194
188
|
placement = 'below',
|
|
195
|
-
hasAutoFocus = true,
|
|
196
189
|
className,
|
|
197
190
|
style,
|
|
198
191
|
xstyle,
|
|
@@ -249,6 +242,9 @@ export function DropdownMenu({
|
|
|
249
242
|
hasLightDismiss: true,
|
|
250
243
|
hasCloseButton: false,
|
|
251
244
|
hasAutoFocus: false,
|
|
245
|
+
// The popup's own role="menu" is the exposed semantics; wrapping it in a
|
|
246
|
+
// modal dialog would announce an unnamed dialog around the menu.
|
|
247
|
+
role: 'none',
|
|
252
248
|
});
|
|
253
249
|
|
|
254
250
|
const closeMenu = useCallback(() => {
|
|
@@ -260,27 +256,49 @@ export function DropdownMenu({
|
|
|
260
256
|
listRef,
|
|
261
257
|
handleKeyDown: listNavKeyDown,
|
|
262
258
|
focusFirst,
|
|
259
|
+
focusItem,
|
|
263
260
|
} = useListFocus<HTMLDivElement>({
|
|
264
261
|
itemSelector: '[role="menuitem"]:not([aria-disabled="true"])',
|
|
265
262
|
wrap: false,
|
|
266
263
|
onEscape: closeMenu,
|
|
267
264
|
});
|
|
268
265
|
|
|
266
|
+
// First-character typeahead over the (enabled) menu items — jump to the next
|
|
267
|
+
// item whose label starts with the typed text (menus-11).
|
|
268
|
+
const getMenuItems = useCallback(
|
|
269
|
+
(): HTMLElement[] =>
|
|
270
|
+
listRef.current
|
|
271
|
+
? Array.from(
|
|
272
|
+
listRef.current.querySelectorAll<HTMLElement>(
|
|
273
|
+
'[role="menuitem"]:not([aria-disabled="true"])',
|
|
274
|
+
),
|
|
275
|
+
)
|
|
276
|
+
: [],
|
|
277
|
+
[listRef],
|
|
278
|
+
);
|
|
279
|
+
const typeahead = useTypeahead({
|
|
280
|
+
getItemLabels: () => getMenuItems().map(el => el.textContent),
|
|
281
|
+
onMatch: focusItem,
|
|
282
|
+
getCurrentIndex: () =>
|
|
283
|
+
getMenuItems().findIndex(
|
|
284
|
+
el =>
|
|
285
|
+
el === document.activeElement || el.contains(document.activeElement),
|
|
286
|
+
),
|
|
287
|
+
});
|
|
288
|
+
|
|
269
289
|
// Sync controlled open state → popover, and focus first item on open
|
|
270
290
|
useEffect(() => {
|
|
271
291
|
if (isControlled) {
|
|
272
292
|
if (controlledIsOpen && !popover.isOpen) {
|
|
273
293
|
popover.show();
|
|
274
|
-
|
|
275
|
-
requestAnimationFrame(() => focusFirst());
|
|
276
|
-
}
|
|
294
|
+
requestAnimationFrame(() => focusFirst());
|
|
277
295
|
} else if (!controlledIsOpen && popover.isOpen) {
|
|
278
296
|
popover.hide();
|
|
279
297
|
}
|
|
280
298
|
}
|
|
281
|
-
}, [controlledIsOpen, isControlled, popover,
|
|
299
|
+
}, [controlledIsOpen, isControlled, popover, focusFirst]);
|
|
282
300
|
|
|
283
|
-
// Extend useListFocus with Enter/Space activation
|
|
301
|
+
// Extend useListFocus with Enter/Space activation + typeahead
|
|
284
302
|
const listKeyDown = useCallback(
|
|
285
303
|
(e: React.KeyboardEvent) => {
|
|
286
304
|
if (e.key === 'Enter' || e.key === ' ') {
|
|
@@ -291,17 +309,29 @@ export function DropdownMenu({
|
|
|
291
309
|
}
|
|
292
310
|
return;
|
|
293
311
|
}
|
|
312
|
+
// APG menu-button pattern: Tab closes the menu. Menu items are
|
|
313
|
+
// tabIndex={-1} so the focus trap has nothing trappable and Tab would
|
|
314
|
+
// otherwise leak into the page while the menu stayed open (menus-5).
|
|
315
|
+
// Do NOT preventDefault — closing restores focus to the trigger, and the
|
|
316
|
+
// browser's default Tab then continues from there to the next element.
|
|
317
|
+
if (e.key === 'Tab') {
|
|
318
|
+
closeMenu();
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
// Type-to-focus next; if it consumed a printable key, stop here.
|
|
322
|
+
if (typeahead.onKeyDown(e)) {
|
|
323
|
+
e.preventDefault();
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
294
326
|
listNavKeyDown(e);
|
|
295
327
|
},
|
|
296
|
-
[listNavKeyDown],
|
|
328
|
+
[listNavKeyDown, closeMenu, typeahead],
|
|
297
329
|
);
|
|
298
330
|
|
|
299
331
|
const openAndFocus = useCallback(() => {
|
|
300
332
|
popover.show();
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
}
|
|
304
|
-
}, [popover, hasAutoFocus, focusFirst]);
|
|
333
|
+
requestAnimationFrame(() => focusFirst());
|
|
334
|
+
}, [popover, focusFirst]);
|
|
305
335
|
|
|
306
336
|
const handleButtonClick = useCallback(() => {
|
|
307
337
|
// If the menu was just closed by light dismiss (e.g. iOS Safari fires
|
|
@@ -399,6 +429,10 @@ export function DropdownMenu({
|
|
|
399
429
|
ref={listRef}
|
|
400
430
|
id={menuId}
|
|
401
431
|
role="menu"
|
|
432
|
+
// Give the menu an accessible name from its trigger's label, so
|
|
433
|
+
// screen readers announce e.g. "Actions menu" rather than an unnamed
|
|
434
|
+
// menu (menus-13).
|
|
435
|
+
aria-label={button.label}
|
|
402
436
|
onKeyDown={listKeyDown}
|
|
403
437
|
{...mergeProps(
|
|
404
438
|
themeProps('dropdown-menu'),
|
package/src/Field/Field.doc.mjs
CHANGED
|
@@ -11,7 +11,18 @@ export const docs = {
|
|
|
11
11
|
playground: {
|
|
12
12
|
defaults: {
|
|
13
13
|
label: 'Email address',
|
|
14
|
-
|
|
14
|
+
inputID: 'email-input',
|
|
15
|
+
description: 'Use Field for controls that do not already provide field under the hood.',
|
|
16
|
+
descriptionID: 'email-help',
|
|
17
|
+
children: {
|
|
18
|
+
__element: 'input',
|
|
19
|
+
props: {
|
|
20
|
+
id: 'email-input',
|
|
21
|
+
'aria-describedby': 'email-help',
|
|
22
|
+
placeholder: 'you@example.com',
|
|
23
|
+
type: 'email',
|
|
24
|
+
},
|
|
25
|
+
},
|
|
15
26
|
},
|
|
16
27
|
},
|
|
17
28
|
theming: {
|
|
@@ -27,7 +38,7 @@ export const docs = {
|
|
|
27
38
|
{property: 'borderRadius', vars: ['--_field-radius']},
|
|
28
39
|
],
|
|
29
40
|
},
|
|
30
|
-
description: '
|
|
41
|
+
description: 'Low-level form field wrapper for custom controls that need a label, description, and optional/required indicators.',
|
|
31
42
|
props: [
|
|
32
43
|
{
|
|
33
44
|
name: 'label',
|
|
@@ -133,36 +144,65 @@ export const docs = {
|
|
|
133
144
|
{name: 'FieldStatus'},
|
|
134
145
|
],
|
|
135
146
|
usage: {
|
|
136
|
-
description: 'Field
|
|
147
|
+
description: 'Field is a low-level wrapper for custom, native, or third-party controls that do not already provide field label, description, and status UI. Use it when you need the Field shell around a control you own; use styled Astryx inputs like TextInput, Typeahead, and Select directly when they already expose label, description, and validation props.',
|
|
137
148
|
bestPractices: [
|
|
149
|
+
{ guidance: true, description: 'Wrap custom controls, native inputs, or third-party widgets that need labeling, helper text, optional/required indicators, or validation status.' },
|
|
138
150
|
{ guidance: true, description: 'Always provide a label for accessibility, even if visually hidden with isLabelHidden.' },
|
|
139
|
-
{ guidance: true, description: 'Use
|
|
140
|
-
{ guidance:
|
|
151
|
+
{ guidance: true, description: 'Use inputID and descriptionID to connect the label and description to the inner control with htmlFor and aria-describedby.' },
|
|
152
|
+
{ guidance: false, description: 'Nest Field around styled inputs such as TextInput, Typeahead, Select, DateInput, or TextArea; those components already render their own Field shell.' },
|
|
153
|
+
{ guidance: false, description: 'Use the attached status variant on non-bordered controls such as sliders, switches, or checkboxes; use detached so the message does not overlap the control.' },
|
|
141
154
|
{ guidance: false, description: 'Set both isOptional and isRequired on the same field.' },
|
|
142
|
-
{ guidance: false, description: 'Use the detached status variant on bordered inputs; reserve it for checkboxes, switches, and sliders.' },
|
|
143
155
|
{ guidance: false, description: 'Hide the label without providing an alternative way for the user to understand the field purpose.' },
|
|
144
156
|
],
|
|
145
157
|
anatomy: [
|
|
146
158
|
{name: 'Label', required: true, description: 'Text identifying the field. Always rendered for accessibility, optionally hidden visually.'},
|
|
147
159
|
{name: 'Description', required: false, description: 'Helper text between the label and input explaining what to enter.'},
|
|
148
|
-
{name: '
|
|
160
|
+
{name: 'Control slot', required: true, description: 'A custom, native, or third-party control that does not already render a field shell.'},
|
|
149
161
|
{name: 'Status message', required: false, description: 'Inline validation feedback showing error, warning, or success with a message.'},
|
|
150
162
|
{name: 'Optional/Required indicator', required: false, description: 'Badge next to the label showing whether the field is optional or required.'},
|
|
151
163
|
{name: 'Label tooltip', required: false, description: 'Info icon at the end of the label with a tooltip explaining the field.'},
|
|
152
164
|
],
|
|
153
165
|
},
|
|
166
|
+
examples: [
|
|
167
|
+
{
|
|
168
|
+
label: 'Wrap a custom control',
|
|
169
|
+
code: `
|
|
170
|
+
function CustomSliderField() {
|
|
171
|
+
return (
|
|
172
|
+
<Field
|
|
173
|
+
label="Confidence"
|
|
174
|
+
inputID="confidence-slider"
|
|
175
|
+
description="Choose how strict the review should be."
|
|
176
|
+
descriptionID="confidence-help"
|
|
177
|
+
status={{type: 'success', message: 'Recommended default'}}
|
|
178
|
+
statusVariant="detached">
|
|
179
|
+
<input
|
|
180
|
+
id="confidence-slider"
|
|
181
|
+
type="range"
|
|
182
|
+
min={0}
|
|
183
|
+
max={100}
|
|
184
|
+
defaultValue={60}
|
|
185
|
+
aria-describedby="confidence-help"
|
|
186
|
+
/>
|
|
187
|
+
</Field>
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
`,
|
|
191
|
+
},
|
|
192
|
+
],
|
|
154
193
|
};
|
|
155
194
|
|
|
156
195
|
/** @type {import('../docs-types').TranslationDoc} */
|
|
157
196
|
export const docsZh = {
|
|
158
197
|
usage: {
|
|
159
|
-
description: 'Field
|
|
198
|
+
description: 'Field is a low-level wrapper for custom, native, or third-party controls that do not already provide field label, description, and status UI. Use it when you need the Field shell around a control you own; use styled Astryx inputs like TextInput, Typeahead, and Select directly when they already expose label, description, and validation props.',
|
|
160
199
|
bestPractices: [
|
|
200
|
+
{ guidance: true, description: 'Wrap custom controls, native inputs, or third-party widgets that need labeling, helper text, optional/required indicators, or validation status.' },
|
|
161
201
|
{ guidance: true, description: 'Always provide a label for accessibility, even if visually hidden with isLabelHidden.' },
|
|
162
|
-
{ guidance: true, description: 'Use
|
|
163
|
-
{ guidance:
|
|
202
|
+
{ guidance: true, description: 'Use inputID and descriptionID to connect the label and description to the inner control with htmlFor and aria-describedby.' },
|
|
203
|
+
{ guidance: false, description: 'Nest Field around styled inputs such as TextInput, Typeahead, Select, DateInput, or TextArea; those components already render their own Field shell.' },
|
|
204
|
+
{ guidance: false, description: 'Use the attached status variant on non-bordered controls such as sliders, switches, or checkboxes; use detached so the message does not overlap the control.' },
|
|
164
205
|
{ guidance: false, description: 'Set both isOptional and isRequired on the same field.' },
|
|
165
|
-
{ guidance: false, description: 'Use the detached status variant on bordered inputs; reserve it for checkboxes, switches, and sliders.' },
|
|
166
206
|
{ guidance: false, description: 'Hide the label without providing an alternative way for the user to understand the field purpose.' },
|
|
167
207
|
],
|
|
168
208
|
},
|
|
@@ -171,16 +211,17 @@ export const docsZh = {
|
|
|
171
211
|
/** @type {import('../docs-types').TranslationDoc} */
|
|
172
212
|
export const docsDense = {
|
|
173
213
|
description:
|
|
174
|
-
'
|
|
214
|
+
'Low-level field shell for custom controls needing label/description/status.',
|
|
175
215
|
usage: {
|
|
176
|
-
description: 'Field wraps
|
|
216
|
+
description: 'Field wraps custom/native/third-party controls lacking field UI. Use TextInput, Typeahead, Select, DateInput, or TextArea directly when they already expose label/description/status props.',
|
|
177
217
|
bestPractices: [
|
|
178
|
-
{ guidance: true, description: '
|
|
179
|
-
{ guidance: true, description: '
|
|
180
|
-
{ guidance: true, description: '
|
|
218
|
+
{ guidance: true, description: 'Wrap custom controls/widgets that need labeling, helper text, optional/required indicators, or validation status.' },
|
|
219
|
+
{ guidance: true, description: 'Always provide a label; visually hide it only when context is clear.' },
|
|
220
|
+
{ guidance: true, description: 'Wire inputID/descriptionID to htmlFor and aria-describedby on the inner control.' },
|
|
221
|
+
{ guidance: false, description: 'Nest Field around styled inputs; it double-renders labels and status UI.' },
|
|
222
|
+
{ guidance: false, description: 'Use attached status on sliders/switches/checkboxes; use detached so messages do not overlap.' },
|
|
181
223
|
{ guidance: false, description: 'Set both isOptional and isRequired on the same field.' },
|
|
182
|
-
{ guidance: false, description: '
|
|
183
|
-
{ guidance: false, description: 'Hide the label without providing an alternative way for user to understand the field purpose.' },
|
|
224
|
+
{ guidance: false, description: 'Hide the label without another way to understand field purpose.' },
|
|
184
225
|
],
|
|
185
226
|
},
|
|
186
227
|
};
|
package/src/Field/Field.test.tsx
CHANGED
|
@@ -93,6 +93,38 @@ describe('Field', () => {
|
|
|
93
93
|
expect(label).toBeVisible();
|
|
94
94
|
});
|
|
95
95
|
|
|
96
|
+
it('renders a single-control label as a <label> with htmlFor', () => {
|
|
97
|
+
render(
|
|
98
|
+
<Field label="Email" inputID="email-input">
|
|
99
|
+
<input id="email-input" />
|
|
100
|
+
</Field>,
|
|
101
|
+
);
|
|
102
|
+
const labelEl = screen.getByText('Email');
|
|
103
|
+
expect(labelEl.tagName).toBe('LABEL');
|
|
104
|
+
expect(labelEl).toHaveAttribute('for', 'email-input');
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('renders a group label as a <span> (not <label>) with no htmlFor', () => {
|
|
108
|
+
render(
|
|
109
|
+
<Field
|
|
110
|
+
label="Plan"
|
|
111
|
+
inputID="plan-group"
|
|
112
|
+
labelID="plan-label"
|
|
113
|
+
isGroupLabel>
|
|
114
|
+
<div role="radiogroup" aria-labelledby="plan-label" />
|
|
115
|
+
</Field>,
|
|
116
|
+
);
|
|
117
|
+
const labelEl = screen.getByText('Plan');
|
|
118
|
+
// A group's accessible-name element must not be a literal <label>.
|
|
119
|
+
expect(labelEl.tagName).toBe('SPAN');
|
|
120
|
+
expect(labelEl.closest('label')).toBeNull();
|
|
121
|
+
expect(labelEl).not.toHaveAttribute('for');
|
|
122
|
+
// labelID is applied to the label element and referenced by the group.
|
|
123
|
+
expect(labelEl).toHaveAttribute('id', 'plan-label');
|
|
124
|
+
const group = screen.getByRole('radiogroup', {name: 'Plan'});
|
|
125
|
+
expect(group.getAttribute('aria-labelledby')).toBe(labelEl.id);
|
|
126
|
+
});
|
|
127
|
+
|
|
96
128
|
it('forwards ref correctly', () => {
|
|
97
129
|
const ref = vi.fn();
|
|
98
130
|
render(
|
package/src/Field/Field.tsx
CHANGED
|
@@ -99,9 +99,26 @@ export interface FieldProps extends Omit<
|
|
|
99
99
|
*/
|
|
100
100
|
description?: string;
|
|
101
101
|
/**
|
|
102
|
-
* ID
|
|
102
|
+
* ID of the input element this label points AT (used as the label's
|
|
103
|
+
* `htmlFor`). This is the id of the *control*, not of the label element —
|
|
104
|
+
* see `labelID` for the latter.
|
|
103
105
|
*/
|
|
104
106
|
inputID: string;
|
|
107
|
+
/**
|
|
108
|
+
* The `id` applied TO the label element itself (distinct from `inputID`,
|
|
109
|
+
* which is the control the label points at). A grouping control
|
|
110
|
+
* (radiogroup, checkbox group) references this via `aria-labelledby` to take
|
|
111
|
+
* the label as its accessible name. Pair with `isGroupLabel`.
|
|
112
|
+
*/
|
|
113
|
+
labelID?: string;
|
|
114
|
+
/**
|
|
115
|
+
* When the field wraps a group of controls rather than a single input, set
|
|
116
|
+
* this so the label renders as a non-`<label>` element (a `<span>`): a
|
|
117
|
+
* `<label>` semantically names one control and can't be associated with a
|
|
118
|
+
* group. Pair with `labelID` + `aria-labelledby` on the group.
|
|
119
|
+
* @default false
|
|
120
|
+
*/
|
|
121
|
+
isGroupLabel?: boolean;
|
|
105
122
|
/**
|
|
106
123
|
* ID for the description element (use for aria-describedby on the input).
|
|
107
124
|
*/
|
|
@@ -172,6 +189,8 @@ export function Field({
|
|
|
172
189
|
isLabelHidden = false,
|
|
173
190
|
description,
|
|
174
191
|
inputID,
|
|
192
|
+
labelID,
|
|
193
|
+
isGroupLabel = false,
|
|
175
194
|
descriptionID,
|
|
176
195
|
isOptional = false,
|
|
177
196
|
isRequired = false,
|
|
@@ -206,6 +225,8 @@ export function Field({
|
|
|
206
225
|
<FieldLabel
|
|
207
226
|
label={label}
|
|
208
227
|
inputID={inputID}
|
|
228
|
+
labelID={labelID}
|
|
229
|
+
isGroupLabel={isGroupLabel}
|
|
209
230
|
isLabelHidden={isLabelHidden}
|
|
210
231
|
isDisabled={isDisabled}
|
|
211
232
|
isOptional={isOptional}
|
|
@@ -245,10 +266,7 @@ export function Field({
|
|
|
245
266
|
<div {...stylex.props(styles.horizontalLabelAlign)}>{labelNode}</div>
|
|
246
267
|
<div {...stylex.props(styles.inputStatusWrapper)}>
|
|
247
268
|
{description && (
|
|
248
|
-
<Text
|
|
249
|
-
type="supporting"
|
|
250
|
-
display="block"
|
|
251
|
-
id={resolvedDescriptionID}>
|
|
269
|
+
<Text type="supporting" display="block" id={resolvedDescriptionID}>
|
|
252
270
|
{description}
|
|
253
271
|
</Text>
|
|
254
272
|
)}
|
package/src/Field/FieldLabel.tsx
CHANGED
|
@@ -35,6 +35,7 @@ const styles = stylex.create({
|
|
|
35
35
|
gap: spacingVars['--spacing-1'],
|
|
36
36
|
fontFamily: typographyVars['--font-family-body'],
|
|
37
37
|
fontSize: typeScaleVars['--text-label-size'],
|
|
38
|
+
lineHeight: typeScaleVars['--text-label-leading'],
|
|
38
39
|
fontWeight: fontWeightVars['--font-weight-medium'],
|
|
39
40
|
color: colorVars['--color-text-secondary'],
|
|
40
41
|
cursor: 'pointer',
|
|
@@ -61,6 +62,7 @@ const styles = stylex.create({
|
|
|
61
62
|
optionalRequired: {
|
|
62
63
|
fontWeight: fontWeightVars['--font-weight-normal'],
|
|
63
64
|
fontSize: typeScaleVars['--text-supporting-size'],
|
|
65
|
+
lineHeight: typeScaleVars['--text-supporting-leading'],
|
|
64
66
|
color: colorVars['--color-text-secondary'],
|
|
65
67
|
},
|
|
66
68
|
description: {
|
|
@@ -80,9 +82,28 @@ export interface FieldLabelProps extends BaseProps<HTMLLabelElement> {
|
|
|
80
82
|
*/
|
|
81
83
|
label: string;
|
|
82
84
|
/**
|
|
83
|
-
* ID of the input element this label
|
|
85
|
+
* ID of the input element this label points AT (rendered as `htmlFor` on the
|
|
86
|
+
* label). This is *not* the id of the label element itself — see
|
|
87
|
+
* `labelID` for that.
|
|
84
88
|
*/
|
|
85
89
|
inputID: string;
|
|
90
|
+
/**
|
|
91
|
+
* The `id` applied TO the label element itself (not the element it points
|
|
92
|
+
* at — that's `inputID`). A grouping control (e.g. `role="radiogroup"`) can
|
|
93
|
+
* reference this via `aria-labelledby` to take the label as its accessible
|
|
94
|
+
* name.
|
|
95
|
+
*/
|
|
96
|
+
labelID?: string;
|
|
97
|
+
/**
|
|
98
|
+
* When true, the field wraps a *group* of controls (e.g. a radiogroup)
|
|
99
|
+
* rather than a single input. In that case the label is rendered as a
|
|
100
|
+
* `<span>` instead of a `<label>` — a `<label>` semantically names one form
|
|
101
|
+
* control and can't be associated with a group, so it must not be a literal
|
|
102
|
+
* label element. The group takes the label as its name via
|
|
103
|
+
* `labelID` + `aria-labelledby`.
|
|
104
|
+
* @default false
|
|
105
|
+
*/
|
|
106
|
+
isGroupLabel?: boolean;
|
|
86
107
|
/**
|
|
87
108
|
* Whether to visually hide the label and description (still accessible
|
|
88
109
|
* to screen readers). When hidden, the entire label group is rendered
|
|
@@ -140,6 +161,8 @@ export interface FieldLabelProps extends BaseProps<HTMLLabelElement> {
|
|
|
140
161
|
export function FieldLabel({
|
|
141
162
|
label,
|
|
142
163
|
inputID,
|
|
164
|
+
labelID,
|
|
165
|
+
isGroupLabel = false,
|
|
143
166
|
isLabelHidden = false,
|
|
144
167
|
isDisabled = false,
|
|
145
168
|
isOptional = false,
|
|
@@ -152,11 +175,38 @@ export function FieldLabel({
|
|
|
152
175
|
}: FieldLabelProps) {
|
|
153
176
|
const statusText = isOptional ? 'Optional' : isRequired ? 'Required' : null;
|
|
154
177
|
|
|
178
|
+
// A group label (e.g. for a radiogroup) must not be a literal `<label>`
|
|
179
|
+
// element: a `<label>` semantically names a single form control and can't be
|
|
180
|
+
// associated with a group. Render it as a `<span>` instead, keeping all the
|
|
181
|
+
// label styling and slots. The group references it via `aria-labelledby`.
|
|
182
|
+
const LabelElement = isGroupLabel ? 'span' : 'label';
|
|
183
|
+
|
|
184
|
+
const labelContent = (
|
|
185
|
+
<>
|
|
186
|
+
{labelIcon && renderIconSlot(labelIcon, {size: 'sm', color: 'inherit'})}
|
|
187
|
+
{label}
|
|
188
|
+
{statusText && (
|
|
189
|
+
<span {...stylex.props(styles.optionalRequired)}>
|
|
190
|
+
<span aria-hidden="true"> ∙ </span>
|
|
191
|
+
{statusText}
|
|
192
|
+
</span>
|
|
193
|
+
)}
|
|
194
|
+
{labelTooltip && (
|
|
195
|
+
<Tooltip content={labelTooltip} placement="above">
|
|
196
|
+
<Icon icon="info" size="sm" color="inherit" />
|
|
197
|
+
</Tooltip>
|
|
198
|
+
)}
|
|
199
|
+
</>
|
|
200
|
+
);
|
|
201
|
+
|
|
155
202
|
return (
|
|
156
203
|
<>
|
|
157
|
-
<
|
|
204
|
+
<LabelElement
|
|
158
205
|
ref={ref}
|
|
159
|
-
|
|
206
|
+
id={labelID}
|
|
207
|
+
// `htmlFor` only applies to a real `<label>` associating with a single
|
|
208
|
+
// control; a group label (span) has no `htmlFor`.
|
|
209
|
+
htmlFor={isGroupLabel ? undefined : inputID}
|
|
160
210
|
{...mergeProps(
|
|
161
211
|
themeProps('field-label'),
|
|
162
212
|
stylex.props(
|
|
@@ -165,20 +215,8 @@ export function FieldLabel({
|
|
|
165
215
|
isLabelHidden && styles.srOnly,
|
|
166
216
|
),
|
|
167
217
|
)}>
|
|
168
|
-
{
|
|
169
|
-
|
|
170
|
-
{statusText && (
|
|
171
|
-
<span {...stylex.props(styles.optionalRequired)}>
|
|
172
|
-
<span aria-hidden="true"> ∙ </span>
|
|
173
|
-
{statusText}
|
|
174
|
-
</span>
|
|
175
|
-
)}
|
|
176
|
-
{labelTooltip && (
|
|
177
|
-
<Tooltip content={labelTooltip} placement="above">
|
|
178
|
-
<Icon icon="info" size="sm" color="inherit" />
|
|
179
|
-
</Tooltip>
|
|
180
|
-
)}
|
|
181
|
-
</label>
|
|
218
|
+
{labelContent}
|
|
219
|
+
</LabelElement>
|
|
182
220
|
{description && (
|
|
183
221
|
<span
|
|
184
222
|
id={descriptionID}
|