diff --git a/package-lock.json b/package-lock.json index 9649251e7..d5f25689a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2399,6 +2399,7 @@ }, "node_modules/@clack/prompts/node_modules/is-unicode-supported": { "version": "1.3.0", + "extraneous": true, "inBundle": true, "license": "MIT", "engines": { diff --git a/packages/components/combobox/src/Combobox.doc.mdx b/packages/components/combobox/src/Combobox.doc.mdx index d39ac4619..542dfc380 100644 --- a/packages/components/combobox/src/Combobox.doc.mdx +++ b/packages/components/combobox/src/Combobox.doc.mdx @@ -30,10 +30,15 @@ import { Combobox } from '@spark-ui/combobox' of={Combobox} description="A form input used for selecting a value: when collapsed it shows the currently selected option and when expanded, it shows a scrollable list of predefined options for the user to choose from." subcomponents={{ + 'Combobox.Trigger': { + of: Combobox.Trigger, + description: + 'The area that toggles the combobox popover. The Select.Popover will position itself by aligning with its trigger.', + }, 'Combobox.Input': { of: Combobox.Input, description: - 'The button that toggles the select. The Select.Popover will position itself by aligning over the trigger.', + 'The typing area in which the user can type. The input behaviour will differ if `autoComplete` is used or not.', }, 'Combobox.LeadingIcon': { of: Combobox.LeadingIcon, @@ -136,7 +141,7 @@ You can style this element directly, or you can use it as a wrapper to put an ic ### Leading icon -Use `Combobox.LeadingIcon` inside `Combobox.Input` to prefix your trigger with an icon. +Use `Combobox.LeadingIcon` inside `Combobox.Trigger` to prefix your trigger with an icon. diff --git a/packages/components/combobox/src/Combobox.stories.tsx b/packages/components/combobox/src/Combobox.stories.tsx index b4bd620bf..1436aca67 100644 --- a/packages/components/combobox/src/Combobox.stories.tsx +++ b/packages/components/combobox/src/Combobox.stories.tsx @@ -64,7 +64,9 @@ export const Default: StoryFn = _args => { return (
- + + + @@ -88,12 +90,14 @@ export const Controlled: StoryFn = () => { return (
- + + + @@ -126,7 +130,9 @@ export const ControlledOpenState: StoryFn = () => {
- + + + @@ -148,7 +154,9 @@ export const CustomItem: StoryFn = _args => { return (
- + + + @@ -187,7 +195,9 @@ export const Disabled: StoryFn = _args => { return (
- + + + @@ -217,7 +227,9 @@ export const FilteringAutoFilter: StoryFn = _args => { return (
- + + + @@ -248,12 +260,14 @@ export const FilteringManual: StoryFn = () => { return (
- + + + @@ -278,7 +292,9 @@ export const ReadOnly: StoryFn = _args => { return (
- + + + @@ -299,7 +315,9 @@ export const DisabledItem: StoryFn = _args => { return (
- + + + @@ -322,7 +340,9 @@ export const Grouped: StoryFn = _args => { return (
- + + + @@ -351,7 +371,9 @@ export const LeadingIcon: StoryFn = _args => { return (
- + + + @@ -374,7 +396,9 @@ export const ItemIndicator: StoryFn = _args => { return (
- + + + @@ -419,7 +443,9 @@ export const Statuses: StoryFn = () => { {statuses.map(status => { return ( - + + + @@ -442,7 +468,9 @@ export const MultipleSelection: StoryFn = _args => { return (
- + + + @@ -475,12 +503,14 @@ export const MultipleSelectionControlled: StoryFn = () => { return (
- + + + @@ -505,7 +535,9 @@ export const FormFieldLabel: StoryFn = _args => { Book - + + + To Kill a Mockingbird @@ -530,7 +562,9 @@ export const FormFieldHiddenLabel: StoryFn = _args => { Book - + + + To Kill a Mockingbird @@ -553,7 +587,9 @@ export const FormFieldReadOnly: StoryFn = _args => { Book - + + + @@ -577,7 +613,9 @@ export const FormFieldDisabled: StoryFn = _args => { Book - + + + To Kill a Mockingbird @@ -600,7 +638,9 @@ export const FormFieldRequired: StoryFn = _args => { Book - + + + To Kill a Mockingbird @@ -629,7 +669,9 @@ export const FormFieldValidation: StoryFn = () => { setState(value === 'default' ? undefined : (value as 'success' | 'alert' | 'error')) }} > - + + + default diff --git a/packages/components/combobox/src/ComboboxInput.tsx b/packages/components/combobox/src/ComboboxInput.tsx index cf4237955..f2a7b37b8 100644 --- a/packages/components/combobox/src/ComboboxInput.tsx +++ b/packages/components/combobox/src/ComboboxInput.tsx @@ -1,20 +1,17 @@ -/* eslint-disable complexity */ -import { Icon } from '@spark-ui/icon' -import { IconButton } from '@spark-ui/icon-button' -import { ArrowHorizontalDown } from '@spark-ui/icons/dist/icons/ArrowHorizontalDown' import { Popover } from '@spark-ui/popover' import { useMergeRefs } from '@spark-ui/use-merge-refs' import { VisuallyHidden } from '@spark-ui/visually-hidden' +import { cx } from 'class-variance-authority' import { ComponentPropsWithoutRef, forwardRef, Fragment, type Ref, useEffect } from 'react' import { useComboboxContext } from './ComboboxContext' -import { styles } from './ComboboxInput.styles' -import { LeadingIcon } from './ComboboxLeadingIcon' type InputPrimitiveProps = ComponentPropsWithoutRef<'input'> -interface InputProps extends InputPrimitiveProps { +interface InputProps extends Omit { className?: string + placeholder?: string + value?: string onValueChange?: (value: string) => void } @@ -31,7 +28,6 @@ export const Input = forwardRef( forwardedRef: Ref ) => { const { - getToggleButtonProps, getDropdownProps, getInputProps, getLabelProps, @@ -41,7 +37,6 @@ export const Input = forwardRef( inputValue, setInputValue, setIsInputControlled, - state, setLastInteractionType, setOnInputValueChange, multiple, @@ -72,10 +67,6 @@ export const Input = forwardRef( } }, []) - const [PopoverAnchor, popoverAnchorProps] = hasPopover - ? [Popover.Anchor, { asChild: true, type: undefined }] - : [Fragment, {}] - const [PopoverTrigger, popoverTriggerProps] = hasPopover ? [Popover.Trigger, { asChild: true, type: undefined }] : [Fragment, {}] @@ -96,50 +87,20 @@ export const Input = forwardRef( )} - -
- {/* 1 - Leading icon (optional) */} - - - - - {/* 2 - TODO - selected items (optional, multiple selection only) */} -

[selected items chips (v2)]

- - {/* 3 - Input typing area - MANDATORY */} - - - - - {/* 4 - Combobox clear button (optional) */} -

[clear]

- {/* 5 - Combobox disclosure button (optional, advised for autoComplete not autoSuggest) */} - - - - - - - -
-
+ + + ) } diff --git a/packages/components/combobox/src/ComboboxInput.styles.tsx b/packages/components/combobox/src/ComboboxTrigger.styles.tsx similarity index 100% rename from packages/components/combobox/src/ComboboxInput.styles.tsx rename to packages/components/combobox/src/ComboboxTrigger.styles.tsx diff --git a/packages/components/combobox/src/ComboboxTrigger.tsx b/packages/components/combobox/src/ComboboxTrigger.tsx new file mode 100644 index 000000000..eef3e8411 --- /dev/null +++ b/packages/components/combobox/src/ComboboxTrigger.tsx @@ -0,0 +1,63 @@ +/* eslint-disable complexity */ +import { Icon } from '@spark-ui/icon' +import { IconButton } from '@spark-ui/icon-button' +import { ArrowHorizontalDown } from '@spark-ui/icons/dist/icons/ArrowHorizontalDown' +import { Popover } from '@spark-ui/popover' +import { forwardRef, Fragment, ReactNode, type Ref } from 'react' + +import { useComboboxContext } from './ComboboxContext' +import { LeadingIcon } from './ComboboxLeadingIcon' +import { styles } from './ComboboxTrigger.styles' + +interface TriggerProps { + className?: string + children?: ReactNode +} + +export const Trigger = forwardRef( + ({ className, children }: TriggerProps, forwardedRef: Ref) => { + const { getToggleButtonProps, hasPopover, disabled, readOnly, state } = useComboboxContext() + + const [PopoverAnchor, popoverAnchorProps] = hasPopover + ? [Popover.Anchor, { asChild: true, type: undefined }] + : [Fragment, {}] + + return ( + <> + +
+ {/* 1 - Leading icon (optional) */} + + + + + {/* 2 - TODO - selected items (optional, multiple selection only) */} +

[selected items chips (v2)]

+ + {children} + + {/* 4 - Combobox clear button (optional) */} +

[clear]

+ + {/* 5 - Combobox disclosure button (optional, advised for autoComplete not autoSuggest) */} + + + + + + + +
+
+ + ) + } +) + +Trigger.displayName = 'Combobox.Trigger' diff --git a/packages/components/combobox/src/index.ts b/packages/components/combobox/src/index.ts index ec95b688a..645be9df5 100644 --- a/packages/components/combobox/src/index.ts +++ b/packages/components/combobox/src/index.ts @@ -13,6 +13,7 @@ import { ItemText } from './ComboboxItemText' import { Label } from './ComboboxLabel' import { LeadingIcon } from './ComboboxLeadingIcon' import { Popover } from './ComboboxPopover' +import { Trigger } from './ComboboxTrigger' export { useComboboxContext, ComboboxProvider } @@ -25,9 +26,10 @@ export const Combobox: FC & { Label: typeof Label Popover: typeof Popover Divider: typeof Divider - Input: typeof Input + Trigger: typeof Trigger LeadingIcon: typeof LeadingIcon Empty: typeof Empty + Input: typeof Input } = Object.assign(Root, { Group, Item, @@ -37,9 +39,10 @@ export const Combobox: FC & { Label, Popover, Divider, - Input, + Trigger, LeadingIcon, Empty, + Input, }) Combobox.displayName = 'Combobox' @@ -51,6 +54,7 @@ ItemIndicator.displayName = 'Combobox.ItemIndicator' Label.displayName = 'Combobox.Label' Popover.displayName = 'Combobox.Popover' Divider.displayName = 'Combobox.Divider' -Input.displayName = 'Combobox.Input' +Trigger.displayName = 'Combobox.Trigger' LeadingIcon.displayName = 'Combobox.LeadingIcon' Empty.displayName = 'Combobox.Empty' +Input.displayName = 'Combobox.Input' diff --git a/packages/components/combobox/src/tests/Combobox.test.tsx b/packages/components/combobox/src/tests/Combobox.test.tsx index 5e65439d5..cb7f6aa88 100644 --- a/packages/components/combobox/src/tests/Combobox.test.tsx +++ b/packages/components/combobox/src/tests/Combobox.test.tsx @@ -10,7 +10,9 @@ describe('Combobox', () => { it('should render input and list of items', () => { render( - + + + War and Peace @@ -37,7 +39,9 @@ describe('Combobox', () => { // Given a close combobox (default state) render( - + + + War and Peace @@ -71,7 +75,9 @@ describe('Combobox', () => { // Given a close combobox without a popover(default state) render( - + + + War and Peace @@ -104,7 +110,9 @@ describe('Combobox', () => { // Given a combobox that should remain opened render( - + + + War and Peace @@ -130,7 +138,9 @@ describe('Combobox', () => { // Given a combobox that should remain opened render( - + + + War and Peace @@ -156,7 +166,9 @@ describe('Combobox', () => { // Given a combobox with no selected value yet render( - + + + No results found @@ -186,7 +198,9 @@ describe('Combobox', () => { // Given a combobox with no selected value yet render( - + + + War and Peace @@ -218,7 +232,9 @@ describe('Combobox', () => { // Given a combobox with a default selected value render( - + + + War and Peace @@ -237,7 +253,9 @@ describe('Combobox', () => { // Given a combobox with a default selected value render( - + + + @@ -271,7 +289,9 @@ describe('Combobox', () => { return ( - + + + War and Peace @@ -303,7 +323,9 @@ describe('Combobox', () => { // Given a combobox with no selected value yet render( - + + + War and Peace diff --git a/packages/components/combobox/src/tests/itemsGroups.test.tsx b/packages/components/combobox/src/tests/itemsGroups.test.tsx index 9c01c24bd..19c35faa4 100644 --- a/packages/components/combobox/src/tests/itemsGroups.test.tsx +++ b/packages/components/combobox/src/tests/itemsGroups.test.tsx @@ -10,7 +10,9 @@ describe('Combobox', () => { // Given a combobox with items groups and group labels render( - + + + diff --git a/packages/components/combobox/src/tests/multipleSelection.test.tsx b/packages/components/combobox/src/tests/multipleSelection.test.tsx index c03fdd931..581ed0571 100644 --- a/packages/components/combobox/src/tests/multipleSelection.test.tsx +++ b/packages/components/combobox/src/tests/multipleSelection.test.tsx @@ -13,7 +13,9 @@ describe('Combobox', () => { // Given a combobox with no selected value yet render( - + + + War and Peace @@ -47,7 +49,9 @@ describe('Combobox', () => { // Given a combobox with no selected value yet render( - + + + War and Peace @@ -82,7 +86,9 @@ describe('Combobox', () => { // Given a combobox with no selected value yet render( - + + + War and Peace diff --git a/packages/components/combobox/src/tests/withFormField.test.tsx b/packages/components/combobox/src/tests/withFormField.test.tsx index 689c28a66..be1272735 100644 --- a/packages/components/combobox/src/tests/withFormField.test.tsx +++ b/packages/components/combobox/src/tests/withFormField.test.tsx @@ -12,7 +12,9 @@ describe('Combobox', () => { Book - + + + War and Peace