@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,214 @@
|
|
|
1
|
+
# Tooltip
|
|
2
|
+
|
|
3
|
+
A headless, accessible compound component implementing the
|
|
4
|
+
[WAI-ARIA Tooltip pattern](https://www.w3.org/WAI/ARIA/apg/patterns/tooltip/).
|
|
5
|
+
|
|
6
|
+
Tooltip is built as a pure React state machine with no floating-UI or
|
|
7
|
+
measurement dependency. Positioning is fully the consumer's responsibility
|
|
8
|
+
via CSS Anchor Positioning — the component emits no inline styles.
|
|
9
|
+
|
|
10
|
+
- Hover and focus open/close with configurable delay.
|
|
11
|
+
- Provider-level skip-delay: a second tooltip opens instantly when one is
|
|
12
|
+
already open and the cursor moves between triggers.
|
|
13
|
+
- Grace period: 100 ms after leaving the trigger lets users move the cursor
|
|
14
|
+
into the tooltip content without it closing.
|
|
15
|
+
- `onEscapeKeyDown` and `onPointerDownOutside` escape hatches.
|
|
16
|
+
- Portal support for breaking out of stacking contexts.
|
|
17
|
+
- `forceMount` on Portal and Content for CSS exit animations.
|
|
18
|
+
- `asChild` composition on Trigger and Arrow.
|
|
19
|
+
|
|
20
|
+
```tsx
|
|
21
|
+
import { Tooltip } from "@primitiv-ui/react";
|
|
22
|
+
|
|
23
|
+
<Tooltip.Provider>
|
|
24
|
+
<Tooltip.Root>
|
|
25
|
+
<Tooltip.Trigger className="save-btn">Save</Tooltip.Trigger>
|
|
26
|
+
<Tooltip.Portal>
|
|
27
|
+
<Tooltip.Content className="tooltip">
|
|
28
|
+
Save your changes
|
|
29
|
+
</Tooltip.Content>
|
|
30
|
+
</Tooltip.Portal>
|
|
31
|
+
</Tooltip.Root>
|
|
32
|
+
</Tooltip.Provider>
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Sub-components
|
|
36
|
+
|
|
37
|
+
| Export | Element | Notes |
|
|
38
|
+
| ------------------- | ---------------- | ------------------------------------------------------------------------------------------------------ |
|
|
39
|
+
| `Tooltip.Provider` | context provider | Sets `delayDuration` (default 700 ms) and `skipDelayDuration` (default 300 ms) for all descendants |
|
|
40
|
+
| `Tooltip.Root` | context provider | Owns open state per tooltip. `open`, `defaultOpen`, `onOpenChange`, `delayDuration`, `disableHoverableContent` |
|
|
41
|
+
| `Tooltip.Trigger` | `<button>` | Wires `aria-describedby`, hover/focus handlers. `asChild` |
|
|
42
|
+
| `Tooltip.Portal` | `createPortal` | `container?: HTMLElement` (default `document.body`). `forceMount` |
|
|
43
|
+
| `Tooltip.Content` | `<div role="tooltip">` | `data-state`. Escape hatches for Esc / pointer-down-outside. `forceMount` |
|
|
44
|
+
| `Tooltip.Arrow` | `<span>` | Optional visual pointer. All positioning via consumer CSS. `asChild` |
|
|
45
|
+
|
|
46
|
+
## Keyboard interaction
|
|
47
|
+
|
|
48
|
+
| Key | Behaviour |
|
|
49
|
+
| ----- | ------------------------------------------------------------------------ |
|
|
50
|
+
| `Tab` | Moves focus to the trigger; opens the tooltip immediately (no delay) |
|
|
51
|
+
| `Tab` (away) | Closes the tooltip immediately |
|
|
52
|
+
| `Esc` | Closes the tooltip. Preventable via `onEscapeKeyDown` |
|
|
53
|
+
|
|
54
|
+
## State modes
|
|
55
|
+
|
|
56
|
+
- **Uncontrolled** — pass `defaultOpen` (or omit for closed on mount).
|
|
57
|
+
- **Controlled** — pass `open` and `onOpenChange` together.
|
|
58
|
+
|
|
59
|
+
The two shapes are statically discriminated at the type level.
|
|
60
|
+
|
|
61
|
+
```tsx
|
|
62
|
+
// Uncontrolled
|
|
63
|
+
<Tooltip.Root defaultOpen>…</Tooltip.Root>
|
|
64
|
+
|
|
65
|
+
// Controlled
|
|
66
|
+
const [open, setOpen] = useState(false);
|
|
67
|
+
<Tooltip.Root open={open} onOpenChange={setOpen}>
|
|
68
|
+
…
|
|
69
|
+
</Tooltip.Root>
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Delay and skip-delay
|
|
73
|
+
|
|
74
|
+
`delayDuration` (default 700 ms) controls how long the pointer must rest on
|
|
75
|
+
the trigger before the tooltip opens. Set it on `Tooltip.Provider` to apply
|
|
76
|
+
globally, or on an individual `Tooltip.Root` to override.
|
|
77
|
+
|
|
78
|
+
`skipDelayDuration` (default 300 ms) controls the "skip window". Once one
|
|
79
|
+
tooltip is open, moving to any other trigger opens it instantly. After the
|
|
80
|
+
last tooltip closes, the skip window is active for `skipDelayDuration` ms
|
|
81
|
+
before the normal delay resumes.
|
|
82
|
+
|
|
83
|
+
```tsx
|
|
84
|
+
<Tooltip.Provider delayDuration={400} skipDelayDuration={150}>
|
|
85
|
+
…
|
|
86
|
+
</Tooltip.Provider>
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Hoverable content
|
|
90
|
+
|
|
91
|
+
By default, users can move the cursor from the trigger into the tooltip
|
|
92
|
+
content without it closing (a 100 ms grace period bridges the gap). Set
|
|
93
|
+
`disableHoverableContent` on `Tooltip.Root` to close the tooltip immediately
|
|
94
|
+
when the cursor leaves the trigger:
|
|
95
|
+
|
|
96
|
+
```tsx
|
|
97
|
+
<Tooltip.Root disableHoverableContent>
|
|
98
|
+
…
|
|
99
|
+
</Tooltip.Root>
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Escape hatches
|
|
103
|
+
|
|
104
|
+
Both close paths — Escape and pointer-down-outside — fire observable
|
|
105
|
+
callbacks on `Tooltip.Content`. Call `event.preventDefault()` to keep the
|
|
106
|
+
tooltip open:
|
|
107
|
+
|
|
108
|
+
```tsx
|
|
109
|
+
<Tooltip.Content
|
|
110
|
+
onEscapeKeyDown={(event) => {
|
|
111
|
+
event.preventDefault(); // keep open when Escape is pressed
|
|
112
|
+
}}
|
|
113
|
+
onPointerDownOutside={(event) => {
|
|
114
|
+
event.preventDefault(); // keep open when clicking outside
|
|
115
|
+
}}
|
|
116
|
+
>
|
|
117
|
+
…
|
|
118
|
+
</Tooltip.Content>
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## CSS anchor positioning
|
|
122
|
+
|
|
123
|
+
This component is headless — it applies **no inline styles** and no
|
|
124
|
+
positioning logic. Position the tooltip with CSS Anchor Positioning:
|
|
125
|
+
|
|
126
|
+
```css
|
|
127
|
+
/* Trigger declares an anchor name */
|
|
128
|
+
.my-trigger {
|
|
129
|
+
anchor-name: --my-tooltip;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/* Content attaches to that anchor */
|
|
133
|
+
.my-tooltip {
|
|
134
|
+
position: absolute;
|
|
135
|
+
position-anchor: --my-tooltip;
|
|
136
|
+
position-area: top;
|
|
137
|
+
position-try-fallbacks: bottom, left, right;
|
|
138
|
+
margin-bottom: 8px;
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
For multiple tooltips on a page, assign each trigger/content pair a
|
|
143
|
+
distinct `anchor-name`. A data attribute or CSS `:nth-child` selector
|
|
144
|
+
can automate this without per-element class names.
|
|
145
|
+
|
|
146
|
+
## Arrow
|
|
147
|
+
|
|
148
|
+
`Tooltip.Arrow` renders a `<span>` (or a custom element via `asChild`).
|
|
149
|
+
Arrow position is entirely consumer CSS. A common pattern places it with
|
|
150
|
+
`position: absolute` relative to the content and uses `position-area` or
|
|
151
|
+
a CSS pseudo-element triangle:
|
|
152
|
+
|
|
153
|
+
```tsx
|
|
154
|
+
<Tooltip.Content className="tooltip">
|
|
155
|
+
Save your changes
|
|
156
|
+
<Tooltip.Arrow className="tooltip__arrow" />
|
|
157
|
+
</Tooltip.Content>
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
```css
|
|
161
|
+
.tooltip__arrow {
|
|
162
|
+
position: absolute;
|
|
163
|
+
bottom: -4px;
|
|
164
|
+
left: 50%;
|
|
165
|
+
translate: -50% 0;
|
|
166
|
+
width: 8px;
|
|
167
|
+
height: 8px;
|
|
168
|
+
background: inherit;
|
|
169
|
+
rotate: 45deg;
|
|
170
|
+
}
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
## Animation hooks
|
|
174
|
+
|
|
175
|
+
`Tooltip.Portal` and `Tooltip.Content` accept `forceMount`. When set, the
|
|
176
|
+
subtree stays in the DOM while closed so a CSS animation can play on
|
|
177
|
+
`data-state="closed"`:
|
|
178
|
+
|
|
179
|
+
```tsx
|
|
180
|
+
<Tooltip.Portal forceMount>
|
|
181
|
+
<Tooltip.Content forceMount>…</Tooltip.Content>
|
|
182
|
+
</Tooltip.Portal>
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
```css
|
|
186
|
+
[data-state="open"] { animation: fade-in 120ms ease-out; }
|
|
187
|
+
[data-state="closed"] { animation: fade-out 80ms ease-in forwards; }
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
## `asChild` composition
|
|
191
|
+
|
|
192
|
+
`Tooltip.Trigger` and `Tooltip.Arrow` accept `asChild`. The component
|
|
193
|
+
merges its own props (event handlers, ARIA, `data-state`) onto the
|
|
194
|
+
child element; the child's own handlers run first.
|
|
195
|
+
|
|
196
|
+
```tsx
|
|
197
|
+
<Tooltip.Trigger asChild>
|
|
198
|
+
<a href="/help">Help</a>
|
|
199
|
+
</Tooltip.Trigger>
|
|
200
|
+
|
|
201
|
+
<Tooltip.Arrow asChild>
|
|
202
|
+
<svg viewBox="0 0 10 5">…</svg>
|
|
203
|
+
</Tooltip.Arrow>
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
## Styling hooks
|
|
207
|
+
|
|
208
|
+
`data-state="open" | "closed"` is set on both `Tooltip.Trigger` and
|
|
209
|
+
`Tooltip.Content`:
|
|
210
|
+
|
|
211
|
+
```css
|
|
212
|
+
.tooltip[data-state="open"] { opacity: 1; }
|
|
213
|
+
.tooltip[data-state="closed"] { opacity: 0; }
|
|
214
|
+
```
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
import { Portal } from "../Portal";
|
|
2
|
+
import { Slot, composeRefs } from "../Slot";
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
TooltipProvider,
|
|
6
|
+
TooltipProviderProvider,
|
|
7
|
+
useTooltipContext,
|
|
8
|
+
} from "./TooltipContext";
|
|
9
|
+
import {
|
|
10
|
+
useTooltipContent,
|
|
11
|
+
useTooltipProvider,
|
|
12
|
+
useTooltipRoot,
|
|
13
|
+
useTooltipTrigger,
|
|
14
|
+
} from "./hooks";
|
|
15
|
+
import type {
|
|
16
|
+
TooltipArrowProps,
|
|
17
|
+
TooltipContentProps,
|
|
18
|
+
TooltipPortalProps,
|
|
19
|
+
TooltipProviderProps,
|
|
20
|
+
TooltipRootProps,
|
|
21
|
+
TooltipTriggerProps,
|
|
22
|
+
} from "./types";
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Wraps one or more `Tooltip.Root` instances and provides shared
|
|
26
|
+
* delay configuration. Required ancestor — rendering `Tooltip.Root`
|
|
27
|
+
* without a Provider throws a context error.
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```tsx
|
|
31
|
+
* <Tooltip.Provider delayDuration={400}>
|
|
32
|
+
* <App />
|
|
33
|
+
* </Tooltip.Provider>
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
function TooltipProviderComponent({
|
|
37
|
+
children,
|
|
38
|
+
delayDuration = 700,
|
|
39
|
+
skipDelayDuration = 300,
|
|
40
|
+
}: TooltipProviderProps) {
|
|
41
|
+
const { contextValue } = useTooltipProvider({ delayDuration, skipDelayDuration });
|
|
42
|
+
return (
|
|
43
|
+
<TooltipProviderProvider value={contextValue}>
|
|
44
|
+
{children}
|
|
45
|
+
</TooltipProviderProvider>
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
TooltipProviderComponent.displayName = "TooltipProvider";
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* The state boundary for a single tooltip. Owns the open/closed
|
|
53
|
+
* state and wires it down to all sub-components via context.
|
|
54
|
+
*
|
|
55
|
+
* Supports two state modes, statically discriminated at the type level:
|
|
56
|
+
*
|
|
57
|
+
* - **Uncontrolled** — pass `defaultOpen` or omit for closed-on-mount.
|
|
58
|
+
* - **Controlled** — pass `open` (and optionally `onOpenChange`).
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* ```tsx
|
|
62
|
+
* <Tooltip.Root>
|
|
63
|
+
* <Tooltip.Trigger>Save</Tooltip.Trigger>
|
|
64
|
+
* <Tooltip.Content>Save your changes</Tooltip.Content>
|
|
65
|
+
* </Tooltip.Root>
|
|
66
|
+
* ```
|
|
67
|
+
*/
|
|
68
|
+
function TooltipRoot({
|
|
69
|
+
children,
|
|
70
|
+
defaultOpen,
|
|
71
|
+
open,
|
|
72
|
+
onOpenChange,
|
|
73
|
+
delayDuration,
|
|
74
|
+
disableHoverableContent,
|
|
75
|
+
}: TooltipRootProps) {
|
|
76
|
+
const { contextValue } = useTooltipRoot({
|
|
77
|
+
defaultOpen,
|
|
78
|
+
open,
|
|
79
|
+
onOpenChange,
|
|
80
|
+
delayDuration,
|
|
81
|
+
disableHoverableContent,
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
return <TooltipProvider value={contextValue}>{children}</TooltipProvider>;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
TooltipRoot.displayName = "TooltipRoot";
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* The element that triggers the tooltip on hover and focus. Renders a
|
|
91
|
+
* `<button type="button">` by default; use `asChild` to wrap a custom
|
|
92
|
+
* element.
|
|
93
|
+
*
|
|
94
|
+
* ARIA wiring:
|
|
95
|
+
* - `aria-describedby` points at `Tooltip.Content`'s id.
|
|
96
|
+
* - `data-state="open" | "closed"` mirrors the tooltip state.
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* ```tsx
|
|
100
|
+
* <Tooltip.Trigger>Save</Tooltip.Trigger>
|
|
101
|
+
*
|
|
102
|
+
* <Tooltip.Trigger asChild>
|
|
103
|
+
* <a href="/help">Help</a>
|
|
104
|
+
* </Tooltip.Trigger>
|
|
105
|
+
* ```
|
|
106
|
+
*/
|
|
107
|
+
function TooltipTrigger({
|
|
108
|
+
ref,
|
|
109
|
+
asChild = false,
|
|
110
|
+
onPointerEnter,
|
|
111
|
+
onPointerLeave,
|
|
112
|
+
onFocus,
|
|
113
|
+
onBlur,
|
|
114
|
+
onKeyDown,
|
|
115
|
+
type,
|
|
116
|
+
...rest
|
|
117
|
+
}: TooltipTriggerProps) {
|
|
118
|
+
const { getTriggerProps } = useTooltipTrigger({
|
|
119
|
+
onPointerEnter,
|
|
120
|
+
onPointerLeave,
|
|
121
|
+
onFocus,
|
|
122
|
+
onBlur,
|
|
123
|
+
onKeyDown,
|
|
124
|
+
...rest,
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
if (asChild) {
|
|
128
|
+
return <Slot ref={ref} {...getTriggerProps()} />;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return <button ref={ref} type={type ?? "button"} {...getTriggerProps()} />;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
TooltipTrigger.displayName = "TooltipTrigger";
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Renders its children through `React.createPortal` so the tooltip
|
|
138
|
+
* content is detached from wherever `Tooltip.Root` lives in the tree
|
|
139
|
+
* and becomes a direct child of `container` (default `document.body`).
|
|
140
|
+
*
|
|
141
|
+
* By default the portal only renders while the tooltip is open. Pass
|
|
142
|
+
* `forceMount` to keep the subtree mounted when closed — useful for
|
|
143
|
+
* CSS exit animations driven by `data-state="closed"`.
|
|
144
|
+
*
|
|
145
|
+
* @example
|
|
146
|
+
* ```tsx
|
|
147
|
+
* <Tooltip.Portal>
|
|
148
|
+
* <Tooltip.Content>…</Tooltip.Content>
|
|
149
|
+
* </Tooltip.Portal>
|
|
150
|
+
* ```
|
|
151
|
+
*/
|
|
152
|
+
function TooltipPortal({ children, container, forceMount }: TooltipPortalProps) {
|
|
153
|
+
const { open } = useTooltipContext();
|
|
154
|
+
|
|
155
|
+
if (!open && !forceMount) return null;
|
|
156
|
+
|
|
157
|
+
return <Portal container={container}>{children}</Portal>;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
TooltipPortal.displayName = "TooltipPortal";
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* The tooltip panel. Renders a `<div role="tooltip">` with the shared
|
|
164
|
+
* `contentId` so `Tooltip.Trigger`'s `aria-describedby` resolves correctly.
|
|
165
|
+
*
|
|
166
|
+
* - Unmounts when the tooltip closes unless `forceMount` is set.
|
|
167
|
+
* - `data-state="open" | "closed"` is always present for CSS animation hooks.
|
|
168
|
+
* - Pointer events on the content cancel the grace-period close timer,
|
|
169
|
+
* letting users move the cursor from the trigger into the content without
|
|
170
|
+
* the tooltip dismissing (unless `disableHoverableContent` is set on Root).
|
|
171
|
+
*
|
|
172
|
+
* **Escape hatches:**
|
|
173
|
+
* - `onEscapeKeyDown` — fires when Escape is pressed while the tooltip is
|
|
174
|
+
* open; call `event.preventDefault()` to keep it open.
|
|
175
|
+
* - `onPointerDownOutside` — fires on a pointer-down outside the content;
|
|
176
|
+
* call `event.preventDefault()` to keep it open.
|
|
177
|
+
*
|
|
178
|
+
* CSS anchor positioning is the consumer's responsibility. Apply
|
|
179
|
+
* `anchor-name` to the trigger and `position-anchor` / `position-area`
|
|
180
|
+
* to the content in your own stylesheet.
|
|
181
|
+
*
|
|
182
|
+
* @example
|
|
183
|
+
* ```tsx
|
|
184
|
+
* <Tooltip.Content className="tooltip__content">
|
|
185
|
+
* Save your changes
|
|
186
|
+
* <Tooltip.Arrow className="tooltip__arrow" />
|
|
187
|
+
* </Tooltip.Content>
|
|
188
|
+
* ```
|
|
189
|
+
*/
|
|
190
|
+
function TooltipContent({
|
|
191
|
+
ref,
|
|
192
|
+
forceMount,
|
|
193
|
+
onEscapeKeyDown,
|
|
194
|
+
onPointerDownOutside,
|
|
195
|
+
onPointerEnter,
|
|
196
|
+
onPointerLeave,
|
|
197
|
+
...rest
|
|
198
|
+
}: TooltipContentProps) {
|
|
199
|
+
const { open } = useTooltipContext();
|
|
200
|
+
const { getContentProps, internalRef } = useTooltipContent({
|
|
201
|
+
onEscapeKeyDown,
|
|
202
|
+
onPointerDownOutside,
|
|
203
|
+
onPointerEnter,
|
|
204
|
+
onPointerLeave,
|
|
205
|
+
...rest,
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
if (!open && !forceMount) return null;
|
|
209
|
+
|
|
210
|
+
return <div ref={composeRefs(internalRef, ref)} {...getContentProps()} />;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
TooltipContent.displayName = "TooltipContent";
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* An optional visual pointer that connects the tooltip content to its
|
|
217
|
+
* trigger. Renders a `<span>` by default; use `asChild` for a custom
|
|
218
|
+
* element such as an SVG.
|
|
219
|
+
*
|
|
220
|
+
* All positioning and styling is the consumer's responsibility via CSS.
|
|
221
|
+
*
|
|
222
|
+
* @example
|
|
223
|
+
* ```tsx
|
|
224
|
+
* <Tooltip.Content>
|
|
225
|
+
* Save your changes
|
|
226
|
+
* <Tooltip.Arrow className="tooltip__arrow" />
|
|
227
|
+
* </Tooltip.Content>
|
|
228
|
+
* ```
|
|
229
|
+
*/
|
|
230
|
+
function TooltipArrow({ ref, asChild = false, ...rest }: TooltipArrowProps) {
|
|
231
|
+
if (asChild) {
|
|
232
|
+
return <Slot ref={ref} {...rest} />;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
return <span ref={ref} {...rest} />;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
TooltipArrow.displayName = "TooltipArrow";
|
|
239
|
+
|
|
240
|
+
type TooltipCompound = typeof TooltipRoot & {
|
|
241
|
+
Provider: typeof TooltipProviderComponent;
|
|
242
|
+
Root: typeof TooltipRoot;
|
|
243
|
+
Trigger: typeof TooltipTrigger;
|
|
244
|
+
Portal: typeof TooltipPortal;
|
|
245
|
+
Content: typeof TooltipContent;
|
|
246
|
+
Arrow: typeof TooltipArrow;
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
const TooltipCompound: TooltipCompound = Object.assign(TooltipRoot, {
|
|
250
|
+
Provider: TooltipProviderComponent,
|
|
251
|
+
Root: TooltipRoot,
|
|
252
|
+
Trigger: TooltipTrigger,
|
|
253
|
+
Portal: TooltipPortal,
|
|
254
|
+
Content: TooltipContent,
|
|
255
|
+
Arrow: TooltipArrow,
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
TooltipCompound.displayName = "Tooltip";
|
|
259
|
+
|
|
260
|
+
export { TooltipCompound as Tooltip };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { createStrictContext } from "../utils";
|
|
2
|
+
|
|
3
|
+
import type { TooltipContextValue, TooltipProviderContextValue } from "./types";
|
|
4
|
+
|
|
5
|
+
export const [TooltipProviderContext, useTooltipProviderContext] =
|
|
6
|
+
createStrictContext<TooltipProviderContextValue>(
|
|
7
|
+
"Tooltip sub-components must be rendered inside a <Tooltip.Provider>.",
|
|
8
|
+
"TooltipProviderContext",
|
|
9
|
+
);
|
|
10
|
+
|
|
11
|
+
export const [TooltipContext, useTooltipContext] =
|
|
12
|
+
createStrictContext<TooltipContextValue>(
|
|
13
|
+
"Tooltip sub-components must be rendered inside a <Tooltip.Root>.",
|
|
14
|
+
"TooltipContext",
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
const TooltipProviderProvider = TooltipProviderContext.Provider;
|
|
18
|
+
const TooltipProvider = TooltipContext.Provider;
|
|
19
|
+
|
|
20
|
+
export { TooltipProviderProvider, TooltipProvider };
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { render, screen } from "@testing-library/react";
|
|
2
|
+
import userEvent from "@testing-library/user-event";
|
|
3
|
+
|
|
4
|
+
import { Tooltip } from "../Tooltip";
|
|
5
|
+
|
|
6
|
+
describe("Tooltip — asChild", () => {
|
|
7
|
+
it("Trigger with asChild renders the consumer element instead of a button", () => {
|
|
8
|
+
render(
|
|
9
|
+
<Tooltip.Provider>
|
|
10
|
+
<Tooltip.Root defaultOpen>
|
|
11
|
+
<Tooltip.Trigger asChild>
|
|
12
|
+
<a href="/help">Help</a>
|
|
13
|
+
</Tooltip.Trigger>
|
|
14
|
+
<Tooltip.Content>Helpful tooltip</Tooltip.Content>
|
|
15
|
+
</Tooltip.Root>
|
|
16
|
+
</Tooltip.Provider>,
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
const link = screen.getByRole("link", { name: "Help" });
|
|
20
|
+
expect(link).toBeInTheDocument();
|
|
21
|
+
expect(screen.queryByRole("button")).toBeNull();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it("Trigger with asChild merges aria-describedby onto the consumer element", () => {
|
|
25
|
+
render(
|
|
26
|
+
<Tooltip.Provider>
|
|
27
|
+
<Tooltip.Root defaultOpen>
|
|
28
|
+
<Tooltip.Trigger asChild>
|
|
29
|
+
<a href="/help">Help</a>
|
|
30
|
+
</Tooltip.Trigger>
|
|
31
|
+
<Tooltip.Content>Helpful tooltip</Tooltip.Content>
|
|
32
|
+
</Tooltip.Root>
|
|
33
|
+
</Tooltip.Provider>,
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
const link = screen.getByRole("link", { name: "Help" });
|
|
37
|
+
const tooltip = screen.getByRole("tooltip");
|
|
38
|
+
expect(link).toHaveAttribute("aria-describedby", tooltip.id);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("Trigger with asChild still opens the tooltip on focus", async () => {
|
|
42
|
+
const user = userEvent.setup();
|
|
43
|
+
render(
|
|
44
|
+
<Tooltip.Provider>
|
|
45
|
+
<Tooltip.Root>
|
|
46
|
+
<Tooltip.Trigger asChild>
|
|
47
|
+
<a href="/help">Help</a>
|
|
48
|
+
</Tooltip.Trigger>
|
|
49
|
+
<Tooltip.Content>Helpful tooltip</Tooltip.Content>
|
|
50
|
+
</Tooltip.Root>
|
|
51
|
+
</Tooltip.Provider>,
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
await user.tab();
|
|
55
|
+
|
|
56
|
+
expect(screen.getByRole("tooltip")).toBeInTheDocument();
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("Arrow with asChild renders the consumer element", () => {
|
|
60
|
+
render(
|
|
61
|
+
<Tooltip.Provider>
|
|
62
|
+
<Tooltip.Root defaultOpen>
|
|
63
|
+
<Tooltip.Trigger>Hover me</Tooltip.Trigger>
|
|
64
|
+
<Tooltip.Content>
|
|
65
|
+
Tooltip text
|
|
66
|
+
<Tooltip.Arrow asChild>
|
|
67
|
+
<svg data-testid="arrow-svg" />
|
|
68
|
+
</Tooltip.Arrow>
|
|
69
|
+
</Tooltip.Content>
|
|
70
|
+
</Tooltip.Root>
|
|
71
|
+
</Tooltip.Provider>,
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
expect(screen.getByTestId("arrow-svg")).toBeInTheDocument();
|
|
75
|
+
expect(screen.queryByRole("tooltip")?.querySelector("span")).toBeNull();
|
|
76
|
+
});
|
|
77
|
+
});
|