From 81f7d27172dc50fc99701152461aa22f65d04943 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 5 Nov 2024 11:46:13 +0100 Subject: [PATCH 1/2] fix(SelectInputV2): fix dropdown align on left --- .changeset/soft-swans-cry.md | 5 ++ packages/ui/src/components/MenuV2/index.tsx | 6 +- packages/ui/src/components/Popover/index.tsx | 7 ++- packages/ui/src/components/Popup/helpers.ts | 61 ++++++++++++------- packages/ui/src/components/Popup/index.tsx | 10 ++- .../src/components/SelectInputV2/Dropdown.tsx | 1 + 6 files changed, 64 insertions(+), 26 deletions(-) create mode 100644 .changeset/soft-swans-cry.md diff --git a/.changeset/soft-swans-cry.md b/.changeset/soft-swans-cry.md new file mode 100644 index 0000000000..16d28fced2 --- /dev/null +++ b/.changeset/soft-swans-cry.md @@ -0,0 +1,5 @@ +--- +"@ultraviolet/ui": patch +--- + +Fix `` to align on the left when dropdown is bigger than the actual input diff --git a/packages/ui/src/components/MenuV2/index.tsx b/packages/ui/src/components/MenuV2/index.tsx index 2945cc4517..2b8e7da8e7 100644 --- a/packages/ui/src/components/MenuV2/index.tsx +++ b/packages/ui/src/components/MenuV2/index.tsx @@ -112,7 +112,10 @@ type MenuProps = { * If set to `hover`, the menu will open when the user hovers over the disclosure. */ triggerMethod?: 'click' | 'hover' -} & Pick, 'placement' | 'dynamicDomRendering'> +} & Pick< + ComponentProps, + 'placement' | 'dynamicDomRendering' | 'align' +> const FwdMenu = forwardRef( ( @@ -188,6 +191,7 @@ const FwdMenu = forwardRef( } portalTarget={portalTarget} dynamicDomRendering={dynamicDomRendering} + align="start" > {finalDisclosure} diff --git a/packages/ui/src/components/Popover/index.tsx b/packages/ui/src/components/Popover/index.tsx index 128b8bb335..2cfcea4ee5 100644 --- a/packages/ui/src/components/Popover/index.tsx +++ b/packages/ui/src/components/Popover/index.tsx @@ -117,7 +117,10 @@ type PopoverProps = { * behavior by setting a portalTarget prop. */ portalTarget?: HTMLElement -} & Pick, 'placement' | 'dynamicDomRendering'> +} & Pick< + ComponentProps, + 'placement' | 'dynamicDomRendering' | 'align' +> /** * Popover component is used to display additional information or actions on top of the main content of the page. @@ -140,6 +143,7 @@ export const Popover = forwardRef( 'data-testid': dataTestId, portalTarget, dynamicDomRendering, + align, }: PopoverProps, ref: Ref, ) => { @@ -184,6 +188,7 @@ export const Popover = forwardRef( maxHeight={maxHeight} portalTarget={portalTarget} dynamicDomRendering={dynamicDomRendering} + align={align} > {children} diff --git a/packages/ui/src/components/Popup/helpers.ts b/packages/ui/src/components/Popup/helpers.ts index 13562a88cc..3f466644e1 100644 --- a/packages/ui/src/components/Popup/helpers.ts +++ b/packages/ui/src/components/Popup/helpers.ts @@ -1,6 +1,7 @@ import type { RefObject } from 'react' export type PopupPlacement = 'top' | 'right' | 'bottom' | 'left' | 'auto' +export type PopupAlign = 'start' | 'center' export const DEFAULT_ARROW_WIDTH = 8 // in px const SPACE = 4 // in px const TOTAL_USED_SPACE = 0 // in px @@ -192,6 +193,7 @@ type ComputePositionsTypes = { popupRef: RefObject popupPortalTarget: HTMLElement hasArrow: boolean + align: PopupAlign } /** @@ -203,6 +205,7 @@ export const computePositions = ({ popupRef, popupPortalTarget, hasArrow, + align, }: ComputePositionsTypes) => { const arrowWidth = hasArrow ? DEFAULT_ARROW_WIDTH : 0 const childrenRect = ( @@ -269,10 +272,13 @@ export const computePositions = ({ arrowWidth, ) + const isAligned = align === 'start' + switch (placementBasedOnWindowSize) { case 'bottom': { - const positionX = - overloadedChildrenLeft + childrenWidth / 2 - popupWidth / 2 + const positionX = isAligned + ? overloadedChildrenLeft + : overloadedChildrenLeft + childrenWidth / 2 - popupWidth / 2 const positionY = overloadedChildrenTop + scrollTopValue + @@ -281,31 +287,36 @@ export const computePositions = ({ SPACE return { - arrowLeft: popupWidth / 2 + popupOverflow * -1, + arrowLeft: isAligned + ? childrenWidth / 2 - arrowWidth + : popupWidth / 2 + popupOverflow * -1, arrowTop: -arrowWidth - 5, arrowTransform: '', placement: 'bottom', rotate: 180, - popupInitialPosition: `translate3d(${positionX + popupOverflow}px, ${ + popupInitialPosition: `translate3d(${!isAligned ? positionX + popupOverflow : positionX}px, ${ positionY - TOTAL_USED_SPACE }px, 0)`, popupPosition: `translate3d(${ - positionX + popupOverflow + !isAligned ? positionX + popupOverflow : positionX }px, ${positionY}px, 0)`, } } case 'left': { const positionX = overloadedChildrenLeft - popupWidth - arrowWidth - SPACE * 2 - const positionY = - overloadedChildrenTop + - scrollTopValue - - popupHeight / 2 + - childrenHeight / 2 + const positionY = isAligned + ? overloadedChildrenTop + scrollTopValue + : overloadedChildrenTop + + scrollTopValue - + popupHeight / 2 + + childrenHeight / 2 return { arrowLeft: popupWidth + arrowWidth + 5, - arrowTop: popupHeight / 2 + popupOverflow * -1, + arrowTop: isAligned + ? childrenHeight / 2 - arrowWidth + : popupHeight / 2 + popupOverflow * -1, arrowTransform: 'translate(-50%, -50%)', placement: 'left', rotate: -90, @@ -319,15 +330,18 @@ export const computePositions = ({ } case 'right': { const positionX = overloadedChildrenRight + arrowWidth + SPACE * 2 - const positionY = - overloadedChildrenTop + - scrollTopValue - - popupHeight / 2 + - childrenHeight / 2 + const positionY = isAligned + ? overloadedChildrenTop + scrollTopValue + : overloadedChildrenTop + + scrollTopValue - + popupHeight / 2 + + childrenHeight / 2 return { arrowLeft: -arrowWidth - 5, - arrowTop: popupHeight / 2 + popupOverflow * -1, + arrowTop: isAligned + ? childrenHeight / 2 - arrowWidth + : popupHeight / 2 + popupOverflow * -1, arrowTransform: 'translate(50%, -50%)', placement: 'right', rotate: 90, @@ -341,8 +355,9 @@ export const computePositions = ({ } default: { // top placement is default value - const positionX = - overloadedChildrenLeft + childrenWidth / 2 - popupWidth / 2 + const positionX = isAligned + ? overloadedChildrenLeft + : overloadedChildrenLeft + childrenWidth / 2 - popupWidth / 2 const positionY = overloadedChildrenTop + scrollTopValue - @@ -351,16 +366,18 @@ export const computePositions = ({ SPACE return { - arrowLeft: popupWidth / 2 + popupOverflow * -1, + arrowLeft: isAligned + ? childrenWidth / 2 - arrowWidth + : popupWidth / 2 + popupOverflow * -1, arrowTop: popupHeight - 1, arrowTransform: '', placement: 'top', rotate: 0, - popupInitialPosition: `translate3d(${positionX + popupOverflow}px, ${ + popupInitialPosition: `translate3d(${!isAligned ? positionX + popupOverflow : positionX}px, ${ positionY + TOTAL_USED_SPACE }px, 0)`, popupPosition: `translate3d(${ - positionX + popupOverflow + !isAligned ? positionX + popupOverflow : positionX }px, ${positionY}px, 0)`, } } diff --git a/packages/ui/src/components/Popup/index.tsx b/packages/ui/src/components/Popup/index.tsx index 8b82d185e6..edccb1f834 100644 --- a/packages/ui/src/components/Popup/index.tsx +++ b/packages/ui/src/components/Popup/index.tsx @@ -23,7 +23,7 @@ import { createPortal } from 'react-dom' import { isClientSide } from '../../helpers/isClientSide' import type { PositionsType } from './animations' import { animation, exitAnimation } from './animations' -import type { PopupPlacement } from './helpers' +import type { PopupAlign, PopupPlacement } from './helpers' import { DEFAULT_ARROW_WIDTH, DEFAULT_POSITIONS, @@ -133,6 +133,10 @@ type PopupProps = { * `auto` placement will change the position of the popup if it doesn't fit in the viewport. */ placement?: PopupPlacement + /** + * Align the popup to the start or center of the children. + */ + align?: PopupAlign /** * Content of the popup, preferably text inside. */ @@ -190,6 +194,7 @@ export const Popup = forwardRef( children, text = '', placement = 'auto', + align = 'center', id, className, containerFullWidth, @@ -262,10 +267,11 @@ export const Popup = forwardRef( popupRef: innerPopupRef, popupPortalTarget: popupPortalTarget as HTMLElement, hasArrow, + align, }), ) } - }, [hasArrow, placement, popupPortalTarget]) + }, [hasArrow, placement, popupPortalTarget, align]) /** * This function is called when we need to recompute positions of popup due to window scroll or resize. diff --git a/packages/ui/src/components/SelectInputV2/Dropdown.tsx b/packages/ui/src/components/SelectInputV2/Dropdown.tsx index d406414e29..f544788176 100644 --- a/packages/ui/src/components/SelectInputV2/Dropdown.tsx +++ b/packages/ui/src/components/SelectInputV2/Dropdown.tsx @@ -847,6 +847,7 @@ export const Dropdown = ({ role="dialog" debounceDelay={0} containerFullWidth + align="start" > {children} From 0b0114d5747661f41a393f12aacd427abd82864c Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 5 Nov 2024 11:49:28 +0100 Subject: [PATCH 2/2] fix: update menuv2 --- packages/ui/src/components/MenuV2/index.tsx | 3 ++- .../SelectInputV2/__stories__/Playground.stories.tsx | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/ui/src/components/MenuV2/index.tsx b/packages/ui/src/components/MenuV2/index.tsx index 2b8e7da8e7..95bd5d35e1 100644 --- a/packages/ui/src/components/MenuV2/index.tsx +++ b/packages/ui/src/components/MenuV2/index.tsx @@ -135,6 +135,7 @@ const FwdMenu = forwardRef( size = 'small', triggerMethod = 'click', dynamicDomRendering, + align, }: MenuProps, ref: Ref, ) => { @@ -191,7 +192,7 @@ const FwdMenu = forwardRef( } portalTarget={portalTarget} dynamicDomRendering={dynamicDomRendering} - align="start" + align={align} > {finalDisclosure} diff --git a/packages/ui/src/components/SelectInputV2/__stories__/Playground.stories.tsx b/packages/ui/src/components/SelectInputV2/__stories__/Playground.stories.tsx index 29d038af34..61c0b23337 100644 --- a/packages/ui/src/components/SelectInputV2/__stories__/Playground.stories.tsx +++ b/packages/ui/src/components/SelectInputV2/__stories__/Playground.stories.tsx @@ -6,7 +6,7 @@ export const Playground = Template.bind({}) Playground.args = { ...Template.args, options: dataUnGrouped, helper: 'helper' } Playground.decorators = [ StoryComponent => ( -
+
),