@primitiv-ui/react 0.1.0
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/README.md +79 -0
- package/package.json +59 -0
- package/src/AccessibleIcon/AccessibleIcon.tsx +40 -0
- package/src/AccessibleIcon/README.md +42 -0
- package/src/AccessibleIcon/__tests__/AccessibleIcon.test.tsx +47 -0
- package/src/AccessibleIcon/index.ts +2 -0
- package/src/AccessibleIcon/types.ts +8 -0
- package/src/Accordion/Accordion.tsx +412 -0
- package/src/Accordion/AccordionContext.ts +12 -0
- package/src/Accordion/README.md +202 -0
- package/src/Accordion/__tests__/Accordion.asChild.test.tsx +237 -0
- package/src/Accordion/__tests__/Accordion.basic-rendering.test.tsx +333 -0
- package/src/Accordion/__tests__/Accordion.controlled-state.test.tsx +175 -0
- package/src/Accordion/__tests__/Accordion.data-attributes.test.tsx +272 -0
- package/src/Accordion/__tests__/Accordion.disabled-items.test.tsx +311 -0
- package/src/Accordion/__tests__/Accordion.error-handling.test.tsx +119 -0
- package/src/Accordion/__tests__/Accordion.forceMount.test.tsx +119 -0
- package/src/Accordion/__tests__/Accordion.keyboard-interaction.test.tsx +736 -0
- package/src/Accordion/__tests__/Accordion.mouse-interaction.test.tsx +212 -0
- package/src/Accordion/__tests__/Accordion.multiple-mode.test.tsx +90 -0
- package/src/Accordion/__tests__/Accordion.reading-direction.test.tsx +139 -0
- package/src/Accordion/__tests__/Accordion.uncontrolled-state.test.tsx +154 -0
- package/src/Accordion/hooks/index.ts +6 -0
- package/src/Accordion/hooks/useAccordionContext.ts +1 -0
- package/src/Accordion/hooks/useAccordionHeaderContext.ts +10 -0
- package/src/Accordion/hooks/useAccordionItem.ts +22 -0
- package/src/Accordion/hooks/useAccordionItemContext.ts +1 -0
- package/src/Accordion/hooks/useAccordionRoot.ts +151 -0
- package/src/Accordion/hooks/useAccordionTrigger.ts +90 -0
- package/src/Accordion/index.ts +1 -0
- package/src/Accordion/types.ts +81 -0
- package/src/Alert/Alert.tsx +43 -0
- package/src/Alert/README.md +54 -0
- package/src/Alert/__tests__/Alert.test.tsx +28 -0
- package/src/Alert/index.ts +2 -0
- package/src/Alert/types.ts +5 -0
- package/src/Avatar/Avatar.tsx +149 -0
- package/src/Avatar/AvatarContext.ts +20 -0
- package/src/Avatar/README.md +116 -0
- package/src/Avatar/__tests__/Avatar.asChild.test.tsx +53 -0
- package/src/Avatar/__tests__/Avatar.basic-rendering.test.tsx +14 -0
- package/src/Avatar/__tests__/Avatar.error-handling.test.tsx +30 -0
- package/src/Avatar/__tests__/Avatar.fallback.test.tsx +75 -0
- package/src/Avatar/__tests__/Avatar.image-loading.test.tsx +81 -0
- package/src/Avatar/hooks/index.ts +2 -0
- package/src/Avatar/hooks/useAvatarContext.ts +1 -0
- package/src/Avatar/hooks/useAvatarImage.ts +40 -0
- package/src/Avatar/index.ts +3 -0
- package/src/Avatar/types.ts +44 -0
- package/src/Breadcrumb/Breadcrumb.tsx +234 -0
- package/src/Breadcrumb/README.md +111 -0
- package/src/Breadcrumb/__tests__/Breadcrumb.asChild.test.tsx +33 -0
- package/src/Breadcrumb/__tests__/Breadcrumb.basic-rendering.test.tsx +132 -0
- package/src/Breadcrumb/index.ts +2 -0
- package/src/Breadcrumb/types.ts +22 -0
- package/src/Button/Button.tsx +95 -0
- package/src/Button/README.md +112 -0
- package/src/Button/__tests__/Button.asChild.test.tsx +91 -0
- package/src/Button/__tests__/Button.basic-rendering.test.tsx +126 -0
- package/src/Button/__tests__/Button.contract.test.tsx +72 -0
- package/src/Button/__tests__/Button.disabled.test.tsx +52 -0
- package/src/Button/__tests__/Button.icon-usage.test.tsx +57 -0
- package/src/Button/__tests__/Button.keyboard-interaction.test.tsx +70 -0
- package/src/Button/index.ts +2 -0
- package/src/Button/types.ts +8 -0
- package/src/Carousel/Carousel.tsx +708 -0
- package/src/Carousel/CarouselContext.ts +11 -0
- package/src/Carousel/README.md +848 -0
- package/src/Carousel/__tests__/Carousel.asChild.test.tsx +178 -0
- package/src/Carousel/__tests__/Carousel.auto-play.test.tsx +617 -0
- package/src/Carousel/__tests__/Carousel.basic-rendering.test.tsx +569 -0
- package/src/Carousel/__tests__/Carousel.controlled-state.test.tsx +137 -0
- package/src/Carousel/__tests__/Carousel.error-handling.test.tsx +81 -0
- package/src/Carousel/__tests__/Carousel.ids.test.tsx +111 -0
- package/src/Carousel/__tests__/Carousel.imperative-api.test.tsx +213 -0
- package/src/Carousel/__tests__/Carousel.indicators.test.tsx +560 -0
- package/src/Carousel/__tests__/Carousel.intersection-observer.test.tsx +276 -0
- package/src/Carousel/__tests__/Carousel.keyboard-navigation.test.tsx +158 -0
- package/src/Carousel/__tests__/Carousel.play-pause.test.tsx +232 -0
- package/src/Carousel/__tests__/Carousel.prev-next.test.tsx +68 -0
- package/src/Carousel/__tests__/Carousel.reduced-motion.test.tsx +49 -0
- package/src/Carousel/__tests__/Carousel.refresh-progress.test.tsx +87 -0
- package/src/Carousel/__tests__/Carousel.scroll-snap-change.test.tsx +179 -0
- package/src/Carousel/__tests__/Carousel.scroll-sync.test.tsx +109 -0
- package/src/Carousel/__tests__/Carousel.slides-per-move.test.tsx +151 -0
- package/src/Carousel/__tests__/Carousel.slides-per-page.test.tsx +183 -0
- package/src/Carousel/__tests__/Carousel.touch-interaction.test.tsx +96 -0
- package/src/Carousel/__tests__/Carousel.transition-modes.test.tsx +70 -0
- package/src/Carousel/__tests__/Carousel.translations.test.tsx +157 -0
- package/src/Carousel/__tests__/Carousel.uncontrolled-state.test.tsx +146 -0
- package/src/Carousel/hooks/index.ts +4 -0
- package/src/Carousel/hooks/useCarouselContext.ts +13 -0
- package/src/Carousel/hooks/useCarouselRoot.ts +450 -0
- package/src/Carousel/hooks/useCarouselSlide.ts +45 -0
- package/src/Carousel/hooks/useCarouselViewport.ts +290 -0
- package/src/Carousel/index.ts +3 -0
- package/src/Carousel/types.ts +400 -0
- package/src/Checkbox/Checkbox.tsx +228 -0
- package/src/Checkbox/CheckboxContext.ts +12 -0
- package/src/Checkbox/README.md +156 -0
- package/src/Checkbox/__tests__/Checkbox.asChild.test.tsx +69 -0
- package/src/Checkbox/__tests__/Checkbox.basic-rendering.test.tsx +41 -0
- package/src/Checkbox/__tests__/Checkbox.controlled-state.test.tsx +82 -0
- package/src/Checkbox/__tests__/Checkbox.disabled.test.tsx +15 -0
- package/src/Checkbox/__tests__/Checkbox.indeterminate.test.tsx +82 -0
- package/src/Checkbox/__tests__/Checkbox.indicator.test.tsx +117 -0
- package/src/Checkbox/__tests__/Checkbox.uncontrolled-state.test.tsx +89 -0
- package/src/Checkbox/hooks/index.ts +2 -0
- package/src/Checkbox/hooks/useCheckboxContext.ts +1 -0
- package/src/Checkbox/hooks/useCheckboxRoot.ts +32 -0
- package/src/Checkbox/index.ts +1 -0
- package/src/Checkbox/types.ts +33 -0
- package/src/CheckboxCard/CheckboxCard.tsx +208 -0
- package/src/CheckboxCard/CheckboxCardContext.ts +12 -0
- package/src/CheckboxCard/README.md +114 -0
- package/src/CheckboxCard/__tests__/CheckboxCard.asChild.test.tsx +54 -0
- package/src/CheckboxCard/__tests__/CheckboxCard.basic-rendering.test.tsx +58 -0
- package/src/CheckboxCard/__tests__/CheckboxCard.controlled-state.test.tsx +77 -0
- package/src/CheckboxCard/__tests__/CheckboxCard.disabled.test.tsx +55 -0
- package/src/CheckboxCard/__tests__/CheckboxCard.error-handling.test.tsx +20 -0
- package/src/CheckboxCard/__tests__/CheckboxCard.indeterminate.test.tsx +60 -0
- package/src/CheckboxCard/__tests__/CheckboxCard.indicator.test.tsx +136 -0
- package/src/CheckboxCard/__tests__/CheckboxCard.uncontrolled-state.test.tsx +73 -0
- package/src/CheckboxCard/hooks/index.ts +2 -0
- package/src/CheckboxCard/hooks/useCheckboxCardContext.ts +1 -0
- package/src/CheckboxCard/hooks/useCheckboxCardRoot.ts +30 -0
- package/src/CheckboxCard/index.ts +3 -0
- package/src/CheckboxCard/types.ts +33 -0
- package/src/Collapsible/Collapsible.tsx +316 -0
- package/src/Collapsible/CollapsibleContext.ts +7 -0
- package/src/Collapsible/README.md +174 -0
- package/src/Collapsible/__tests__/Collapsible.asChild.test.tsx +240 -0
- package/src/Collapsible/__tests__/Collapsible.basic-rendering.test.tsx +118 -0
- package/src/Collapsible/__tests__/Collapsible.controlled-state.test.tsx +134 -0
- package/src/Collapsible/__tests__/Collapsible.disabled.test.tsx +132 -0
- package/src/Collapsible/__tests__/Collapsible.error-handling.test.tsx +40 -0
- package/src/Collapsible/__tests__/Collapsible.forceMount.test.tsx +111 -0
- package/src/Collapsible/__tests__/Collapsible.triggerIcon.test.tsx +93 -0
- package/src/Collapsible/__tests__/Collapsible.uncontrolled-state.test.tsx +125 -0
- package/src/Collapsible/hooks/index.ts +2 -0
- package/src/Collapsible/hooks/useCollapsibleRoot.ts +34 -0
- package/src/Collapsible/hooks/useCollapsibleTrigger.ts +49 -0
- package/src/Collapsible/index.ts +1 -0
- package/src/Collapsible/types.ts +48 -0
- package/src/ContextMenu/ContextMenu.tsx +1004 -0
- package/src/ContextMenu/ContextMenuContentContext.ts +15 -0
- package/src/ContextMenu/ContextMenuContext.ts +21 -0
- package/src/ContextMenu/ContextMenuGroupContext.ts +8 -0
- package/src/ContextMenu/ContextMenuItemIndicatorContext.ts +8 -0
- package/src/ContextMenu/ContextMenuRadioGroupContext.ts +9 -0
- package/src/ContextMenu/ContextMenuSubContext.ts +15 -0
- package/src/ContextMenu/README.md +275 -0
- package/src/ContextMenu/__tests__/ContextMenu.asChild.test.tsx +186 -0
- package/src/ContextMenu/__tests__/ContextMenu.basic-rendering.test.tsx +39 -0
- package/src/ContextMenu/__tests__/ContextMenu.checkbox-item.test.tsx +145 -0
- package/src/ContextMenu/__tests__/ContextMenu.error-handling.test.tsx +113 -0
- package/src/ContextMenu/__tests__/ContextMenu.group-label.test.tsx +48 -0
- package/src/ContextMenu/__tests__/ContextMenu.item-indicator.test.tsx +88 -0
- package/src/ContextMenu/__tests__/ContextMenu.item.test.tsx +106 -0
- package/src/ContextMenu/__tests__/ContextMenu.keyboard-interaction.test.tsx +172 -0
- package/src/ContextMenu/__tests__/ContextMenu.mouse-interaction.test.tsx +227 -0
- package/src/ContextMenu/__tests__/ContextMenu.radio-item.test.tsx +127 -0
- package/src/ContextMenu/__tests__/ContextMenu.reading-direction.test.tsx +152 -0
- package/src/ContextMenu/__tests__/ContextMenu.separator.test.tsx +47 -0
- package/src/ContextMenu/__tests__/ContextMenu.state-modes.test.tsx +119 -0
- package/src/ContextMenu/__tests__/ContextMenu.sub.test.tsx +262 -0
- package/src/ContextMenu/__tests__/ContextMenu.typeahead.test.tsx +89 -0
- package/src/ContextMenu/constants.ts +4 -0
- package/src/ContextMenu/index.ts +1 -0
- package/src/ContextMenu/types.ts +199 -0
- package/src/DirectionProvider/DirectionContext.ts +21 -0
- package/src/DirectionProvider/DirectionProvider.tsx +31 -0
- package/src/DirectionProvider/README.md +62 -0
- package/src/DirectionProvider/__tests__/DirectionProvider.test.tsx +29 -0
- package/src/DirectionProvider/index.ts +3 -0
- package/src/DirectionProvider/types.ts +10 -0
- package/src/Divider/Divider.tsx +57 -0
- package/src/Divider/README.md +57 -0
- package/src/Divider/__tests__/Divider.test.tsx +41 -0
- package/src/Divider/index.ts +1 -0
- package/src/Divider/types.ts +5 -0
- package/src/Dropdown/Dropdown.tsx +842 -0
- package/src/Dropdown/DropdownContentContext.ts +15 -0
- package/src/Dropdown/DropdownContext.ts +17 -0
- package/src/Dropdown/DropdownGroupContext.ts +8 -0
- package/src/Dropdown/DropdownItemIndicatorContext.ts +13 -0
- package/src/Dropdown/DropdownRadioGroupContext.ts +9 -0
- package/src/Dropdown/DropdownSubContext.ts +15 -0
- package/src/Dropdown/README.md +284 -0
- package/src/Dropdown/__tests__/Dropdown.asChild.test.tsx +286 -0
- package/src/Dropdown/__tests__/Dropdown.basic-rendering.test.tsx +43 -0
- package/src/Dropdown/__tests__/Dropdown.checkbox-item.test.tsx +121 -0
- package/src/Dropdown/__tests__/Dropdown.disabled.test.tsx +143 -0
- package/src/Dropdown/__tests__/Dropdown.error-handling.test.tsx +85 -0
- package/src/Dropdown/__tests__/Dropdown.group-label.test.tsx +68 -0
- package/src/Dropdown/__tests__/Dropdown.item-indicator.test.tsx +260 -0
- package/src/Dropdown/__tests__/Dropdown.item.test.tsx +72 -0
- package/src/Dropdown/__tests__/Dropdown.keyboard-edge-cases.test.tsx +77 -0
- package/src/Dropdown/__tests__/Dropdown.keyboard-interaction.test.tsx +310 -0
- package/src/Dropdown/__tests__/Dropdown.mouse-interaction.test.tsx +347 -0
- package/src/Dropdown/__tests__/Dropdown.radio-item.test.tsx +134 -0
- package/src/Dropdown/__tests__/Dropdown.reading-direction.test.tsx +153 -0
- package/src/Dropdown/__tests__/Dropdown.separator.test.tsx +46 -0
- package/src/Dropdown/__tests__/Dropdown.state-modes.test.tsx +100 -0
- package/src/Dropdown/__tests__/Dropdown.sub.test.tsx +185 -0
- package/src/Dropdown/__tests__/Dropdown.trigger.test.tsx +110 -0
- package/src/Dropdown/__tests__/Dropdown.typeahead.test.tsx +133 -0
- package/src/Dropdown/constants.ts +4 -0
- package/src/Dropdown/hooks/index.ts +9 -0
- package/src/Dropdown/hooks/useCloseSiblingSub.ts +13 -0
- package/src/Dropdown/hooks/useDropdownContent.ts +162 -0
- package/src/Dropdown/hooks/useDropdownContext.ts +1 -0
- package/src/Dropdown/hooks/useDropdownGroup.ts +18 -0
- package/src/Dropdown/hooks/useDropdownItem.ts +49 -0
- package/src/Dropdown/hooks/useDropdownLabel.ts +15 -0
- package/src/Dropdown/hooks/useDropdownRoot.ts +57 -0
- package/src/Dropdown/hooks/useDropdownSubContext.ts +1 -0
- package/src/Dropdown/hooks/useDropdownTrigger.ts +31 -0
- package/src/Dropdown/index.ts +1 -0
- package/src/Dropdown/types.ts +200 -0
- package/src/EmptyState/EmptyState.tsx +245 -0
- package/src/EmptyState/README.md +129 -0
- package/src/EmptyState/__tests__/EmptyState.Actions.test.tsx +32 -0
- package/src/EmptyState/__tests__/EmptyState.Description.test.tsx +30 -0
- package/src/EmptyState/__tests__/EmptyState.Media.test.tsx +34 -0
- package/src/EmptyState/__tests__/EmptyState.Root.test.tsx +28 -0
- package/src/EmptyState/__tests__/EmptyState.Title.test.tsx +26 -0
- package/src/EmptyState/index.ts +2 -0
- package/src/EmptyState/types.ts +21 -0
- package/src/Field/Field.tsx +239 -0
- package/src/Field/FieldContext.ts +22 -0
- package/src/Field/README.md +167 -0
- package/src/Field/__tests__/Field.asChild.test.tsx +83 -0
- package/src/Field/__tests__/Field.basic-rendering.test.tsx +104 -0
- package/src/Field/__tests__/Field.state-cascade.test.tsx +75 -0
- package/src/Field/hooks/index.ts +2 -0
- package/src/Field/hooks/useFieldContext.ts +1 -0
- package/src/Field/hooks/useFieldProps.ts +57 -0
- package/src/Field/index.ts +2 -0
- package/src/Field/types.ts +33 -0
- package/src/Fieldset/Fieldset.tsx +104 -0
- package/src/Fieldset/README.md +74 -0
- package/src/Fieldset/__tests__/Fieldset.basic-rendering.test.tsx +81 -0
- package/src/Fieldset/__tests__/Fieldset.disabled.test.tsx +41 -0
- package/src/Fieldset/index.ts +2 -0
- package/src/Fieldset/types.ts +5 -0
- package/src/Input/Input.tsx +120 -0
- package/src/Input/README.md +180 -0
- package/src/Input/__tests__/Input.asChild.test.tsx +85 -0
- package/src/Input/__tests__/Input.basic-rendering.test.tsx +118 -0
- package/src/Input/__tests__/Input.disabled.test.tsx +49 -0
- package/src/Input/__tests__/Input.field-integration.test.tsx +148 -0
- package/src/Input/index.ts +2 -0
- package/src/Input/types.ts +7 -0
- package/src/InputGroup/InputGroup.tsx +228 -0
- package/src/InputGroup/README.md +178 -0
- package/src/InputGroup/__tests__/InputGroup.asChild.test.tsx +109 -0
- package/src/InputGroup/__tests__/InputGroup.basic-rendering.test.tsx +106 -0
- package/src/InputGroup/index.ts +2 -0
- package/src/InputGroup/types.ts +13 -0
- package/src/MillerColumns/MillerColumns.tsx +329 -0
- package/src/MillerColumns/MillerColumnsContext.ts +25 -0
- package/src/MillerColumns/README.md +278 -0
- package/src/MillerColumns/__tests__/MillerColumns.aria.test.tsx +82 -0
- package/src/MillerColumns/__tests__/MillerColumns.asChild.test.tsx +106 -0
- package/src/MillerColumns/__tests__/MillerColumns.auto-scroll.test.tsx +68 -0
- package/src/MillerColumns/__tests__/MillerColumns.basic-rendering.test.tsx +52 -0
- package/src/MillerColumns/__tests__/MillerColumns.column-projection.test.tsx +161 -0
- package/src/MillerColumns/__tests__/MillerColumns.controlled-state.test.tsx +90 -0
- package/src/MillerColumns/__tests__/MillerColumns.data-attributes.test.tsx +77 -0
- package/src/MillerColumns/__tests__/MillerColumns.disabled-items.test.tsx +65 -0
- package/src/MillerColumns/__tests__/MillerColumns.error-handling.test.tsx +57 -0
- package/src/MillerColumns/__tests__/MillerColumns.fixtures.ts +15 -0
- package/src/MillerColumns/__tests__/MillerColumns.item-indicator.test.tsx +57 -0
- package/src/MillerColumns/__tests__/MillerColumns.keyboard-interaction.test.tsx +181 -0
- package/src/MillerColumns/__tests__/MillerColumns.preview-panel.test.tsx +47 -0
- package/src/MillerColumns/__tests__/MillerColumns.resize.test.tsx +137 -0
- package/src/MillerColumns/__tests__/MillerColumns.roving-tabindex.test.tsx +91 -0
- package/src/MillerColumns/__tests__/MillerColumns.selection.test.tsx +54 -0
- package/src/MillerColumns/__tests__/MillerColumns.uncontrolled-state.test.tsx +70 -0
- package/src/MillerColumns/hooks/index.ts +7 -0
- package/src/MillerColumns/hooks/useMillerColumnsColumn.ts +23 -0
- package/src/MillerColumns/hooks/useMillerColumnsColumnContext.ts +1 -0
- package/src/MillerColumns/hooks/useMillerColumnsContext.ts +1 -0
- package/src/MillerColumns/hooks/useMillerColumnsItem.ts +157 -0
- package/src/MillerColumns/hooks/useMillerColumnsItemContext.ts +1 -0
- package/src/MillerColumns/hooks/useMillerColumnsResizeHandle.ts +76 -0
- package/src/MillerColumns/hooks/useMillerColumnsRoot.ts +0 -0
- package/src/MillerColumns/index.ts +3 -0
- package/src/MillerColumns/types.ts +93 -0
- package/src/MillerColumns/useMillerColumnsSelection.ts +31 -0
- package/src/MillerColumns/utils.ts +75 -0
- package/src/Modal/Modal.tsx +474 -0
- package/src/Modal/ModalContext.ts +13 -0
- package/src/Modal/README.md +207 -0
- package/src/Modal/__tests__/Modal.accessibility.test.tsx +167 -0
- package/src/Modal/__tests__/Modal.asChild.test.tsx +162 -0
- package/src/Modal/__tests__/Modal.click-outside.test.tsx +115 -0
- package/src/Modal/__tests__/Modal.content.test.tsx +193 -0
- package/src/Modal/__tests__/Modal.controlled-state.test.tsx +120 -0
- package/src/Modal/__tests__/Modal.error-handling.test.tsx +30 -0
- package/src/Modal/__tests__/Modal.escape-hatches.test.tsx +99 -0
- package/src/Modal/__tests__/Modal.imperative-api.test.tsx +119 -0
- package/src/Modal/__tests__/Modal.nested.test.tsx +106 -0
- package/src/Modal/__tests__/Modal.overlay.test.tsx +99 -0
- package/src/Modal/__tests__/Modal.portal.test.tsx +90 -0
- package/src/Modal/__tests__/Modal.presence.test.tsx +111 -0
- package/src/Modal/__tests__/Modal.trigger.test.tsx +70 -0
- package/src/Modal/__tests__/Modal.uncontrolled-state.test.tsx +72 -0
- package/src/Modal/__tests__/dialog-polyfill.ts +40 -0
- package/src/Modal/hooks/index.ts +4 -0
- package/src/Modal/hooks/useModalContent.ts +62 -0
- package/src/Modal/hooks/useModalContext.ts +1 -0
- package/src/Modal/hooks/useModalRoot.ts +81 -0
- package/src/Modal/hooks/useModalTrigger.ts +25 -0
- package/src/Modal/index.ts +3 -0
- package/src/Modal/types.ts +76 -0
- package/src/Portal/Portal.tsx +28 -0
- package/src/Portal/README.md +70 -0
- package/src/Portal/__tests__/Portal.basic-rendering.test.tsx +17 -0
- package/src/Portal/index.ts +2 -0
- package/src/Portal/types.ts +6 -0
- package/src/Progress/Progress.tsx +178 -0
- package/src/Progress/ProgressContext.ts +15 -0
- package/src/Progress/README.md +112 -0
- package/src/Progress/__tests__/Progress.asChild.test.tsx +37 -0
- package/src/Progress/__tests__/Progress.basic-rendering.test.tsx +65 -0
- package/src/Progress/__tests__/Progress.error-handling.test.tsx +40 -0
- package/src/Progress/__tests__/Progress.fixtures.ts +7 -0
- package/src/Progress/__tests__/Progress.value.test.tsx +83 -0
- package/src/Progress/hooks/index.ts +2 -0
- package/src/Progress/hooks/useProgressContext.ts +1 -0
- package/src/Progress/hooks/useProgressRoot.ts +45 -0
- package/src/Progress/index.ts +3 -0
- package/src/Progress/types.ts +43 -0
- package/src/RadioCard/README.md +133 -0
- package/src/RadioCard/RadioCard.tsx +334 -0
- package/src/RadioCard/RadioCardContext.ts +23 -0
- package/src/RadioCard/RadioCardItemContext.ts +10 -0
- package/src/RadioCard/__tests__/RadioCard.asChild.test.tsx +76 -0
- package/src/RadioCard/__tests__/RadioCard.basic-rendering.test.tsx +87 -0
- package/src/RadioCard/__tests__/RadioCard.controlled-state.test.tsx +107 -0
- package/src/RadioCard/__tests__/RadioCard.disabled-items.test.tsx +61 -0
- package/src/RadioCard/__tests__/RadioCard.error-handling.test.tsx +35 -0
- package/src/RadioCard/__tests__/RadioCard.indicator.test.tsx +119 -0
- package/src/RadioCard/__tests__/RadioCard.keyboard-interaction.test.tsx +158 -0
- package/src/RadioCard/__tests__/RadioCard.orientation.test.tsx +90 -0
- package/src/RadioCard/__tests__/RadioCard.reading-direction.test.tsx +65 -0
- package/src/RadioCard/__tests__/RadioCard.uncontrolled-state.test.tsx +108 -0
- package/src/RadioCard/hooks/index.ts +3 -0
- package/src/RadioCard/hooks/useRadioCardContext.ts +1 -0
- package/src/RadioCard/hooks/useRadioCardItemContext.ts +1 -0
- package/src/RadioCard/hooks/useRadioCardRoot.ts +77 -0
- package/src/RadioCard/index.ts +4 -0
- package/src/RadioCard/types.ts +51 -0
- package/src/RadioGroup/README.md +185 -0
- package/src/RadioGroup/RadioGroup.tsx +353 -0
- package/src/RadioGroup/RadioGroupContext.ts +23 -0
- package/src/RadioGroup/RadioGroupItemContext.ts +10 -0
- package/src/RadioGroup/__tests__/RadioGroup.asChild.test.tsx +105 -0
- package/src/RadioGroup/__tests__/RadioGroup.basic-rendering.test.tsx +72 -0
- package/src/RadioGroup/__tests__/RadioGroup.controlled-state.test.tsx +109 -0
- package/src/RadioGroup/__tests__/RadioGroup.disabled-keydown-guards.test.tsx +68 -0
- package/src/RadioGroup/__tests__/RadioGroup.disabled.test.tsx +79 -0
- package/src/RadioGroup/__tests__/RadioGroup.error-handling.test.tsx +33 -0
- package/src/RadioGroup/__tests__/RadioGroup.indicator.test.tsx +85 -0
- package/src/RadioGroup/__tests__/RadioGroup.keyboard-interaction.test.tsx +135 -0
- package/src/RadioGroup/__tests__/RadioGroup.orientation.test.tsx +90 -0
- package/src/RadioGroup/__tests__/RadioGroup.reading-direction.test.tsx +65 -0
- package/src/RadioGroup/__tests__/RadioGroup.ref-forwarding.test.tsx +45 -0
- package/src/RadioGroup/__tests__/RadioGroup.roving-tabindex.test.tsx +105 -0
- package/src/RadioGroup/__tests__/RadioGroup.uncontrolled-state.test.tsx +96 -0
- package/src/RadioGroup/hooks/index.ts +3 -0
- package/src/RadioGroup/hooks/useRadioGroupContext.ts +1 -0
- package/src/RadioGroup/hooks/useRadioGroupItemContext.ts +1 -0
- package/src/RadioGroup/hooks/useRadioGroupRoot.ts +87 -0
- package/src/RadioGroup/index.ts +1 -0
- package/src/RadioGroup/types.ts +51 -0
- package/src/Select/README.md +203 -0
- package/src/Select/Select.tsx +204 -0
- package/src/Select/__tests__/Select.asChild.test.tsx +36 -0
- package/src/Select/__tests__/Select.basic-rendering.test.tsx +17 -0
- package/src/Select/__tests__/Select.controlled-state.test.tsx +69 -0
- package/src/Select/__tests__/Select.data-attributes.test.tsx +29 -0
- package/src/Select/__tests__/Select.field-integration.test.tsx +150 -0
- package/src/Select/__tests__/Select.group.test.tsx +42 -0
- package/src/Select/__tests__/Select.placeholder.test.tsx +32 -0
- package/src/Select/index.ts +2 -0
- package/src/Select/types.ts +89 -0
- package/src/SkipNav/README.md +87 -0
- package/src/SkipNav/SkipNav.tsx +116 -0
- package/src/SkipNav/__tests__/SkipNav.basic-rendering.test.tsx +23 -0
- package/src/SkipNav/__tests__/SkipNav.ids.test.tsx +19 -0
- package/src/SkipNav/index.ts +1 -0
- package/src/SkipNav/types.ts +26 -0
- package/src/Slider/README.md +215 -0
- package/src/Slider/Slider.tsx +308 -0
- package/src/Slider/SliderContext.ts +24 -0
- package/src/Slider/__tests__/Slider.asChild.test.tsx +119 -0
- package/src/Slider/__tests__/Slider.basic-rendering.test.tsx +157 -0
- package/src/Slider/__tests__/Slider.controlled-state.test.tsx +78 -0
- package/src/Slider/__tests__/Slider.disabled.test.tsx +82 -0
- package/src/Slider/__tests__/Slider.error-handling.test.tsx +45 -0
- package/src/Slider/__tests__/Slider.fixtures.ts +53 -0
- package/src/Slider/__tests__/Slider.form.test.tsx +67 -0
- package/src/Slider/__tests__/Slider.inverted.test.tsx +112 -0
- package/src/Slider/__tests__/Slider.keyboard-interaction.test.tsx +118 -0
- package/src/Slider/__tests__/Slider.multiple-thumbs.test.tsx +84 -0
- package/src/Slider/__tests__/Slider.orientation.test.tsx +101 -0
- package/src/Slider/__tests__/Slider.pointer-interaction.test.tsx +205 -0
- package/src/Slider/__tests__/Slider.reading-direction.test.tsx +99 -0
- package/src/Slider/__tests__/Slider.uncontrolled-state.test.tsx +69 -0
- package/src/Slider/__tests__/Slider.value-commit.test.tsx +103 -0
- package/src/Slider/hooks/index.ts +3 -0
- package/src/Slider/hooks/useSliderContext.ts +1 -0
- package/src/Slider/hooks/useSliderRoot.ts +197 -0
- package/src/Slider/hooks/useSliderThumb.ts +77 -0
- package/src/Slider/index.ts +3 -0
- package/src/Slider/types.ts +48 -0
- package/src/Slider/utils.ts +155 -0
- package/src/Slot/Slot.tsx +158 -0
- package/src/Slot/__tests__/Slot.test.tsx +163 -0
- package/src/Slot/__tests__/composeEventHandlers.test.ts +74 -0
- package/src/Slot/composeEventHandlers.ts +38 -0
- package/src/Slot/index.ts +3 -0
- package/src/Slot/types.ts +5 -0
- package/src/Status/README.md +50 -0
- package/src/Status/Status.tsx +44 -0
- package/src/Status/__tests__/Status.test.tsx +28 -0
- package/src/Status/index.ts +2 -0
- package/src/Status/types.ts +5 -0
- package/src/Switch/README.md +121 -0
- package/src/Switch/Switch.tsx +167 -0
- package/src/Switch/SwitchContext.ts +10 -0
- package/src/Switch/__tests__/Switch.asChild.test.tsx +56 -0
- package/src/Switch/__tests__/Switch.basic-rendering.test.tsx +76 -0
- package/src/Switch/__tests__/Switch.contract.test.tsx +109 -0
- package/src/Switch/__tests__/Switch.controlled-state.test.tsx +79 -0
- package/src/Switch/__tests__/Switch.disabled.test.tsx +60 -0
- package/src/Switch/__tests__/Switch.error-handling.test.tsx +20 -0
- package/src/Switch/__tests__/Switch.keyboard-interaction.test.tsx +56 -0
- package/src/Switch/__tests__/Switch.thumb.test.tsx +84 -0
- package/src/Switch/__tests__/Switch.uncontrolled-state.test.tsx +83 -0
- package/src/Switch/hooks/index.ts +2 -0
- package/src/Switch/hooks/useSwitchContext.ts +1 -0
- package/src/Switch/hooks/useSwitchRoot.ts +28 -0
- package/src/Switch/index.ts +3 -0
- package/src/Switch/types.ts +37 -0
- package/src/Table/README.md +205 -0
- package/src/Table/Table.tsx +380 -0
- package/src/Table/__tests__/Table.Body.test.tsx +61 -0
- package/src/Table/__tests__/Table.Caption.test.tsx +70 -0
- package/src/Table/__tests__/Table.Cell.test.tsx +73 -0
- package/src/Table/__tests__/Table.Footer.test.tsx +61 -0
- package/src/Table/__tests__/Table.Head.test.tsx +61 -0
- package/src/Table/__tests__/Table.Header.test.tsx +73 -0
- package/src/Table/__tests__/Table.Root.test.tsx +49 -0
- package/src/Table/__tests__/Table.Row.test.tsx +67 -0
- package/src/Table/__tests__/Table.ScrollArea.test.tsx +83 -0
- package/src/Table/index.ts +1 -0
- package/src/Table/types.ts +63 -0
- package/src/Tabs/README.md +110 -0
- package/src/Tabs/Tabs.tsx +434 -0
- package/src/Tabs/TabsContext.ts +13 -0
- package/src/Tabs/__tests__/Tabs.activation-mode.test.tsx +114 -0
- package/src/Tabs/__tests__/Tabs.asChild.test.tsx +91 -0
- package/src/Tabs/__tests__/Tabs.basic-rendering.test.tsx +483 -0
- package/src/Tabs/__tests__/Tabs.change-event-callbacks.test.tsx +133 -0
- package/src/Tabs/__tests__/Tabs.controlled-state.test.tsx +152 -0
- package/src/Tabs/__tests__/Tabs.disabled-tabs.test.tsx +203 -0
- package/src/Tabs/__tests__/Tabs.error-handling.test.tsx +82 -0
- package/src/Tabs/__tests__/Tabs.fixtures.ts +171 -0
- package/src/Tabs/__tests__/Tabs.imperative-api.test.tsx +118 -0
- package/src/Tabs/__tests__/Tabs.keyboard-interaction.test.tsx +192 -0
- package/src/Tabs/__tests__/Tabs.lazy-mount.test.tsx +61 -0
- package/src/Tabs/__tests__/Tabs.mouse-interaction.test.tsx +216 -0
- package/src/Tabs/__tests__/Tabs.reading-direction.test.tsx +58 -0
- package/src/Tabs/__tests__/Tabs.uncontrolled-state.test.tsx +197 -0
- package/src/Tabs/hooks/index.ts +4 -0
- package/src/Tabs/hooks/useTabsContent.ts +27 -0
- package/src/Tabs/hooks/useTabsContext.ts +1 -0
- package/src/Tabs/hooks/useTabsRoot.ts +148 -0
- package/src/Tabs/hooks/useTabsTrigger.ts +111 -0
- package/src/Tabs/index.ts +3 -0
- package/src/Tabs/types.ts +99 -0
- package/src/Tabs/utils.ts +8 -0
- package/src/Textarea/README.md +98 -0
- package/src/Textarea/Textarea.tsx +93 -0
- package/src/Textarea/__tests__/Textarea.asChild.test.tsx +85 -0
- package/src/Textarea/__tests__/Textarea.basic-rendering.test.tsx +107 -0
- package/src/Textarea/__tests__/Textarea.disabled.test.tsx +49 -0
- package/src/Textarea/__tests__/Textarea.field-integration.test.tsx +134 -0
- package/src/Textarea/index.ts +2 -0
- package/src/Textarea/types.ts +7 -0
- package/src/Toggle/README.md +97 -0
- package/src/Toggle/Toggle.tsx +81 -0
- package/src/Toggle/__tests__/Toggle.asChild.test.tsx +42 -0
- package/src/Toggle/__tests__/Toggle.basic-rendering.test.tsx +28 -0
- package/src/Toggle/__tests__/Toggle.controlled-state.test.tsx +60 -0
- package/src/Toggle/__tests__/Toggle.disabled.test.tsx +34 -0
- package/src/Toggle/__tests__/Toggle.keyboard-interaction.test.tsx +42 -0
- package/src/Toggle/__tests__/Toggle.uncontrolled-state.test.tsx +40 -0
- package/src/Toggle/index.ts +2 -0
- package/src/Toggle/types.ts +23 -0
- package/src/ToggleGroup/README.md +137 -0
- package/src/ToggleGroup/ToggleGroup.tsx +298 -0
- package/src/ToggleGroup/ToggleGroupContext.ts +9 -0
- package/src/ToggleGroup/__tests__/ToggleGroup.asChild.test.tsx +65 -0
- package/src/ToggleGroup/__tests__/ToggleGroup.basic-rendering.test.tsx +50 -0
- package/src/ToggleGroup/__tests__/ToggleGroup.disabled.test.tsx +54 -0
- package/src/ToggleGroup/__tests__/ToggleGroup.keyboard-interaction.test.tsx +151 -0
- package/src/ToggleGroup/__tests__/ToggleGroup.multiple-mode.test.tsx +144 -0
- package/src/ToggleGroup/__tests__/ToggleGroup.reading-direction.test.tsx +28 -0
- package/src/ToggleGroup/__tests__/ToggleGroup.single-mode.test.tsx +139 -0
- package/src/ToggleGroup/hooks/index.ts +2 -0
- package/src/ToggleGroup/hooks/useToggleGroupContext.ts +1 -0
- package/src/ToggleGroup/hooks/useToggleGroupRoot.ts +110 -0
- package/src/ToggleGroup/index.ts +2 -0
- package/src/ToggleGroup/types.ts +72 -0
- package/src/Tooltip/README.md +214 -0
- package/src/Tooltip/Tooltip.tsx +260 -0
- package/src/Tooltip/TooltipContext.ts +20 -0
- package/src/Tooltip/__tests__/Tooltip.asChild.test.tsx +77 -0
- package/src/Tooltip/__tests__/Tooltip.basic-rendering.test.tsx +180 -0
- package/src/Tooltip/__tests__/Tooltip.controlled-state.test.tsx +128 -0
- package/src/Tooltip/__tests__/Tooltip.escape-hatches.test.tsx +73 -0
- package/src/Tooltip/__tests__/Tooltip.focus-interaction.test.tsx +88 -0
- package/src/Tooltip/__tests__/Tooltip.hover-interaction.test.tsx +179 -0
- package/src/Tooltip/__tests__/Tooltip.keyboard-interaction.test.tsx +85 -0
- package/src/Tooltip/__tests__/Tooltip.uncontrolled-state.test.tsx +67 -0
- package/src/Tooltip/hooks/index.ts +4 -0
- package/src/Tooltip/hooks/useTooltipContent.ts +53 -0
- package/src/Tooltip/hooks/useTooltipProvider.ts +41 -0
- package/src/Tooltip/hooks/useTooltipRoot.ts +106 -0
- package/src/Tooltip/hooks/useTooltipTrigger.ts +44 -0
- package/src/Tooltip/index.ts +1 -0
- package/src/Tooltip/types.ts +64 -0
- package/src/Tree/README.md +339 -0
- package/src/Tree/Tree.tsx +571 -0
- package/src/Tree/TreeContext.ts +24 -0
- package/src/Tree/__tests__/Tree.aria.test.tsx +53 -0
- package/src/Tree/__tests__/Tree.asChild.test.tsx +134 -0
- package/src/Tree/__tests__/Tree.basic-rendering.test.tsx +111 -0
- package/src/Tree/__tests__/Tree.branch-behaviour.test.tsx +87 -0
- package/src/Tree/__tests__/Tree.controlled-expansion.test.tsx +92 -0
- package/src/Tree/__tests__/Tree.data-attributes.test.tsx +88 -0
- package/src/Tree/__tests__/Tree.disabled-items.test.tsx +196 -0
- package/src/Tree/__tests__/Tree.error-handling.test.tsx +71 -0
- package/src/Tree/__tests__/Tree.forceMount.test.tsx +72 -0
- package/src/Tree/__tests__/Tree.keyboard-interaction.test.tsx +150 -0
- package/src/Tree/__tests__/Tree.multiple-selection.test.tsx +151 -0
- package/src/Tree/__tests__/Tree.range-selection.test.tsx +200 -0
- package/src/Tree/__tests__/Tree.recursion-depth.test.tsx +73 -0
- package/src/Tree/__tests__/Tree.roving-tabindex.test.tsx +117 -0
- package/src/Tree/__tests__/Tree.selection-path.test.tsx +404 -0
- package/src/Tree/__tests__/Tree.single-selection.test.tsx +108 -0
- package/src/Tree/__tests__/Tree.uncontrolled-expansion.test.tsx +69 -0
- package/src/Tree/hooks/index.ts +3 -0
- package/src/Tree/hooks/useTreeItemKeyboard.ts +86 -0
- package/src/Tree/hooks/useTreePath.ts +68 -0
- package/src/Tree/hooks/useTreeRoot.ts +279 -0
- package/src/Tree/index.ts +3 -0
- package/src/Tree/types.ts +224 -0
- package/src/Tree/utils.ts +59 -0
- package/src/VisuallyHidden/README.md +58 -0
- package/src/VisuallyHidden/VisuallyHidden.tsx +67 -0
- package/src/VisuallyHidden/__tests__/VisuallyHidden.test.tsx +59 -0
- package/src/VisuallyHidden/index.ts +2 -0
- package/src/VisuallyHidden/types.ts +5 -0
- package/src/hooks/index.ts +3 -0
- package/src/hooks/useCollection.ts +74 -0
- package/src/hooks/useControllableState.ts +81 -0
- package/src/hooks/useRovingTabindex.ts +178 -0
- package/src/index.ts +38 -0
- package/src/test/intersectionObserverPolyfill.ts +83 -0
- package/src/test/popoverPolyfill.ts +86 -0
- package/src/test/scrollPolyfill.ts +23 -0
- package/src/types.ts +13 -0
- package/src/utils/__tests__/createStrictContext.test.tsx +69 -0
- package/src/utils/__tests__/deriveId.test.ts +28 -0
- package/src/utils/__tests__/getKeyToActionMap.test.ts +106 -0
- package/src/utils/createStrictContext.ts +49 -0
- package/src/utils/deriveId.ts +31 -0
- package/src/utils/getKeyToActionMap.ts +95 -0
- package/src/utils/index.ts +3 -0
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import { Slot } from "../Slot";
|
|
2
|
+
import { InputGroupAdornmentProps, InputGroupRootProps } from "./types";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* The root of an InputGroup — a stateless `<div>` wrapper that frames a
|
|
6
|
+
* single form control alongside optional leading and trailing adornments.
|
|
7
|
+
*
|
|
8
|
+
* `InputGroup` is intentionally **layout-and-anatomy only**. It owns no
|
|
9
|
+
* state, provides no context, and does not know which control sits
|
|
10
|
+
* inside it. Use CSS `:focus-within` and `:has(input:disabled)` /
|
|
11
|
+
* `:has(input[aria-invalid="true"])` to style the frame in response to
|
|
12
|
+
* the inner control's state — no JS coordination required.
|
|
13
|
+
*
|
|
14
|
+
* **Styling hook.** `data-input-group=""` on the root.
|
|
15
|
+
*
|
|
16
|
+
* **`asChild` composition.** Pass `asChild` to render the consumer's
|
|
17
|
+
* element instead of `<div>` — e.g. a `<label>` so a click anywhere on
|
|
18
|
+
* the frame focuses the wrapped input.
|
|
19
|
+
*
|
|
20
|
+
* @example Basic frame with no adornments
|
|
21
|
+
* ```tsx
|
|
22
|
+
* <InputGroup>
|
|
23
|
+
* <Input aria-label="Search" type="search" />
|
|
24
|
+
* </InputGroup>
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* @example Frame rendered as a <label> so the whole frame is clickable
|
|
28
|
+
* ```tsx
|
|
29
|
+
* <InputGroup asChild>
|
|
30
|
+
* <label>
|
|
31
|
+
* <InputGroup.LeadingAdornment><SearchIcon /></InputGroup.LeadingAdornment>
|
|
32
|
+
* <Input type="search" />
|
|
33
|
+
* </label>
|
|
34
|
+
* </InputGroup>
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
function InputGroupRoot({
|
|
38
|
+
asChild = false,
|
|
39
|
+
children,
|
|
40
|
+
ref,
|
|
41
|
+
...rest
|
|
42
|
+
}: InputGroupRootProps) {
|
|
43
|
+
const rootProps = {
|
|
44
|
+
...rest,
|
|
45
|
+
ref,
|
|
46
|
+
"data-input-group": "",
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
if (asChild) {
|
|
50
|
+
return <Slot {...rootProps}>{children}</Slot>;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return <div {...rootProps}>{children}</div>;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
InputGroupRoot.displayName = "InputGroupRoot";
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* A leading adornment slot — a `<span>` positioned at the start of the
|
|
60
|
+
* frame for icons, currency symbols, prefix text, or interactive
|
|
61
|
+
* buttons.
|
|
62
|
+
*
|
|
63
|
+
* **Styling hook.** `data-input-group-adornment="leading"` on the
|
|
64
|
+
* adornment element.
|
|
65
|
+
*
|
|
66
|
+
* **Accessibility.** No `aria-hidden` is set automatically — the
|
|
67
|
+
* adornment is just a positioned slot. Mark decorative icons
|
|
68
|
+
* `aria-hidden="true"` yourself (or wrap with `AccessibleIcon`), and
|
|
69
|
+
* give interactive children a proper accessible name.
|
|
70
|
+
*
|
|
71
|
+
* **`asChild` composition.** Pass `asChild` to render an interactive
|
|
72
|
+
* element such as `<button>` — for a clickable icon that triggers a
|
|
73
|
+
* search, opens a colour picker, or any other action. Event handlers
|
|
74
|
+
* compose (child runs first); refs forward to the consumer element.
|
|
75
|
+
*
|
|
76
|
+
* @example Decorative leading icon
|
|
77
|
+
* ```tsx
|
|
78
|
+
* <InputGroup.LeadingAdornment>
|
|
79
|
+
* <SearchIcon aria-hidden="true" />
|
|
80
|
+
* </InputGroup.LeadingAdornment>
|
|
81
|
+
* ```
|
|
82
|
+
*
|
|
83
|
+
* @example Interactive leading button via asChild
|
|
84
|
+
* ```tsx
|
|
85
|
+
* <InputGroup.LeadingAdornment asChild>
|
|
86
|
+
* <button type="button" aria-label="Open colour picker" onClick={open}>
|
|
87
|
+
* <ColourSwatch />
|
|
88
|
+
* </button>
|
|
89
|
+
* </InputGroup.LeadingAdornment>
|
|
90
|
+
* ```
|
|
91
|
+
*/
|
|
92
|
+
function InputGroupLeadingAdornment({
|
|
93
|
+
asChild = false,
|
|
94
|
+
children,
|
|
95
|
+
ref,
|
|
96
|
+
...rest
|
|
97
|
+
}: InputGroupAdornmentProps) {
|
|
98
|
+
const adornmentProps = {
|
|
99
|
+
...rest,
|
|
100
|
+
ref,
|
|
101
|
+
"data-input-group-adornment": "leading" as const,
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
if (asChild) {
|
|
105
|
+
return <Slot {...adornmentProps}>{children}</Slot>;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return <span {...adornmentProps}>{children}</span>;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
InputGroupLeadingAdornment.displayName = "InputGroupLeadingAdornment";
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* A trailing adornment slot — a `<span>` positioned at the end of the
|
|
115
|
+
* frame for icons, suffix text, clear buttons, or password-reveal
|
|
116
|
+
* toggles.
|
|
117
|
+
*
|
|
118
|
+
* **Styling hook.** `data-input-group-adornment="trailing"` on the
|
|
119
|
+
* adornment element.
|
|
120
|
+
*
|
|
121
|
+
* **Accessibility.** No `aria-hidden` is set automatically — the
|
|
122
|
+
* adornment is just a positioned slot. Mark decorative icons
|
|
123
|
+
* `aria-hidden="true"` yourself (or wrap with `AccessibleIcon`), and
|
|
124
|
+
* give interactive children a proper accessible name.
|
|
125
|
+
*
|
|
126
|
+
* **`asChild` composition.** Pass `asChild` to render an interactive
|
|
127
|
+
* element such as `<button>`. Event handlers compose (child runs
|
|
128
|
+
* first); refs forward to the consumer element.
|
|
129
|
+
*
|
|
130
|
+
* @example Trailing clear button via asChild
|
|
131
|
+
* ```tsx
|
|
132
|
+
* <InputGroup.TrailingAdornment asChild>
|
|
133
|
+
* <button type="button" aria-label="Clear" onClick={clear}>
|
|
134
|
+
* <XIcon aria-hidden="true" />
|
|
135
|
+
* </button>
|
|
136
|
+
* </InputGroup.TrailingAdornment>
|
|
137
|
+
* ```
|
|
138
|
+
*/
|
|
139
|
+
function InputGroupTrailingAdornment({
|
|
140
|
+
asChild = false,
|
|
141
|
+
children,
|
|
142
|
+
ref,
|
|
143
|
+
...rest
|
|
144
|
+
}: InputGroupAdornmentProps) {
|
|
145
|
+
const adornmentProps = {
|
|
146
|
+
...rest,
|
|
147
|
+
ref,
|
|
148
|
+
"data-input-group-adornment": "trailing" as const,
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
if (asChild) {
|
|
152
|
+
return <Slot {...adornmentProps}>{children}</Slot>;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return <span {...adornmentProps}>{children}</span>;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
InputGroupTrailingAdornment.displayName = "InputGroupTrailingAdornment";
|
|
159
|
+
|
|
160
|
+
type TInputGroupCompound = typeof InputGroupRoot & {
|
|
161
|
+
Root: typeof InputGroupRoot;
|
|
162
|
+
LeadingAdornment: typeof InputGroupLeadingAdornment;
|
|
163
|
+
TrailingAdornment: typeof InputGroupTrailingAdornment;
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Headless, accessible **InputGroup** — a stateless compound that frames
|
|
168
|
+
* a single form control alongside optional leading and trailing
|
|
169
|
+
* adornments. Zero styles ship.
|
|
170
|
+
*
|
|
171
|
+
* `InputGroup` is intentionally **not** input-specific. It maps directly
|
|
172
|
+
* to the `framed-control/*` design-token anatomy (height,
|
|
173
|
+
* padding-inline, gap, icon-size, radius) and works just as well around
|
|
174
|
+
* a `<Textarea>`, a future `NumberInput.Control`, or any other framed
|
|
175
|
+
* control — the only thing tying it to `<Input>` is the name.
|
|
176
|
+
*
|
|
177
|
+
* `InputGroup` is both callable (an alias of `InputGroup.Root`) and
|
|
178
|
+
* carries its sub-components as static properties:
|
|
179
|
+
*
|
|
180
|
+
* - {@link InputGroupRoot | `InputGroup.Root`} — the wrapping `<div>`,
|
|
181
|
+
* `data-input-group=""` styling hook.
|
|
182
|
+
* - {@link InputGroupLeadingAdornment | `InputGroup.LeadingAdornment`} —
|
|
183
|
+
* leading slot, `data-input-group-adornment="leading"`.
|
|
184
|
+
* - {@link InputGroupTrailingAdornment | `InputGroup.TrailingAdornment`} —
|
|
185
|
+
* trailing slot, `data-input-group-adornment="trailing"`.
|
|
186
|
+
*
|
|
187
|
+
* **State coordination.** None. CSS handles disabled / invalid /
|
|
188
|
+
* focus-within styling via `:has()` and `:focus-within`. When `Field`
|
|
189
|
+
* lands it will sit *outside* `InputGroup` and own label / error /
|
|
190
|
+
* description wiring without InputGroup needing to change.
|
|
191
|
+
*
|
|
192
|
+
* @example Leading search icon
|
|
193
|
+
* ```tsx
|
|
194
|
+
* import { InputGroup, Input } from "@primitiv-ui/react";
|
|
195
|
+
*
|
|
196
|
+
* <InputGroup>
|
|
197
|
+
* <InputGroup.LeadingAdornment>
|
|
198
|
+
* <SearchIcon aria-hidden="true" />
|
|
199
|
+
* </InputGroup.LeadingAdornment>
|
|
200
|
+
* <Input aria-label="Search" type="search" />
|
|
201
|
+
* </InputGroup>
|
|
202
|
+
* ```
|
|
203
|
+
*
|
|
204
|
+
* @example Trailing clear button
|
|
205
|
+
* ```tsx
|
|
206
|
+
* <InputGroup>
|
|
207
|
+
* <Input value={q} onChange={onChange} aria-label="Search" />
|
|
208
|
+
* <InputGroup.TrailingAdornment asChild>
|
|
209
|
+
* <button type="button" aria-label="Clear" onClick={clear}>
|
|
210
|
+
* <XIcon aria-hidden="true" />
|
|
211
|
+
* </button>
|
|
212
|
+
* </InputGroup.TrailingAdornment>
|
|
213
|
+
* </InputGroup>
|
|
214
|
+
* ```
|
|
215
|
+
*
|
|
216
|
+
* @see {@link InputGroupRoot} for frame anatomy and `asChild` on Root.
|
|
217
|
+
* @see {@link InputGroupLeadingAdornment} for leading-slot semantics.
|
|
218
|
+
* @see {@link InputGroupTrailingAdornment} for trailing-slot semantics.
|
|
219
|
+
*/
|
|
220
|
+
const InputGroupCompound: TInputGroupCompound = Object.assign(InputGroupRoot, {
|
|
221
|
+
Root: InputGroupRoot,
|
|
222
|
+
LeadingAdornment: InputGroupLeadingAdornment,
|
|
223
|
+
TrailingAdornment: InputGroupTrailingAdornment,
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
InputGroupCompound.displayName = "InputGroup";
|
|
227
|
+
|
|
228
|
+
export { InputGroupCompound as InputGroup };
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
# InputGroup
|
|
2
|
+
|
|
3
|
+
A headless, accessible compound that frames a single form control
|
|
4
|
+
alongside optional leading and trailing adornments — icons, currency
|
|
5
|
+
symbols, clear buttons, password-reveal toggles. Zero styles ship.
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
import { InputGroup, Input } from "@primitiv-ui/react";
|
|
9
|
+
|
|
10
|
+
<InputGroup>
|
|
11
|
+
<InputGroup.LeadingAdornment>
|
|
12
|
+
<SearchIcon aria-hidden="true" />
|
|
13
|
+
</InputGroup.LeadingAdornment>
|
|
14
|
+
<Input aria-label="Search" type="search" />
|
|
15
|
+
</InputGroup>
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Anatomy
|
|
19
|
+
|
|
20
|
+
| Part | Element | Styling hook |
|
|
21
|
+
| --------------------------------- | -------- | --------------------------------------------- |
|
|
22
|
+
| `InputGroup` / `InputGroup.Root` | `<div>` | `data-input-group=""` |
|
|
23
|
+
| `InputGroup.LeadingAdornment` | `<span>` | `data-input-group-adornment="leading"` |
|
|
24
|
+
| `InputGroup.TrailingAdornment` | `<span>` | `data-input-group-adornment="trailing"` |
|
|
25
|
+
|
|
26
|
+
`InputGroup` is intentionally **not** input-specific. The wrapper maps
|
|
27
|
+
to the `framed-control/*` design-token anatomy (height, padding-inline,
|
|
28
|
+
gap, icon-size, radius) and works around `<Input>`, `<Textarea>`, or any
|
|
29
|
+
future framed control — the only thing tying it to `<Input>` is the
|
|
30
|
+
name.
|
|
31
|
+
|
|
32
|
+
## State coordination — there is none
|
|
33
|
+
|
|
34
|
+
`InputGroup` owns no state, provides no context, and accepts no
|
|
35
|
+
`disabled` / `invalid` props. State-dependent styling on the frame
|
|
36
|
+
delegates to CSS:
|
|
37
|
+
|
|
38
|
+
```css
|
|
39
|
+
[data-input-group] {
|
|
40
|
+
display: inline-flex;
|
|
41
|
+
align-items: center;
|
|
42
|
+
gap: var(--frame-gap);
|
|
43
|
+
padding-inline: var(--frame-padding-inline);
|
|
44
|
+
border: 1px solid var(--frame-border);
|
|
45
|
+
border-radius: var(--frame-radius);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/* Focus ring around the whole frame */
|
|
49
|
+
[data-input-group]:focus-within {
|
|
50
|
+
outline: 2px solid var(--focus-ring);
|
|
51
|
+
outline-offset: 1px;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/* Disabled-frame styling — driven by the inner input */
|
|
55
|
+
[data-input-group]:has(input:disabled) {
|
|
56
|
+
opacity: 0.5;
|
|
57
|
+
cursor: not-allowed;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/* Invalid-frame styling — same idea */
|
|
61
|
+
[data-input-group]:has(input[aria-invalid="true"]) {
|
|
62
|
+
border-color: var(--danger);
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
When `Field` lands it will sit *outside* `InputGroup` and own label /
|
|
67
|
+
description / error-text wiring; `InputGroup` will not need to change.
|
|
68
|
+
|
|
69
|
+
## Adornments
|
|
70
|
+
|
|
71
|
+
### Decorative
|
|
72
|
+
|
|
73
|
+
A decorative icon — give the icon `aria-hidden="true"` (or wrap with
|
|
74
|
+
`AccessibleIcon`). The default `<span>` adornment has no automatic ARIA
|
|
75
|
+
semantics — that's the consumer's call.
|
|
76
|
+
|
|
77
|
+
```tsx
|
|
78
|
+
<InputGroup>
|
|
79
|
+
<InputGroup.LeadingAdornment>
|
|
80
|
+
<MailIcon aria-hidden="true" />
|
|
81
|
+
</InputGroup.LeadingAdornment>
|
|
82
|
+
<Input type="email" aria-label="Email" />
|
|
83
|
+
</InputGroup>
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Interactive — `asChild` + `<button>`
|
|
87
|
+
|
|
88
|
+
For a clear button, password-reveal toggle, or any clickable adornment,
|
|
89
|
+
pass `asChild` and supply the `<button>` directly. The data attribute
|
|
90
|
+
and ref merge onto the button; event handlers compose (child runs
|
|
91
|
+
first).
|
|
92
|
+
|
|
93
|
+
```tsx
|
|
94
|
+
<InputGroup>
|
|
95
|
+
<Input value={q} onChange={onChange} aria-label="Search" />
|
|
96
|
+
<InputGroup.TrailingAdornment asChild>
|
|
97
|
+
<button type="button" aria-label="Clear" onClick={() => onChange("")}>
|
|
98
|
+
<XIcon aria-hidden="true" />
|
|
99
|
+
</button>
|
|
100
|
+
</InputGroup.TrailingAdornment>
|
|
101
|
+
</InputGroup>
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Leading and trailing together
|
|
105
|
+
|
|
106
|
+
JSX order matches visual order — put `LeadingAdornment` before the
|
|
107
|
+
control, `TrailingAdornment` after.
|
|
108
|
+
|
|
109
|
+
```tsx
|
|
110
|
+
<InputGroup>
|
|
111
|
+
<InputGroup.LeadingAdornment><MailIcon aria-hidden="true" /></InputGroup.LeadingAdornment>
|
|
112
|
+
<Input type="email" aria-label="Email" />
|
|
113
|
+
<InputGroup.TrailingAdornment asChild>
|
|
114
|
+
<button type="button" aria-label="Clear">
|
|
115
|
+
<XIcon aria-hidden="true" />
|
|
116
|
+
</button>
|
|
117
|
+
</InputGroup.TrailingAdornment>
|
|
118
|
+
</InputGroup>
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## `asChild` on Root
|
|
122
|
+
|
|
123
|
+
Pass `asChild` on `InputGroup.Root` to render the frame as a `<label>`
|
|
124
|
+
so clicking anywhere on the frame focuses the wrapped input:
|
|
125
|
+
|
|
126
|
+
```tsx
|
|
127
|
+
<InputGroup asChild>
|
|
128
|
+
<label>
|
|
129
|
+
<InputGroup.LeadingAdornment><SearchIcon aria-hidden="true" /></InputGroup.LeadingAdornment>
|
|
130
|
+
<Input type="search" />
|
|
131
|
+
</label>
|
|
132
|
+
</InputGroup>
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## With react-hook-form
|
|
136
|
+
|
|
137
|
+
`InputGroup` doesn't get in the way — spread `register` onto the inner
|
|
138
|
+
`<Input>` as usual:
|
|
139
|
+
|
|
140
|
+
```tsx
|
|
141
|
+
import { useForm } from "react-hook-form";
|
|
142
|
+
|
|
143
|
+
const { register, formState: { errors } } = useForm();
|
|
144
|
+
|
|
145
|
+
<label htmlFor="email">Email</label>
|
|
146
|
+
<InputGroup>
|
|
147
|
+
<InputGroup.LeadingAdornment><MailIcon aria-hidden="true" /></InputGroup.LeadingAdornment>
|
|
148
|
+
<Input
|
|
149
|
+
id="email"
|
|
150
|
+
type="email"
|
|
151
|
+
required
|
|
152
|
+
{...register("email")}
|
|
153
|
+
aria-invalid={!!errors.email}
|
|
154
|
+
aria-describedby="email-error"
|
|
155
|
+
/>
|
|
156
|
+
</InputGroup>
|
|
157
|
+
{errors.email && <span id="email-error">{errors.email.message}</span>}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## Ref forwarding
|
|
161
|
+
|
|
162
|
+
Every part accepts a `ref` prop:
|
|
163
|
+
|
|
164
|
+
```tsx
|
|
165
|
+
const groupRef = useRef<HTMLDivElement>(null);
|
|
166
|
+
const inputRef = useRef<HTMLInputElement>(null);
|
|
167
|
+
|
|
168
|
+
<InputGroup ref={groupRef}>
|
|
169
|
+
<Input ref={inputRef} aria-label="Search" />
|
|
170
|
+
</InputGroup>
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
## Coming next
|
|
174
|
+
|
|
175
|
+
- **Field** — the label / description / error-text coordinator that
|
|
176
|
+
wraps an `InputGroup` (or any other control) and auto-wires `id`,
|
|
177
|
+
`aria-describedby`, and the `invalid` cascade. Until it ships, do that
|
|
178
|
+
wiring manually.
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { createRef } from "react";
|
|
2
|
+
import { render, screen } from "@testing-library/react";
|
|
3
|
+
import userEvent from "@testing-library/user-event";
|
|
4
|
+
|
|
5
|
+
import { InputGroup } from "../InputGroup";
|
|
6
|
+
|
|
7
|
+
describe("InputGroup asChild composition", () => {
|
|
8
|
+
it("Root asChild renders the consumer element with data-input-group merged on", () => {
|
|
9
|
+
// Arrange & Act
|
|
10
|
+
render(
|
|
11
|
+
<InputGroup.Root asChild data-testid="group">
|
|
12
|
+
<label />
|
|
13
|
+
</InputGroup.Root>,
|
|
14
|
+
);
|
|
15
|
+
const root = screen.getByTestId("group");
|
|
16
|
+
|
|
17
|
+
// Assert
|
|
18
|
+
expect(root.tagName).toBe("LABEL");
|
|
19
|
+
expect(root).toHaveAttribute("data-input-group", "");
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it("LeadingAdornment asChild renders the consumer element with the leading data attribute", () => {
|
|
23
|
+
// Arrange & Act
|
|
24
|
+
render(
|
|
25
|
+
<InputGroup.Root>
|
|
26
|
+
<InputGroup.LeadingAdornment asChild data-testid="lead">
|
|
27
|
+
<button type="button">Search</button>
|
|
28
|
+
</InputGroup.LeadingAdornment>
|
|
29
|
+
</InputGroup.Root>,
|
|
30
|
+
);
|
|
31
|
+
const lead = screen.getByTestId("lead");
|
|
32
|
+
|
|
33
|
+
// Assert
|
|
34
|
+
expect(lead.tagName).toBe("BUTTON");
|
|
35
|
+
expect(lead).toHaveAttribute("data-input-group-adornment", "leading");
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("TrailingAdornment asChild renders the consumer element with the trailing data attribute", () => {
|
|
39
|
+
// Arrange & Act
|
|
40
|
+
render(
|
|
41
|
+
<InputGroup.Root>
|
|
42
|
+
<InputGroup.TrailingAdornment asChild data-testid="trail">
|
|
43
|
+
<button type="button">Clear</button>
|
|
44
|
+
</InputGroup.TrailingAdornment>
|
|
45
|
+
</InputGroup.Root>,
|
|
46
|
+
);
|
|
47
|
+
const trail = screen.getByTestId("trail");
|
|
48
|
+
|
|
49
|
+
// Assert
|
|
50
|
+
expect(trail.tagName).toBe("BUTTON");
|
|
51
|
+
expect(trail).toHaveAttribute("data-input-group-adornment", "trailing");
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it("composes onClick when an adornment wraps a button via asChild", async () => {
|
|
55
|
+
// Arrange
|
|
56
|
+
const user = userEvent.setup();
|
|
57
|
+
const order: string[] = [];
|
|
58
|
+
render(
|
|
59
|
+
<InputGroup.Root>
|
|
60
|
+
<InputGroup.TrailingAdornment
|
|
61
|
+
asChild
|
|
62
|
+
onClick={() => order.push("adornment")}
|
|
63
|
+
>
|
|
64
|
+
<button type="button" onClick={() => order.push("child")}>
|
|
65
|
+
Clear
|
|
66
|
+
</button>
|
|
67
|
+
</InputGroup.TrailingAdornment>
|
|
68
|
+
</InputGroup.Root>,
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
// Act
|
|
72
|
+
await user.click(screen.getByRole("button", { name: "Clear" }));
|
|
73
|
+
|
|
74
|
+
// Assert
|
|
75
|
+
expect(order).toEqual(["child", "adornment"]);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it("forwards a ref through Slot on the Root", () => {
|
|
79
|
+
// Arrange
|
|
80
|
+
const ref = createRef<HTMLLabelElement>();
|
|
81
|
+
|
|
82
|
+
// Act
|
|
83
|
+
render(
|
|
84
|
+
<InputGroup.Root asChild data-testid="group" ref={ref}>
|
|
85
|
+
<label />
|
|
86
|
+
</InputGroup.Root>,
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
// Assert
|
|
90
|
+
expect(ref.current).toBe(screen.getByTestId("group"));
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it("forwards a ref through Slot on an Adornment", () => {
|
|
94
|
+
// Arrange
|
|
95
|
+
const ref = createRef<HTMLButtonElement>();
|
|
96
|
+
|
|
97
|
+
// Act
|
|
98
|
+
render(
|
|
99
|
+
<InputGroup.Root>
|
|
100
|
+
<InputGroup.TrailingAdornment asChild ref={ref}>
|
|
101
|
+
<button type="button">Clear</button>
|
|
102
|
+
</InputGroup.TrailingAdornment>
|
|
103
|
+
</InputGroup.Root>,
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
// Assert
|
|
107
|
+
expect(ref.current).toBe(screen.getByRole("button", { name: "Clear" }));
|
|
108
|
+
});
|
|
109
|
+
});
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { createRef } from "react";
|
|
2
|
+
import { render, screen } from "@testing-library/react";
|
|
3
|
+
|
|
4
|
+
import { InputGroup } from "../InputGroup";
|
|
5
|
+
|
|
6
|
+
describe("InputGroup basic rendering", () => {
|
|
7
|
+
it("renders a <div> wrapper element", () => {
|
|
8
|
+
// Arrange & Act
|
|
9
|
+
render(<InputGroup.Root data-testid="group" />);
|
|
10
|
+
|
|
11
|
+
// Assert
|
|
12
|
+
expect(screen.getByTestId("group").tagName).toBe("DIV");
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it("sets data-input-group on the root as a CSS styling hook", () => {
|
|
16
|
+
// Arrange & Act
|
|
17
|
+
render(<InputGroup.Root data-testid="group" />);
|
|
18
|
+
|
|
19
|
+
// Assert
|
|
20
|
+
expect(screen.getByTestId("group")).toHaveAttribute(
|
|
21
|
+
"data-input-group",
|
|
22
|
+
"",
|
|
23
|
+
);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it("InputGroup is callable as an alias of InputGroup.Root", () => {
|
|
27
|
+
// Arrange & Act
|
|
28
|
+
render(<InputGroup data-testid="group" />);
|
|
29
|
+
|
|
30
|
+
// Assert
|
|
31
|
+
expect(screen.getByTestId("group").tagName).toBe("DIV");
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it("forwards a ref to the root element", () => {
|
|
35
|
+
// Arrange
|
|
36
|
+
const ref = createRef<HTMLDivElement>();
|
|
37
|
+
|
|
38
|
+
// Act
|
|
39
|
+
render(<InputGroup.Root data-testid="group" ref={ref} />);
|
|
40
|
+
|
|
41
|
+
// Assert
|
|
42
|
+
expect(ref.current).toBe(screen.getByTestId("group"));
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it("renders LeadingAdornment as a <span> with data-input-group-adornment='leading'", () => {
|
|
46
|
+
// Arrange & Act
|
|
47
|
+
render(
|
|
48
|
+
<InputGroup.Root>
|
|
49
|
+
<InputGroup.LeadingAdornment data-testid="lead" />
|
|
50
|
+
</InputGroup.Root>,
|
|
51
|
+
);
|
|
52
|
+
const span = screen.getByTestId("lead");
|
|
53
|
+
|
|
54
|
+
// Assert
|
|
55
|
+
expect(span.tagName).toBe("SPAN");
|
|
56
|
+
expect(span).toHaveAttribute("data-input-group-adornment", "leading");
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("renders TrailingAdornment as a <span> with data-input-group-adornment='trailing'", () => {
|
|
60
|
+
// Arrange & Act
|
|
61
|
+
render(
|
|
62
|
+
<InputGroup.Root>
|
|
63
|
+
<InputGroup.TrailingAdornment data-testid="trail" />
|
|
64
|
+
</InputGroup.Root>,
|
|
65
|
+
);
|
|
66
|
+
const span = screen.getByTestId("trail");
|
|
67
|
+
|
|
68
|
+
// Assert
|
|
69
|
+
expect(span.tagName).toBe("SPAN");
|
|
70
|
+
expect(span).toHaveAttribute("data-input-group-adornment", "trailing");
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it("renders children inside the wrapper", () => {
|
|
74
|
+
// Arrange & Act
|
|
75
|
+
render(
|
|
76
|
+
<InputGroup.Root>
|
|
77
|
+
<input aria-label="Search" />
|
|
78
|
+
</InputGroup.Root>,
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
// Assert
|
|
82
|
+
expect(screen.getByRole("textbox", { name: "Search" })).toBeInTheDocument();
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it("passes through className on the root", () => {
|
|
86
|
+
// Arrange & Act
|
|
87
|
+
render(<InputGroup.Root className="frame" data-testid="g" />);
|
|
88
|
+
|
|
89
|
+
// Assert
|
|
90
|
+
expect(screen.getByTestId("g")).toHaveClass("frame");
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it("passes through className on adornments", () => {
|
|
94
|
+
// Arrange & Act
|
|
95
|
+
render(
|
|
96
|
+
<InputGroup.Root>
|
|
97
|
+
<InputGroup.LeadingAdornment className="lead" data-testid="lead" />
|
|
98
|
+
<InputGroup.TrailingAdornment className="trail" data-testid="trail" />
|
|
99
|
+
</InputGroup.Root>,
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
// Assert
|
|
103
|
+
expect(screen.getByTestId("lead")).toHaveClass("lead");
|
|
104
|
+
expect(screen.getByTestId("trail")).toHaveClass("trail");
|
|
105
|
+
});
|
|
106
|
+
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { ComponentProps, ReactNode, Ref } from "react";
|
|
2
|
+
|
|
3
|
+
export type InputGroupRootProps = ComponentProps<"div"> & {
|
|
4
|
+
asChild?: boolean;
|
|
5
|
+
ref?: Ref<HTMLDivElement>;
|
|
6
|
+
children?: ReactNode;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export type InputGroupAdornmentProps = ComponentProps<"span"> & {
|
|
10
|
+
asChild?: boolean;
|
|
11
|
+
ref?: Ref<HTMLSpanElement>;
|
|
12
|
+
children?: ReactNode;
|
|
13
|
+
};
|