From 25be4adff210302259a18d52909a623e20397f87 Mon Sep 17 00:00:00 2001 From: vapersmile <120370047+vapersmile@users.noreply.github.com> Date: Tue, 12 Dec 2023 15:30:30 +0100 Subject: [PATCH 01/13] Feat/browsing add modal see more (#84) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix/table grid view actions (#74) * refactor(redmine 1246968): simplify `ThumbnailGrid` actions and `TableGridView` actions refactor: moved typeGuard helper, added typeGuard for interface, added tests * chore: update changeset configuration and ci config (#75) * feat: added `defaultOpened` props to `CollapseButton` (#79) * feat: added `defaultOpened` props to `CollapseButton` * Feat/document list (#80) * feat: added `ActionBar`, `SelectableList` and `DocumentList` refactor: moved action bar from `ThumbnailGrid` into `ActionBar` refactor: updated `SearchPage` * Feat/filters (#81) * feat(redmine 1245005): add filters component in searchResultPage * feat(redmine 1245005): add number counter fo actif filters * feat(redmine 1245005): add indentation without line style for collapseButton * feat(redmine 1245005): add new fonctionality Button remove location and active filters collapse * feat(redmine 1245005): add button close all + open all + add function getFiltersMenuId * feat(redmine 1245005): add line for between button open and close * feat(redmine 1245005): use flat() for collapse menu active * feat(redmine 1245005): modify story and rename Fiters to filtersBar component * feat(redmine 1245005): modify story and rename Fiters to filtersBar component * feat(redmine 1245005): add dynamique data for search results * feat(redmine 1245005): fix no-wrap error for badge in filters active section * feat(redmine 1245005): add filters numbers active value on searchbar * feat(redmine 1245005): fixe test mantine ids test error * feat(redmine 1245005): fixe intégration error * feat(redmine 1245005): fixe pr review by Quentin * feat(redmine 1245005): fixe pr review by Quentin * feat(redmine 1245005): fix pr conflicts * feat(redmine 1245003): add modal for label see properties * feat(redmine 1245003): add modal for label see properties * feat(redmine 1245003): add changeset * feat(redmine 1245003): add modal for label see properties Fix/table grid view actions (#74) * refactor(redmine 1246968): simplify `ThumbnailGrid` actions and `TableGridView` actions refactor: moved typeGuard helper, added typeGuard for interface, added tests feat: added `defaultOpened` props to `CollapseButton` (#79) * feat: added `defaultOpened` props to `CollapseButton` Feat/document list (#80) * feat: added `ActionBar`, `SelectableList` and `DocumentList` refactor: moved action bar from `ThumbnailGrid` into `ActionBar` refactor: updated `SearchPage` feat(redmine 1245003): add modal for label see properties feat(redmine 1245003): add changeset * feat: fix squash errors * feat(1245003): optimisation --------- Co-authored-by: Quentin Le Caignec <12102823+QuentinLeCaignec@users.noreply.github.com> Co-authored-by: Tony Cabaye --- .changeset/spicy-roses-sparkle.md | 5 + package-lock.json | 20 +- .../Pages/BrowsingPage/BrowsingPage.style.tsx | 49 ++++ .../src/Pages/BrowsingPage/BrowsingPage.tsx | 65 ++++- .../DocumentDetails/DocumentDetails.mock.tsx | 227 ---------------- .../Pages/DocumentDetails/DocumentDetails.tsx | 17 +- .../storybook-pages/src/Pages/pages.mock.tsx | 245 +++++++++++++++++- 7 files changed, 376 insertions(+), 252 deletions(-) create mode 100644 .changeset/spicy-roses-sparkle.md create mode 100644 packages/storybook-pages/src/Pages/BrowsingPage/BrowsingPage.style.tsx delete mode 100644 packages/storybook-pages/src/Pages/DocumentDetails/DocumentDetails.mock.tsx diff --git a/.changeset/spicy-roses-sparkle.md b/.changeset/spicy-roses-sparkle.md new file mode 100644 index 00000000..84e212a8 --- /dev/null +++ b/.changeset/spicy-roses-sparkle.md @@ -0,0 +1,5 @@ +--- +'storybook-pages': minor +--- + +add modal for label see details in browsingPage diff --git a/package-lock.json b/package-lock.json index f7364854..c31ce678 100644 --- a/package-lock.json +++ b/package-lock.json @@ -31430,10 +31430,10 @@ }, "packages/react-front-kit": { "name": "@smile/react-front-kit", - "version": "0.5.0", + "version": "0.6.0", "license": "MIT", "dependencies": { - "@smile/react-front-kit-shared": "0.5.0", + "@smile/react-front-kit-shared": "0.6.0", "pretty-bytes": "^6.1.1" }, "devDependencies": { @@ -31469,11 +31469,11 @@ }, "packages/react-front-kit-dropzone": { "name": "@smile/react-front-kit-dropzone", - "version": "0.5.0", + "version": "0.6.0", "license": "MIT", "dependencies": { - "@smile/react-front-kit": "0.5.0", - "@smile/react-front-kit-shared": "0.5.0" + "@smile/react-front-kit": "0.6.0", + "@smile/react-front-kit-shared": "0.6.0" }, "devDependencies": { "@babel/preset-env": "^7.22.20", @@ -32551,7 +32551,7 @@ }, "packages/react-front-kit-shared": { "name": "@smile/react-front-kit-shared", - "version": "0.5.0", + "version": "0.6.0", "license": "MIT", "devDependencies": { "@babel/preset-env": "^7.22.20", @@ -33627,11 +33627,11 @@ }, "packages/react-front-kit-table": { "name": "@smile/react-front-kit-table", - "version": "0.5.0", + "version": "0.6.0", "license": "MIT", "dependencies": { - "@smile/react-front-kit": "0.5.0", - "@smile/react-front-kit-shared": "0.5.0" + "@smile/react-front-kit": "0.6.0", + "@smile/react-front-kit-shared": "0.6.0" }, "devDependencies": { "@babel/preset-env": "^7.22.20", @@ -35753,7 +35753,7 @@ } }, "packages/storybook-pages": { - "version": "0.1.0", + "version": "0.6.0", "license": "MIT", "dependencies": { "@smile/react-front-kit": "*", diff --git a/packages/storybook-pages/src/Pages/BrowsingPage/BrowsingPage.style.tsx b/packages/storybook-pages/src/Pages/BrowsingPage/BrowsingPage.style.tsx new file mode 100644 index 00000000..a52ea774 --- /dev/null +++ b/packages/storybook-pages/src/Pages/BrowsingPage/BrowsingPage.style.tsx @@ -0,0 +1,49 @@ +import { createStyles } from '@mantine/styles'; + +export const useStyles = createStyles((theme) => ({ + accordionChevron: { + '&[data-rotate=true]': { + color: theme.colors.gray[0], + }, + color: theme.colors.cyan[9], + }, + accordionContent: { + background: theme.colors.cyan[9], + borderRadius: '0px 0px 8px 8px', + marginBottom: '8px', + padding: '24px', + }, + accordionControl: { + '&:hover': { + background: theme.colors.gray[2], + }, + '&[aria-expanded=true]': { + background: theme.colors.cyan[9], + borderRadius: '8px 8px 0px 0px', + color: theme.colors.gray[0], + fontWeight: 700, + marginBottom: 0, + }, + background: theme.colors.gray[1], + borderBottom: 0, + borderRadius: '8px', + color: theme.colors.dark[6], + marginBottom: '8px', + }, + accordionItem: { + borderBottom: 'none', + }, + modalBody: { + padding: '0px 48px 50px 48px', + }, + modalClose: { + position: 'relative', + right: 0, + top: 0, + }, + modalTitle: { + fontSize: '26px', + fontWeight: 700, + marginTop: 0, + }, +})); diff --git a/packages/storybook-pages/src/Pages/BrowsingPage/BrowsingPage.tsx b/packages/storybook-pages/src/Pages/BrowsingPage/BrowsingPage.tsx index b6d841e1..e72571ba 100644 --- a/packages/storybook-pages/src/Pages/BrowsingPage/BrowsingPage.tsx +++ b/packages/storybook-pages/src/Pages/BrowsingPage/BrowsingPage.tsx @@ -4,12 +4,15 @@ import type { IFile } from '@smile/react-front-kit-dropzone'; import type { FormEvent, ReactElement } from 'react'; import { + Accordion, AppShell, Button, Flex, MantineProvider, + Modal, useMantineTheme, } from '@mantine/core'; +import { useDisclosure } from '@mantine/hooks'; import { Eye, FolderPlus, Suitcase, User } from '@phosphor-icons/react'; import { Breadcrumbs, @@ -25,9 +28,19 @@ import { primaryTheme, secondaryTheme } from '@smile/react-front-kit-shared'; import { TableGridView } from '@smile/react-front-kit-table'; import { useState } from 'react'; -import { headerContent, headerLeft, headerRight } from '../pages.mock'; +import { + CardAction, + CardIdentity, + CardNative, + CardPermissions, + CardsMetadata, + headerContent, + headerLeft, + headerRight, +} from '../pages.mock'; import { actions, data, gridProps, tableProps } from './BrowsingPage.mock'; +import { useStyles } from './BrowsingPage.style'; /** * Primary UI component for user interaction @@ -37,9 +50,19 @@ export function BrowsingPage(): ReactElement { const [sidebarMenu, setSidebarMenu] = useState(menuMock); const [files, setFiles] = useState([]); const [gridCols, setGridCols] = useState(4); + const [seeMoreModal, { open, close }] = useDisclosure(false); + const { classes } = useStyles(); const theme = useMantineTheme(); + const accordionItems = [ + { content: CardAction, key: 1, title: 'Action' }, + { content: CardsMetadata, key: 2, title: 'Métadonnées' }, + { content: CardNative, key: 3, title: 'Historique' }, + { content: CardPermissions, key: 4, title: 'Droits' }, + { content: CardIdentity, key: 5, title: 'Cycle de vie' }, + ]; + function handleSearchSubmit(event: FormEvent): void { event.preventDefault(); } @@ -56,6 +79,32 @@ export function BrowsingPage(): ReactElement { ); } + function getAccordionItems(): ReactElement { + const items = accordionItems.map((item) => ( + + {item.title} + {item.content} + + )); + + return ( + + + {items} + + + ); + } + return ( ); } diff --git a/packages/storybook-pages/src/Pages/DocumentDetails/DocumentDetails.mock.tsx b/packages/storybook-pages/src/Pages/DocumentDetails/DocumentDetails.mock.tsx deleted file mode 100644 index 60977fd8..00000000 --- a/packages/storybook-pages/src/Pages/DocumentDetails/DocumentDetails.mock.tsx +++ /dev/null @@ -1,227 +0,0 @@ -import { - Button, - Card, - Checkbox, - Chip, - Divider, - Grid, - Group, - Space, - Text, -} from '@mantine/core'; -import { - PencilSimple, - PencilSimpleLine, - Plus, - ShareNetwork, - TrashSimple, -} from '@phosphor-icons/react'; -import { FolderMove } from '@smile/react-front-kit-shared'; - -export const CardAction = ( - - - Générales - - - - - - - - - -); - -export const CardMetadata = ( - - - Métadonnées - - - - - - - -); - -export const CardNative = ( - - - Native - - - - - - Créer - - - - 22 mai 2021 - - - - - - - - Dernière modification - - - - 22 mai 2021 - - - - - - - - Créer par - - - - 22 mai 2021 - - - - - - - - Modifier par - - - - 22 mai 2021 - - - - - - - - Version - - - - V2 - - - - - -); - -export const CardPermissions = ( - - - Accés au document - - - - - - - - - -); - -export const CardIdentity = ( - - - Identité - - - - - - Titre - - - - 22 mai 2021 - - - - - - - - Description - - - - 22 mai 2021 - - - - - - - - Type de document - - - - 22 mai 2021 - - - - - - - - - Titre - - - - Facture_Medor - - - - - - - - Description - - - - - - - - - - - - - Type de document - - - - Facture - - - - - -); diff --git a/packages/storybook-pages/src/Pages/DocumentDetails/DocumentDetails.tsx b/packages/storybook-pages/src/Pages/DocumentDetails/DocumentDetails.tsx index 561e83d9..6673d537 100644 --- a/packages/storybook-pages/src/Pages/DocumentDetails/DocumentDetails.tsx +++ b/packages/storybook-pages/src/Pages/DocumentDetails/DocumentDetails.tsx @@ -9,14 +9,13 @@ import { Button, Flex, Grid, - Group, MantineProvider, Menu, Space, Tabs, } from '@mantine/core'; import { createStyles } from '@mantine/styles'; -import { CaretLeft, Plus, Star } from '@phosphor-icons/react'; +import { CaretLeft, Star } from '@phosphor-icons/react'; import { DropdownButton, Header, ResponsiveTabs } from '@smile/react-front-kit'; import { FolderMove, primaryTheme } from '@smile/react-front-kit-shared'; @@ -26,7 +25,9 @@ import { CardMetadata, CardNative, CardPermissions, -} from './DocumentDetails.mock'; + CardsMetadata, +} from '../pages.mock'; + import { DocumentView } from './DocumentView'; const useStyles = createStyles((theme) => ({ @@ -153,15 +154,7 @@ export function DocumentDetails(): ReactElement { <>{CardPermissions} - - <>{CardNative} - - <>{CardIdentity} - - - - - + {CardsMetadata} {CardNative} {CardPermissions} {CardIdentity} diff --git a/packages/storybook-pages/src/Pages/pages.mock.tsx b/packages/storybook-pages/src/Pages/pages.mock.tsx index 241a41a0..dfb3390d 100644 --- a/packages/storybook-pages/src/Pages/pages.mock.tsx +++ b/packages/storybook-pages/src/Pages/pages.mock.tsx @@ -1,8 +1,29 @@ +/* eslint-disable react-refresh/only-export-components */ import type { IActionBarAction, IDocument } from '@smile/react-front-kit'; -import { Avatar, Button, Menu, Space } from '@mantine/core'; -import { DownloadSimple } from '@phosphor-icons/react'; +import { + Avatar, + Button, + Card, + Checkbox, + Chip, + Divider, + Grid, + Group, + Menu, + Space, + Text, +} from '@mantine/core'; +import { + DownloadSimple, + PencilSimple, + PencilSimpleLine, + Plus, + ShareNetwork, + TrashSimple, +} from '@phosphor-icons/react'; import { DropdownButton } from '@smile/react-front-kit'; +import { FolderMove } from '@smile/react-front-kit-shared'; import { action } from '@storybook/addon-actions'; export const headerContent = ( @@ -119,3 +140,223 @@ export const searchActions: IActionBarAction[] = [ onAction: action('Download files'), }, ]; + +export const CardAction = ( + + + Générales + + + + + + + + + +); + +export const CardMetadata = ( + + + Métadonnées + + + + + + + +); + +export const CardNative = ( + + + Native + + + + + + Créer + + + + 22 mai 2021 + + + + + + + + Dernière modification + + + + 22 mai 2021 + + + + + + + + Créer par + + + + 22 mai 2021 + + + + + + + + Modifier par + + + + 22 mai 2021 + + + + + + + + Version + + + + V2 + + + + + +); + +export const CardPermissions = ( + + + Accés au document + + + + + + + + + +); + +export const CardIdentity = ( + + + Identité + + + + + + Titre + + + + 22 mai 2021 + + + + + + + + Description + + + + 22 mai 2021 + + + + + + + + Type de document + + + + 22 mai 2021 + + + + + + + + + Titre + + + + Facture_Medor + + + + + + + + Description + + + + - + + + + + + + + Type de document + + + + Facture + + + + + +); + +export const CardsMetadata = ( + <> + <>{CardNative} + + <>{CardIdentity} + + + + + +); From 480af1122b41e28d938bd665c1aa998272c99d9a Mon Sep 17 00:00:00 2001 From: Quentin Le Caignec <12102823+QuentinLeCaignec@users.noreply.github.com> Date: Thu, 14 Dec 2023 14:49:18 +0100 Subject: [PATCH 02/13] Feat/responsive header (#88) * feat: added HeaderMobile, HeaderNav refactor: modified Header, SearchBar, SidebarMenu, CollapseButton --- .changeset/tiny-pillows-kick.md | 5 + .../CollapseButtonControlled.tsx | 2 +- .../CollapseButton.test.tsx.snap | 2 +- .../__snapshots__/FiltersBar.test.tsx.snap | 14 +- .../src/Components/Header/Header.mock.tsx | 63 +++ .../src/Components/Header/Header.stories.tsx | 46 +- .../src/Components/Header/Header.style.tsx | 65 +++ .../src/Components/Header/Header.test.tsx | 2 +- .../src/Components/Header/Header.tsx | 191 ++++----- .../Header/__snapshots__/Header.test.tsx.snap | 194 +++++++-- .../HeaderMobile/HeaderMobile.stories.tsx | 44 ++ .../HeaderMobile/HeaderMobile.style.tsx | 43 ++ .../HeaderMobile/HeaderMobile.test.tsx | 16 + .../Components/HeaderMobile/HeaderMobile.tsx | 107 +++++ .../__snapshots__/HeaderMobile.test.tsx.snap | 102 +++++ .../HeaderNav/HeaderNav.stories.tsx | 34 ++ .../Components/HeaderNav/HeaderNav.style.tsx | 47 ++ .../Components/HeaderNav/HeaderNav.test.tsx | 22 + .../src/Components/HeaderNav/HeaderNav.tsx | 122 ++++++ .../__snapshots__/HeaderNav.test.tsx.snap | 404 ++++++++++++++++++ .../src/Components/SearchBar/SearchBar.tsx | 3 + .../Components/SidebarMenu/SidebarMenu.tsx | 14 +- .../__snapshots__/SidebarMenu.test.tsx.snap | 30 +- packages/react-front-kit/src/index.tsx | 2 + 24 files changed, 1373 insertions(+), 201 deletions(-) create mode 100644 .changeset/tiny-pillows-kick.md create mode 100644 packages/react-front-kit/src/Components/Header/Header.mock.tsx create mode 100644 packages/react-front-kit/src/Components/Header/Header.style.tsx create mode 100644 packages/react-front-kit/src/Components/HeaderMobile/HeaderMobile.stories.tsx create mode 100644 packages/react-front-kit/src/Components/HeaderMobile/HeaderMobile.style.tsx create mode 100644 packages/react-front-kit/src/Components/HeaderMobile/HeaderMobile.test.tsx create mode 100644 packages/react-front-kit/src/Components/HeaderMobile/HeaderMobile.tsx create mode 100644 packages/react-front-kit/src/Components/HeaderMobile/__snapshots__/HeaderMobile.test.tsx.snap create mode 100644 packages/react-front-kit/src/Components/HeaderNav/HeaderNav.stories.tsx create mode 100644 packages/react-front-kit/src/Components/HeaderNav/HeaderNav.style.tsx create mode 100644 packages/react-front-kit/src/Components/HeaderNav/HeaderNav.test.tsx create mode 100644 packages/react-front-kit/src/Components/HeaderNav/HeaderNav.tsx create mode 100644 packages/react-front-kit/src/Components/HeaderNav/__snapshots__/HeaderNav.test.tsx.snap diff --git a/.changeset/tiny-pillows-kick.md b/.changeset/tiny-pillows-kick.md new file mode 100644 index 00000000..07b2782d --- /dev/null +++ b/.changeset/tiny-pillows-kick.md @@ -0,0 +1,5 @@ +--- +'@smile/react-front-kit': minor +--- + +Added `HeaderMobile` and `HeaderNav` components, reworked `Header` with mobile mode, added optional placeholder to `SearchBar`, improved info returned by collapseButtonProps in `SidebarMenu` diff --git a/packages/react-front-kit/src/Components/CollapseButton/CollapseButtonControlled.tsx b/packages/react-front-kit/src/Components/CollapseButton/CollapseButtonControlled.tsx index 212efed7..f95bb14b 100644 --- a/packages/react-front-kit/src/Components/CollapseButton/CollapseButtonControlled.tsx +++ b/packages/react-front-kit/src/Components/CollapseButton/CollapseButtonControlled.tsx @@ -185,7 +185,7 @@ export function CollapseButtonControlled< {...buttonProps} > - )} - {Boolean(opened) && ( - - - - )} - {right} - - - + {children} + + +
+ {Boolean(hasSearch) && ( + + )} + {Boolean(searchOpened) && ( + + + + )} +
+ {right} +
+ + + + {/* Mobile Header */} +
+ +
+ ); } diff --git a/packages/react-front-kit/src/Components/Header/__snapshots__/Header.test.tsx.snap b/packages/react-front-kit/src/Components/Header/__snapshots__/Header.test.tsx.snap index 7b12d0a2..0e979f0d 100644 --- a/packages/react-front-kit/src/Components/Header/__snapshots__/Header.test.tsx.snap +++ b/packages/react-front-kit/src/Components/Header/__snapshots__/Header.test.tsx.snap @@ -2,66 +2,170 @@ exports[`Header matches snapshot 1`] = ` `; diff --git a/packages/react-front-kit/src/Components/HeaderMobile/HeaderMobile.stories.tsx b/packages/react-front-kit/src/Components/HeaderMobile/HeaderMobile.stories.tsx new file mode 100644 index 00000000..c7807cc4 --- /dev/null +++ b/packages/react-front-kit/src/Components/HeaderMobile/HeaderMobile.stories.tsx @@ -0,0 +1,44 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import { useStorybookArgsConnect } from '@smile/react-front-kit-shared/storybook-utils'; +import { action } from '@storybook/addon-actions'; + +import { + childrenMock, + leftContentMock, + rightContentMobileMock, +} from '../Header/Header.mock'; + +import { HeaderMobile as Cmp } from './HeaderMobile'; + +const meta = { + component: Cmp, + decorators: [ + function Component(Story, ctx) { + const args = useStorybookArgsConnect(ctx.args, { + onSearchChange: 'searchValue', + }); + return ; + }, + ], + parameters: { + layout: 'fullscreen', + }, + tags: ['autodocs'], + title: '3-custom/Components/HeaderMobile', +} satisfies Meta; + +export default meta; +type IStory = StoryObj; + +export const HeaderMobile: IStory = { + args: { + children: childrenMock(true), + height: 90, + left: leftContentMock, + onSearchSubmit: action('search input submitted'), + right: rightContentMobileMock, + searchValue: '', + withBorder: false, + }, +}; diff --git a/packages/react-front-kit/src/Components/HeaderMobile/HeaderMobile.style.tsx b/packages/react-front-kit/src/Components/HeaderMobile/HeaderMobile.style.tsx new file mode 100644 index 00000000..7b72af73 --- /dev/null +++ b/packages/react-front-kit/src/Components/HeaderMobile/HeaderMobile.style.tsx @@ -0,0 +1,43 @@ +import { createStyles } from '@mantine/core'; + +export const useStyles = createStyles((theme, height: number) => ({ + around: { + alignItems: 'center', + gap: theme.spacing.xs, + }, + burgerMenu: { + backgroundColor: theme.white, + borderTop: '1px solid', + borderTopColor: theme.colors.gray[2], + height: `calc(100vh - ${height}px)`, + padding: '40px 24px', + }, + burgerSearch: { + '.mantine-TextInput-input': { + padding: '6px 68px 6px 24px', + }, + marginTop: 'auto', + }, + containerMobile: { + alignItems: 'center', + justifyContent: 'space-between', + padding: '16px 24px', + position: 'relative', + width: '100%', + }, + menu: { + alignItems: 'center', + gap: theme.spacing.xl, + left: '50%', + margin: 'auto', + position: 'absolute', + top: '50%', + translate: '-50% -50%', + }, + searchIcon: { + marginRight: '24px', + }, + separator: { + borderColor: theme.colors.gray[3], + }, +})); diff --git a/packages/react-front-kit/src/Components/HeaderMobile/HeaderMobile.test.tsx b/packages/react-front-kit/src/Components/HeaderMobile/HeaderMobile.test.tsx new file mode 100644 index 00000000..d00a9ddf --- /dev/null +++ b/packages/react-front-kit/src/Components/HeaderMobile/HeaderMobile.test.tsx @@ -0,0 +1,16 @@ +import { renderWithProviders } from '@smile/react-front-kit-shared/test-utils'; + +import { HeaderMobile } from './HeaderMobile'; + +describe('HeaderMobile', () => { + it('matches snapshot', () => { + const { container } = renderWithProviders( + + Espace documentaire + Espace workflow + Archives + , + ); + expect(container).toMatchSnapshot(); + }); +}); diff --git a/packages/react-front-kit/src/Components/HeaderMobile/HeaderMobile.tsx b/packages/react-front-kit/src/Components/HeaderMobile/HeaderMobile.tsx new file mode 100644 index 00000000..6a237285 --- /dev/null +++ b/packages/react-front-kit/src/Components/HeaderMobile/HeaderMobile.tsx @@ -0,0 +1,107 @@ +'use client'; + +import type { + BurgerProps, + CollapseProps, + HeaderProps, + TextInputProps, +} from '@mantine/core'; +import type { FormEvent, ReactElement, ReactNode } from 'react'; + +import { + Burger, + Collapse, + Divider, + Flex, + Header as MantineHeader, + Stack, + TextInput, + useMantineTheme, +} from '@mantine/core'; +import { MagnifyingGlass } from '@phosphor-icons/react'; +import { useState } from 'react'; + +import { useStyles } from './HeaderMobile.style'; + +export interface IHeaderMobileProps + extends Omit { + burgerProps?: Omit; + children?: ReactNode; + collapseProps?: Omit; + hasSearch?: boolean; + height?: number; + left?: ReactNode; + onSearchChange?: (value: string) => void; + onSearchSubmit?: (event: FormEvent) => void; + right?: ReactNode; + searchInputProps?: Omit; + searchValue?: string; +} + +/** Additional props will be forwarded to the [Mantine AppShell (Header) component](https://mantine.dev/core/app-shell/) */ +export function HeaderMobile(props: IHeaderMobileProps): ReactElement { + const { + burgerProps, + children, + collapseProps, + hasSearch = true, + height = 76, + left, + onSearchChange, + onSearchSubmit, + right, + searchInputProps, + searchValue, + withBorder = true, + ...headerProps + } = props; + const [burgerOpened, setBurgerOpened] = useState(false); + + const theme = useMantineTheme(); + const { classes } = useStyles(height); + + return ( + + + + setBurgerOpened((o) => !o)} + opened={burgerOpened} + size={22} + title="Open navigation" + {...burgerProps} + /> + + + {left} + {right} + + + + {children} + {Boolean(hasSearch) && ( +
+ onSearchChange?.(e.target.value)} + placeholder="Search on the site" + radius={32} + rightSection={ + + } + size="md" + value={searchValue} + {...searchInputProps} + /> + + )} +
+
+
+ ); +} diff --git a/packages/react-front-kit/src/Components/HeaderMobile/__snapshots__/HeaderMobile.test.tsx.snap b/packages/react-front-kit/src/Components/HeaderMobile/__snapshots__/HeaderMobile.test.tsx.snap new file mode 100644 index 00000000..282c86e0 --- /dev/null +++ b/packages/react-front-kit/src/Components/HeaderMobile/__snapshots__/HeaderMobile.test.tsx.snap @@ -0,0 +1,102 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`HeaderMobile matches snapshot 1`] = ` +
+
+
+
+ + +
+
+
+ +
+
+`; diff --git a/packages/react-front-kit/src/Components/HeaderNav/HeaderNav.stories.tsx b/packages/react-front-kit/src/Components/HeaderNav/HeaderNav.stories.tsx new file mode 100644 index 00000000..c4a77cee --- /dev/null +++ b/packages/react-front-kit/src/Components/HeaderNav/HeaderNav.stories.tsx @@ -0,0 +1,34 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import { Flex } from '@mantine/core'; + +import { menusMock } from '../Header/Header.mock'; + +import { HeaderNav as Cmp } from './HeaderNav'; + +const meta = { + component: Cmp, + tags: ['autodocs'], + title: '3-custom/Components/HeaderNav', +} satisfies Meta; + +export default meta; +type IStory = StoryObj; + +export const HeaderNav: IStory = { + args: { + menus: menusMock, + }, + render: ({ ...props }) => ( + + + + ), +}; + +export const Mobile: IStory = { + args: { + isMobile: true, + menus: menusMock, + }, +}; diff --git a/packages/react-front-kit/src/Components/HeaderNav/HeaderNav.style.tsx b/packages/react-front-kit/src/Components/HeaderNav/HeaderNav.style.tsx new file mode 100644 index 00000000..2660f338 --- /dev/null +++ b/packages/react-front-kit/src/Components/HeaderNav/HeaderNav.style.tsx @@ -0,0 +1,47 @@ +import { createStyles } from '@mantine/core'; + +export const useStyles = createStyles((theme) => ({ + activeLabel: { + '& span': { + color: theme.colors.dark[6], + }, + }, + activeRootLabel: { + '& span': { + color: theme.fn.primaryColor(), + }, + }, + dropdown: { + borderRadius: 10, + padding: '16px 20px', + }, + menu: { + ':hover': { backgroundColor: theme.colors.gray[1] }, + backgroundColor: 'transparent', + }, + navLink: { + borderRadius: 10, + width: 'unset', + }, + navLinkLabel: { + fontSize: '14px', + fontWeight: 400, + margin: 0, + padding: 0, + }, + navLinkParentLabel: { + fontSize: '14px', + fontWeight: 600, + margin: 0, + padding: 0, + }, + rootMenu: { + '& span': { + color: theme.colors.dark[6], + }, + ':not(:first-of-type)': { + marginTop: '32px', + }, + fontSize: '18px', + }, +})); diff --git a/packages/react-front-kit/src/Components/HeaderNav/HeaderNav.test.tsx b/packages/react-front-kit/src/Components/HeaderNav/HeaderNav.test.tsx new file mode 100644 index 00000000..77d2dd92 --- /dev/null +++ b/packages/react-front-kit/src/Components/HeaderNav/HeaderNav.test.tsx @@ -0,0 +1,22 @@ +import { renderWithProviders } from '@smile/react-front-kit-shared/test-utils'; + +import { menusMock } from '../Header/Header.mock'; + +import { HeaderNav } from './HeaderNav'; + +describe('HeaderNav', () => { + beforeEach(() => { + // Prevent mantine random ID + Math.random = () => 0.42; + }); + it('matches snapshot in desktop mode', () => { + const { container } = renderWithProviders(); + expect(container).toMatchSnapshot(); + }); + it('matches snapshot in mobile mode', () => { + const { container } = renderWithProviders( + , + ); + expect(container).toMatchSnapshot(); + }); +}); diff --git a/packages/react-front-kit/src/Components/HeaderNav/HeaderNav.tsx b/packages/react-front-kit/src/Components/HeaderNav/HeaderNav.tsx new file mode 100644 index 00000000..620afbe8 --- /dev/null +++ b/packages/react-front-kit/src/Components/HeaderNav/HeaderNav.tsx @@ -0,0 +1,122 @@ +'use client'; + +import type { IMenuItem } from '../SidebarMenu/SidebarMenu'; +import type { ElementType, ReactElement, ReactNode } from 'react'; + +import { Menu, NavLink, useMantineTheme } from '@mantine/core'; +import { CaretDown } from '@phosphor-icons/react'; + +import { SidebarMenu } from '../SidebarMenu/SidebarMenu'; + +import { useStyles } from './HeaderNav.style'; + +function recursiveNavLinks( + menus: IHeaderNavMenu[], + component: ElementType, + className: string, +): ReactNode { + return menus.map((menu) => ( + + {menu.children + ? recursiveNavLinks(menu.children, component, className) + : null} + + )); +} + +export interface IHeaderNavMenu + extends IMenuItem { + children?: IHeaderNavMenu[]; + url?: string; +} + +export interface IHeaderNavProps { + isMobile?: boolean; + menus: IHeaderNavMenu[]; + navLinkComponent?: ElementType; +} + +export function HeaderNav( + props: IHeaderNavProps, +): ReactElement { + const { menus, isMobile = false, navLinkComponent = 'a' } = props; + const theme = useMantineTheme(); + const { classes } = useStyles(); + + return ( + <> + {isMobile ? ( + , + level: number, + isSelected: boolean, + opened: boolean, + ) => { + return { + className: [ + classes.menu, + level === 0 ? classes.rootMenu : '', + opened || isSelected + ? level === 0 + ? classes.activeRootLabel + : classes.activeLabel + : '', + ].join(' '), + ...(level >= 1 && { + collapseProps: { + className: '', + }, + }), + }; + }} + menu={menus} + p={0} + /> + ) : ( + menus.map((menu) => { + if (menu.children) { + return ( + + + + } + /> + + + {recursiveNavLinks( + menu.children, + navLinkComponent, + classes.navLink, + )} + + + ); + } + return ( + + ); + }) + )} + + ); +} diff --git a/packages/react-front-kit/src/Components/HeaderNav/__snapshots__/HeaderNav.test.tsx.snap b/packages/react-front-kit/src/Components/HeaderNav/__snapshots__/HeaderNav.test.tsx.snap new file mode 100644 index 00000000..8901705a --- /dev/null +++ b/packages/react-front-kit/src/Components/HeaderNav/__snapshots__/HeaderNav.test.tsx.snap @@ -0,0 +1,404 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`HeaderNav matches snapshot in desktop mode 1`] = ` +
+ +