From ea090144c4651e5bebfa8af3fe2092982acd2baa Mon Sep 17 00:00:00 2001 From: Tom King Date: Fri, 27 Oct 2023 08:47:18 +1000 Subject: [PATCH 01/24] - WIP commit for popover enhancement --- packages/ui/.storybook/preview.tsx | 2 +- .../components/panel/panel.component.tsx | 50 ++++++++++-- .../popover/components/panel/panel.styles.ts | 16 +++- .../popover/components/panel/panel.types.ts | 8 +- .../components/popover/popover.component.tsx | 5 +- .../src/components/popover/popover.hooks.tsx | 79 +++++++++++++++++++ .../components/popover/popover.stories.tsx | 12 ++- .../src/components/popover/popover.types.ts | 2 +- 8 files changed, 157 insertions(+), 17 deletions(-) create mode 100644 packages/ui/src/components/popover/popover.hooks.tsx diff --git a/packages/ui/.storybook/preview.tsx b/packages/ui/.storybook/preview.tsx index 844060bb7..24323740a 100644 --- a/packages/ui/.storybook/preview.tsx +++ b/packages/ui/.storybook/preview.tsx @@ -5,7 +5,7 @@ import * as React from 'react'; const withThemeProvider = (Story, context) => { const theme = context.globals?.theme || 'WBC'; return ( -
+
); diff --git a/packages/ui/src/components/popover/components/panel/panel.component.tsx b/packages/ui/src/components/popover/components/panel/panel.component.tsx index bdf5b6496..a214d11e0 100644 --- a/packages/ui/src/components/popover/components/panel/panel.component.tsx +++ b/packages/ui/src/components/popover/components/panel/panel.component.tsx @@ -1,18 +1,58 @@ -import React from 'react'; +import React, { useCallback, useEffect, useRef, useState } from 'react'; import { FocusScope } from 'react-aria'; import { Button } from '../../../button/index.js'; import { CloseIcon } from '../../../icon/index.js'; +import { usePopoverPosition } from '../../popover.hooks.js'; import { styles as panelStyles } from './panel.styles.js'; import { type PanelProps } from './panel.types.js'; -export function Panel({ state, heading, headingTag: Tag = 'h1', content, placement = 'top', id }: PanelProps) { - const styles = panelStyles({ placement }); +interface Position { + arrowPosition?: number; + empty?: boolean; + offset?: 'left' | 'right'; + panelPosition?: number | string; + placement?: 'top' | 'bottom'; +} + +export function Panel({ state, heading, headingTag: Tag = 'h1', content, placement, id, triggerRef }: PanelProps) { + const popoverRef = useRef(null); + const arrowRef = useRef(null); + + const [position, setPosition] = useState({ + placement, + empty: !placement, + offset: 'left', + }); + + useEffect(() => { + if (state.isOpen) { + setPosition(usePopoverPosition(triggerRef, popoverRef, arrowRef, placement)); + } + }, [state.isOpen]); + + const getPopoverClass = useCallback(() => { + return { + [position.offset as string]: + position.offset === 'left' ? `${position.panelPosition}px` : `-${position.panelPosition}px`, + // transform: position.offset === 'left' ? 'translateX(-50%)' : 'none', + }; + }, [position]); + + const getArrowClass = useCallback(() => { + return { + [!position.offset || position.offset === 'left' ? 'left' : 'right']: `${position.arrowPosition}px`, + }; + }, [position]); + + const styles = panelStyles({ placement: position.placement, offset: position.offset }); + + console.log(position); return ( -
+
{heading}
{content}
@@ -24,7 +64,7 @@ export function Panel({ state, heading, headingTag: Tag = 'h1', content, placeme aria-label="Close popover" />
-
+
); diff --git a/packages/ui/src/components/popover/components/panel/panel.styles.ts b/packages/ui/src/components/popover/components/panel/panel.styles.ts index bd18c9817..f23d0bb93 100644 --- a/packages/ui/src/components/popover/components/panel/panel.styles.ts +++ b/packages/ui/src/components/popover/components/panel/panel.styles.ts @@ -15,12 +15,20 @@ export const styles = tv( variants: { placement: { top: { - popover: 'bottom-full left-1/2 mb-[0.9375rem] -translate-x-1/2', - arrow: 'left-1/2 top-full -translate-x-1/2 after:top-[-12px] after:translate-x-[-7px]', + popover: 'bottom-full mb-[0.9375rem]', + arrow: 'top-full after:top-[-12px] after:translate-x-[-6.5px]', }, bottom: { - popover: 'left-1/2 top-full mt-[0.9375rem] -translate-x-1/2', - arrow: 'bottom-full left-1/2 -translate-x-1/2 rotate-180 after:bottom-[1px] after:translate-x-[-7px]', + popover: 'top-full mt-[0.9375rem]', + arrow: 'bottom-full rotate-180 after:bottom-[1px] after:translate-x-[-6.5px]', + }, + }, + offset: { + left: { + popover: '-translate-x-1/2', + }, + right: { + popover: '', }, }, }, diff --git a/packages/ui/src/components/popover/components/panel/panel.types.ts b/packages/ui/src/components/popover/components/panel/panel.types.ts index 34c0760ce..f34a474da 100644 --- a/packages/ui/src/components/popover/components/panel/panel.types.ts +++ b/packages/ui/src/components/popover/components/panel/panel.types.ts @@ -1,4 +1,4 @@ -import { HTMLAttributes } from 'react'; +import { HTMLAttributes, RefObject } from 'react'; import { OverlayTriggerState } from 'react-stately'; export type PanelProps = { @@ -15,11 +15,15 @@ export type PanelProps = { */ headingTag?: keyof Pick; /** - * Overlay trigger state + * Placement of popover. If no placement provided it will default to top unless there is no space then will appear on bottom. */ placement?: 'top' | 'bottom'; /** * Overlay trigger state */ state: OverlayTriggerState; + /** + * Ref for the trigger + */ + triggerRef: RefObject; } & HTMLAttributes; diff --git a/packages/ui/src/components/popover/popover.component.tsx b/packages/ui/src/components/popover/popover.component.tsx index 8eaa8c53e..b8c7fdc35 100644 --- a/packages/ui/src/components/popover/popover.component.tsx +++ b/packages/ui/src/components/popover/popover.component.tsx @@ -1,6 +1,6 @@ 'use client'; -import React, { useCallback, useEffect, useId } from 'react'; +import React, { useCallback, useEffect, useId, useRef } from 'react'; import { useOverlayTriggerState } from 'react-stately'; import { Button } from '../button/index.js'; @@ -24,6 +24,7 @@ export function Popover({ const state = useOverlayTriggerState({ defaultOpen: open }); const panelId = useId(); const styles = popoverStyles({}); + const ref = useRef(null); const handleClick = useCallback(() => { onClick(); @@ -52,6 +53,7 @@ export function Popover({ aria-expanded={state.isOpen} aria-controls={panelId} onClick={handleClick} + ref={ref} > {children} @@ -63,6 +65,7 @@ export function Popover({ content={content} state={state} id={panelId} + triggerRef={ref} /> )}
diff --git a/packages/ui/src/components/popover/popover.hooks.tsx b/packages/ui/src/components/popover/popover.hooks.tsx new file mode 100644 index 000000000..bc01c7672 --- /dev/null +++ b/packages/ui/src/components/popover/popover.hooks.tsx @@ -0,0 +1,79 @@ +import { RefObject } from 'react'; + +interface Position { + arrowPosition?: number; + empty?: boolean; + offset: 'left' | 'right'; + panelPosition?: number | string; + placement?: 'top' | 'bottom'; +} + +export const usePopoverPosition = ( + triggerRef: RefObject, + popoverRef: RefObject, + arrowRef: RefObject, + placement?: 'top' | 'bottom', +): Position => { + //TODO: Update arrow location on left offset + //TODO: Update space between left side of screen and popover when open and offset + + // bail early without refs + if (!triggerRef.current || !popoverRef.current || !arrowRef.current) { + throw new Error('You must pass two valid refs.'); + } + + let position: Position = { placement: undefined, offset: 'left', panelPosition: 0, arrowPosition: 0 }; + + if (typeof window === 'undefined') { + return position; + } + const remSize = parseInt(window.getComputedStyle(document.getElementsByTagName('html')[0]).fontSize); + + const trigger = triggerRef.current.getBoundingClientRect(); + const popover = popoverRef.current.getBoundingClientRect(); + const arrow = arrowRef.current.getBoundingClientRect(); + + console.log(popover); + // console.log(window.innerWidth - (trigger.left + trigger.width / 2) - (remSize + arrow.width / 2)); + + const offLeft = popover.left <= 0; + const offRight = popover.right >= window.innerWidth; + + let offset: 'left' | 'right' = 'left'; + if (popover.right > window.innerWidth) offset = 'right'; + + console.log(offLeft); + console.log(offRight); + + let panelPosition = trigger.width / 2; + let arrowPosition = (popover.width - arrow.width) / 2; + + if (offLeft) { + panelPosition = popover.width / 2 + remSize; + arrowPosition = popover.width / 2 + remSize; + } else if (offRight) { + panelPosition = window.innerWidth - trigger.right - remSize; + arrowPosition = window.innerWidth - (trigger.left + trigger.width / 2) - (remSize + arrow.width / 2); + } + // const left = offLeft ? popover.width / 2 + remSize : trigger.width / 2; + // const right = offRight ? window.innerWidth - trigger.right - remSize : 0; + // const center = window.innerWidth - (trigger.right - trigger.width / 2) - 24; + + if (popover.top > trigger.top) { + position = { + placement: placement ? placement : 'bottom', + offset, + panelPosition, + arrowPosition, + }; + } else { + position = { + placement: placement ? placement : 'top', + offset, + panelPosition, + arrowPosition, + }; + } + + return position; +}; diff --git a/packages/ui/src/components/popover/popover.stories.tsx b/packages/ui/src/components/popover/popover.stories.tsx index 98fae36c2..a862874c1 100644 --- a/packages/ui/src/components/popover/popover.stories.tsx +++ b/packages/ui/src/components/popover/popover.stories.tsx @@ -13,7 +13,7 @@ const meta: Meta = { tags: ['autodocs'], decorators: [ (Story: StoryFn) => ( -
+
), @@ -71,11 +71,17 @@ export const NoHeading: Story = { */ export const PopoverPlacement = () => (
- - Top Popover + + test Bottom Popover + + test + + + test +
); diff --git a/packages/ui/src/components/popover/popover.types.ts b/packages/ui/src/components/popover/popover.types.ts index 467fa30ca..3050e7c44 100644 --- a/packages/ui/src/components/popover/popover.types.ts +++ b/packages/ui/src/components/popover/popover.types.ts @@ -33,7 +33,7 @@ export type PopoverProps = { */ open?: boolean; /** - * Placement of popover + * Placement of popover. If no placement provided it will default to top unless there is no space then will appear on bottom. */ placement?: 'top' | 'bottom'; } & HTMLAttributes & From fe42599847997b1650e329becb8c267319689ffa Mon Sep 17 00:00:00 2001 From: samithaf Date: Tue, 31 Oct 2023 09:58:15 +1100 Subject: [PATCH 02/24] added a changeset --- .changeset/slow-plums-flow.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/slow-plums-flow.md diff --git a/.changeset/slow-plums-flow.md b/.changeset/slow-plums-flow.md new file mode 100644 index 000000000..310763a5b --- /dev/null +++ b/.changeset/slow-plums-flow.md @@ -0,0 +1,5 @@ +--- +'@westpac/ui': minor +--- + +styling fixes based on UX feedback From 5db0331c0e3e8415577e2858a918aaad35da5447 Mon Sep 17 00:00:00 2001 From: samithaf Date: Tue, 31 Oct 2023 21:25:05 +1100 Subject: [PATCH 03/24] removed scaffold and .storybook folders from npm package --- packages/ui/package.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/ui/package.json b/packages/ui/package.json index 30bb51330..2e0c4cfd8 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -40,7 +40,9 @@ }, "files": [ "*", + "!.storybook", "!coverage", + "!scaffold", "!stories", "!vitest.setup.ts", "!vitest.config.ts", From 61e6f8b207381af51312484a5ace3eed195ce499 Mon Sep 17 00:00:00 2001 From: samithaf Date: Tue, 31 Oct 2023 21:26:15 +1100 Subject: [PATCH 04/24] added changeset --- .changeset/fifty-pens-shop.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/fifty-pens-shop.md diff --git a/.changeset/fifty-pens-shop.md b/.changeset/fifty-pens-shop.md new file mode 100644 index 000000000..9f2d83532 --- /dev/null +++ b/.changeset/fifty-pens-shop.md @@ -0,0 +1,5 @@ +--- +'@westpac/ui': minor +--- + +Removed .storybook and scaffold folders from node module From 376b08e1f3b560d0f90ff5ca59c810442ce3c2b5 Mon Sep 17 00:00:00 2001 From: Jeremy Ortiz Date: Wed, 1 Nov 2023 13:13:46 +1100 Subject: [PATCH 05/24] feature(css): add minor spacing scale, update default border radius value and some component cleanup --- .../components/accordion/accordion.spec.tsx | 2 +- .../components/accordion/accordion.styles.ts | 2 +- .../ui/src/components/alert/alert.spec.tsx | 2 +- .../ui/src/components/alert/alert.styles.ts | 8 +- .../ui/src/components/badge/badge.styles.ts | 2 +- .../breadcrumb-item.component.tsx | 2 +- .../components/panel/panel.styles.ts | 3 +- .../components/button/button.styles.ts | 2 +- .../ui/src/components/button/button.spec.tsx | 2 +- .../ui/src/components/button/button.styles.ts | 8 +- .../components/checkbox/checkbox.styles.ts | 2 +- .../components/compacta/compacta.styles.ts | 8 +- .../error-message/error-message.component.tsx | 8 +- .../flexi-cell/flexi-cell.styles.ts | 2 +- .../form-label/form-label.styles.ts | 2 +- .../icon-add-on/icon-add-on.styles.ts | 32 ++--- .../text-add-on/text-add-on.styles.ts | 10 +- .../list/components/item/item.styles.ts | 8 +- packages/ui/src/components/list/list.spec.tsx | 10 +- .../modal/components/dialog/dialog.styles.ts | 2 +- .../pagination-item/pagination-item.styles.ts | 6 +- .../ui/src/components/panel/panel.styles.ts | 2 +- .../popover/components/panel/panel.styles.ts | 6 +- .../progress-bar/progress-bar.styles.ts | 4 +- .../components/repeater/repeater.styles.ts | 8 +- .../ui/src/components/switch/switch.styles.ts | 2 +- .../table/components/row/row.styles.ts | 2 +- .../ui/src/components/table/table.spec.tsx | 2 +- .../tabs/components/tab/tab.styles.ts | 4 +- .../ui/src/components/well/well.styles.ts | 2 +- .../stories/foundation/spacing.stories.tsx | 111 +++++++++++------- .../src/tailwind/__mocks__/utils.constants.ts | 107 +++++++++++------ packages/ui/src/tailwind/constants/spacing.ts | 3 +- packages/ui/src/tailwind/tailwind-plugin.ts | 3 + packages/ui/src/tailwind/types/index.ts | 1 - packages/ui/src/tailwind/types/theme.types.ts | 37 ------ .../ui/src/tailwind/utils/create-spacing.ts | 22 ++-- packages/ui/src/tailwind/utils/utils.spec.ts | 2 +- 38 files changed, 230 insertions(+), 211 deletions(-) delete mode 100644 packages/ui/src/tailwind/types/theme.types.ts diff --git a/packages/ui/src/components/accordion/accordion.spec.tsx b/packages/ui/src/components/accordion/accordion.spec.tsx index 97564a686..b3128d951 100644 --- a/packages/ui/src/components/accordion/accordion.spec.tsx +++ b/packages/ui/src/components/accordion/accordion.spec.tsx @@ -52,6 +52,6 @@ describe('Accordion', () => { }); it('renders the style correctly', () => { const style = styles({ rounded: true }); - expect(style).toBe('flex flex-col border border-border text-text overflow-hidden rounded-[0.1875rem]'); + expect(style).toBe('flex flex-col border border-border text-text overflow-hidden rounded'); }); }); diff --git a/packages/ui/src/components/accordion/accordion.styles.ts b/packages/ui/src/components/accordion/accordion.styles.ts index 0a1eef9a5..e5f95a64e 100644 --- a/packages/ui/src/components/accordion/accordion.styles.ts +++ b/packages/ui/src/components/accordion/accordion.styles.ts @@ -5,7 +5,7 @@ export const styles = tv( base: 'flex flex-col border border-border text-text', variants: { rounded: { - true: 'overflow-hidden rounded-[0.1875rem]', + true: 'overflow-hidden rounded', }, }, }, diff --git a/packages/ui/src/components/alert/alert.spec.tsx b/packages/ui/src/components/alert/alert.spec.tsx index 0697f49d3..74c78ab43 100644 --- a/packages/ui/src/components/alert/alert.spec.tsx +++ b/packages/ui/src/components/alert/alert.spec.tsx @@ -18,7 +18,7 @@ describe('Alert', () => { it('generates the base style correctly', () => { const style = styles({ look: 'info', mode: 'box' }); expect(style.base()).toBe( - 'typography-body-10 xsl:flex relative mb-4 text-info border-y p-3 border-info-50 bg-info-5', + 'typography-body-10 relative mb-4 xsl:flex text-info border-y p-3 border-info-50 bg-info-5', ); }); diff --git a/packages/ui/src/components/alert/alert.styles.ts b/packages/ui/src/components/alert/alert.styles.ts index cc607ff58..b43e3f202 100644 --- a/packages/ui/src/components/alert/alert.styles.ts +++ b/packages/ui/src/components/alert/alert.styles.ts @@ -3,11 +3,11 @@ import { tv } from 'tailwind-variants'; export const styles = tv( { slots: { - base: 'typography-body-10 xsl:flex relative mb-4', - icon: 'xsl:mr-2 float-left mr-1 flex-none', - body: 'xsl:top-[0.125rem] relative flex-1 overflow-hidden [&_a]:underline', + base: 'typography-body-10 relative mb-4 xsl:flex', + icon: 'float-left mr-1 flex-none xsl:mr-2', + body: 'relative flex-1 overflow-hidden xsl:top-[0.125rem] [&_a]:underline', heading: 'typography-body-9 mb-2 font-bold', - close: 'absolute right-[0.1875rem] top-[0.1875rem] p-1 hover:opacity-[0.8]', + close: 'absolute right-0.5 top-0.5 p-1 hover:opacity-80', }, variants: { look: { diff --git a/packages/ui/src/components/badge/badge.styles.ts b/packages/ui/src/components/badge/badge.styles.ts index 16915f8cc..070aaacfc 100644 --- a/packages/ui/src/components/badge/badge.styles.ts +++ b/packages/ui/src/components/badge/badge.styles.ts @@ -24,7 +24,7 @@ export const styles = tv( }, type: { pill: 'typography-body-10 rounded-xl px-[0.4375rem] py-[0.25rem] font-bold leading-none', - default: 'rounded-sm px-1 py-[0.1875rem] pb-[0.125rem] text-[0.75rem] leading-none', + default: 'rounded-sm px-1 py-0.5 pb-[0.125rem] text-[0.75rem] leading-none', }, }, }, diff --git a/packages/ui/src/components/breadcrumb/components/breadcrumb-item/breadcrumb-item.component.tsx b/packages/ui/src/components/breadcrumb/components/breadcrumb-item/breadcrumb-item.component.tsx index c03f69ad2..6ab8a6deb 100644 --- a/packages/ui/src/components/breadcrumb/components/breadcrumb-item/breadcrumb-item.component.tsx +++ b/packages/ui/src/components/breadcrumb/components/breadcrumb-item/breadcrumb-item.component.tsx @@ -39,7 +39,7 @@ export function BaseBreadcrumbItem( {children} {!isCurrent && ( -