@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,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns an event handler that runs the consumer-supplied handler first,
|
|
3
|
+
* then the library's own handler — unless the consumer called
|
|
4
|
+
* `event.preventDefault()`, in which case the library handler is skipped.
|
|
5
|
+
*
|
|
6
|
+
* This is the standard composition pattern used throughout headless
|
|
7
|
+
* components to let consumers attach their own listeners onto a
|
|
8
|
+
* sub-component without clobbering the component's own behaviour, while
|
|
9
|
+
* still giving them an opt-out via `preventDefault()`.
|
|
10
|
+
*
|
|
11
|
+
* @example Consumer logs every click; component still closes the modal
|
|
12
|
+
* afterwards:
|
|
13
|
+
* ```tsx
|
|
14
|
+
* <Modal.Overlay onClick={(e) => console.log("overlay clicked")} />
|
|
15
|
+
* ```
|
|
16
|
+
*
|
|
17
|
+
* @example Consumer vetoes the component's behaviour:
|
|
18
|
+
* ```tsx
|
|
19
|
+
* <Modal.Overlay onClick={(e) => {
|
|
20
|
+
* if (formIsDirty) e.preventDefault(); // don't close
|
|
21
|
+
* }} />
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export function composeEventHandlers<E>(
|
|
25
|
+
theirHandler: ((event: E) => void) | undefined,
|
|
26
|
+
ourHandler: (event: E) => void,
|
|
27
|
+
{ checkForDefaultPrevented = true }: { checkForDefaultPrevented?: boolean } = {},
|
|
28
|
+
) {
|
|
29
|
+
return function handleEvent(event: E) {
|
|
30
|
+
theirHandler?.(event);
|
|
31
|
+
if (
|
|
32
|
+
!checkForDefaultPrevented ||
|
|
33
|
+
!(event as unknown as Event).defaultPrevented
|
|
34
|
+
) {
|
|
35
|
+
ourHandler(event);
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Status
|
|
2
|
+
|
|
3
|
+
A polite live region for low-priority, non-urgent status messages,
|
|
4
|
+
implementing the
|
|
5
|
+
[WAI-ARIA `status` role](https://www.w3.org/TR/wai-aria-1.2/#status).
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
import { Status } from "@primitiv-ui/react";
|
|
9
|
+
|
|
10
|
+
{
|
|
11
|
+
saved && <Status>All changes saved.</Status>;
|
|
12
|
+
}
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Props
|
|
16
|
+
|
|
17
|
+
| Prop | Type | Default | Notes |
|
|
18
|
+
| ----------- | ----------------------- | ------- | -------------------------------------------------- |
|
|
19
|
+
| `asChild` | `boolean` | `false` | Render the consumer's element instead of a `<div>` |
|
|
20
|
+
| `className` | `string` | — | Applied directly to the rendered element |
|
|
21
|
+
| `...rest` | `ComponentProps<"div">` | — | All other `<div>` props, including `aria-*` |
|
|
22
|
+
|
|
23
|
+
## Behaviour
|
|
24
|
+
|
|
25
|
+
`Status` renders a `<div role="status">`. The `status` role carries an
|
|
26
|
+
implicit `aria-live="polite"` and `aria-atomic="true"`, so assistive
|
|
27
|
+
technology announces the message once the user is idle, without
|
|
28
|
+
interrupting them. Use it for confirmations, counts, and background
|
|
29
|
+
progress — for errors the user must see immediately, reach for
|
|
30
|
+
[`Alert`](../Alert/README.md).
|
|
31
|
+
|
|
32
|
+
## Announce on change
|
|
33
|
+
|
|
34
|
+
A live region announces content that changes *after* the region is in
|
|
35
|
+
the DOM. Either render the `Status` conditionally, or keep it mounted
|
|
36
|
+
and update its text content:
|
|
37
|
+
|
|
38
|
+
```tsx
|
|
39
|
+
<Status>{count} items in your cart</Status>
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## asChild
|
|
43
|
+
|
|
44
|
+
Pass `asChild` to apply `role="status"` to the consumer's own element:
|
|
45
|
+
|
|
46
|
+
```tsx
|
|
47
|
+
<Status asChild>
|
|
48
|
+
<output>{count} items in your cart</output>
|
|
49
|
+
</Status>
|
|
50
|
+
```
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { Slot } from "../Slot";
|
|
2
|
+
import { StatusProps } from "./types";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* A polite live region for low-priority, non-urgent status messages.
|
|
6
|
+
*
|
|
7
|
+
* Renders a `<div role="status">`. The `status` role carries an
|
|
8
|
+
* implicit `aria-live="polite"` and `aria-atomic="true"`, so assistive
|
|
9
|
+
* technology announces the message once the user is idle, without
|
|
10
|
+
* interrupting them — use it for confirmations, counts, and background
|
|
11
|
+
* progress. For errors the user must see immediately, reach for
|
|
12
|
+
* `Alert` instead.
|
|
13
|
+
*
|
|
14
|
+
* Render the `Status` conditionally, or update its text content while
|
|
15
|
+
* it stays mounted: the live region announces content that changes
|
|
16
|
+
* *after* it is already in the DOM.
|
|
17
|
+
*
|
|
18
|
+
* **`asChild` composition.** Renders the consumer's element instead of
|
|
19
|
+
* a `<div>`, merging `role="status"` and all other props in via the
|
|
20
|
+
* {@link Slot} utility.
|
|
21
|
+
*
|
|
22
|
+
* @example Confirmation message
|
|
23
|
+
* ```tsx
|
|
24
|
+
* {saved && <Status>All changes saved.</Status>}
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* @example asChild — keep semantic markup
|
|
28
|
+
* ```tsx
|
|
29
|
+
* <Status asChild>
|
|
30
|
+
* <output>{count} items in your cart</output>
|
|
31
|
+
* </Status>
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
export function Status({ asChild = false, children, ...rest }: StatusProps) {
|
|
35
|
+
const rootProps = { role: "status", ...rest };
|
|
36
|
+
|
|
37
|
+
if (asChild) {
|
|
38
|
+
return <Slot {...rootProps}>{children}</Slot>;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return <div {...rootProps}>{children}</div>;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
Status.displayName = "Status";
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Status } from "..";
|
|
2
|
+
import { render, screen } from "@testing-library/react";
|
|
3
|
+
|
|
4
|
+
describe("Status component", () => {
|
|
5
|
+
it("should render a div with role status containing its children", () => {
|
|
6
|
+
// Arrange
|
|
7
|
+
render(<Status>3 items added to your cart</Status>);
|
|
8
|
+
|
|
9
|
+
// Assert
|
|
10
|
+
const status = screen.getByRole("status");
|
|
11
|
+
expect(status.tagName).toBe("DIV");
|
|
12
|
+
expect(status).toHaveTextContent("3 items added to your cart");
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it("should render the consumer element with asChild, keeping role status", () => {
|
|
16
|
+
// Arrange
|
|
17
|
+
render(
|
|
18
|
+
<Status asChild>
|
|
19
|
+
<section>Saved</section>
|
|
20
|
+
</Status>,
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
// Assert
|
|
24
|
+
const status = screen.getByRole("status");
|
|
25
|
+
expect(status.tagName).toBe("SECTION");
|
|
26
|
+
expect(status).toHaveTextContent("Saved");
|
|
27
|
+
});
|
|
28
|
+
});
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# Switch
|
|
2
|
+
|
|
3
|
+
Headless, accessible **Switch** — a compound component implementing the
|
|
4
|
+
[WAI-ARIA Switch pattern](https://www.w3.org/WAI/ARIA/apg/patterns/switch/)
|
|
5
|
+
on a native `<button role="switch">`. Semantically represents an immediate
|
|
6
|
+
on/off action (as opposed to a selection choice). Zero styles ship.
|
|
7
|
+
|
|
8
|
+
```tsx
|
|
9
|
+
import { Switch } from "@primitiv-ui/react";
|
|
10
|
+
|
|
11
|
+
<Switch.Root defaultChecked aria-label="Enable notifications">
|
|
12
|
+
<Switch.Thumb />
|
|
13
|
+
</Switch.Root>
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Sub-components
|
|
17
|
+
|
|
18
|
+
| Export | Element | ARIA / data hooks | `asChild` |
|
|
19
|
+
|--------|---------|------------------|-----------|
|
|
20
|
+
| `Switch.Root` | `<button>` | `role="switch"`, `aria-checked`, `data-state`, `data-disabled` | yes |
|
|
21
|
+
| `Switch.Thumb` | `<span>` | `aria-hidden="true"`, `data-state` | yes |
|
|
22
|
+
|
|
23
|
+
## State modes
|
|
24
|
+
|
|
25
|
+
### Uncontrolled
|
|
26
|
+
|
|
27
|
+
Pass `defaultChecked` (or omit for unchecked on mount). The component owns
|
|
28
|
+
the value internally.
|
|
29
|
+
|
|
30
|
+
```tsx
|
|
31
|
+
<Switch.Root defaultChecked aria-label="Enable dark mode">
|
|
32
|
+
<Switch.Thumb />
|
|
33
|
+
</Switch.Root>
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Controlled
|
|
37
|
+
|
|
38
|
+
Pass `checked` and `onCheckedChange` together. The parent owns the value.
|
|
39
|
+
|
|
40
|
+
```tsx
|
|
41
|
+
const [enabled, setEnabled] = useState(false);
|
|
42
|
+
|
|
43
|
+
<Switch.Root checked={enabled} onCheckedChange={setEnabled} aria-label="…">
|
|
44
|
+
<Switch.Thumb />
|
|
45
|
+
</Switch.Root>
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Keyboard interaction
|
|
49
|
+
|
|
50
|
+
| Key | Behaviour |
|
|
51
|
+
|-----|-----------|
|
|
52
|
+
| `Space` | Toggle the switch (native `<button>` behaviour) |
|
|
53
|
+
| `Enter` | Toggle the switch (native `<button>` behaviour) |
|
|
54
|
+
| `Tab` | Move focus to or from the switch |
|
|
55
|
+
|
|
56
|
+
## Disabled
|
|
57
|
+
|
|
58
|
+
Pass `disabled` on the Root. The native attribute suppresses clicks and removes
|
|
59
|
+
the switch from the focus ring. `data-disabled=""` is set for CSS targeting.
|
|
60
|
+
|
|
61
|
+
```tsx
|
|
62
|
+
<Switch.Root aria-label="Enable feature" disabled>
|
|
63
|
+
<Switch.Thumb />
|
|
64
|
+
</Switch.Root>
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## The Thumb
|
|
68
|
+
|
|
69
|
+
`Switch.Thumb` is **always mounted** — unlike `Checkbox.Indicator`, it never
|
|
70
|
+
conditionally unmounts. Its visual position (left for off, right for on) is
|
|
71
|
+
driven entirely by CSS targeting `data-state`. This gives consumers a real DOM
|
|
72
|
+
node to animate with `transition` or Web Animations, rather than being
|
|
73
|
+
constrained to pseudo-elements.
|
|
74
|
+
|
|
75
|
+
```scss
|
|
76
|
+
.switch-root {
|
|
77
|
+
position: relative;
|
|
78
|
+
width: 2.75rem;
|
|
79
|
+
height: 1.5rem;
|
|
80
|
+
border-radius: 9999px;
|
|
81
|
+
background: #d1d5db;
|
|
82
|
+
transition: background 120ms ease;
|
|
83
|
+
|
|
84
|
+
&[data-state="checked"] {
|
|
85
|
+
background: #6366f1;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.switch-thumb {
|
|
90
|
+
position: absolute;
|
|
91
|
+
top: 0.125rem;
|
|
92
|
+
left: 0.125rem;
|
|
93
|
+
width: 1.25rem;
|
|
94
|
+
height: 1.25rem;
|
|
95
|
+
border-radius: 50%;
|
|
96
|
+
background: white;
|
|
97
|
+
transition: translate 120ms ease;
|
|
98
|
+
|
|
99
|
+
&[data-state="checked"] {
|
|
100
|
+
translate: 1.25rem 0;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## `asChild` composition
|
|
106
|
+
|
|
107
|
+
Both sub-components accept `asChild`. The library's ARIA attributes,
|
|
108
|
+
`data-state`, event handlers, and ref are merged onto the consumer's element.
|
|
109
|
+
|
|
110
|
+
```tsx
|
|
111
|
+
<Switch.Root asChild aria-label="Enable notifications">
|
|
112
|
+
<div role="switch">…</div>
|
|
113
|
+
</Switch.Root>
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## Styling hooks
|
|
117
|
+
|
|
118
|
+
| Attribute | Values | Set on |
|
|
119
|
+
|-----------|--------|--------|
|
|
120
|
+
| `data-state` | `"checked"` \| `"unchecked"` | `Switch.Root`, `Switch.Thumb` |
|
|
121
|
+
| `data-disabled` | `""` (present when disabled) | `Switch.Root` |
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { useMemo } from "react";
|
|
2
|
+
|
|
3
|
+
import { Slot, composeEventHandlers } from "../Slot";
|
|
4
|
+
|
|
5
|
+
import { SwitchContext } from "./SwitchContext";
|
|
6
|
+
import { useSwitchContext, useSwitchRoot } from "./hooks";
|
|
7
|
+
import { SwitchRootProps, SwitchThumbProps } from "./types";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* The root of a Switch — a native `<button type="button" role="switch">` that
|
|
11
|
+
* owns the binary checked state and provides {@link SwitchContext} to
|
|
12
|
+
* descendant {@link SwitchThumb | `Switch.Thumb`}s.
|
|
13
|
+
*
|
|
14
|
+
* Supports two state modes, statically discriminated at the type level:
|
|
15
|
+
*
|
|
16
|
+
* - **Uncontrolled** — pass
|
|
17
|
+
* {@link SwitchRootProps.defaultChecked | `defaultChecked`} (or omit for
|
|
18
|
+
* unchecked-on-mount). The component owns the value internally.
|
|
19
|
+
* - **Controlled** — pass
|
|
20
|
+
* {@link SwitchRootProps.checked | `checked`} *and*
|
|
21
|
+
* {@link SwitchRootProps.onCheckedChange | `onCheckedChange`} together.
|
|
22
|
+
* The parent owns the value; every click defers back through the callback.
|
|
23
|
+
*
|
|
24
|
+
* **ARIA.** `role="switch"` and `aria-checked` are set automatically.
|
|
25
|
+
* Provide an accessible name via `aria-label`, `aria-labelledby`, or
|
|
26
|
+
* a visible label element.
|
|
27
|
+
*
|
|
28
|
+
* **Styling hooks.** `data-state="checked" | "unchecked"` on the root, plus
|
|
29
|
+
* `data-disabled=""` when disabled.
|
|
30
|
+
*
|
|
31
|
+
* **`asChild` prop.** Pass `asChild` to render any consumer-supplied element
|
|
32
|
+
* with the switch's ARIA attributes, `data-state`, composed `onClick`, and
|
|
33
|
+
* `ref` merged in. The native `<button>` is dropped.
|
|
34
|
+
*
|
|
35
|
+
* @example Uncontrolled
|
|
36
|
+
* ```tsx
|
|
37
|
+
* <Switch.Root defaultChecked aria-label="Enable notifications">
|
|
38
|
+
* <Switch.Thumb />
|
|
39
|
+
* </Switch.Root>
|
|
40
|
+
* ```
|
|
41
|
+
*
|
|
42
|
+
* @example Controlled
|
|
43
|
+
* ```tsx
|
|
44
|
+
* const [enabled, setEnabled] = useState(false);
|
|
45
|
+
*
|
|
46
|
+
* <Switch.Root checked={enabled} onCheckedChange={setEnabled} aria-label="…">
|
|
47
|
+
* <Switch.Thumb />
|
|
48
|
+
* </Switch.Root>
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
function SwitchRoot({
|
|
52
|
+
defaultChecked,
|
|
53
|
+
checked,
|
|
54
|
+
onCheckedChange,
|
|
55
|
+
disabled,
|
|
56
|
+
asChild = false,
|
|
57
|
+
onClick,
|
|
58
|
+
children,
|
|
59
|
+
ref,
|
|
60
|
+
...rest
|
|
61
|
+
}: SwitchRootProps) {
|
|
62
|
+
const { checked: isChecked, toggle } = useSwitchRoot({
|
|
63
|
+
defaultChecked,
|
|
64
|
+
checked,
|
|
65
|
+
onCheckedChange,
|
|
66
|
+
});
|
|
67
|
+
const contextValue = useMemo(() => ({ checked: isChecked }), [isChecked]);
|
|
68
|
+
const rootProps = {
|
|
69
|
+
...rest,
|
|
70
|
+
ref,
|
|
71
|
+
role: "switch" as const,
|
|
72
|
+
"aria-checked": isChecked,
|
|
73
|
+
"data-state": isChecked ? ("checked" as const) : ("unchecked" as const),
|
|
74
|
+
"data-disabled": disabled ? "" : undefined,
|
|
75
|
+
disabled,
|
|
76
|
+
onClick: composeEventHandlers(onClick, toggle),
|
|
77
|
+
};
|
|
78
|
+
return (
|
|
79
|
+
<SwitchContext.Provider value={contextValue}>
|
|
80
|
+
{asChild ? (
|
|
81
|
+
<Slot {...rootProps}>{children}</Slot>
|
|
82
|
+
) : (
|
|
83
|
+
<button type="button" {...rootProps}>
|
|
84
|
+
{children}
|
|
85
|
+
</button>
|
|
86
|
+
)}
|
|
87
|
+
</SwitchContext.Provider>
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
SwitchRoot.displayName = "SwitchRoot";
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* A decorative `<span aria-hidden="true">` that represents the sliding thumb
|
|
95
|
+
* of the switch. Always mounted — its position is driven entirely by CSS
|
|
96
|
+
* targeting `data-state="checked" | "unchecked"`. Unlike
|
|
97
|
+
* {@link Checkbox.Indicator}, it never conditionally unmounts.
|
|
98
|
+
*
|
|
99
|
+
* **Styling hook.** Mirrors the parent Root's
|
|
100
|
+
* `data-state="checked" | "unchecked"` for CSS transitions.
|
|
101
|
+
*
|
|
102
|
+
* **`asChild` prop.** Pass `asChild` to render the consumer's own element
|
|
103
|
+
* as the thumb, with `aria-hidden` and `data-state` merged in.
|
|
104
|
+
*
|
|
105
|
+
* @example
|
|
106
|
+
* ```tsx
|
|
107
|
+
* <Switch.Root aria-label="Enable notifications">
|
|
108
|
+
* <Switch.Thumb />
|
|
109
|
+
* </Switch.Root>
|
|
110
|
+
* ```
|
|
111
|
+
*
|
|
112
|
+
* @throws if rendered outside a `Switch.Root`.
|
|
113
|
+
*/
|
|
114
|
+
function SwitchThumb({ children, asChild = false, ...rest }: SwitchThumbProps) {
|
|
115
|
+
const { checked } = useSwitchContext();
|
|
116
|
+
const thumbProps = {
|
|
117
|
+
...rest,
|
|
118
|
+
"aria-hidden": "true" as const,
|
|
119
|
+
"data-state": checked ? ("checked" as const) : ("unchecked" as const),
|
|
120
|
+
};
|
|
121
|
+
if (asChild) {
|
|
122
|
+
return <Slot {...thumbProps}>{children}</Slot>;
|
|
123
|
+
}
|
|
124
|
+
return <span {...thumbProps}>{children}</span>;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
SwitchThumb.displayName = "SwitchThumb";
|
|
128
|
+
|
|
129
|
+
type TSwitchCompound = typeof SwitchRoot & {
|
|
130
|
+
Root: typeof SwitchRoot;
|
|
131
|
+
Thumb: typeof SwitchThumb;
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Headless, accessible **Switch** — a compound component implementing the
|
|
136
|
+
* [WAI-ARIA Switch pattern](https://www.w3.org/WAI/ARIA/apg/patterns/switch/)
|
|
137
|
+
* on a native `<button role="switch">`. Semantically represents an immediate
|
|
138
|
+
* on/off action (as opposed to a selection choice). Zero styles ship.
|
|
139
|
+
*
|
|
140
|
+
* `Switch` is both callable (an alias of
|
|
141
|
+
* {@link SwitchRoot | `Switch.Root`}) and carries its sub-components as
|
|
142
|
+
* static properties.
|
|
143
|
+
*
|
|
144
|
+
* - {@link SwitchRoot | `Switch.Root`} — state owner, context provider, toggle button.
|
|
145
|
+
* - {@link SwitchThumb | `Switch.Thumb`} — always-mounted decorative thumb;
|
|
146
|
+
* position driven by `data-state` via CSS.
|
|
147
|
+
*
|
|
148
|
+
* @example Minimal usage
|
|
149
|
+
* ```tsx
|
|
150
|
+
* import { Switch } from "@primitiv-ui/react";
|
|
151
|
+
*
|
|
152
|
+
* <Switch.Root aria-label="Enable notifications">
|
|
153
|
+
* <Switch.Thumb />
|
|
154
|
+
* </Switch.Root>
|
|
155
|
+
* ```
|
|
156
|
+
*
|
|
157
|
+
* @see {@link SwitchRoot} for state modes and ARIA details.
|
|
158
|
+
* @see {@link SwitchThumb} for styling the sliding thumb.
|
|
159
|
+
*/
|
|
160
|
+
const SwitchCompound: TSwitchCompound = Object.assign(SwitchRoot, {
|
|
161
|
+
Root: SwitchRoot,
|
|
162
|
+
Thumb: SwitchThumb,
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
SwitchCompound.displayName = "Switch";
|
|
166
|
+
|
|
167
|
+
export { SwitchCompound as Switch };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { createStrictContext } from "../utils";
|
|
2
|
+
|
|
3
|
+
export type SwitchContextValue = {
|
|
4
|
+
checked: boolean;
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export const [SwitchContext, useSwitchContext] =
|
|
8
|
+
createStrictContext<SwitchContextValue>(
|
|
9
|
+
"Switch.Thumb must be rendered inside a <Switch.Root>.",
|
|
10
|
+
);
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { render, screen } from "@testing-library/react";
|
|
2
|
+
import userEvent from "@testing-library/user-event";
|
|
3
|
+
|
|
4
|
+
import { Switch } from "../Switch";
|
|
5
|
+
|
|
6
|
+
describe("Switch asChild", () => {
|
|
7
|
+
it("renders the Root as the consumer element when asChild is set", () => {
|
|
8
|
+
// Arrange & Act
|
|
9
|
+
render(
|
|
10
|
+
<Switch.Root asChild aria-label="Enable notifications">
|
|
11
|
+
<div>Toggle</div>
|
|
12
|
+
</Switch.Root>,
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
// Assert — div rendered, not button; role and aria-checked merged
|
|
16
|
+
const sw = screen.getByRole("switch", { name: "Enable notifications" });
|
|
17
|
+
expect(sw.tagName).toBe("DIV");
|
|
18
|
+
expect(sw).toHaveAttribute("aria-checked", "false");
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it("merges onClick and toggles state on the asChild Root element", async () => {
|
|
22
|
+
// Arrange
|
|
23
|
+
const user = userEvent.setup();
|
|
24
|
+
const onClick = vi.fn();
|
|
25
|
+
render(
|
|
26
|
+
<Switch.Root asChild aria-label="Enable notifications" onClick={onClick}>
|
|
27
|
+
<div>Toggle</div>
|
|
28
|
+
</Switch.Root>,
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
// Act
|
|
32
|
+
await user.click(screen.getByRole("switch", { name: "Enable notifications" }));
|
|
33
|
+
|
|
34
|
+
// Assert — consumer onClick ran and state toggled
|
|
35
|
+
expect(onClick).toHaveBeenCalledOnce();
|
|
36
|
+
expect(
|
|
37
|
+
screen.getByRole("switch", { name: "Enable notifications" }),
|
|
38
|
+
).toHaveAttribute("aria-checked", "true");
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("renders the Thumb as the consumer element when asChild is set", () => {
|
|
42
|
+
// Arrange & Act
|
|
43
|
+
render(
|
|
44
|
+
<Switch.Root aria-label="Enable notifications">
|
|
45
|
+
<Switch.Thumb asChild>
|
|
46
|
+
<span data-testid="custom-thumb" />
|
|
47
|
+
</Switch.Thumb>
|
|
48
|
+
</Switch.Root>,
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
// Assert — our span rendered; aria-hidden and data-state merged onto it
|
|
52
|
+
const thumb = screen.getByTestId("custom-thumb");
|
|
53
|
+
expect(thumb).toHaveAttribute("aria-hidden", "true");
|
|
54
|
+
expect(thumb).toHaveAttribute("data-state", "unchecked");
|
|
55
|
+
});
|
|
56
|
+
});
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { render, screen } from "@testing-library/react";
|
|
2
|
+
|
|
3
|
+
import { Switch } from "../Switch";
|
|
4
|
+
|
|
5
|
+
describe("Switch basic rendering", () => {
|
|
6
|
+
it('renders a <button type="button" role="switch">', () => {
|
|
7
|
+
// Arrange & Act
|
|
8
|
+
render(<Switch.Root aria-label="Enable notifications" />);
|
|
9
|
+
const sw = screen.getByRole("switch", { name: "Enable notifications" });
|
|
10
|
+
|
|
11
|
+
// Assert
|
|
12
|
+
expect(sw.tagName).toBe("BUTTON");
|
|
13
|
+
expect(sw).toHaveAttribute("type", "button");
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('defaults aria-checked="false" when unchecked', () => {
|
|
17
|
+
// Arrange & Act
|
|
18
|
+
render(<Switch.Root aria-label="Enable notifications" />);
|
|
19
|
+
|
|
20
|
+
// Assert
|
|
21
|
+
expect(
|
|
22
|
+
screen.getByRole("switch", { name: "Enable notifications" }),
|
|
23
|
+
).toHaveAttribute("aria-checked", "false");
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('defaults data-state="unchecked" on mount', () => {
|
|
27
|
+
// Arrange & Act
|
|
28
|
+
render(<Switch.Root aria-label="Enable notifications" />);
|
|
29
|
+
|
|
30
|
+
// Assert
|
|
31
|
+
expect(
|
|
32
|
+
screen.getByRole("switch", { name: "Enable notifications" }),
|
|
33
|
+
).toHaveAttribute("data-state", "unchecked");
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it("renders Switch.Thumb as a <span> inside the root", () => {
|
|
37
|
+
// Arrange & Act
|
|
38
|
+
render(
|
|
39
|
+
<Switch.Root aria-label="Enable notifications">
|
|
40
|
+
<Switch.Thumb data-testid="thumb" />
|
|
41
|
+
</Switch.Root>,
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
// Assert
|
|
45
|
+
const thumb = screen.getByTestId("thumb");
|
|
46
|
+
expect(thumb.tagName).toBe("SPAN");
|
|
47
|
+
expect(thumb).toBeInTheDocument();
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('sets aria-hidden="true" on the Thumb', () => {
|
|
51
|
+
// Arrange & Act
|
|
52
|
+
render(
|
|
53
|
+
<Switch.Root aria-label="Enable notifications">
|
|
54
|
+
<Switch.Thumb data-testid="thumb" />
|
|
55
|
+
</Switch.Root>,
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
// Assert
|
|
59
|
+
expect(screen.getByTestId("thumb")).toHaveAttribute("aria-hidden", "true");
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('sets data-state="unchecked" on Thumb by default', () => {
|
|
63
|
+
// Arrange & Act
|
|
64
|
+
render(
|
|
65
|
+
<Switch.Root aria-label="Enable notifications">
|
|
66
|
+
<Switch.Thumb data-testid="thumb" />
|
|
67
|
+
</Switch.Root>,
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
// Assert
|
|
71
|
+
expect(screen.getByTestId("thumb")).toHaveAttribute(
|
|
72
|
+
"data-state",
|
|
73
|
+
"unchecked",
|
|
74
|
+
);
|
|
75
|
+
});
|
|
76
|
+
});
|