diff --git a/.changeset/silly-impalas-cry.md b/.changeset/silly-impalas-cry.md new file mode 100644 index 00000000..fccfea56 --- /dev/null +++ b/.changeset/silly-impalas-cry.md @@ -0,0 +1,5 @@ +--- +"@cube-dev/ui-kit": patch +--- + +[CC-1616](https://cubedevinc.atlassian.net/browse/CC-1635) Recalculate position of a drodown on filter change diff --git a/src/components/pickers/ComboBox/ComboBox.stories.tsx b/src/components/pickers/ComboBox/ComboBox.stories.tsx index 070b2267..a74e5f8b 100644 --- a/src/components/pickers/ComboBox/ComboBox.stories.tsx +++ b/src/components/pickers/ComboBox/ComboBox.stories.tsx @@ -1,5 +1,6 @@ import { DollarCircleOutlined } from '@ant-design/icons'; import { Meta, Story } from '@storybook/react'; +import { userEvent, within } from '@storybook/testing-library'; import { SELECTED_KEY_ARG } from '../../../stories/FormFieldArgs'; import { baseProps } from '../../../stories/lists/baseProps'; @@ -10,7 +11,7 @@ export default { title: 'Pickers/ComboBox', component: ComboBox, subcomponents: { Item: ComboBox.Item }, - args: { id: 'name', width: '200px' }, + args: { id: 'name', width: '200px', label: 'Choose your favourite color' }, parameters: { controls: { exclude: baseProps } }, argTypes: { ...SELECTED_KEY_ARG }, } as Meta>; @@ -62,3 +63,35 @@ export const Wide: Story> = (args) => ( ); Wide.args = { width: '600px', defaultSelectedKey: 'red' }; + +export const With1LongOption: Story> = (args) => ( + + Red + Orange + Yellow + + green lorem ipsum dolor sit amet, consectetur adipiscing elit + + Blue + Purple + Violet + +); +With1LongOption.parameters = { layout: 'centered' }; +With1LongOption.play = async ({ canvasElement }) => { + const { getByRole } = within(canvasElement); + + const openButton = getByRole('button'); + + await userEvent.click(openButton); +}; + +export const With1LongOptionFiltered = With1LongOption.bind({}); +With1LongOptionFiltered.parameters = { layout: 'centered' }; +With1LongOptionFiltered.play = async ({ canvasElement }) => { + const { getByRole } = within(canvasElement); + + const combobox = getByRole('combobox'); + + await userEvent.type(combobox, 'Red'); +}; diff --git a/src/components/pickers/ComboBox/ComboBox.tsx b/src/components/pickers/ComboBox/ComboBox.tsx index 05ba19dd..80a2bec6 100644 --- a/src/components/pickers/ComboBox/ComboBox.tsx +++ b/src/components/pickers/ComboBox/ComboBox.tsx @@ -27,7 +27,12 @@ import { tasty, } from '../../../tasty'; import { useFocus } from '../../../utils/react/interactions'; -import { mergeProps, modAttrs, useCombinedRefs } from '../../../utils/react'; +import { + mergeProps, + modAttrs, + useCombinedRefs, + useLayoutEffect, +} from '../../../utils/react'; import { FieldWrapper } from '../../forms/FieldWrapper'; import { CubeSelectBaseProps, ListBoxPopup } from '../Select/Select'; import { @@ -186,6 +191,17 @@ function ComboBox(props: CubeComboBoxProps, ref) { popoverRef = useCombinedRefs(popoverRef); listBoxRef = useCombinedRefs(listBoxRef); + let { overlayProps, placement, updatePosition } = useOverlayPosition({ + targetRef: triggerRef, + overlayRef: popoverRef, + scrollRef: listBoxRef, + placement: `${direction} end`, + shouldFlip: shouldFlip, + isOpen: state.isOpen, + onClose: state.close, + offset: overlayOffset, + }); + let { labelProps, inputProps, @@ -203,17 +219,6 @@ function ComboBox(props: CubeComboBoxProps, ref) { state, ); - let { overlayProps, placement } = useOverlayPosition({ - targetRef: triggerRef, - overlayRef: popoverRef, - scrollRef: listBoxRef, - placement: `${direction} end`, - shouldFlip: shouldFlip, - isOpen: state.isOpen, - onClose: state.close, - offset: overlayOffset, - }); - let { isFocused, focusProps } = useFocus({ isDisabled }); let { hoverProps, isHovered } = useHover({ isDisabled }); @@ -230,6 +235,12 @@ function ComboBox(props: CubeComboBoxProps, ref) { true, ); + useLayoutEffect(() => { + if (state.isOpen) { + updatePosition(); + } + }, [updatePosition, state.isOpen, state.collection.size]); + let isInvalid = validationState === 'invalid'; let validationIcon = isInvalid ? (