From bacfc7890f18bade3d0a98baef16f188877d8431 Mon Sep 17 00:00:00 2001 From: Kushal <44560227+ksolanki7@users.noreply.github.com> Date: Tue, 10 Dec 2024 09:52:44 +1100 Subject: [PATCH 01/14] feat: #218 v5 Table Container and Table Toolbar (#234) * Initial commit - Table Container / Table Toolbar * feat: #218 v5 Table Container and Toolbar * Rename folder/file name to hypen case * Fix issue with import names in mdx file --- src/components/skeleton/styles.ts | 2 +- .../table-container.test.tsx.snap | 24 ++++ .../__tests__/table-container.test.tsx | 14 ++ src/components/table/table-container/index.ts | 2 + .../table/table-container/styles.ts | 3 + .../table/table-container/table-container.mdx | 13 ++ .../table-container.stories.tsx | 51 +++++++ .../table/table-container/table-container.tsx | 10 ++ .../__snapshots__/table-toolbar.test.tsx.snap | 20 +++ .../__tests__/table-toolbar.test.tsx | 9 ++ src/components/table/table-toolbar/index.ts | 2 + src/components/table/table-toolbar/styles.ts | 24 ++++ .../table/table-toolbar/table-toolbar.mdx | 33 +++++ .../table-toolbar/table-toolbar.stories.tsx | 128 ++++++++++++++++++ .../table/table-toolbar/table-toolbar.tsx | 16 +++ src/index.ts | 2 + 16 files changed, 352 insertions(+), 1 deletion(-) create mode 100644 src/components/table/table-container/__tests__/__snapshots__/table-container.test.tsx.snap create mode 100644 src/components/table/table-container/__tests__/table-container.test.tsx create mode 100644 src/components/table/table-container/index.ts create mode 100644 src/components/table/table-container/styles.ts create mode 100644 src/components/table/table-container/table-container.mdx create mode 100644 src/components/table/table-container/table-container.stories.tsx create mode 100644 src/components/table/table-container/table-container.tsx create mode 100644 src/components/table/table-toolbar/__tests__/__snapshots__/table-toolbar.test.tsx.snap create mode 100644 src/components/table/table-toolbar/__tests__/table-toolbar.test.tsx create mode 100644 src/components/table/table-toolbar/index.ts create mode 100644 src/components/table/table-toolbar/styles.ts create mode 100644 src/components/table/table-toolbar/table-toolbar.mdx create mode 100644 src/components/table/table-toolbar/table-toolbar.stories.tsx create mode 100644 src/components/table/table-toolbar/table-toolbar.tsx diff --git a/src/components/skeleton/styles.ts b/src/components/skeleton/styles.ts index 108d9fa6..869c58b3 100644 --- a/src/components/skeleton/styles.ts +++ b/src/components/skeleton/styles.ts @@ -1,7 +1,7 @@ import { styled } from '@linaria/react' export const ElSkeleton = styled.span` - display: inline-block; + display: flex; background: var(--fill-default-light, #e5e9ed); animation: blink 1.5s infinite; diff --git a/src/components/table/table-container/__tests__/__snapshots__/table-container.test.tsx.snap b/src/components/table/table-container/__tests__/__snapshots__/table-container.test.tsx.snap new file mode 100644 index 00000000..4289fe2c --- /dev/null +++ b/src/components/table/table-container/__tests__/__snapshots__/table-container.test.tsx.snap @@ -0,0 +1,24 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`Table Container > should match snapshot 1`] = ` + + + + + 125 Properties + + + Page Size menu component goes here + + + + +`; diff --git a/src/components/table/table-container/__tests__/table-container.test.tsx b/src/components/table/table-container/__tests__/table-container.test.tsx new file mode 100644 index 00000000..ef86e16a --- /dev/null +++ b/src/components/table/table-container/__tests__/table-container.test.tsx @@ -0,0 +1,14 @@ +import { render } from '@testing-library/react' +import { TableContainer } from '../index' +import { TableToolbar } from '../../table-toolbar' + +describe('Table Container', () => { + test('should match snapshot', () => { + const { asFragment } = render( + + + , + ) + expect(asFragment()).toMatchSnapshot() + }) +}) diff --git a/src/components/table/table-container/index.ts b/src/components/table/table-container/index.ts new file mode 100644 index 00000000..faa6cc2a --- /dev/null +++ b/src/components/table/table-container/index.ts @@ -0,0 +1,2 @@ +export * from './styles' +export * from './table-container' diff --git a/src/components/table/table-container/styles.ts b/src/components/table/table-container/styles.ts new file mode 100644 index 00000000..81ca4401 --- /dev/null +++ b/src/components/table/table-container/styles.ts @@ -0,0 +1,3 @@ +import { styled } from '@linaria/react' + +export const ElTableContainer = styled.div`` diff --git a/src/components/table/table-container/table-container.mdx b/src/components/table/table-container/table-container.mdx new file mode 100644 index 00000000..e5ddcb2f --- /dev/null +++ b/src/components/table/table-container/table-container.mdx @@ -0,0 +1,13 @@ +import { Meta, Canvas, Controls } from '@storybook/blocks' +import { RenderHtmlMarkup } from '../../../storybook/render-html-markup' +import * as TableContainerStories from './table-container.stories' + + + +# Table Container + +## Basic Usage + + + + diff --git a/src/components/table/table-container/table-container.stories.tsx b/src/components/table/table-container/table-container.stories.tsx new file mode 100644 index 00000000..745056c8 --- /dev/null +++ b/src/components/table/table-container/table-container.stories.tsx @@ -0,0 +1,51 @@ +import { Meta } from '@storybook/react' +// import { figmaDesignUrls } from '../../storybook/figma' +import { TableContainer } from './table-container' +import { TableToolbar } from '../table-toolbar' +import { Menu, MenuItem, MenuItemGroup, MenuList } from '../../menu' +import { MenuPopover, MenuTrigger } from '#src/components/menu/menu-popover' +import { Button } from '../../button' +import { Icon } from '../../icon' + +const meta: Meta = { + title: 'Components/TableContainer', + component: TableContainer, +} + +export default meta + +export const BasicUsage = { + render: ({}) => ( + + + + {({ getTriggerProps }) => ( + // To do: Once Button component is update with more props for no-padding, please make updates here + } + > + Page size: 25 + + )} + + + + + 25 + 50 + 100 + + + + + } + /> + + ), +} diff --git a/src/components/table/table-container/table-container.tsx b/src/components/table/table-container/table-container.tsx new file mode 100644 index 00000000..17685b58 --- /dev/null +++ b/src/components/table/table-container/table-container.tsx @@ -0,0 +1,10 @@ +import { HTMLAttributes, ReactNode } from 'react' +import { ElTableContainer } from './styles' + +interface TableContainerProps extends HTMLAttributes { + children: ReactNode +} + +export const TableContainer: React.FC = ({ children, ...rest }) => { + return {children} +} diff --git a/src/components/table/table-toolbar/__tests__/__snapshots__/table-toolbar.test.tsx.snap b/src/components/table/table-toolbar/__tests__/__snapshots__/table-toolbar.test.tsx.snap new file mode 100644 index 00000000..0b971cc2 --- /dev/null +++ b/src/components/table/table-toolbar/__tests__/__snapshots__/table-toolbar.test.tsx.snap @@ -0,0 +1,20 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`Table Toolbar > should match snapshot 1`] = ` + + + + 125 Properties + + + Page Size menu + + + +`; diff --git a/src/components/table/table-toolbar/__tests__/table-toolbar.test.tsx b/src/components/table/table-toolbar/__tests__/table-toolbar.test.tsx new file mode 100644 index 00000000..f59d614f --- /dev/null +++ b/src/components/table/table-toolbar/__tests__/table-toolbar.test.tsx @@ -0,0 +1,9 @@ +import { render } from '@testing-library/react' +import { TableToolbar } from '../index' + +describe('Table Toolbar', () => { + test('should match snapshot', () => { + const { asFragment } = render() + expect(asFragment()).toMatchSnapshot() + }) +}) diff --git a/src/components/table/table-toolbar/index.ts b/src/components/table/table-toolbar/index.ts new file mode 100644 index 00000000..ebf191de --- /dev/null +++ b/src/components/table/table-toolbar/index.ts @@ -0,0 +1,2 @@ +export * from './styles' +export * from './table-toolbar' diff --git a/src/components/table/table-toolbar/styles.ts b/src/components/table/table-toolbar/styles.ts new file mode 100644 index 00000000..8a113ca2 --- /dev/null +++ b/src/components/table/table-toolbar/styles.ts @@ -0,0 +1,24 @@ +import { styled } from '@linaria/react' + +export const ElTableToolbar = styled.div` + display: flex; + width: 100%; + background: var(--fill-white); + padding: var(--spacing-2) 0px var(--spacing-2) 0px; + gap: 0px; + justify-content: space-between; + align-items: center; +` + +export const ElTableToolbarDescription = styled.div` + font-family: var(--font-family); + font-size: var(--font-size-sm); + font-weight: 400; + color: var(--text-colour-text-primary); + line-height: var(--line-height-sm); + letter-spacing: var(--letter-spacing-sm); + text-align: left; + gap: var(--spacing-1); +` + +export const ElTableToolbarActions = styled.div`` diff --git a/src/components/table/table-toolbar/table-toolbar.mdx b/src/components/table/table-toolbar/table-toolbar.mdx new file mode 100644 index 00000000..08105321 --- /dev/null +++ b/src/components/table/table-toolbar/table-toolbar.mdx @@ -0,0 +1,33 @@ +import { Meta, Canvas, Controls, Description } from '@storybook/blocks' +import { RenderHtmlMarkup } from '../../../storybook/render-html-markup' +import * as TableToolbarStories from './table-toolbar.stories' + + + +# Table Toolbar + + + +## Basic Usage + + + + + + + +## With Bulk Actions + + + + + + + +## Toolbar Skeleton + + + + + + diff --git a/src/components/table/table-toolbar/table-toolbar.stories.tsx b/src/components/table/table-toolbar/table-toolbar.stories.tsx new file mode 100644 index 00000000..d64b3fdf --- /dev/null +++ b/src/components/table/table-toolbar/table-toolbar.stories.tsx @@ -0,0 +1,128 @@ +import { Meta } from '@storybook/react' +// import { figmaDesignUrls } from '../../storybook/figma' +import { TableToolbar } from './table-toolbar.js' +import { Button } from '#src/components/button/button' +import { Icon } from '#src/components/icon/icon-component' +import { Menu } from '#src/components/menu/menu' +import { MenuPopover, MenuTrigger } from '#src/components/menu/menu-popover' +import { MenuItem, MenuItemGroup, MenuList } from '#src/components/menu/menu.atoms' +import { ButtonGroup } from '#src/components/button-group/button-group' +import { Skeleton } from '#src/components/skeleton/skeleton' + +const meta: Meta = { + title: 'Components/TableToolbar', + component: TableToolbar, +} + +export default meta + +/** A simple toolbar for tables. + * When no items are selected, it displays the total item count and default actions. + */ + +export const BasicUsage = { + render: ({}) => ( + + + {({ getTriggerProps }) => ( + // To do: Once Button component is update with more props for no-padding, please make updates here + } + > + Page size: 25 + + )} + + + + + 25 + 50 + 100 + + + + + } + /> + ), +} + +/** + * In tables with batch actions, when one or more items have been selected, + * the toolbar changes to display the number of selected items and the available actions + */ +export const WithBulkActions = { + render: ({}) => ( + + Button 1 + Button 2 + Button 3 + + + {({ getTriggerProps }) => ( + } /> + )} + + + + + 25 + 50 + 100 + + + + + + } + /> + ), +} + +/** Skeleton state for the table toolbar + * To display until the data is retrieved and rendered in tabel + */ + +export const ToolbarSkeleton = { + render: ({}) => ( + } + actions={ + + + {({ getTriggerProps }) => ( + // To do: Once Button component is update with more props for no-padding, please make updates here + } + > + Page size: 25 + + )} + + + + + 25 + 50 + 100 + + + + + } + /> + ), +} diff --git a/src/components/table/table-toolbar/table-toolbar.tsx b/src/components/table/table-toolbar/table-toolbar.tsx new file mode 100644 index 00000000..7333923e --- /dev/null +++ b/src/components/table/table-toolbar/table-toolbar.tsx @@ -0,0 +1,16 @@ +import { HTMLAttributes, ReactNode } from 'react' +import { ElTableToolbar, ElTableToolbarActions, ElTableToolbarDescription } from './styles' + +interface TableToolbarProps extends HTMLAttributes { + description?: ReactNode + actions?: ReactNode +} + +export const TableToolbar: React.FC = ({ description, actions }) => { + return ( + + {description} + {actions} + + ) +} diff --git a/src/index.ts b/src/index.ts index 7b9e7339..7993408d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -57,6 +57,8 @@ export * from './components/menu' export * from './components/dialog' export * from './components/avatar' export * from './components/skeleton' +export * from './components/table/table-container' +export * from './components/table/table-toolbar' export * from './hooks/use-portal' export * from './hooks/use-snack' From 76eb8dc6a51695b844a60bb0731b394315c4330e Mon Sep 17 00:00:00 2001 From: Kushal <44560227+ksolanki7@users.noreply.github.com> Date: Wed, 11 Dec 2024 15:00:29 +1100 Subject: [PATCH 02/14] fix: #249 using correct size variables matching DS figma (#250) * fix: #249 using correct size variables matching DS figma * Updated usage of size variable across the repo with correct values reflecting DS figma --- src/components/avatar-rectangle/styles.ts | 10 +++--- src/components/avatar/styles.ts | 8 ++--- src/components/bottom-bar-item/styles.ts | 4 +-- src/components/mobile-nav-item/styles.ts | 4 +-- src/components/nav-icon-item/styles.ts | 4 +-- src/components/top-bar/styles.ts | 2 +- src/tokens/build.mjs | 6 ++-- src/tokens/payprop/tokens.css | 29 ++++++++-------- src/tokens/payprop/tokens.ts | 29 ++++++++-------- src/tokens/reapit/tokens.css | 29 ++++++++-------- src/tokens/reapit/tokens.ts | 29 ++++++++-------- src/tokens/tokens.json | 40 ++++++++++++++++------- 12 files changed, 112 insertions(+), 82 deletions(-) diff --git a/src/components/avatar-rectangle/styles.ts b/src/components/avatar-rectangle/styles.ts index 5f42b9bd..be2e8022 100644 --- a/src/components/avatar-rectangle/styles.ts +++ b/src/components/avatar-rectangle/styles.ts @@ -7,23 +7,23 @@ import CommercialBottomImage from './icons/bottom-commercial-image.svg?react' import CommercialSmallBottomImage from './icons/bottom-commercial-image-small.svg?react' const baseAvatarRectMediumSize = ` - width: var(--size-size-12, 72px); + width: var(--size-18); height: 54px; ` const baseAvatarRectSmallSize = ` - width: var(--size-size-11, 64px); - height: var(--size-size-9, 48px); + width: var(--size-16); + height: var(--size-12); ` // The commercial consists of two sections: the commercial image and the bottom placeholder. const baseCommercialMediumImagesSize = ` width: 54px; - height: var(--size-size-8, 40px); + height: var(--size-10); ` const baseCommercialBottomSmallPlaceholderSize = ` - width: var(--size-size-9, 48px); + width: var(--size-12); height: 36px; ` diff --git a/src/components/avatar/styles.ts b/src/components/avatar/styles.ts index d017e0bf..8a9e4a07 100644 --- a/src/components/avatar/styles.ts +++ b/src/components/avatar/styles.ts @@ -5,8 +5,8 @@ const baseCircleStyle = ` ` const baseMediumSizeStyle = ` - width: var(--size-8, 40px); - height: var(--size-8, 40px); + width: var(--size-10); + height: var(--size-10); font-size: var(--font-size-base, 15px); line-height: var(--line-height-base, 24px); letter-spacing: var(--letter-spacing-base, -0.15px); @@ -50,8 +50,8 @@ export const ElAvatar = styled.span` } &[data-size='small'] { - width: var(--size-7, 32px); - height: var(--size-7, 32px); + width: var(--size-8); + height: var(--size-8); font-size: var(--font-size-2xs, 12px); line-height: var(--line-height-2xs, 16px); letter-spacing: var(--letter-spacing-2xs, -0.12px); diff --git a/src/components/bottom-bar-item/styles.ts b/src/components/bottom-bar-item/styles.ts index d52dc753..d1e76d5d 100644 --- a/src/components/bottom-bar-item/styles.ts +++ b/src/components/bottom-bar-item/styles.ts @@ -48,8 +48,8 @@ export const ElBottomBarItemBadge = styled.span` position: absolute; top: -3px; right: -3px; - width: var(--size-2, 8px); - height: var(--size-2, 8px); + width: var(--size-2); + height: var(--size-2); background-color: var(--icon-error, #f01830); border-radius: 100%; ` diff --git a/src/components/mobile-nav-item/styles.ts b/src/components/mobile-nav-item/styles.ts index 3bf272ab..8becdc2e 100644 --- a/src/components/mobile-nav-item/styles.ts +++ b/src/components/mobile-nav-item/styles.ts @@ -55,8 +55,8 @@ export const ElMobileNavItemContent = styled.span` export const ElMobileNavItemBadge = styled.span` display: block; - width: var(--size-2, 8px); - height: var(--size-2, 8px); + width: var(--size-2); + height: var(--size-2); background-color: var(--icon-error, #f01830); border-radius: 100%; ` diff --git a/src/components/nav-icon-item/styles.ts b/src/components/nav-icon-item/styles.ts index ecb44b43..8c88f7fb 100644 --- a/src/components/nav-icon-item/styles.ts +++ b/src/components/nav-icon-item/styles.ts @@ -41,8 +41,8 @@ export const ElNavIconItemBadge = styled.span` position: absolute; right: 5px; top: 5px; - width: var(--size-2, 8px); - height: var(--size-2, 8px); + width: var(--size-2); + height: var(--size-2); background-color: var(--icon-error, #f01830); border-radius: 100%; ` diff --git a/src/components/top-bar/styles.ts b/src/components/top-bar/styles.ts index fadffe93..2acacd26 100644 --- a/src/components/top-bar/styles.ts +++ b/src/components/top-bar/styles.ts @@ -29,7 +29,7 @@ export const ElTopBarSecondaryNav = styled(ElButtonGroup)` export const ElTopBar = styled.div` display: flex; align-items: center; - height: var(--size-10, 56px); + height: var(--size-14); padding: var(--spacing-2, 8px) var(--spacing-5, 20px); border-bottom: var(--border-default, 1px) solid var(--outline-default, #e5e9ed); background: var(--fill-white, #fff); diff --git a/src/tokens/build.mjs b/src/tokens/build.mjs index dd29167c..55fc9044 100644 --- a/src/tokens/build.mjs +++ b/src/tokens/build.mjs @@ -5,7 +5,7 @@ const themes = ['Reapit', 'PayProp'] const getStyleDictionaryConfig = (theme) => { const lowerCasedTheme = theme.toLowerCase() return { - source: ['./tokens/tokens.json'], + source: ['./src/tokens/tokens.json'], parsers: [ { pattern: /\.json$/, @@ -27,7 +27,7 @@ const getStyleDictionaryConfig = (theme) => { ], platforms: { css: { - buildPath: `tokens/${lowerCasedTheme}/`, + buildPath: `src/tokens/${lowerCasedTheme}/`, files: [ { destination: 'tokens.css', @@ -36,7 +36,7 @@ const getStyleDictionaryConfig = (theme) => { ], }, ts: { - buildPath: `tokens/${lowerCasedTheme}/`, + buildPath: `src/tokens/${lowerCasedTheme}/`, files: [ { destination: 'tokens.ts', diff --git a/src/tokens/payprop/tokens.css b/src/tokens/payprop/tokens.css index 7e36fcdc..e7568429 100644 --- a/src/tokens/payprop/tokens.css +++ b/src/tokens/payprop/tokens.css @@ -1,6 +1,6 @@ /** * Do not edit directly - * Generated on Tue, 02 Jul 2024 10:36:54 GMT + * Generated on Tue, 10 Dec 2024 06:08:05 GMT */ :root { @@ -281,18 +281,21 @@ --size-4: 1rem; --size-5: 1.25rem; --size-6: 1.5rem; - --size-7: 2rem; - --size-8: 2.5rem; - --size-9: 3rem; - --size-10: 3.5rem; - --size-11: 4rem; - --size-12: 4.5rem; - --size-13: 5rem; - --size-14: 6rem; - --size-15: 7rem; - --size-16: 8rem; - --size-17: 9rem; - --size-18: 10rem; + --size-7: 1.75rem; + --size-8: 2rem; + --size-9: 2.25rem; + --size-10: 2.5rem; + --size-11: 2.75rem; + --size-12: 3rem; + --size-14: 3.5rem; + --size-16: 4rem; + --size-18: 4.5rem; + --size-20: 5rem; + --size-24: 6rem; + --size-28: 7rem; + --size-32: 8rem; + --size-36: 9rem; + --size-40: 10rem; --size-px: 0.0625rem; --icon-xs: 0.75rem; --icon-sm: 1rem; diff --git a/src/tokens/payprop/tokens.ts b/src/tokens/payprop/tokens.ts index d13e90dc..6f853b27 100644 --- a/src/tokens/payprop/tokens.ts +++ b/src/tokens/payprop/tokens.ts @@ -1,6 +1,6 @@ /** * Do not edit directly - * Generated on Tue, 02 Jul 2024 10:36:54 GMT + * Generated on Tue, 10 Dec 2024 06:08:05 GMT */ export const neutral900 = '#222b33' @@ -280,18 +280,21 @@ export const sizeSize3 = '0.75rem' export const sizeSize4 = '1rem' export const sizeSize5 = '1.25rem' export const sizeSize6 = '1.5rem' -export const sizeSize7 = '2rem' -export const sizeSize8 = '2.5rem' -export const sizeSize9 = '3rem' -export const sizeSize10 = '3.5rem' -export const sizeSize11 = '4rem' -export const sizeSize12 = '4.5rem' -export const sizeSize13 = '5rem' -export const sizeSize14 = '6rem' -export const sizeSize15 = '7rem' -export const sizeSize16 = '8rem' -export const sizeSize17 = '9rem' -export const sizeSize18 = '10rem' +export const sizeSize7 = '1.75rem' +export const sizeSize8 = '2rem' +export const sizeSize9 = '2.25rem' +export const sizeSize10 = '2.5rem' +export const sizeSize11 = '2.75rem' +export const sizeSize12 = '3rem' +export const sizeSize14 = '3.5rem' +export const sizeSize16 = '4rem' +export const sizeSize18 = '4.5rem' +export const sizeSize20 = '5rem' +export const sizeSize24 = '6rem' +export const sizeSize28 = '7rem' +export const sizeSize32 = '8rem' +export const sizeSize36 = '9rem' +export const sizeSize40 = '10rem' export const sizeSizePx = '0.0625rem' export const iconSizeIconXs = '0.75rem' export const iconSizeIconSm = '1rem' diff --git a/src/tokens/reapit/tokens.css b/src/tokens/reapit/tokens.css index 7e36fcdc..e7568429 100644 --- a/src/tokens/reapit/tokens.css +++ b/src/tokens/reapit/tokens.css @@ -1,6 +1,6 @@ /** * Do not edit directly - * Generated on Tue, 02 Jul 2024 10:36:54 GMT + * Generated on Tue, 10 Dec 2024 06:08:05 GMT */ :root { @@ -281,18 +281,21 @@ --size-4: 1rem; --size-5: 1.25rem; --size-6: 1.5rem; - --size-7: 2rem; - --size-8: 2.5rem; - --size-9: 3rem; - --size-10: 3.5rem; - --size-11: 4rem; - --size-12: 4.5rem; - --size-13: 5rem; - --size-14: 6rem; - --size-15: 7rem; - --size-16: 8rem; - --size-17: 9rem; - --size-18: 10rem; + --size-7: 1.75rem; + --size-8: 2rem; + --size-9: 2.25rem; + --size-10: 2.5rem; + --size-11: 2.75rem; + --size-12: 3rem; + --size-14: 3.5rem; + --size-16: 4rem; + --size-18: 4.5rem; + --size-20: 5rem; + --size-24: 6rem; + --size-28: 7rem; + --size-32: 8rem; + --size-36: 9rem; + --size-40: 10rem; --size-px: 0.0625rem; --icon-xs: 0.75rem; --icon-sm: 1rem; diff --git a/src/tokens/reapit/tokens.ts b/src/tokens/reapit/tokens.ts index d13e90dc..6f853b27 100644 --- a/src/tokens/reapit/tokens.ts +++ b/src/tokens/reapit/tokens.ts @@ -1,6 +1,6 @@ /** * Do not edit directly - * Generated on Tue, 02 Jul 2024 10:36:54 GMT + * Generated on Tue, 10 Dec 2024 06:08:05 GMT */ export const neutral900 = '#222b33' @@ -280,18 +280,21 @@ export const sizeSize3 = '0.75rem' export const sizeSize4 = '1rem' export const sizeSize5 = '1.25rem' export const sizeSize6 = '1.5rem' -export const sizeSize7 = '2rem' -export const sizeSize8 = '2.5rem' -export const sizeSize9 = '3rem' -export const sizeSize10 = '3.5rem' -export const sizeSize11 = '4rem' -export const sizeSize12 = '4.5rem' -export const sizeSize13 = '5rem' -export const sizeSize14 = '6rem' -export const sizeSize15 = '7rem' -export const sizeSize16 = '8rem' -export const sizeSize17 = '9rem' -export const sizeSize18 = '10rem' +export const sizeSize7 = '1.75rem' +export const sizeSize8 = '2rem' +export const sizeSize9 = '2.25rem' +export const sizeSize10 = '2.5rem' +export const sizeSize11 = '2.75rem' +export const sizeSize12 = '3rem' +export const sizeSize14 = '3.5rem' +export const sizeSize16 = '4rem' +export const sizeSize18 = '4.5rem' +export const sizeSize20 = '5rem' +export const sizeSize24 = '6rem' +export const sizeSize28 = '7rem' +export const sizeSize32 = '8rem' +export const sizeSize36 = '9rem' +export const sizeSize40 = '10rem' export const sizeSizePx = '0.0625rem' export const iconSizeIconXs = '0.75rem' export const iconSizeIconSm = '1rem' diff --git a/src/tokens/tokens.json b/src/tokens/tokens.json index 751b5f64..5e95ad2c 100644 --- a/src/tokens/tokens.json +++ b/src/tokens/tokens.json @@ -647,72 +647,90 @@ "description": "Used for sizing shapes except icons. For icons use icon sizes" }, "size-7": { - "value": "{unit-8}", + "value": "{unit-7}", "type": "dimension", "parent": "Semantic variables/Reapit", "description": "Used for sizing shapes except icons. For icons use icon sizes" }, "size-8": { - "value": "{unit-10}", + "value": "{unit-8}", "type": "dimension", "parent": "Semantic variables/Reapit", "description": "Used for sizing shapes except icons. For icons use icon sizes" }, "size-9": { - "value": "{unit-12}", + "value": "{unit-9}", "type": "dimension", "parent": "Semantic variables/Reapit", "description": "Used for sizing shapes except icons. For icons use icon sizes" }, "size-10": { - "value": "{unit-14}", + "value": "{unit-10}", "type": "dimension", "parent": "Semantic variables/Reapit", "description": "Used for sizing shapes except icons. For icons use icon sizes" }, "size-11": { - "value": "{unit-16}", + "value": "{unit-11}", "type": "dimension", "parent": "Semantic variables/Reapit", "description": "Used for sizing shapes except icons. For icons use icon sizes" }, "size-12": { + "value": "{unit-12}", + "type": "dimension", + "parent": "Semantic variables/Reapit", + "description": "Used for sizing shapes except icons. For icons use icon sizes" + }, + "size-14": { + "value": "{unit-14}", + "type": "dimension", + "parent": "Semantic variables/Reapit", + "description": "Used for sizing shapes except icons. For icons use icon sizes" + }, + "size-16": { + "value": "{unit-16}", + "type": "dimension", + "parent": "Semantic variables/Reapit", + "description": "Used for sizing shapes except icons. For icons use icon sizes" + }, + "size-18": { "value": "{unit-18}", "type": "dimension", "parent": "Semantic variables/Reapit", "description": "Used for sizing shapes except icons. For icons use icon sizes" }, - "size-13": { + "size-20": { "value": "{unit-20}", "type": "dimension", "parent": "Semantic variables/Reapit", "description": "Used for sizing shapes except icons. For icons use icon sizes" }, - "size-14": { + "size-24": { "value": "{unit-24}", "type": "dimension", "parent": "Semantic variables/Reapit", "description": "Used for sizing shapes except icons. For icons use icon sizes" }, - "size-15": { + "size-28": { "value": "{unit-28}", "type": "dimension", "parent": "Semantic variables/Reapit", "description": "Used for sizing shapes except icons. For icons use icon sizes" }, - "size-16": { + "size-32": { "value": "{unit-32}", "type": "dimension", "parent": "Semantic variables/Reapit", "description": "Used for sizing shapes except icons. For icons use icon sizes" }, - "size-17": { + "size-36": { "value": "{unit-36}", "type": "dimension", "parent": "Semantic variables/Reapit", "description": "Used for sizing shapes except icons. For icons use icon sizes" }, - "size-18": { + "size-40": { "value": "{unit-40}", "type": "dimension", "parent": "Semantic variables/Reapit", From 9066a35247c91c38c04a6685fd6ce5ef6b1801d7 Mon Sep 17 00:00:00 2001 From: Nur M <106480336+nurm717123@users.noreply.github.com> Date: Fri, 13 Dec 2024 09:24:10 +0700 Subject: [PATCH 03/14] feat: #153 v5 top bar update docs (#240) * fix: update default top-bar popover y offset * feat: update docs to include different viewport --- src/components/top-bar/styles.ts | 10 +- src/components/top-bar/top-bar.mdx | 19 +- src/components/top-bar/top-bar.stories.tsx | 248 ++++++---- .../figma/guidelines-images/index.ts | 3 +- .../figma/guidelines-images/top-bar.svg | 442 ------------------ src/storybook/figma/index.tsx | 2 +- src/styles/media.ts | 4 +- 7 files changed, 172 insertions(+), 556 deletions(-) delete mode 100644 src/storybook/figma/guidelines-images/top-bar.svg diff --git a/src/components/top-bar/styles.ts b/src/components/top-bar/styles.ts index 2acacd26..e216a470 100644 --- a/src/components/top-bar/styles.ts +++ b/src/components/top-bar/styles.ts @@ -1,5 +1,5 @@ import { styled } from '@linaria/react' -import { isDesktop, isDesktopOrBelow, isTablet, isTabletOrBelow } from '../../styles/media' +import { isBelowWideScreen, isTablet, isBelowDesktop, isWideScreen } from '../../styles/media' import { ElButtonGroup } from '../button-group' import { css } from '@linaria/core' @@ -34,7 +34,7 @@ export const ElTopBar = styled.div` border-bottom: var(--border-default, 1px) solid var(--outline-default, #e5e9ed); background: var(--fill-white, #fff); - ${isDesktopOrBelow} { + ${isBelowWideScreen} { padding: var(--spacing-2, 8px) var(--spacing-4, 16px); ${ElTopBarLogo} { @@ -46,7 +46,7 @@ export const ElTopBar = styled.div` } } - ${isTabletOrBelow} { + ${isBelowDesktop} { ${ElTopBarProfile} { display: none; } @@ -66,7 +66,7 @@ export const ElTopBarMobileNav = styled.div` display: inline-block; padding-right: var(--spacing-2, 8px); - ${isDesktop} { + ${isWideScreen} { display: none; } ` @@ -74,5 +74,5 @@ export const ElTopBarMobileNav = styled.div` export const elTopBarMenuPopover = css` // To adjust the menu popover's Y offset so it appears below the top bar instead of directly below the button. // The "important" rule is used to override the Menu's auto-anchor yOffset. - top: 44px !important; + top: var(--spacing-10) !important; ` diff --git a/src/components/top-bar/top-bar.mdx b/src/components/top-bar/top-bar.mdx index 558ddd32..73a61247 100644 --- a/src/components/top-bar/top-bar.mdx +++ b/src/components/top-bar/top-bar.mdx @@ -1,24 +1,25 @@ import { Canvas, Controls, Meta } from '@storybook/blocks' import { RenderHtmlMarkup } from '../../storybook/render-html-markup' import * as TopBarStories from './top-bar.stories' -import { GuidelinesImage } from '../../storybook/figma' - +# Top bar -## Styles Only Usage +Top bar component is part of the element's navigation, It is composed of several components to easily create a top navigation structure. -To display `TopBar` with vanilla usage, you will need to compose your component using `.el-top-bar, .el-top-bar-main-nav, .el-top-bar-search, .el-top-bar-secondary-nav, and .el-top-bar-profile` classes as the example below. +## Default - +To display `TopBar` with vanilla usage, you will need to compose your component using `.el-top-bar, .el-top-bar-main-nav, .el-top-bar-search, .el-top-bar-secondary-nav, and .el-top-bar-profile` classes or respective component for each class for React usage as the example below. - + + + ## React Usage -In React version, you will need to use `TopBar` compound component to compose the `TopBar` as shown below. +In the React usage, you can utilize `CSSContainerQuery` component to conditionally render the Main navigation section based on the viewport size, as demonstrated below (on wider screens all buttons should visible). - + - + diff --git a/src/components/top-bar/top-bar.stories.tsx b/src/components/top-bar/top-bar.stories.tsx index 186b5fe1..cc8319fc 100644 --- a/src/components/top-bar/top-bar.stories.tsx +++ b/src/components/top-bar/top-bar.stories.tsx @@ -1,124 +1,127 @@ import { figmaDesignUrls } from '#src/storybook/figma/index' import type { Meta, StoryObj } from '@storybook/react' -import { ElAvatar } from '../avatar' -import { AvatarButton, ElAvatarButton } from '../avatar-button' +import { AvatarButton } from '../avatar-button' import { elIcon } from '../button' import { CSSContainerQuery } from '../container-query/container-query' import { Icon } from '../icon' -import { ElMenu, ElMenuItemAnchor, ElMenuItemButton, ElMenuList, ElMenuPopover, Menu } from '../menu' -import { ElNavDropdownButton, NavDropdownButton } from '../nav-dropdown-button' -import { ElButtonNavIconItem, NavIconItem } from '../nav-icon-item' -import { ElNavItemAnchor, ElNavItemLabelContainer, NavItem } from '../nav-item' +import { Menu } from '../menu' +import { NavDropdownButton } from '../nav-dropdown-button' +import { NavIconItem } from '../nav-icon-item' +import { NavItem } from '../nav-item' import { NavSearchButton } from '../nav-search-button/nav-search-button' -import { - ElNavSearchButton, - ElNavSearchButtonContainer, - ElNavSearchButtonIcon, - ElNavSearchButtonPlaceholder, - ElNavSearchButtonShortcutText, -} from '../nav-search-button/styles' import { ReapitLogo } from '../reapit-logo' -import reapitLogoIconUrl from '../reapit-logo/icons/brand-reapit.svg' import MenuIcon from './icons/menu-icon.svg?react' -import { - ElTopBar, - ElTopBarLogo, - ElTopBarMainNav, - ElTopBarMobileNav, - ElTopBarProfile, - ElTopBarSearch, - ElTopBarSecondaryNav, - elTopBarMenuPopover, -} from './styles' +import { elTopBarMenuPopover } from './styles' import { TopBar } from './top-bar' +import { INITIAL_VIEWPORTS } from '@storybook/addon-viewport' + +const viewports: typeof INITIAL_VIEWPORTS = { + superWideScreen: { + name: 'Super Wide Screen', + type: 'desktop', + styles: { + width: '1920px', + height: '1500px', + }, + }, + wideScreen: { + name: 'Wide Screen', + type: 'desktop', + styles: { + width: '1440px', + height: '900px', + }, + }, + desktop: { + name: 'Desktop', + type: 'desktop', + styles: { + width: '1024px', + height: '900px', + }, + }, + ipad11p: INITIAL_VIEWPORTS.ipad11p, + iphone14: INITIAL_VIEWPORTS.iphone14, +} export default { title: 'Components/Top bar', component: TopBar, + parameters: { + viewport: { defaultViewport: 'responsive', viewports }, + }, } as Meta -export const StylesOnlyUsage: StoryObj = { +type Story = StoryObj + +export const Default: Story = { + // NOTE: this version is without `CSSContainerQuery` so the non react user doesn't see it in the docs render: () => { return ( - - - - - - - - Button 1 - - - Button 2 - - - - More - - - - - Button 3 - Button 4 - - Button 5 - - - - - - - - - - - - - - Search - Ctrl + K - - - - - - - - - - - - - - - + + + + + + Button 1 + Button 2 + + + {({ getTriggerProps, isOpen }) => ( + + More + + )} + + + + Button 3 + Button 4 + Button 5 + + + + + + + - - - - - + + } /> + } /> + } /> + + + } /> + - - - - AD - - - - - + + + + {({ getTriggerProps, isOpen }) => } + + + + User menu 1 + User menu 2 + User menu 3 + + + + + ) }, parameters: { design: { type: 'figma', url: figmaDesignUrls.appBar, + allowFullscreen: true, }, }, } -export const ReactUsage: StoryObj = { + +export const ResponsiveMainNav: Story = { render: () => { return ( @@ -166,12 +169,22 @@ export const ReactUsage: StoryObj = { - + + + {({ getTriggerProps, isOpen }) => } + + + + User menu 1 + User menu 2 + User menu 3 + + + ) }, - parameters: { design: { type: 'figma', @@ -180,3 +193,48 @@ export const ReactUsage: StoryObj = { }, }, } + +export const Mobile: Story = { + render: ResponsiveMainNav.render, + parameters: { + ...ResponsiveMainNav.parameters, + viewport: { defaultViewport: 'iphone14' }, + }, +} + +export const Tablet: Story = { + render: ResponsiveMainNav.render, + parameters: { + ...ResponsiveMainNav.parameters, + viewport: { defaultViewport: 'ipad11p' }, + }, +} +export const Desktop: Story = { + render: ResponsiveMainNav.render, + parameters: { + ...ResponsiveMainNav.parameters, + viewport: { + defaultViewport: 'desktop', + }, + }, +} + +export const WideScreen: Story = { + render: ResponsiveMainNav.render, + parameters: { + ...ResponsiveMainNav.parameters, + viewport: { + defaultViewport: 'wideScreen', + }, + }, +} + +export const SuperWideScreen: Story = { + render: ResponsiveMainNav.render, + parameters: { + ...ResponsiveMainNav.parameters, + viewport: { + defaultViewport: 'superWideScreen', + }, + }, +} diff --git a/src/storybook/figma/guidelines-images/index.ts b/src/storybook/figma/guidelines-images/index.ts index b0e8e769..cf2be470 100644 --- a/src/storybook/figma/guidelines-images/index.ts +++ b/src/storybook/figma/guidelines-images/index.ts @@ -1,7 +1,6 @@ import Accordion from './accordion.svg' import ButtonGroup from './button-group.svg' -import TopBar from './top-bar.svg' +// TODO: can delete this, as we're not planning to use Design Guidelines docs as a documentation for developer export { Accordion } -export { TopBar } export { ButtonGroup } diff --git a/src/storybook/figma/guidelines-images/top-bar.svg b/src/storybook/figma/guidelines-images/top-bar.svg deleted file mode 100644 index 40abdac1..00000000 --- a/src/storybook/figma/guidelines-images/top-bar.svg +++ /dev/null @@ -1,442 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/storybook/figma/index.tsx b/src/storybook/figma/index.tsx index d141b9db..199003c2 100644 --- a/src/storybook/figma/index.tsx +++ b/src/storybook/figma/index.tsx @@ -11,7 +11,7 @@ export const GuidelinesImage: FC = ({ name }) => ( export const figmaDesignUrls = { appBar: - 'https://www.figma.com/design/6CaivqdlTX0UkFYJkpBKDu/Reapit-DS?node-id=428-7209&node-type=frame&t=RHaBJeesRCccFe53-0', + 'https://www.figma.com/design/XJ6qcAV8gHscsUodqJMNEF/Reapit-Elements-production-ready-components?node-id=16-4840&m=dev', accordion: 'https://www.figma.com/design/6CaivqdlTX0UkFYJkpBKDu/Reapit-DS?node-id=141-4180&t=k8kHuB2wp3KZoKMw-4', buttonGroup: 'https://www.figma.com/design/6CaivqdlTX0UkFYJkpBKDu/Reapit-DS?node-id=428-7408&t=8GcgX59FmafMRAda-4', } diff --git a/src/styles/media.ts b/src/styles/media.ts index 5b494dc9..2bfdae1e 100644 --- a/src/styles/media.ts +++ b/src/styles/media.ts @@ -5,5 +5,5 @@ export const isWideScreen = '@media screen and (min-width: 1440px)' export const isSuperWideScreen = '@media screen and (min-width: 1920px)' export const is4KScreen = '@media screen and (min-width: 2560px)' -export const isDesktopOrBelow = '@media screen and (max-width: 1024px)' -export const isTabletOrBelow = '@media screen and (max-width: 768px)' +export const isBelowWideScreen = '@media screen and (max-width: 1439px)' +export const isBelowDesktop = '@media screen and (max-width: 1023px) ' From 48452c6e5ed894a86cb43625c07b4a165fe65dad Mon Sep 17 00:00:00 2001 From: Nur M <106480336+nurm717123@users.noreply.github.com> Date: Fri, 13 Dec 2024 13:28:39 +0700 Subject: [PATCH 04/14] chore: fix css var name and remove fallback value (#251) --- src/components/avatar-button/styles.ts | 4 ++-- src/components/avatar/styles.ts | 30 +++++++++++++------------- src/components/menu/styles.ts | 2 +- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/components/avatar-button/styles.ts b/src/components/avatar-button/styles.ts index 37c62b04..242fc7d6 100644 --- a/src/components/avatar-button/styles.ts +++ b/src/components/avatar-button/styles.ts @@ -14,11 +14,11 @@ export const ElAvatarButton = styled.button` ${ElAvatar} { box-shadow: 0px 0px 0px 1px #fff, - 0px 0px 0px 4px var(--Colours-Purple-purple-300, #7e9bfa); + 0px 0px 0px 4px var(--purple-300); } } &:hover ${ElAvatar} { - background: var(--fill-colour-fill-action-light, #d6e1ff); + background: var(--fill-action-light); } ` diff --git a/src/components/avatar/styles.ts b/src/components/avatar/styles.ts index 8a9e4a07..f1d6ca2d 100644 --- a/src/components/avatar/styles.ts +++ b/src/components/avatar/styles.ts @@ -1,24 +1,24 @@ import { styled } from '@linaria/react' const baseCircleStyle = ` - border-radius: var(--corner-3xl, 24px); + border-radius: var(--corner-3xl); ` const baseMediumSizeStyle = ` width: var(--size-10); height: var(--size-10); - font-size: var(--font-size-base, 15px); - line-height: var(--line-height-base, 24px); - letter-spacing: var(--letter-spacing-base, -0.15px); + font-size: var(--font-size-base); + line-height: var(--line-height-base); + letter-spacing: var(--letter-spacing-base); ` const baseColourDefaultStyle = ` - background: var(--fill-default-medium, #9faebc); - color: var(--text-white, #fff); + background: var(--fill-default-medium); + color: var(--text-white); /* override Icon element colour */ svg { - color: var(--text-white, #fff); + color: var(--text-white); } ` @@ -28,7 +28,7 @@ export const ElAvatar = styled.span` justify-content: center; align-items: center; text-align: center; - font-family: var(--font-family, Inter); + font-family: var(--font-family); font-style: normal; font-weight: 600; @@ -37,23 +37,23 @@ export const ElAvatar = styled.span` ${baseMediumSizeStyle} &[data-shape='square'] { - border-radius: var(--corner-lg, 8px); + border-radius: var(--corner-lg); } &[data-colour='purple'] { - background: var(--fill-action-lightest, #ecf3ff); - color: var(--text-action, #4e56ea); + background: var(--fill-action-lightest); + color: var(--text-action); /* override Icon element colour */ svg { - color: var(--text-action, #4e56ea); + color: var(--text-action); } } &[data-size='small'] { width: var(--size-8); height: var(--size-8); - font-size: var(--font-size-2xs, 12px); - line-height: var(--line-height-2xs, 16px); - letter-spacing: var(--letter-spacing-2xs, -0.12px); + font-size: var(--font-size-2xs); + line-height: var(--line-height-2xs); + letter-spacing: var(--letter-spacing-2xs); } ` diff --git a/src/components/menu/styles.ts b/src/components/menu/styles.ts index 9633f59c..e9eaad00 100644 --- a/src/components/menu/styles.ts +++ b/src/components/menu/styles.ts @@ -79,7 +79,7 @@ export const ElMenuItemGroupTitle = styled.div` text-transform: uppercase; display: flex; height: 32px; - padding: var(--spacing-none, 0px) var(--spacing-4, 16px); + padding: var(--spacing-none) var(--spacing-4); align-items: center; align-self: stretch; ` From 7783d9e7d7bc9a1556055e0771dff3956db6955a Mon Sep 17 00:00:00 2001 From: Dimas M <96416644+ss-dimasm@users.noreply.github.com> Date: Mon, 16 Dec 2024 09:39:33 +0700 Subject: [PATCH 05/14] feat: add bottom bar component (#233) * refactor: ui tweak to make badge position fixed * feat: add bottom bar navigation * refactor: redundant state setter * chore: exclude snapshot * chore: change css variable reference * chore: resolve feedbacks * fix: ts issue * chore: avoid descendants component styling * refactor: resolve pr feedback * chore: add snapshpt --- src/components/bottom-bar-item/styles.ts | 2 +- .../__snapshots__/bottom-bar.test.tsx.snap | 260 ++++++++++++++++++ .../bottom-bar/__tests__/bottom-bar.test.tsx | 35 +++ .../use-bottom-bar-visibility.test.ts | 54 ++++ .../bottom-bar/bottom-bar.atoms.tsx | 31 +++ src/components/bottom-bar/bottom-bar.mdx | 34 +++ .../bottom-bar/bottom-bar.stories.tsx | 85 ++++++ src/components/bottom-bar/bottom-bar.tsx | 43 +++ src/components/bottom-bar/index.ts | 4 + src/components/bottom-bar/styles.ts | 33 +++ .../bottom-bar/use-bottom-bar-visibility.ts | 47 ++++ 11 files changed, 627 insertions(+), 1 deletion(-) create mode 100644 src/components/bottom-bar/__tests__/__snapshots__/bottom-bar.test.tsx.snap create mode 100644 src/components/bottom-bar/__tests__/bottom-bar.test.tsx create mode 100644 src/components/bottom-bar/__tests__/use-bottom-bar-visibility.test.ts create mode 100644 src/components/bottom-bar/bottom-bar.atoms.tsx create mode 100644 src/components/bottom-bar/bottom-bar.mdx create mode 100644 src/components/bottom-bar/bottom-bar.stories.tsx create mode 100644 src/components/bottom-bar/bottom-bar.tsx create mode 100644 src/components/bottom-bar/index.ts create mode 100644 src/components/bottom-bar/styles.ts create mode 100644 src/components/bottom-bar/use-bottom-bar-visibility.ts diff --git a/src/components/bottom-bar-item/styles.ts b/src/components/bottom-bar-item/styles.ts index d1e76d5d..f7f0a4c3 100644 --- a/src/components/bottom-bar-item/styles.ts +++ b/src/components/bottom-bar-item/styles.ts @@ -12,7 +12,7 @@ export const ElBottomBarItemLabel = styled.span` color: inherit; text-align: center; font-family: var(--font-family, Inter); - font-size: var(--font-size-2xs, 12px); + font-size: var(--font-size-3xs, 10px); font-style: normal; font-weight: var(--font-weight-regular, Regular); line-height: var(--line-height-3xs, 12px); diff --git a/src/components/bottom-bar/__tests__/__snapshots__/bottom-bar.test.tsx.snap b/src/components/bottom-bar/__tests__/__snapshots__/bottom-bar.test.tsx.snap new file mode 100644 index 00000000..5028fa8f --- /dev/null +++ b/src/components/bottom-bar/__tests__/__snapshots__/bottom-bar.test.tsx.snap @@ -0,0 +1,260 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`BottomBar > can render 4 items and an overflow menu 1`] = ` + + + + + + + mock icon + + + + + Menu 1 + + + + + + + mock icon + + + + + Menu 2 + + + + + + + mock icon + + + + + Menu 3 + + + + + + + mock icon + + + + + Menu 4 + + + + + + + + + + + + + + + + + More + + + + + +`; + +exports[`BottomBar > can render 5 items 1`] = ` + + + + + + + mock icon + + + + + Menu 1 + + + + + + + mock icon + + + + + Menu 2 + + + + + + + mock icon + + + + + Menu 3 + + + + + + + mock icon + + + + + Menu 4 + + + + + + + mock icon + + + + + Menu 5 + + + + +`; diff --git a/src/components/bottom-bar/__tests__/bottom-bar.test.tsx b/src/components/bottom-bar/__tests__/bottom-bar.test.tsx new file mode 100644 index 00000000..fd4c6ff9 --- /dev/null +++ b/src/components/bottom-bar/__tests__/bottom-bar.test.tsx @@ -0,0 +1,35 @@ +import { render } from '@testing-library/react' +import { BottomBar } from '../bottom-bar' + +describe('BottomBar', () => { + it('can render 5 items', () => { + expect( + render( + + mock icon}>Menu 1 + mock icon}>Menu 2 + mock icon}>Menu 3 + mock icon}>Menu 4 + mock icon}>Menu 5 + , + ).asFragment(), + ).toMatchSnapshot() + }) + + it('can render 4 items and an overflow menu', () => { + expect( + render( + + mock icon}>Menu 1 + mock icon}>Menu 2 + mock icon}>Menu 3 + mock icon}>Menu 4 + + Menu 5 + Menu 6 + + , + ).asFragment(), + ).toMatchSnapshot() + }) +}) diff --git a/src/components/bottom-bar/__tests__/use-bottom-bar-visibility.test.ts b/src/components/bottom-bar/__tests__/use-bottom-bar-visibility.test.ts new file mode 100644 index 00000000..81bba8bd --- /dev/null +++ b/src/components/bottom-bar/__tests__/use-bottom-bar-visibility.test.ts @@ -0,0 +1,54 @@ +import { RefObject } from 'react' +import { handleChangeBottomBarVisibility, useBottomBarVisibility } from '../use-bottom-bar-visibility' +import { renderHook } from '@testing-library/react-hooks' +import { act } from '@testing-library/react' + +describe('handleChangeBottomBarVisibility', () => { + afterEach(() => { + vi.clearAllMocks() + }) + + it('should call the setScrollStates function while acting to scroll down', () => { + const mockSetScrollStates = vi.fn().mockImplementation(() => ({ previousTopPosition: 0 })) + + handleChangeBottomBarVisibility({ scrollTop: 10 } as any, mockSetScrollStates) + + expect(mockSetScrollStates).toHaveBeenCalledWith(expect.any(Function)) + }) + + it('should call the setScrollStates function while acting to scroll up', () => { + const mockSetScrollStates = vi.fn().mockImplementation(() => ({ previousTopPosition: 10 })) + + handleChangeBottomBarVisibility({ scrollTop: 0 } as any, mockSetScrollStates) + + expect(mockSetScrollStates).toHaveBeenCalledWith(expect.any(Function)) + }) +}) + +describe('useBottomBarVisibility', () => { + it('should trigger the scroll event listener', () => { + const abort = vi.fn() + vi.spyOn(AbortController.prototype, 'abort').mockImplementation(abort) as any + + const mockUseRef = { + current: { + scrollTop: 0, + addEventListener: vi.fn(), + }, + } as unknown as RefObject + + const render = renderHook(() => useBottomBarVisibility(mockUseRef)) + + expect(mockUseRef.current?.addEventListener).toHaveBeenCalledWith('scroll', expect.any(Function), { + signal: expect.any(AbortSignal), + }) + + expect(abort).not.toHaveBeenCalled() + + act(() => { + render.unmount() + }) + + expect(abort).toHaveBeenCalledTimes(1) + }) +}) diff --git a/src/components/bottom-bar/bottom-bar.atoms.tsx b/src/components/bottom-bar/bottom-bar.atoms.tsx new file mode 100644 index 00000000..3539ed8c --- /dev/null +++ b/src/components/bottom-bar/bottom-bar.atoms.tsx @@ -0,0 +1,31 @@ +import type { FC, ReactNode } from 'react' + +import { Menu, MenuItemProps } from '../menu' +import { BottomBarItem } from '../bottom-bar-item' +import { Icon } from '../icon' + +export interface BottomBarMoreMenuProps { + children: ReactNode +} + +export const BottomBarMoreMenu: FC = ({ children }) => { + return ( + + + {({ getTriggerProps }) => ( + }> + More + + )} + + + {children} + + + ) +} + +export type BottomBarMoreMenuItemProps = MenuItemProps +export const BottomBarMoreMenuItem: FC = (args) => { + return +} diff --git a/src/components/bottom-bar/bottom-bar.mdx b/src/components/bottom-bar/bottom-bar.mdx new file mode 100644 index 00000000..8d767f42 --- /dev/null +++ b/src/components/bottom-bar/bottom-bar.mdx @@ -0,0 +1,34 @@ +import { Meta, Canvas, Controls } from '@storybook/blocks' +import { RenderHtmlMarkup } from '../../storybook/render-html-markup' +import * as BottomBarItemStories from './bottom-bar.stories' + + + +# Bottom Bar + +A component designed to render a navigation bar at the bottom of the screen + +This component is also capable of sliding up and down as the user scrolls the parent element + +`BottomBar` have additional export that can be used to render the following components: + +- `Item`: Renders a single item +- `MoreMenu`: Renders an expandable menu +- `MoreMenuItem`: Renders an expandable menu item + +There are two variants of the `BottomBar` component: + +- Fixed: The `BottomBar` component that only contains less or equal to 5 items +- Expandable: The `BottomBar` component is expandable and can contain more than 5 items + +## Fixed Usage + +Demonstrates how to render the fixed usage of the `BottomBar` component that contains 5 items + + + +## Expandable Usage + +Demonstrates how to render the expandable usage of the `BottomBar` component that contains more than 5 items + + diff --git a/src/components/bottom-bar/bottom-bar.stories.tsx b/src/components/bottom-bar/bottom-bar.stories.tsx new file mode 100644 index 00000000..ebc450f3 --- /dev/null +++ b/src/components/bottom-bar/bottom-bar.stories.tsx @@ -0,0 +1,85 @@ +import { action } from '@storybook/addon-actions' +import type { Meta, StoryObj } from '@storybook/react' +import { useRef } from 'react' +import { Icon } from '../icon' +import { BottomBar } from './bottom-bar' + +const meta = { + title: 'Components/Bottom Bar', + component: BottomBar, + parameters: { + layout: 'fullscreen', + }, + args: { + children: null, + parentRef: null, + }, +} satisfies Meta + +export default meta +type Story = StoryObj + +export const Standard: Story = { + render: () => { + const ref = useRef(null) + + return ( + + + long content + short content + + } isActive onClick={action('clicked')}> + Menu 1 + + } onClick={action('clicked')} hasBadge> + Menu 2 + + } onClick={action('clicked')}> + Menu 3 + + } onClick={action('clicked')}> + Menu 4 + + } onClick={action('clicked')}> + Menu 5 + + + + + ) + }, +} + +export const WithOverflowMenu: Story = { + render: () => { + const ref = useRef(null) + + return ( + + + long content + short content + + } isActive onClick={action('clicked')}> + Menu 1 + + } onClick={action('clicked')} hasBadge> + Menu 2 + + } onClick={action('clicked')}> + Menu 3 + + } onClick={action('clicked')}> + Menu 4 + + + Menu 5 + Menu 6 + + + + + ) + }, +} diff --git a/src/components/bottom-bar/bottom-bar.tsx b/src/components/bottom-bar/bottom-bar.tsx new file mode 100644 index 00000000..247a9689 --- /dev/null +++ b/src/components/bottom-bar/bottom-bar.tsx @@ -0,0 +1,43 @@ +import type { FC, HTMLAttributes, ReactNode, RefObject } from 'react' + +import { BottomBarItem } from '../bottom-bar-item' + +import { BottomBarMoreMenu, BottomBarMoreMenuItem } from './bottom-bar.atoms' +import { ElBottomBar } from './styles' +import { useBottomBarVisibility } from './use-bottom-bar-visibility' + +export interface BottomBarProps extends HTMLAttributes { + /** + * The children of the bottom bar + **/ + children: ReactNode + + /** + * The reference of the parent element that the bottom bar is attached to + * + * @description see the story for an example + */ + parentRef: RefObject | null +} + +type BottomBarFC = FC & { + Item: typeof BottomBarItem + MoreMenu: typeof BottomBarMoreMenu + MoreMenuItem: typeof BottomBarMoreMenuItem +} + +const BottomBar: BottomBarFC = ({ children, parentRef, ...rest }) => { + const { isOpen } = useBottomBarVisibility(parentRef) + + return ( + + {children} + + ) +} + +BottomBar.Item = BottomBarItem +BottomBar.MoreMenu = BottomBarMoreMenu +BottomBar.MoreMenuItem = BottomBarMoreMenuItem + +export { BottomBar } diff --git a/src/components/bottom-bar/index.ts b/src/components/bottom-bar/index.ts new file mode 100644 index 00000000..a590b73e --- /dev/null +++ b/src/components/bottom-bar/index.ts @@ -0,0 +1,4 @@ +export * from './bottom-bar' +export * from './bottom-bar.atoms' +export * from './styles' +export * from './use-bottom-bar-visibility' diff --git a/src/components/bottom-bar/styles.ts b/src/components/bottom-bar/styles.ts new file mode 100644 index 00000000..bf572f64 --- /dev/null +++ b/src/components/bottom-bar/styles.ts @@ -0,0 +1,33 @@ +import { styled } from '@linaria/react' + +export const ElBottomBar = styled.div` + display: flex; + padding: var(--spacing-2, 8px); + justify-content: center; + align-items: center; + align-self: stretch; + border-top: var(--border-default, 1px) solid var(--outline-default, #e5e9ed); + background: var(--fill-white, #fff); + + position: absolute; + bottom: 0; + left: 0; + z-index: 5; + overflow: visible; + margin-top: auto; + width: 100%; + + transition: + transform 0.3s ease-in-out, + visibility 0.3s ease-in-out; + + &[data-is-open='true'] { + transform: translateY(0); + visibility: visible; + } + + &[data-is-open='false'] { + transform: translateY(100%); + visibility: hidden; + } +` diff --git a/src/components/bottom-bar/use-bottom-bar-visibility.ts b/src/components/bottom-bar/use-bottom-bar-visibility.ts new file mode 100644 index 00000000..0a4d39ba --- /dev/null +++ b/src/components/bottom-bar/use-bottom-bar-visibility.ts @@ -0,0 +1,47 @@ +import { Dispatch, RefObject, SetStateAction, useEffect, useState } from 'react' + +type BottomBarVisibility = { + isOpen: boolean | undefined + previousTopPosition: number +} + +export const handleChangeBottomBarVisibility = ( + element: HTMLElement, + setScrollStates: Dispatch>, +) => { + const { scrollTop } = element ?? {} + + setScrollStates((state) => ({ + isOpen: state.previousTopPosition > scrollTop, + previousTopPosition: scrollTop, + })) +} + +/** + * Hook to get the vertical scroll direction of the parent element + */ +export const useBottomBarVisibility = (ref: RefObject | null): BottomBarVisibility => { + const [scrollStates, setScrollStates] = useState({ + isOpen: undefined, + previousTopPosition: 0, + }) + + useEffect( + function handleScrollListenerRegistry() { + const element = ref?.current + if (!element) return + const abortController = new AbortController() + + element.addEventListener('scroll', () => handleChangeBottomBarVisibility(element, setScrollStates), { + signal: abortController.signal, + }) + + return () => { + abortController.abort() + } + }, + [ref], + ) + + return scrollStates +} From 08ee3777ea9b9a61c5fc0da5331144d93343f334 Mon Sep 17 00:00:00 2001 From: Nur M <106480336+nurm717123@users.noreply.github.com> Date: Tue, 17 Dec 2024 09:54:11 +0700 Subject: [PATCH 06/14] chore: fix top-bar style var name and remove fallback (#259) --- src/components/nav-item/styles.ts | 26 +++++++++++++------------- src/components/top-bar/styles.ts | 22 +++++++++++----------- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/components/nav-item/styles.ts b/src/components/nav-item/styles.ts index dd5c4bb4..37a1ef7c 100644 --- a/src/components/nav-item/styles.ts +++ b/src/components/nav-item/styles.ts @@ -2,33 +2,33 @@ import { styled } from '@linaria/react' export const ElNavItemLabelContainer = styled.span` display: flex; - padding: var(--spacing-spacing-half, 2px); + padding: var(--spacing-half); align-items: flex-start; - font-family: var(--font-family, Inter); - font-size: var(--font-size-sm, 14px); + font-family: var(--font-family); + font-size: var(--font-size-sm); font-style: normal; font-weight: 500; - line-height: var(--line-height-sm, 20px); - letter-spacing: var(--letter-spacing-sm, -0.14px); + line-height: var(--line-height-sm); + letter-spacing: var(--letter-spacing-sm); ` const baseNavItemStyle = ` display: inline-flex; - padding: var(--spacing-spacing-1, 4px) var(--spacing-spacing-3, 12px); + padding: var(--spacing-1) var(--spacing-3); align-items: center; - border-radius: var(--corner-radius-corner-default, 4px); - background: var(--fill-colour-fill-white, #fff); + border-radius: var(--corner-default); + background: var(--fill-white); &:focus { box-shadow: 0px 0px 0px 1px #fff, - 0px 0px 0px 4px var(--Colours-Purple-purple-300, #7e9bfa); + 0px 0px 0px 4px var(--purple-300); } &:hover { - background: var(--fill-colour-fill-default-lightest, #f2f4f6); + background: var(--fill-default-lightest); } &:active { - background: var(--fill-colour-fill-default-lightest, #f2f4f6); + background: var(--fill-default-lightest); } &:focus-visible { outline: none; @@ -36,9 +36,9 @@ const baseNavItemStyle = ` cursor: pointer; border: none; - color: var(--text-colour-text-secondary, #607890); + color: var(--text-secondary); &:active, &[aria-current="true"], &[aria-current="page"] { - color: var(--text-colour-text-action, #4e56ea); + color: var(--text-action); } white-space: nowrap diff --git a/src/components/top-bar/styles.ts b/src/components/top-bar/styles.ts index e216a470..10415fe4 100644 --- a/src/components/top-bar/styles.ts +++ b/src/components/top-bar/styles.ts @@ -4,38 +4,38 @@ import { ElButtonGroup } from '../button-group' import { css } from '@linaria/core' export const ElTopBarLogo = styled.a` - padding-right: var(--spacing-2, 8px); + padding-right: var(--spacing-2); display: inline-flex; ` export const ElTopBarProfile = styled.div` - padding: var(--spacing-1, 4px) var(--spacing-none, 0px); + padding: var(--spacing-1) var(--spacing-none); ` export const ElTopBarMainNav = styled(ElButtonGroup)` display: flex; flex-direction: row; align-items: center; - padding-left: var(--spacing-6, 24px); + padding-left: var(--spacing-6); flex-grow: 1; container-type: inline-size; ` export const ElTopBarSecondaryNav = styled(ElButtonGroup)` flex-wrap: nowrap; - padding-right: var(--spacing-2, 8px); + padding-right: var(--spacing-2); ` export const ElTopBar = styled.div` display: flex; align-items: center; height: var(--size-14); - padding: var(--spacing-2, 8px) var(--spacing-5, 20px); - border-bottom: var(--border-default, 1px) solid var(--outline-default, #e5e9ed); - background: var(--fill-white, #fff); + padding: var(--spacing-2) var(--spacing-5); + border-bottom: var(--border-default) solid var(--outline-default); + background: var(--fill-white); ${isBelowWideScreen} { - padding: var(--spacing-2, 8px) var(--spacing-4, 16px); + padding: var(--spacing-2) var(--spacing-4); ${ElTopBarLogo} { margin-right: auto; @@ -54,17 +54,17 @@ export const ElTopBar = styled.div` ` export const ElTopBarSearch = styled.div` - padding-right: var(--spacing-2, 8px); + padding-right: var(--spacing-2); ${isTablet} { width: 216px; - padding: var(--spacing-1, 4px) var(--spacing-4, 16px) var(--spacing-1, 4px) var(--spacing-none, 0px); + padding: var(--spacing-1) var(--spacing-4) var(--spacing-1) var(--spacing-none); } ` export const ElTopBarMobileNav = styled.div` display: inline-block; - padding-right: var(--spacing-2, 8px); + padding-right: var(--spacing-2); ${isWideScreen} { display: none; From 61885a191a92c9046b972b0cb7bf8fcbedcb96d0 Mon Sep 17 00:00:00 2001 From: Dimas M <96416644+ss-dimasm@users.noreply.github.com> Date: Tue, 17 Dec 2024 10:07:56 +0700 Subject: [PATCH 07/14] chore: remove fallback css variables `NavIconItem` (#257) * chore: remove css variables fallback values for `NavIconItem` * chore: amend comma * chore: update heading mdx doc * chore: update storybook --- .../nav-icon-item/nav-icon-item.mdx | 42 +++------- .../nav-icon-item/nav-icon-item.stories.tsx | 76 +++---------------- src/components/nav-icon-item/styles.ts | 32 ++++---- 3 files changed, 36 insertions(+), 114 deletions(-) diff --git a/src/components/nav-icon-item/nav-icon-item.mdx b/src/components/nav-icon-item/nav-icon-item.mdx index 76c9c79b..07710a17 100644 --- a/src/components/nav-icon-item/nav-icon-item.mdx +++ b/src/components/nav-icon-item/nav-icon-item.mdx @@ -12,56 +12,36 @@ A versatile component designed to render navigation icon items within an AppBar -## Default State +## Default The default appearance of the `NavIconItem` -## Active State +## Active The visual state of the `NavIconItem` when it's currently selected or active - + -## Badge State +## With Badge The visual state of the `NavIconItem` when it's currently selected or active with a badge - + -## Style Anchor Usage - -Demonstrates how to use the `NavIconItem` using a standard `HTMLAnchorElement` - -**note:** to make the `NavIconItem` have an "active" state, you must pass the `aria-current` attribute with a value of `page` - - - - - -## Style Button Usage - -Demonstrates how to use the `NavIconItem` using a standard `HTMLButtonElement` - -**note:** to make the `NavIconItem` have an "active" state, you must pass the `aria-current` attribute with a value of `true` - - - - - -## React Anchor Usage +## With Href Demonstrates how to render the `NavIconItem` using a anchor version - + - + -## React Button Usage +## With OnClick Demonstrates how to render the `NavIconItem` using a button version - + - + diff --git a/src/components/nav-icon-item/nav-icon-item.stories.tsx b/src/components/nav-icon-item/nav-icon-item.stories.tsx index b5d5c0ca..3ed98727 100644 --- a/src/components/nav-icon-item/nav-icon-item.stories.tsx +++ b/src/components/nav-icon-item/nav-icon-item.stories.tsx @@ -2,7 +2,6 @@ import type { Meta, StoryObj } from '@storybook/react' import { NavIconItem } from './nav-icon-item' import { Icon } from '../icon' import { FlexContainer } from '../layout' -import { ElAnchorNavIconItem, ElButtonNavIconItem, ElNavIconItemBadge } from './styles' import { action } from '@storybook/addon-actions' const meta = { @@ -55,91 +54,33 @@ export const Default: Story = { onClick: action('handleOnClick'), }, render: (args) => { - return ( - - {args?.icon} - - ) + return }, } -export const ActiveState: Story = { +export const Active: Story = { args: { icon: , onClick: action('handleOnClick'), + isActive: true, }, render: (args) => { - return ( - - {args?.icon} - - ) + return }, } -export const BadgeState: Story = { +export const WithBadge: Story = { args: { icon: , onClick: action('handleOnClick'), hasBadge: true, }, render: (args) => { - return ( - - {args?.icon} - - - ) - }, -} - -export const StyleAnchorUsage: Story = { - args: { - icon: , - href: '#', - }, - render: (args) => { - return ( - - - {args?.icon} - - - {args?.icon} - - - {args?.icon} - - - - ) - }, -} - -export const StyleButtonUsage: Story = { - args: { - icon: , - onClick: action('handleClick'), - }, - render: (args) => { - return ( - - - {args?.icon} - - - {args?.icon} - - - {args?.icon} - - - - ) + return }, } -export const ReactAnchorUsage: Story = { +export const WithHref: Story = { args: { icon: , href: '#', @@ -155,7 +96,8 @@ export const ReactAnchorUsage: Story = { }, } -export const ReactButtonUsage: Story = { +export const WithOnClick: Story = { + name: 'With OnClick', args: { icon: , onClick: action('handleClick'), diff --git a/src/components/nav-icon-item/styles.ts b/src/components/nav-icon-item/styles.ts index 8c88f7fb..dd5b7f72 100644 --- a/src/components/nav-icon-item/styles.ts +++ b/src/components/nav-icon-item/styles.ts @@ -6,34 +6,34 @@ import { ElIcon } from '../icon' const baseStyles = ` position: relative; display: inline-flex; - padding: var(--space-2, 8px); + padding: var(--spacing-2); justify-content: center; align-items: center; - gap: var(--space-none, 0px); - border-radius: var(--corner-default, 4px); - background: var(--fill-white, #ffffff); - border: var(--border-none, 0px); - color: var(--icon-app_bar-default, #798da1); + gap: var(--spacing-none); + border-radius: var(--corner-defaul); + background: var(--fill-white); + border: var(--border-none); + color: var(--icon-app_bar-default); outline: none; &:focus-visible { - border-radius: var(--corner-default, 4px); - background: var(--fill-white, #ffffff); + border-radius: var(--corner-default); + background: var(--fill-white); box-shadow: - 0px 0px 0px 1px #fff, - 0px 0px 0px 4px var(--icon-button_primary-hover, #7e9bfa); + 0px 0px 0px 1px var(--fill-white), + 0px 0px 0px 4px var(--icon-button_primary-hover); } &:hover { cursor: pointer; - border-radius: var(--corner-default, 4px); - background: var(--fill-default-lightest, #f2f4f6); + border-radius: var(--corner-default); + background: var(--fill-default-lightest); } &:active, &[aria-current="page"], &[aria-current="true"] { - color: var(--fill-action-dark, #4e56ea); - border-radius: var(--corner-default, 4px); - background: var(--fill-default-lightest, #f2f4f6); + color: var(--fill-action-dark); + border-radius: var(--corner-default); + background: var(--fill-default-lightest); } ` @@ -43,7 +43,7 @@ export const ElNavIconItemBadge = styled.span` top: 5px; width: var(--size-2); height: var(--size-2); - background-color: var(--icon-error, #f01830); + background-color: var(--icon-error); border-radius: 100%; ` From 8e5182c7bca5163d7e76f2c39588b7892606b37e Mon Sep 17 00:00:00 2001 From: Dimas M <96416644+ss-dimasm@users.noreply.github.com> Date: Tue, 17 Dec 2024 10:20:37 +0700 Subject: [PATCH 08/14] chore: remove fallback css variables for `BottomBarItem` and update token variable (#256) * chore: update css token * chore: remove css fallback variable for bottom bar item * chore: update heading name * chore: update storybook --- .../bottom-bar-item/bottom-bar-item.mdx | 38 +++------ .../bottom-bar-item.stories.tsx | 83 ++----------------- src/components/bottom-bar-item/styles.ts | 28 +++---- src/tokens/payprop/tokens.css | 3 +- src/tokens/payprop/tokens.ts | 3 +- src/tokens/reapit/tokens.css | 3 +- src/tokens/reapit/tokens.ts | 3 +- src/tokens/tokens.json | 6 ++ 8 files changed, 47 insertions(+), 120 deletions(-) diff --git a/src/components/bottom-bar-item/bottom-bar-item.mdx b/src/components/bottom-bar-item/bottom-bar-item.mdx index 28f485ae..16358b8f 100644 --- a/src/components/bottom-bar-item/bottom-bar-item.mdx +++ b/src/components/bottom-bar-item/bottom-bar-item.mdx @@ -12,52 +12,36 @@ A versatile component designed to render navigation icon items within a `BottomB -## Default State +## Default The default appearance of the `BottomBarItem` -## Active State +## Active The visual state of the `BottomBarItem` when it's currently selected or active - + -## Badge State +## With Badge The visual state of the `BottomBarItem` when it's currently selected or active with a badge - + -## Style Anchor Usage - -Demonstrates how to use the `BottomBarItem` using a standard `HTMLAnchorElement` - - - - - -## Style Button Usage - -Demonstrates how to use the `BottomBarItem` using a standard `HTMLButtonElement` - - - - - -## React Anchor Usage +## With Href Demonstrates how to render the `BottomBarItem` using a anchor version - + - + -## React Button Usage +## With OnClick Demonstrates how to render the `BottomBarItem` using a button version - + - + diff --git a/src/components/bottom-bar-item/bottom-bar-item.stories.tsx b/src/components/bottom-bar-item/bottom-bar-item.stories.tsx index a69a78e5..9fe81ea6 100644 --- a/src/components/bottom-bar-item/bottom-bar-item.stories.tsx +++ b/src/components/bottom-bar-item/bottom-bar-item.stories.tsx @@ -1,17 +1,9 @@ -import type { Meta, StoryObj } from '@storybook/react' import { action } from '@storybook/addon-actions' +import type { Meta, StoryObj } from '@storybook/react' -import { BottomBarItem } from './bottom-bar-item' -import { FlexContainer } from '../layout' import { Icon } from '../icon' -import { - ElAnchorBottomBarItemContainer, - ElBottomBarItemBadge, - ElBottomBarItemContent, - ElBottomBarItemIcon, - ElBottomBarItemLabel, - ElButtonBottomBarItemContainer, -} from './styles' +import { FlexContainer } from '../layout' +import { BottomBarItem } from './bottom-bar-item' const meta = { title: 'Components/Bottom Bar Item', @@ -62,7 +54,7 @@ export const Default: Story = { }, } -export const ActiveState: Story = { +export const Active: Story = { args: { isActive: true, children: 'Label', @@ -70,75 +62,15 @@ export const ActiveState: Story = { }, } -export const BadgeState: Story = { +export const WithBadge: Story = { args: { children: 'Label', icon: , hasBadge: true, }, } -export const StyleAnchorUsage: Story = { - args: { - href: '#', - children: 'Label', - icon: , - }, - render: (args) => { - return ( - - - {args?.icon} - {args?.children} - - - - {args?.icon} - {args?.children} - - - - - {args?.icon} - - - {args?.children} - - - ) - }, -} - -export const StyleButtonUsage: Story = { - args: { - children: 'Label', - icon: , - }, - render: (args) => { - return ( - - - {args?.icon} - {args?.children} - - - - {args?.icon} - {args?.children} - - - - - {args?.icon} - - - {args?.children}{' '} - - - ) - }, -} -export const ReactAnchorUsage: Story = { +export const WithHref: Story = { args: { href: '#', icon: , @@ -155,7 +87,8 @@ export const ReactAnchorUsage: Story = { }, } -export const ReactButtonUsage: Story = { +export const WithOnClick: Story = { + name: 'With OnClick', args: { onClick: action('handleClick'), icon: , diff --git a/src/components/bottom-bar-item/styles.ts b/src/components/bottom-bar-item/styles.ts index f7f0a4c3..234360d4 100644 --- a/src/components/bottom-bar-item/styles.ts +++ b/src/components/bottom-bar-item/styles.ts @@ -3,40 +3,40 @@ import { AnchorHTMLAttributes, ButtonHTMLAttributes } from 'react' import { ElIcon } from '../icon' export const ElBottomBarItemIcon = styled.div` - width: var(--icon-default, 24px); - height: var(--icon-default, 24px); + width: var(--icon-default); + height: var(--icon-default); color: inherit; ` export const ElBottomBarItemLabel = styled.span` color: inherit; text-align: center; - font-family: var(--font-family, Inter); - font-size: var(--font-size-3xs, 10px); + font-family: var(--font-family); + font-size: var(--font-size-3xs); font-style: normal; - font-weight: var(--font-weight-regular, Regular); - line-height: var(--line-height-3xs, 12px); - letter-spacing: var(--letter-spacing-2xs, 0px); + font-weight: var(--font-weight-regular); + line-height: var(--line-height-3xs); + letter-spacing: var(--letter-spacing-2xs); ` const baseStyles = ` - background-color: var(--fill-white, white); + background-color: var(--fill-white); outline: none; width: 44px; - border: var(--border-none, 0); + border: var(--border-none); display: flex; cursor: pointer; - padding: var(--space-half, 2px) var(--space-none, 0px); + padding: var(--spacing-half) var(--spacing-none); flex-direction: column; align-items: center; - gap: var(--space-half, 2px); + gap: var(--spacing-half); position: relative; flex: 1 0 0; - color: var(--icon-secondary, #607890); + color: var(--icon-secondary); &:active, &[aria-current="true"], &[aria-current="page"] { - color: var(--icon-action, #4e56ea) + color: var(--icon-action) } ` @@ -50,7 +50,7 @@ export const ElBottomBarItemBadge = styled.span` right: -3px; width: var(--size-2); height: var(--size-2); - background-color: var(--icon-error, #f01830); + background-color: var(--icon-error); border-radius: 100%; ` diff --git a/src/tokens/payprop/tokens.css b/src/tokens/payprop/tokens.css index e7568429..cda5f97b 100644 --- a/src/tokens/payprop/tokens.css +++ b/src/tokens/payprop/tokens.css @@ -1,6 +1,6 @@ /** * Do not edit directly - * Generated on Tue, 10 Dec 2024 06:08:05 GMT + * Generated on Mon, 16 Dec 2024 06:57:57 GMT */ :root { @@ -139,6 +139,7 @@ --font-size-sm: 0.875rem; --font-size-xs: 0.8125rem; --font-size-2xs: 0.75rem; + --font-size-3xs: 0.625rem; --line-height-3xl: 2.5rem; --line-height-2xl: 2rem; --line-height-xl: 1.75rem; diff --git a/src/tokens/payprop/tokens.ts b/src/tokens/payprop/tokens.ts index 6f853b27..50b6fe64 100644 --- a/src/tokens/payprop/tokens.ts +++ b/src/tokens/payprop/tokens.ts @@ -1,6 +1,6 @@ /** * Do not edit directly - * Generated on Tue, 10 Dec 2024 06:08:05 GMT + * Generated on Mon, 16 Dec 2024 06:57:57 GMT */ export const neutral900 = '#222b33' @@ -138,6 +138,7 @@ export const fontSizeBase = '0.9375rem' export const fontSizeSm = '0.875rem' export const fontSizeXs = '0.8125rem' export const fontSize2xs = '0.75rem' +export const fontSize3xs = '0.625rem' export const lineHeight3xl = '2.5rem' export const lineHeight2xl = '2rem' export const lineHeightXl = '1.75rem' diff --git a/src/tokens/reapit/tokens.css b/src/tokens/reapit/tokens.css index e7568429..cda5f97b 100644 --- a/src/tokens/reapit/tokens.css +++ b/src/tokens/reapit/tokens.css @@ -1,6 +1,6 @@ /** * Do not edit directly - * Generated on Tue, 10 Dec 2024 06:08:05 GMT + * Generated on Mon, 16 Dec 2024 06:57:57 GMT */ :root { @@ -139,6 +139,7 @@ --font-size-sm: 0.875rem; --font-size-xs: 0.8125rem; --font-size-2xs: 0.75rem; + --font-size-3xs: 0.625rem; --line-height-3xl: 2.5rem; --line-height-2xl: 2rem; --line-height-xl: 1.75rem; diff --git a/src/tokens/reapit/tokens.ts b/src/tokens/reapit/tokens.ts index 6f853b27..50b6fe64 100644 --- a/src/tokens/reapit/tokens.ts +++ b/src/tokens/reapit/tokens.ts @@ -1,6 +1,6 @@ /** * Do not edit directly - * Generated on Tue, 10 Dec 2024 06:08:05 GMT + * Generated on Mon, 16 Dec 2024 06:57:57 GMT */ export const neutral900 = '#222b33' @@ -138,6 +138,7 @@ export const fontSizeBase = '0.9375rem' export const fontSizeSm = '0.875rem' export const fontSizeXs = '0.8125rem' export const fontSize2xs = '0.75rem' +export const fontSize3xs = '0.625rem' export const lineHeight3xl = '2.5rem' export const lineHeight2xl = '2rem' export const lineHeightXl = '1.75rem' diff --git a/src/tokens/tokens.json b/src/tokens/tokens.json index 5e95ad2c..77f2056a 100644 --- a/src/tokens/tokens.json +++ b/src/tokens/tokens.json @@ -1581,6 +1581,12 @@ "parent": "_Primitives/Value", "description": "" }, + "font-size-3xs": { + "value": "0.625rem", + "type": "dimension", + "parent": "_Primitives/Value", + "description": "" + }, "line-height-3xl": { "value": "2.5rem", "type": "dimension", From 2660c86568870297ddbce4912cd566c2ab0ed3e8 Mon Sep 17 00:00:00 2001 From: Dimas M <96416644+ss-dimasm@users.noreply.github.com> Date: Tue, 17 Dec 2024 10:38:14 +0700 Subject: [PATCH 09/14] chore: remove fallback css variables for `MobileNavItem` and amend the stories (#255) * chore: remove fallback css variables for `MobileNavMenu` and amend the stories * chore: update storybook --- .../mobile-nav-item/mobile-nav-item.mdx | 9 ++--- .../mobile-nav-item.stories.tsx | 14 +++---- src/components/mobile-nav-item/styles.ts | 38 +++++++++---------- 3 files changed, 30 insertions(+), 31 deletions(-) diff --git a/src/components/mobile-nav-item/mobile-nav-item.mdx b/src/components/mobile-nav-item/mobile-nav-item.mdx index d2e9a949..e252e813 100644 --- a/src/components/mobile-nav-item/mobile-nav-item.mdx +++ b/src/components/mobile-nav-item/mobile-nav-item.mdx @@ -29,14 +29,13 @@ A basic `MobileNavItem` without any special styling or interactions -**With Badge** - - +**Active** -**With Active State** + - +**With Badge** + ## Expandable Variant **Basic Usage** diff --git a/src/components/mobile-nav-item/mobile-nav-item.stories.tsx b/src/components/mobile-nav-item/mobile-nav-item.stories.tsx index 72666a58..73f13088 100644 --- a/src/components/mobile-nav-item/mobile-nav-item.stories.tsx +++ b/src/components/mobile-nav-item/mobile-nav-item.stories.tsx @@ -19,19 +19,19 @@ export default meta type Story = StoryObj export const Simple: Story = { - render: () => , + render: ({}) => , } -export const SimpleWithBadge: Story = { - render: () => , +export const Active: Story = { + render: ({}) => , } -export const SimpleWithActiveState: Story = { - render: () => , +export const WithBadge: Story = { + render: ({}) => , } export const Expandable: Story = { - render: () => ( + render: ({}) => ( @@ -41,7 +41,7 @@ export const Expandable: Story = { } export const DefaultExpanded: Story = { - render: () => ( + render: ({}) => ( diff --git a/src/components/mobile-nav-item/styles.ts b/src/components/mobile-nav-item/styles.ts index 8becdc2e..fa7212aa 100644 --- a/src/components/mobile-nav-item/styles.ts +++ b/src/components/mobile-nav-item/styles.ts @@ -10,26 +10,26 @@ const baseStyles = ` border-radius: inherit; border: 4px solid transparent; background: inherit; - padding: var(--space-2, 8px) var(--space-4, 16px); - font-family: var(--font-family, Inter); - font-size: var(--font-size-base, 15px); + padding: var(--spacing-2) var(--spacing-4); + font-family: var(--font-family); + font-size: var(--font-size-base); font-style: normal; - font-weight: var(--font-weight-regular, Regular); - line-height: var(--line-height-base, 24px); - letter-spacing: var(--letter-spacing-base, -0.15px); + font-weight: var(--font-weight-regular); + line-height: var(--line-height-base); + letter-spacing: var(--letter-spacing-base); &:hover { - background: var(--fill-default-light, #e5e9ed); + background: var(--fill-default-light); } &:focus-visible { - border: 4px solid var(--purple-300, #7e9bfa); + border: 4px solid var(--purple-300); } &:active, &[aria-current='true'], &[aria-current='page'] { - color: var(--text-action, #4e56ea); + color: var(--text-action); } ` @@ -49,7 +49,7 @@ export const ElMobileNavItemContent = styled.span` display: flex; align-items: center; justify-content: flex-start; - gap: var(--space-2, 8px); + gap: var(--spacing-2); flex-grow: 1; ` @@ -57,7 +57,7 @@ export const ElMobileNavItemBadge = styled.span` display: block; width: var(--size-2); height: var(--size-2); - background-color: var(--icon-error, #f01830); + background-color: var(--icon-error); border-radius: 100%; ` @@ -70,25 +70,25 @@ export const ElMobileNavSubItemUnorderedList = styled.ul` export const ElMobileNavItemListItem = styled.li` display: flex; flex-direction: column; - background: var(--fill-white, #fff); - border-radius: var(--corner-lg, 8px); + background: var(--fill-white); + border-radius: var(--corner-lg); &[data-is-expanded='true'] { - background: var(--fill-default-lightest, #f2f4f6); + background: var(--fill-default-lightest); > ${ElMobileNavItemExpanderButton} { - border-radius: var(--corner-lg, 8px) var(--corner-lg, 8px) var(--corner-none, 0) var(--corner-none, 0); + border-radius: var(--corner-lg) var(--corner-lg) var(--corner-none) var(--corner-none); } ${ElMobileNavSubItemUnorderedList} > * { - background: var(--fill-default-lightest, #f2f4f6); - border-radius: var(--corner-none, 0px); + background: var(--fill-default-lightest); + border-radius: var(--corner-none); } ${ElMobileNavSubItemUnorderedList} > :last-child { - border-radius: var(--corner-none, 0) var(--corner-none, 0) var(--corner-lg, 8px) var(--corner-lg, 8px); + border-radius: var(--corner-none) var(--corner-none) var(--corner-lg) var(--corner-lg); } ${ElMobileNavItemAnchor}, ${ElMobileNavItemExpanderButton} { &:hover { - background: var(--fill-default-light, #e5e9ed); + background: var(--fill-default-light); } } } From 34b4daff167423aab8fb7595c9e9fd8fd6d29ba4 Mon Sep 17 00:00:00 2001 From: Nur M <106480336+nurm717123@users.noreply.github.com> Date: Wed, 18 Dec 2024 13:33:24 +0700 Subject: [PATCH 10/14] chore: v5 update Avatar docs (#262) * chore: update avatar docs to be more aligned with existing convention --- .../avatar-rectangle/avatar-rectangle.mdx | 22 +++---- .../avatar-rectangle.stories.tsx | 45 +++++--------- src/components/avatar/avatar.mdx | 42 ++++++------- src/components/avatar/avatar.stories.tsx | 62 +++++++++---------- 4 files changed, 72 insertions(+), 99 deletions(-) diff --git a/src/components/avatar-rectangle/avatar-rectangle.mdx b/src/components/avatar-rectangle/avatar-rectangle.mdx index c6a97f57..89bc30f7 100644 --- a/src/components/avatar-rectangle/avatar-rectangle.mdx +++ b/src/components/avatar-rectangle/avatar-rectangle.mdx @@ -1,4 +1,4 @@ -import { Meta, Canvas, Controls } from '@storybook/blocks' +import { Meta, Canvas, Controls, Description } from '@storybook/blocks' import { RenderHtmlMarkup } from '../../storybook/render-html-markup' import * as AvatarRectangleStories from './avatar-rectangle.stories' @@ -10,30 +10,24 @@ A versatile component designed to render property image. -## Default Usage - -The default usage use the residential variant and medium size, each can be set using the `data-` attribute. +## Default + +## Avatar Rectangle Variant + + + + ## Using Residential Placeholder -## Using Commercial Variant - - - ## Using Commercial Placeholder - -## React Usage - - - - diff --git a/src/components/avatar-rectangle/avatar-rectangle.stories.tsx b/src/components/avatar-rectangle/avatar-rectangle.stories.tsx index 945652c3..0c2cefd3 100644 --- a/src/components/avatar-rectangle/avatar-rectangle.stories.tsx +++ b/src/components/avatar-rectangle/avatar-rectangle.stories.tsx @@ -1,11 +1,6 @@ import type { Meta, StoryObj } from '@storybook/react' import { AvatarRectangle } from '.' -import { - ElAvatarRectangle, - ElAvatarRectResidentialPlaceholder, - ElAvatarRectBottomImage, - ElAvatarRectCommercialPlaceholder, -} from './styles' +import { ElAvatarRectResidentialPlaceholder, ElAvatarRectCommercialPlaceholder } from './styles' export default { title: 'Components/Avatar Rectangle', @@ -13,6 +8,7 @@ export default { args: { variant: 'residential', size: 'medium', + src: 'https://picsum.photos/id/206/100/100', }, argTypes: { variant: { @@ -26,36 +22,23 @@ export default { }, } as Meta -const exampleImageUrl = 'https://picsum.photos/id/206/100/100' +type Story = StoryObj -export const DefaultUsage = { - render: ({}) => ( - - - - ), -} +/** + * The default usage use the residential variant and medium size, each can be set using the `data-` attribute. + */ +export const DefaultUsage: Story = {} -export const UsingResidentialPlaceholder = { - render: ({}) => , +export const AvatarRectangleVariant: Story = { + args: { + variant: 'commercial', + }, } -export const UsingCommercialVariant = { - render: ({}) => ( - - - - - ), +export const UsingResidentialPlaceholder = { + render: () => , } export const UsingCommercialPlaceholder = { - render: ({}) => , -} - -export const ReactUsage: StoryObj = { - args: { - src: exampleImageUrl, - }, - render: (props) => , + render: () => , } diff --git a/src/components/avatar/avatar.mdx b/src/components/avatar/avatar.mdx index f8cd7f69..53c26bf5 100644 --- a/src/components/avatar/avatar.mdx +++ b/src/components/avatar/avatar.mdx @@ -1,4 +1,5 @@ -import { Meta, Story, Canvas, Controls } from '@storybook/blocks' +import { Meta, Story, Canvas, Controls, Description +} from '@storybook/blocks' import { RenderHtmlMarkup } from '../../storybook/render-html-markup' import * as AvatarStories from './avatar.stories' @@ -6,42 +7,37 @@ import * as AvatarStories from './avatar.stories' # Avatar -An avatar component to be used typically with a `Card` or `Nav` component. The default variant renders a circle with children. + -There are multiple variants that can be used by utilizing the data props, such as size, colour, and shape (see the HTML version of React usage). In addition to text, it also accepts an Icon as a child. - -## Style Only Usage - - + - +## Default - + - + -## With Colour +## Avatar with Icon - + - + -## With Square Shape - +## Avatar Colour - + -## With Small Size + - +## Avatar Shape - + -## React Usage + - +## Avatar Size - + -WithColourWithColour + diff --git a/src/components/avatar/avatar.stories.tsx b/src/components/avatar/avatar.stories.tsx index f4ded079..bc9a6d1d 100644 --- a/src/components/avatar/avatar.stories.tsx +++ b/src/components/avatar/avatar.stories.tsx @@ -1,49 +1,49 @@ -import type { StoryObj } from '@storybook/react' -import { Avatar, ElAvatar } from '.' +import type { Meta, StoryObj } from '@storybook/react' +import { Avatar } from '.' import { Icon } from '../icon' export default { title: 'Components/Avatar', component: Avatar, -} + args: { + children: 'AD', + }, +} as Meta -export const StyleOnlyUsage = { - render: ({}) => AD, -} +type Story = StoryObj + +/** + * An avatar component to be used typically with a `Card` or `Nav` component. + * The default variant renders a circle with children. + * + * There are multiple variants that can be used by utilizing the `data-` props + * such as `size`, `colour`, and `shape` (see the HTML version of each version). + * In addition to text, it also accepts an Icon as a child. + */ +export const Default: Story = {} -export const WithIconUsage = { - render: ({}) => ( - +export const AvatarWithIcon: Story = { + render: (props) => ( + - + ), } -export const WithColour = { - render: ({}) => AD, - name: 'Colour: Purple', -} - -export const WithSquareShape = { - render: ({}) => AD, - name: 'Shape: Square', +export const AvatarColour: Story = { + args: { + colour: 'purple', + }, } -export const WithSmallSize = { - render: ({}) => ( - - - - ), - name: 'Size: Small', +export const AvatarShape: Story = { + args: { + shape: 'square', + }, } -export const ReactUsage: StoryObj = { +export const AvatarSize: Story = { args: { - shape: 'circle', - size: 'medium', - colour: 'default', - children: 'AD', + size: 'small', }, - render: (props) => {props.children}, } From 7be95415e7a5ea87ddbc23e694e3273d9552f493 Mon Sep 17 00:00:00 2001 From: Nur M <106480336+nurm717123@users.noreply.github.com> Date: Wed, 18 Dec 2024 17:25:12 +0700 Subject: [PATCH 11/14] feat: #253 new app-bar (existing app switcher) (#261) * feat: init top-bar's app-switcher --- src/components/nav/__styles__/index.ts | 8 +++++ src/components/nav/nav-responsive.tsx | 4 ++- .../__snapshots__/top-bar.test.tsx.snap | 19 +++++++----- .../top-bar/__test__/top-bar.test.tsx | 1 + src/components/top-bar/styles.ts | 8 +++++ src/components/top-bar/top-bar.stories.tsx | 31 +++++++++++++++++++ src/components/top-bar/top-bar.tsx | 3 ++ 7 files changed, 66 insertions(+), 8 deletions(-) diff --git a/src/components/nav/__styles__/index.ts b/src/components/nav/__styles__/index.ts index f49eb048..61225539 100644 --- a/src/components/nav/__styles__/index.ts +++ b/src/components/nav/__styles__/index.ts @@ -350,3 +350,11 @@ export const ElNavResponsiveAppSwitcherIconWrap = styled.div` } } ` + +export const elNewTopBarAppSwitcher = css` + ${ElNavResponsiveAppSwitcherIconWrap} { + margin-right: 0; + padding: var(--spacing-half); + box-sizing: content-box; + } +` diff --git a/src/components/nav/nav-responsive.tsx b/src/components/nav/nav-responsive.tsx index 70f88fe1..a017ac8b 100644 --- a/src/components/nav/nav-responsive.tsx +++ b/src/components/nav/nav-responsive.tsx @@ -72,6 +72,7 @@ export interface NavResponsiveAvatarProps { export interface NavResponsiveAppSwitcherProps { options: NavResponsiveAppSwitcherOption[] + className?: string // Note: probably only required for new AppBar } export type LogoIcon = 'reapitLogoSelectedMenu' | 'reapitLogoMenu' @@ -141,7 +142,7 @@ export const NavResponsiveAvatar: FC = ({ options, isH ) } -export const NavResponsiveAppSwitcher: FC = ({ options }) => { +export const NavResponsiveAppSwitcher: FC = ({ options, className }) => { const [appSwitcherOpen, setAppSwitcherOpen] = useState(false) const marketplaceCallback = () => { @@ -164,6 +165,7 @@ export const NavResponsiveAppSwitcher: FC = ({ op onKeyDown={handleKeyboardEvent('Enter', handleToggleMenu(setAppSwitcherOpen))} role="button" tabIndex={0} + className={className} > diff --git a/src/components/top-bar/__test__/__snapshots__/top-bar.test.tsx.snap b/src/components/top-bar/__test__/__snapshots__/top-bar.test.tsx.snap index 1fb79a21..ef11a0ab 100644 --- a/src/components/top-bar/__test__/__snapshots__/top-bar.test.tsx.snap +++ b/src/components/top-bar/__test__/__snapshots__/top-bar.test.tsx.snap @@ -4,45 +4,50 @@ exports[`TopBar Snapshot > should match snapshot 1`] = ` + App-switcher + + { it('should match snapshot', () => { const { asFragment } = render( + App-switcher diff --git a/src/components/top-bar/styles.ts b/src/components/top-bar/styles.ts index 10415fe4..3b6a2524 100644 --- a/src/components/top-bar/styles.ts +++ b/src/components/top-bar/styles.ts @@ -3,6 +3,14 @@ import { isBelowWideScreen, isTablet, isBelowDesktop, isWideScreen } from '../.. import { ElButtonGroup } from '../button-group' import { css } from '@linaria/core' +export const ElTopBarAppSwitcher = styled.a` + padding-right: var(--spacing-4); + + ${isWideScreen} { + padding-right: var(--spacing-5); + } +` + export const ElTopBarLogo = styled.a` padding-right: var(--spacing-2); display: inline-flex; diff --git a/src/components/top-bar/top-bar.stories.tsx b/src/components/top-bar/top-bar.stories.tsx index cc8319fc..9103cfcc 100644 --- a/src/components/top-bar/top-bar.stories.tsx +++ b/src/components/top-bar/top-bar.stories.tsx @@ -14,6 +14,7 @@ import MenuIcon from './icons/menu-icon.svg?react' import { elTopBarMenuPopover } from './styles' import { TopBar } from './top-bar' import { INITIAL_VIEWPORTS } from '@storybook/addon-viewport' +import { elNewTopBarAppSwitcher, NavResponsiveAppSwitcher } from '../nav' const viewports: typeof INITIAL_VIEWPORTS = { superWideScreen: { @@ -59,6 +60,21 @@ export const Default: Story = { render: () => { return ( + + + @@ -125,6 +141,21 @@ export const ResponsiveMainNav: Story = { render: () => { return ( + + + diff --git a/src/components/top-bar/top-bar.tsx b/src/components/top-bar/top-bar.tsx index 46aa3ee6..b3f9ea47 100644 --- a/src/components/top-bar/top-bar.tsx +++ b/src/components/top-bar/top-bar.tsx @@ -7,9 +7,11 @@ import { ElTopBarProfile, ElTopBarMainNav, ElTopBarLogo, + ElTopBarAppSwitcher, } from './styles' const TopBar: FC> & { + AppSwitcher: typeof ElTopBarAppSwitcher Logo: typeof ElTopBarLogo MainNav: typeof ElTopBarMainNav SecondaryNav: typeof ElTopBarSecondaryNav @@ -24,6 +26,7 @@ const TopBar: FC> & { ) } +TopBar.AppSwitcher = ElTopBarAppSwitcher TopBar.Logo = ElTopBarLogo TopBar.MainNav = ElTopBarMainNav TopBar.SecondaryNav = ElTopBarSecondaryNav From bd661b85e7a27a2d1f8f3324e813813e84487c3f Mon Sep 17 00:00:00 2001 From: Nur M <106480336+nurm717123@users.noreply.github.com> Date: Fri, 20 Dec 2024 09:30:39 +0700 Subject: [PATCH 12/14] fix: update menu docs (#254) --- src/components/menu/menu.mdx | 20 ++++---- src/components/menu/menu.stories.tsx | 71 +++++++++++----------------- 2 files changed, 38 insertions(+), 53 deletions(-) diff --git a/src/components/menu/menu.mdx b/src/components/menu/menu.mdx index 541528d4..c7ae692b 100644 --- a/src/components/menu/menu.mdx +++ b/src/components/menu/menu.mdx @@ -14,22 +14,22 @@ More features will be added to Menu in the future -## Styles Only Usage +## Default -Default style of the `Menu`, can set `data-alignment` to "right" to move the x-position to the right of the button +By default, the `Menu` is left-aligned, but this can be adjusted using the `data-alignment`. Additionally (for React user), the vertical position of the Menu is automatically adjusted based on the available space below the trigger and `window` each time the menu is opened (can use `yOffset` to have more adjustment). - +`closeMenu`={false} can be set to keep the menu open when it is clicked. - +Note: `closeMenu` and `yOffset` are react only props. -## React Usage + -In the React Usage, you can compose your component using `Menu` component as example here + -And the y-position of the menu will be automatically adjusted based on the available space below the trigger and `window` each time the menu is opened +## With Alignment -`closeMenu`={false} or `data-close-menu`="false" (note: this is only for React users) can be set to keep the menu open when it is clicked." +Example for `Menu` with right Alignment. - + - + diff --git a/src/components/menu/menu.stories.tsx b/src/components/menu/menu.stories.tsx index c215fd11..e2f0bbc5 100644 --- a/src/components/menu/menu.stories.tsx +++ b/src/components/menu/menu.stories.tsx @@ -1,59 +1,21 @@ import type { Meta, StoryObj } from '@storybook/react' -import { - ElMenu, - ElMenuItemAnchor, - ElMenuItemButton, - ElMenuItemGroup, - ElMenuItemGroupTitle, - ElMenuList, - ElMenuPopover, - Menu, -} from '.' +import type { ComponentProps } from 'react' +import { Menu } from '.' import { Button } from '../button' import { Icon } from '../icon' -import type { ComponentProps } from 'react' import { FlexContainer } from '../layout' const meta: Meta = { title: 'Components/Menu', - argTypes: { - 'data-alignment': { - control: 'inline-radio', - options: ['left', 'right'], - description: - 'to control the alignment of Menu container, will default to left if not provided (please see `More Complex Usage Example` for interactive example)', - }, - }, } export default meta type Story = StoryObj -export const StylesOnlyUsage: StoryObj = { +export const Default: StoryObj = { render: () => { return ( - - - - - Group Title - Menu Item - Menu Item - - Menu Item as anchor - - - - - - ) - }, -} - -export const ReactUsage: Story = { - render: (props) => { - return ( - + {({ getTriggerProps }) => } />} @@ -71,7 +33,30 @@ export const ReactUsage: Story = { }, } -export const MoreComplexUsageExample: StoryObj> = { +export const WithCustomAlignment: Story = { + render: () => { + return ( + + + + {({ getTriggerProps }) => } />} + + + + + Menu Item + Menu Item as anchor + Menu Item (keep open) + + + + + + ) + }, +} + +export const MoreComplexUsageExample: Story = { render: (props) => { const NavDropdownButtonUsageExample = ({ title, From 2b68a180531defc0152528240bf2ef2c20cd94c0 Mon Sep 17 00:00:00 2001 From: Danish Nadir Ali Date: Wed, 8 Jan 2025 13:03:54 +1100 Subject: [PATCH 13/14] feat: #242 v4 Deprecated Existing Badge (#271) * feat: #242 v4 Deprecated Existing Badge * feat: #242 updated test snapshot * feat: #242 updated test snapshot file --------- Co-authored-by: Danish Ali --- .../__snapshots__/badge.test.tsx.snap | 114 ------------------ src/components/badge/__tests__/badge.test.tsx | 56 --------- src/components/badge/badge.stories.tsx | 28 ----- src/components/badge/badge.tsx | 20 --- .../__styles__/index.ts | 9 +- .../__snapshots__/badge.test.tsx.snap | 114 ++++++++++++++++++ .../deprecated-badge/__tests__/badge.test.tsx | 56 +++++++++ .../{badge => deprecated-badge}/badge.mdx | 3 +- .../deprecated-badge/badge.stories.tsx | 28 +++++ src/components/deprecated-badge/badge.tsx | 23 ++++ .../{badge => deprecated-badge}/index.ts | 0 src/index.ts | 2 +- 12 files changed, 229 insertions(+), 224 deletions(-) delete mode 100644 src/components/badge/__tests__/__snapshots__/badge.test.tsx.snap delete mode 100644 src/components/badge/__tests__/badge.test.tsx delete mode 100644 src/components/badge/badge.stories.tsx delete mode 100644 src/components/badge/badge.tsx rename src/components/{badge => deprecated-badge}/__styles__/index.ts (86%) create mode 100644 src/components/deprecated-badge/__tests__/__snapshots__/badge.test.tsx.snap create mode 100644 src/components/deprecated-badge/__tests__/badge.test.tsx rename src/components/{badge => deprecated-badge}/badge.mdx (94%) create mode 100644 src/components/deprecated-badge/badge.stories.tsx create mode 100644 src/components/deprecated-badge/badge.tsx rename src/components/{badge => deprecated-badge}/index.ts (100%) diff --git a/src/components/badge/__tests__/__snapshots__/badge.test.tsx.snap b/src/components/badge/__tests__/__snapshots__/badge.test.tsx.snap deleted file mode 100644 index 73bb47e1..00000000 --- a/src/components/badge/__tests__/__snapshots__/badge.test.tsx.snap +++ /dev/null @@ -1,114 +0,0 @@ -// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html - -exports[`Badge component > should match a snapshot 1`] = ` - - - 50% - - -`; - -exports[`Badge component > should match a snapshot for intent 1`] = ` - - - Some Content - - -`; - -exports[`Badge component > should match a snapshot for intent 2`] = ` - - - Some Content - - -`; - -exports[`Badge component > should match a snapshot for intent 3`] = ` - - - Some Content - - -`; - -exports[`Badge component > should match a snapshot for intent 4`] = ` - - - Some Content - - -`; - -exports[`Badge component > should match a snapshot for intent 5`] = ` - - - Some Content - - -`; - -exports[`Badge component > should match a snapshot for intent 6`] = ` - - - Some Content - - -`; - -exports[`Badge component > should match a snapshot for intent 7`] = ` - - - Some Content - - -`; - -exports[`BadgeGroup component > should match a snapshot 1`] = ` - - - - - Some Content - - - Some Content - - - - -`; diff --git a/src/components/badge/__tests__/badge.test.tsx b/src/components/badge/__tests__/badge.test.tsx deleted file mode 100644 index 0b018aad..00000000 --- a/src/components/badge/__tests__/badge.test.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import { render } from '@testing-library/react' -import { Badge, BadgeGroup } from '..' - -describe('Badge component', () => { - it('should match a snapshot', () => { - const wrapper = render(50%) - expect(wrapper.asFragment()).toMatchSnapshot() - }) - - it('should match a snapshot for intent', () => { - const wrapper = render(Some Content) - expect(wrapper.asFragment()).toMatchSnapshot() - }) - - it('should match a snapshot for intent', () => { - const wrapper = render(Some Content) - expect(wrapper.asFragment()).toMatchSnapshot() - }) - - it('should match a snapshot for intent', () => { - const wrapper = render(Some Content) - expect(wrapper.asFragment()).toMatchSnapshot() - }) - - it('should match a snapshot for intent', () => { - const wrapper = render(Some Content) - expect(wrapper.asFragment()).toMatchSnapshot() - }) - - it('should match a snapshot for intent', () => { - const wrapper = render(Some Content) - expect(wrapper.asFragment()).toMatchSnapshot() - }) - - it('should match a snapshot for intent', () => { - const wrapper = render(Some Content) - expect(wrapper.asFragment()).toMatchSnapshot() - }) - - it('should match a snapshot for intent', () => { - const wrapper = render(Some Content) - expect(wrapper.asFragment()).toMatchSnapshot() - }) -}) - -describe('BadgeGroup component', () => { - it('should match a snapshot', () => { - const wrapper = render( - - Some Content - Some Content - , - ) - expect(wrapper.asFragment()).toMatchSnapshot() - }) -}) diff --git a/src/components/badge/badge.stories.tsx b/src/components/badge/badge.stories.tsx deleted file mode 100644 index 6a1adb6b..00000000 --- a/src/components/badge/badge.stories.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import { Badge, BadgeGroup } from '.' - -export default { - title: 'Badge', - component: Badge, -} - -export const BasicUsage = { - render: ({}) => ( - - 100 - - ), -} - -export const WithIntent = { - render: ({}) => ( - - primary - neutral - success - pending - warning - danger - default - - ), -} diff --git a/src/components/badge/badge.tsx b/src/components/badge/badge.tsx deleted file mode 100644 index 0cd9cce0..00000000 --- a/src/components/badge/badge.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import React, { FC, HTMLAttributes } from 'react' -import { cx } from '@linaria/core' -import { ElBadge, ElBadgeGroup, ElBadgeGroupInner } from './__styles__' -import { Intent, getIntentClassName } from '../../helpers/intent' - -export interface BadgeProps extends HTMLAttributes { - intent?: Intent -} - -export const Badge: FC = ({ children, intent, className, ...rest }) => ( - - {children} - -) - -export const BadgeGroup: FC> = ({ children, className, ...rest }) => ( - - {children} - -) diff --git a/src/components/badge/__styles__/index.ts b/src/components/deprecated-badge/__styles__/index.ts similarity index 86% rename from src/components/badge/__styles__/index.ts rename to src/components/deprecated-badge/__styles__/index.ts index 737ed72f..2e7684b8 100644 --- a/src/components/badge/__styles__/index.ts +++ b/src/components/deprecated-badge/__styles__/index.ts @@ -9,7 +9,8 @@ import { elIntentDefault, } from '../../../styles/intent' -export const ElBadge = styled.span` +/** @deprecated */ +export const ElDeprecatedBadge = styled.span` border-radius: 0.75rem; display: inline-block; font-size: var(--font-size-small); @@ -54,11 +55,13 @@ export const ElBadge = styled.span` } ` -export const ElBadgeGroup = styled.div` +/** @deprecated */ +export const ElDeprecatedBadgeGroup = styled.div` display: grid; ` -export const ElBadgeGroupInner = styled.div` +/** @deprecated */ +export const ElDeprecatedBadgeGroupInner = styled.div` display: flex; flex-wrap: wrap; grid-auto-flow: column; diff --git a/src/components/deprecated-badge/__tests__/__snapshots__/badge.test.tsx.snap b/src/components/deprecated-badge/__tests__/__snapshots__/badge.test.tsx.snap new file mode 100644 index 00000000..d8a9685e --- /dev/null +++ b/src/components/deprecated-badge/__tests__/__snapshots__/badge.test.tsx.snap @@ -0,0 +1,114 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`DeprecatedBadge component > should match a snapshot 1`] = ` + + + 50% + + +`; + +exports[`DeprecatedBadge component > should match a snapshot for intent 1`] = ` + + + Some Content + + +`; + +exports[`DeprecatedBadge component > should match a snapshot for intent 2`] = ` + + + Some Content + + +`; + +exports[`DeprecatedBadge component > should match a snapshot for intent 3`] = ` + + + Some Content + + +`; + +exports[`DeprecatedBadge component > should match a snapshot for intent 4`] = ` + + + Some Content + + +`; + +exports[`DeprecatedBadge component > should match a snapshot for intent 5`] = ` + + + Some Content + + +`; + +exports[`DeprecatedBadge component > should match a snapshot for intent 6`] = ` + + + Some Content + + +`; + +exports[`DeprecatedBadge component > should match a snapshot for intent 7`] = ` + + + Some Content + + +`; + +exports[`DeprecatedBadgeGroup component > should match a snapshot 1`] = ` + + + + + Some Content + + + Some Content + + + + +`; diff --git a/src/components/deprecated-badge/__tests__/badge.test.tsx b/src/components/deprecated-badge/__tests__/badge.test.tsx new file mode 100644 index 00000000..a2b7f2b4 --- /dev/null +++ b/src/components/deprecated-badge/__tests__/badge.test.tsx @@ -0,0 +1,56 @@ +import { render } from '@testing-library/react' +import { DeprecatedBadge, DeprecatedBadgeGroup } from '..' + +describe('DeprecatedBadge component', () => { + it('should match a snapshot', () => { + const wrapper = render(50%) + expect(wrapper.asFragment()).toMatchSnapshot() + }) + + it('should match a snapshot for intent', () => { + const wrapper = render(Some Content) + expect(wrapper.asFragment()).toMatchSnapshot() + }) + + it('should match a snapshot for intent', () => { + const wrapper = render(Some Content) + expect(wrapper.asFragment()).toMatchSnapshot() + }) + + it('should match a snapshot for intent', () => { + const wrapper = render(Some Content) + expect(wrapper.asFragment()).toMatchSnapshot() + }) + + it('should match a snapshot for intent', () => { + const wrapper = render(Some Content) + expect(wrapper.asFragment()).toMatchSnapshot() + }) + + it('should match a snapshot for intent', () => { + const wrapper = render(Some Content) + expect(wrapper.asFragment()).toMatchSnapshot() + }) + + it('should match a snapshot for intent', () => { + const wrapper = render(Some Content) + expect(wrapper.asFragment()).toMatchSnapshot() + }) + + it('should match a snapshot for intent', () => { + const wrapper = render(Some Content) + expect(wrapper.asFragment()).toMatchSnapshot() + }) +}) + +describe('DeprecatedBadgeGroup component', () => { + it('should match a snapshot', () => { + const wrapper = render( + + Some Content + Some Content + , + ) + expect(wrapper.asFragment()).toMatchSnapshot() + }) +}) diff --git a/src/components/badge/badge.mdx b/src/components/deprecated-badge/badge.mdx similarity index 94% rename from src/components/badge/badge.mdx rename to src/components/deprecated-badge/badge.mdx index 4520ab4f..b9ed2936 100644 --- a/src/components/badge/badge.mdx +++ b/src/components/deprecated-badge/badge.mdx @@ -1,11 +1,10 @@ import { Meta, Story, Canvas, Controls } from '@storybook/blocks' -import { Badge } from '.' import { RenderHtmlMarkup } from '../../storybook/render-html-markup' import * as BadgeStories from './badge.stories' -# Badge +# DeprecatedBadge A simple low impact visual indicator label. The component simply renders children and does not have any additional props. diff --git a/src/components/deprecated-badge/badge.stories.tsx b/src/components/deprecated-badge/badge.stories.tsx new file mode 100644 index 00000000..c4b8aa09 --- /dev/null +++ b/src/components/deprecated-badge/badge.stories.tsx @@ -0,0 +1,28 @@ +import { DeprecatedBadge, DeprecatedBadgeGroup } from '.' + +export default { + title: 'DeprecatedBadge', + component: DeprecatedBadge, +} + +export const BasicUsage = { + render: ({}) => ( + + 100 + + ), +} + +export const WithIntent = { + render: ({}) => ( + + primary + neutral + success + pending + warning + danger + default + + ), +} diff --git a/src/components/deprecated-badge/badge.tsx b/src/components/deprecated-badge/badge.tsx new file mode 100644 index 00000000..bba7ede2 --- /dev/null +++ b/src/components/deprecated-badge/badge.tsx @@ -0,0 +1,23 @@ +import React, { FC, HTMLAttributes } from 'react' +import { cx } from '@linaria/core' +import { ElDeprecatedBadge, ElDeprecatedBadgeGroup, ElDeprecatedBadgeGroupInner } from './__styles__' +import { Intent, getIntentClassName } from '../../helpers/intent' + +/** @deprecated */ +export interface DeprecatedBadgeProps extends HTMLAttributes { + intent?: Intent +} + +/** @deprecated */ +export const DeprecatedBadge: FC = ({ children, intent, className, ...rest }) => ( + + {children} + +) + +/** @deprecated */ +export const DeprecatedBadgeGroup: FC> = ({ children, className, ...rest }) => ( + + {children} + +) diff --git a/src/components/badge/index.ts b/src/components/deprecated-badge/index.ts similarity index 100% rename from src/components/badge/index.ts rename to src/components/deprecated-badge/index.ts diff --git a/src/index.ts b/src/index.ts index 7993408d..fbfbcc97 100644 --- a/src/index.ts +++ b/src/index.ts @@ -46,7 +46,7 @@ export * from './components/mobile-controls' export * from './components/drawer' export * from './components/accordion' export * from './components/tag' -export * from './components/badge' +export * from './components/deprecated-badge' export * from './components/chip' export * from './components/tile' export * from './components/deprecated-avatar' From 5e0a20e3c59ed29088beba178da7b3512e6770a4 Mon Sep 17 00:00:00 2001 From: Kushal <44560227+ksolanki7@users.noreply.github.com> Date: Thu, 9 Jan 2025 18:19:21 +1100 Subject: [PATCH 14/14] chore: 248 v5 component refactor button refactor UI (#260) * chore: #248 Refactor v5 Button component * Added prop removePadding to support compact view of the Button component withouth height, width and padding * Fix issue with unnecessary import not being used throwing eslint error * PR feedback fix for naming convention for boolean prop --- src/components/button/button.mdx | 8 ++ src/components/button/button.stories.tsx | 38 +++++++- src/components/button/button.tsx | 110 +++++++++++++---------- src/components/button/styles.ts | 24 +++-- 4 files changed, 121 insertions(+), 59 deletions(-) diff --git a/src/components/button/button.mdx b/src/components/button/button.mdx index 13a99914..dad2fc04 100644 --- a/src/components/button/button.mdx +++ b/src/components/button/button.mdx @@ -58,6 +58,14 @@ With href prop, link can be displayed as button. Anchor attributes such as `targ +## Button With hasNoPadding + +The `hasNoPadding` prop is used with the `tertiary` variant of the button to demonstrate how removing padding and resetting the height can create a more minimalistic, compact button style. +This is useful when you need a button with no extra spacing or fixed height, while maintaining the button's core functionality and appearance. The prop is designed exclusively for the tertiary variant and will not affect other button variant types. + + + + ## Other attributes All other standard HTML attributes for `` are supported and are passed through to the React component, e.g. type and onClick diff --git a/src/components/button/button.stories.tsx b/src/components/button/button.stories.tsx index 8752273f..4c8025fd 100644 --- a/src/components/button/button.stories.tsx +++ b/src/components/button/button.stories.tsx @@ -2,7 +2,9 @@ import { Meta } from '@storybook/react' import { Button } from './button' import { ButtonGroup } from '../button-group' import { action } from '@storybook/addon-actions' -import { Icon } from '../icon' +import { Icon, IconNames } from '../icon' + +const ICON_OPTIONS: IconNames[] = ['star', 'check', 'add', 'chevronDown', 'email'] const meta: Meta = { title: 'Components/Button', @@ -13,8 +15,24 @@ const meta: Meta = { options: ['primary', 'secondary', 'tertiary', 'destructive', 'busy'], description: 'Defines the button style variant.', }, - iconLeft: { control: 'text', description: 'The icon displayed on the left side of the button.' }, - iconRight: { control: 'text', description: 'The icon displayed on the right side of the button.' }, + iconLeft: { + control: 'select', // Changed to select control + options: ICON_OPTIONS, + description: 'The icon displayed on the left side of the button.', + mapping: ICON_OPTIONS.reduce((acc, iconName) => { + acc[iconName] = + return acc + }, {}), + }, + iconRight: { + control: 'select', // Changed to select control + options: ICON_OPTIONS, + description: 'The icon displayed on the right side of the button.', + mapping: ICON_OPTIONS.reduce((acc, iconName) => { + acc[iconName] = + return acc + }, {}), + }, size: { control: 'select', options: ['small', 'medium', 'large'], @@ -34,6 +52,11 @@ const meta: Meta = { control: 'text', description: 'CSS class for additional styling', }, + hasNoPadding: { + control: 'boolean', + description: + 'Set this prop to true to remove the padding and height adjustments for the tertiary variant of the button. This will apply no padding and reset the height, giving a more compact button appearance. It is only applicable when the button variant is tertiary.', + }, }, } @@ -127,6 +150,15 @@ export const ButtonWithHref = { ), } +export const ButtonWithHasNoPadding = { + name: 'Button With HasNoPadding', + args: { + children: 'Button Text', + variant: 'tertiary', + hasNoPadding: true, + }, +} + export const StandardAttributes = { name: 'Standard attributes', diff --git a/src/components/button/button.tsx b/src/components/button/button.tsx index c5e2ac01..151139d2 100644 --- a/src/components/button/button.tsx +++ b/src/components/button/button.tsx @@ -1,12 +1,4 @@ -import { - HTMLAttributes, - AnchorHTMLAttributes, - ButtonHTMLAttributes, - FC, - MouseEvent, - MouseEventHandler, - ReactNode, -} from 'react' +import { HTMLAttributes, AnchorHTMLAttributes, ButtonHTMLAttributes, FC, MouseEventHandler, ReactNode } from 'react' import { ElButton, elButtonSizeLarge, @@ -33,7 +25,6 @@ type ButtonAsAnchorVariant = Exclude interface CommonButtonProps { children?: ReactNode - variant?: ButtonAsButtonVariant size?: ButtonSize iconLeft?: ReactNode iconRight?: ReactNode @@ -42,38 +33,51 @@ interface CommonButtonProps { className?: string } -interface ButtonAsButtonElementProps extends CommonButtonProps, ButtonHTMLAttributes { - href?: never - target?: never - disabled?: boolean - onClick?: MouseEventHandler +// Define a specialized type for tertiary variant for prop hasNoPadding +type TertiaryButtonProps = { + variant: 'tertiary' + hasNoPadding?: boolean // Only tertiary buttons can use hasNoPadding } -interface ButtonAsAnchorElementProps extends CommonButtonProps, AnchorHTMLAttributes { - variant?: ButtonAsAnchorVariant - /** Button styled element should always have href */ - href: string - target?: string - rel?: string - /** Anchor elements cannot be disabled. Use a button element if the component needs to be in a disabled state */ - disabled?: never - onClick?: MouseEventHandler +// Define the general type for non-tertiary variants +type NonTertiaryButtonProps = { + variant?: Exclude + hasNoPadding?: never // Disallow hasNoPadding for other variants } +// Combine the above two into a Variant-specific prop type +type VariantSpecificProps = TertiaryButtonProps | NonTertiaryButtonProps + +type ButtonAsButtonElementProps = CommonButtonProps & + VariantSpecificProps & + ButtonHTMLAttributes & { + href?: never + target?: never + disabled?: boolean + onClick?: MouseEventHandler + } + +type ButtonAsAnchorElementProps = CommonButtonProps & + VariantSpecificProps & + AnchorHTMLAttributes & { + variant?: ButtonAsAnchorVariant + /** Button styled element should always have href */ + href: string + target?: string + rel?: string + /** Anchor elements cannot be disabled. Use a button element if the component needs to be in a disabled state */ + disabled?: never + onClick?: MouseEventHandler + } + export type ButtonProps = ButtonAsButtonElementProps | ButtonAsAnchorElementProps function isButtonAsButtonElement(props: ButtonProps): props is ButtonAsButtonElementProps { return !props.href } -export const handleButtonClick = - (onClick?: MouseEventHandler) => - (e: MouseEvent) => { - if (onClick) onClick(e) - } - /** @deprecated */ -export interface FloatingButtonProps extends ButtonAsButtonElementProps { +export type FloatingButtonProps = ButtonAsButtonElementProps & { icon: IconNames } @@ -87,21 +91,23 @@ export interface ButtonGroupProps extends HTMLAttributes { alignment?: ButtonGroupAlignment } -export const Button: FC = ({ - children, - variant, - size = 'medium', - iconLeft, - iconRight, - onClick, - 'aria-label': ariaLabel, - disabled = false, - href, - target, - rel, - className, - ...rest -}) => { +export const Button: FC = (props) => { + const { + children, + variant, + size = 'medium', + iconLeft, + iconRight, + 'aria-label': ariaLabel, + disabled = false, + href, + target, + rel, + className, + hasNoPadding, + ...rest + } = props + const sizeClass = cx( size === 'small' && elButtonSizeSmall, size === 'medium' && elButtonSizeMedium, @@ -114,14 +120,17 @@ export const Button: FC = ({ const combinedClassName = cx(className, sizeClass, miscellaneousClass) - if (!isButtonAsButtonElement({ href })) { + if (!isButtonAsButtonElement(props)) { return ( { - handleButtonClick(onClick)(e) + if (props.onClick) { + props.onClick(e) + } }} aria-label={ariaLabel} role="button" @@ -139,10 +148,13 @@ export const Button: FC = ({ return ( { if (!disabled) { - handleButtonClick(onClick)(e) + if (props.onClick) { + props.onClick(e) + } } else { e.preventDefault() // Explicitly prevent default if disabled } diff --git a/src/components/button/styles.ts b/src/components/button/styles.ts index 9e07fdd3..b0f793de 100644 --- a/src/components/button/styles.ts +++ b/src/components/button/styles.ts @@ -52,7 +52,8 @@ const baseButtonStyles = ` text-align: left; color: var(--text-secondary); background: var(--fill-white); - padding: var(--spacing-2) var(--spacing-4); + height: var(--size-9); + padding: 0px var(--spacing-4); gap: var(--spacing-1); border-radius: var(--corner-default); border: 1px solid var(--outline-default); @@ -72,19 +73,19 @@ const baseButtonStyles = ` } &.${elButtonIconOnly} { - padding: var(--spacing-2); + padding: 0px; + width: var(--size-9); .${elButtonSpinner} { margin: 0 0.125rem; } } &.${elButtonSizeSmall} { - padding: var(--spacing-1) var(--spacing-3); - .${elButtonLabel} { - padding: var(--spacing-half) - } + padding: 0px var(--spacing-3); + height: var(--size-8); &.${elButtonIconOnly} { - padding: var(--spacing-1); + padding: 0px; + width: var(--size-8); .${elIcon} { padding: var(--spacing-1); } @@ -92,6 +93,10 @@ const baseButtonStyles = ` } &.${elButtonSizeLarge} { + height: var(--size-10); + &.${elButtonIconOnly} { + width: var(--size-10); + } .${elButtonLabel} { font-size: var(--font-size-base); line-height: var(--line-height-base); @@ -138,6 +143,11 @@ const baseButtonStyles = ` color: var(--icon-secondary); } } + &[data-has-no-padding='true'] { + height: unset; + width: unset; /* To unset width for iconOnly button */ + padding: 0px; + } } &[data-variant='destructive'] {