diff --git a/.changeset/mighty-ghosts-grin.md b/.changeset/mighty-ghosts-grin.md new file mode 100644 index 00000000..4d618f80 --- /dev/null +++ b/.changeset/mighty-ghosts-grin.md @@ -0,0 +1,5 @@ +--- +'storybook-pages': minor +--- + +Added ThemePage to show the application of a customized theme (DSFR) diff --git a/.changeset/small-singers-juggle.md b/.changeset/small-singers-juggle.md new file mode 100644 index 00000000..82e3dbab --- /dev/null +++ b/.changeset/small-singers-juggle.md @@ -0,0 +1,5 @@ +--- +'storybook-pages': minor +--- + +Add inputs to modify title and description in detail tab on agenda item page diff --git a/package-lock.json b/package-lock.json index e015a0c2..2244a257 100644 --- a/package-lock.json +++ b/package-lock.json @@ -29059,9 +29059,9 @@ ] }, "node_modules/qs": { - "version": "6.12.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.12.1.tgz", - "integrity": "sha512-zWmv4RSuB9r2mYQw3zxQuHWeU+42aKi1wWig/j4ele4ygELZ7PEO6MM7rim9oAQH2A5MWfsAVf/jPvTPgCbvUQ==", + "version": "6.12.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.12.2.tgz", + "integrity": "sha512-x+NLUpx9SYrcwXtX7ob1gnkSems4i/mGZX5SlYxwIau6RrUSODO89TR/XDGGpn5RPWSYIB+aSfuSlV5+CmbTBg==", "dev": true, "dependencies": { "side-channel": "^1.0.6" diff --git a/packages/haring-react-table/src/Components/Table/Table.mock.tsx b/packages/haring-react-table/src/Components/Table/Table.mock.tsx index f1c52ed5..561ba76b 100644 --- a/packages/haring-react-table/src/Components/Table/Table.mock.tsx +++ b/packages/haring-react-table/src/Components/Table/Table.mock.tsx @@ -143,5 +143,5 @@ export const tableMock: ITableProps> = { }, ], manualSorting: false, - rowActionNumber: 3, + maxVisibleActions: 3, }; diff --git a/packages/haring-react-table/src/Components/Table/Table.tsx b/packages/haring-react-table/src/Components/Table/Table.tsx index deb30c91..2870af22 100644 --- a/packages/haring-react-table/src/Components/Table/Table.tsx +++ b/packages/haring-react-table/src/Components/Table/Table.tsx @@ -39,9 +39,9 @@ import classes from './Table.module.css'; export interface ITableProps> extends MRT_TableOptions { actions?: ITableAction[]; + maxVisibleActions?: number; menuLabel?: string; paginationProps?: Partial; - rowActionNumber?: number; } const tooltipProps = { @@ -63,7 +63,7 @@ export function Table>( menuLabel = 'Other actions', paginationDisplayMode = 'custom', paginationProps, - rowActionNumber = 0, + maxVisibleActions = 0, ...mantineTableProps } = props; const { enablePagination = true, manualPagination } = mantineTableProps; @@ -76,8 +76,8 @@ export function Table>( // Calculated values const massActions = actions.filter(({ isMassAction }) => isMassAction); const rowActions = actions.filter(({ isItemAction = true }) => isItemAction); - const visibleRowActions = rowActions.slice(0, rowActionNumber); - const menuRowActions = rowActions.slice(rowActionNumber); + const visibleRowActions = rowActions.slice(0, maxVisibleActions); + const menuRowActions = rowActions.slice(maxVisibleActions); // Handle function handleMenuChange(opened: boolean, index: number): void { diff --git a/packages/storybook-pages/src/Pages/AgendaItemPage/AgendaItemPage.mock.tsx b/packages/storybook-pages/src/Pages/AgendaItemPage/AgendaItemPage.mock.tsx index 17b9ee8f..4d133f63 100644 --- a/packages/storybook-pages/src/Pages/AgendaItemPage/AgendaItemPage.mock.tsx +++ b/packages/storybook-pages/src/Pages/AgendaItemPage/AgendaItemPage.mock.tsx @@ -4,13 +4,16 @@ import type { ReactNode } from 'react'; export const texts = { conflicts: `Conflits d'intérêt`, decisions: 'Décisions', + descriptionLabel: 'Description :', details: 'Détails', history: 'Historique', + modifyLabel: 'Modify', next: 'Suivant', order: `Ordre du jour`, previous: 'Précédent', pv: 'PV', sends: 'Envois', + titleLabel: 'Title :', toggleLabel: `Voir l'ordre du jour`, }; @@ -42,7 +45,11 @@ export const menusMock: IMenuItem[] = [ ]; export interface IAgendaItemTab { - content: ReactNode; + content: { + body?: ReactNode; + description?: string; + title?: string; + }; id: string; } @@ -56,66 +63,77 @@ export const tabsMock: IAgendaItemOrder[] = [ id: '1', tabs: [ { - content: ( - <> -

Content of Order 1

-

Tab order

- - ), + content: { + body: ( + <> +

Content of Order 1

+

Tab order

+ + ), + }, + id: 'order', }, { - content: ( - <> -

Content of Order 1

-

Tab details

- - ), + content: { + description: '', + title: 'Titre tab détails order 1', + }, id: 'details', }, { - content: ( - <> -

Content of Order 1

-

Tab conflicts

- - ), + content: { + body: ( + <> +

Content of Order 1

+

Tab conflicts

+ + ), + }, id: 'conflicts', }, { - content: ( - <> -

Content of Order 1

-

Tab sends

- - ), + content: { + body: ( + <> +

Content of Order 1

+

Tab sends

+ + ), + }, id: 'sends', }, { - content: ( - <> -

Content of Order 1

-

Tab pv

- - ), + content: { + body: ( + <> +

Content of Order 1

+

Tab pv

+ + ), + }, id: 'pv', }, { - content: ( - <> -

Content of Order 1

-

Tab decisions

- - ), + content: { + body: ( + <> +

Content of Order 1

+

Tab decisions

+ + ), + }, id: 'decisions', }, { - content: ( - <> -

Content of Order 1

-

Tab history

- - ), + content: { + body: ( + <> +

Content of Order 1

+

Tab history

+ + ), + }, id: 'history', }, ], @@ -123,25 +141,55 @@ export const tabsMock: IAgendaItemOrder[] = [ { id: '2', tabs: [ - { content: 'Content of Order 2, Tab order', id: 'order' }, - { content: 'Content of Order 2, Tab details', id: 'details' }, - { content: 'Content of Order 2, Tab conflicts', id: 'conflicts' }, - { content: 'Content of Order 2, Tab sends', id: 'sends' }, - { content: 'Content of Order 2, Tab pv', id: 'pv' }, - { content: 'Content of Order 2, Tab decisions', id: 'decisions' }, - { content: 'Content of Order 2, Tab history', id: 'history' }, + { content: { body: 'Content of Order 2, Tab order' }, id: 'order' }, + { + content: { + description: '', + title: 'Titre tab détails order 2', + }, + id: 'details', + }, + { + content: { body: 'Content of Order 2, Tab conflicts' }, + id: 'conflicts', + }, + { content: { body: 'Content of Order 2, Tab sends' }, id: 'sends' }, + { content: { body: 'Content of Order 2, Tab pv' }, id: 'pv' }, + { + content: { body: 'Content of Order 2, Tab decisions' }, + id: 'decisions', + }, + { + content: { body: 'Content of Order 2, Tab history' }, + id: 'history', + }, ], }, { id: '3', tabs: [ - { content: 'Content of Order 3, Tab order', id: 'order' }, - { content: 'Content of Order 3, Tab details', id: 'details' }, - { content: 'Content of Order 3, Tab conflicts', id: 'conflicts' }, - { content: 'Content of Order 3, Tab sends', id: 'sends' }, - { content: 'Content of Order 3, Tab pv', id: 'pv' }, - { content: 'Content of Order 3, Tab decisions', id: 'decisions' }, - { content: 'Content of Order 3, Tab history', id: 'history' }, + { content: { body: 'Content of Order 3, Tab order' }, id: 'order' }, + { + content: { + description: '', + title: 'Titre tab détails order 3', + }, + id: 'details', + }, + { + content: { body: 'Content of Order 3, Tab conflicts' }, + id: 'conflicts', + }, + { content: { body: 'Content of Order 3, Tab order' }, id: 'sends' }, + { content: { body: 'Content of Order 3, Tab pv' }, id: 'pv' }, + { + content: { body: 'Content of Order 3, Tab decisions' }, + id: 'decisions', + }, + { + content: { body: 'Content of Order 3, Tab history' }, + id: 'history', + }, ], }, ]; diff --git a/packages/storybook-pages/src/Pages/AgendaItemPage/AgendaItemPage.tsx b/packages/storybook-pages/src/Pages/AgendaItemPage/AgendaItemPage.tsx index 472a83c4..ea608b77 100644 --- a/packages/storybook-pages/src/Pages/AgendaItemPage/AgendaItemPage.tsx +++ b/packages/storybook-pages/src/Pages/AgendaItemPage/AgendaItemPage.tsx @@ -1,10 +1,13 @@ 'use client'; -import type { ReactElement } from 'react'; +import type { IAgendaItemOrder } from './AgendaItemPage.mock'; +import type { ReactElement, ReactNode } from 'react'; -import { AppShell, Button, Tabs } from '@mantine/core'; +import { AppShell, Button, Group, Stack, Tabs, TextInput } from '@mantine/core'; +import { useDisclosure } from '@mantine/hooks'; import { Breadcrumbs, + ConfirmModal, FoldableColumnLayout, SidebarMenu, } from '@smile/haring-react'; @@ -18,41 +21,138 @@ import { } from './AgendaItemPage.mock'; import classes from './AgendaItemPage.module.css'; +interface IModalConfig { + mode: 'description' | 'title'; + value?: string; +} + export function AgendaItemPage(): ReactElement { - const [openedMenu, setOpenedMenu] = useState(menusMock[0].id); + const [openedMenu, setOpenedMenu] = useState(0); const [activeTab, setActiveTab] = useState('order'); + const [tabData, setTabData] = useState(tabsMock); + const [modalConfig, setModalConfig] = useState({ + mode: 'title', + value: '', + }); + + const [opened, { open, close }] = useDisclosure(false); function handlePrevious(): void { - const previousIndex = menusMock.findIndex((m) => m.id === openedMenu) - 1; - const newOpenedId: string = - previousIndex === -1 - ? menusMock[menusMock.length - 1].id - : menusMock[previousIndex].id; + const previousIndex = openedMenu - 1; + const newOpenedId: number = + previousIndex === -1 ? menusMock.length - 1 : previousIndex; setOpenedMenu(newOpenedId); } function handleNext(): void { - const nextIndex = menusMock.findIndex((m) => m.id === openedMenu) + 1; - const newOpenedId: string = - nextIndex > menusMock.length - 1 - ? menusMock[0].id - : menusMock[nextIndex].id; + const nextIndex = openedMenu + 1; + const newOpenedId: number = + nextIndex > menusMock.length - 1 ? 0 : nextIndex; setOpenedMenu(newOpenedId); } + const tabContent = (): ReactNode => { + if (activeTab === 'details') { + const activeTabData = tabData.find( + (order) => order.id === menusMock[openedMenu].id, + ); + const tabContent = activeTabData?.tabs.find((tab) => tab.id === activeTab) + ?.content; + + if (tabContent) { + const { title, description } = tabContent; + return ( + + +

{texts.titleLabel}

+

{title}

+ +
+ + + +

{texts.descriptionLabel}

+ +
+

{description}

+
+
+ ); + } + + return

Details

; + } + return tabData + .find((order) => order.id === menusMock[openedMenu].id) + ?.tabs.find((tab) => tab.id === activeTab)?.content.body; + }; + + const handleConfirmModal = (): void => { + const newTabData = tabData.map((order) => { + if (order.id === menusMock[openedMenu].id) { + return { + ...order, + tabs: order.tabs.map((tab) => { + if (tab.id === activeTab) { + return { + ...tab, + content: { + ...tab.content, + [modalConfig.mode]: modalConfig.value, + }, + }; + } + return tab; + }), + }; + } + return order; + }); + + setTabData(newTabData); + close(); + }; + return ( - defaultSelectedId={openedMenu[0]} + defaultSelectedId={menusMock[0].id} hasOnlyOneOpenMenu menu={menusMock} - menuOpenValue={[openedMenu]} - onMenuOpenChange={(v: string[]) => setOpenedMenu(v[0])} - onSelectedChange={(v?: string) => (v ? setOpenedMenu(v) : null)} - selectedValue={openedMenu} + menuOpenValue={[menusMock[openedMenu].id]} + onMenuOpenChange={() => setOpenedMenu(0)} + onSelectedChange={(value?: string) => { + const newIndex = menusMock.findIndex( + (menu) => menu.id === value, + ); + if (newIndex !== -1) { + setOpenedMenu(newIndex); + } + }} + selectedValue={menusMock[openedMenu].id} /> } sidebarToggleLabel={texts.toggleLabel} @@ -70,13 +170,27 @@ export function AgendaItemPage(): ReactElement { {texts.history} -
- { - tabsMock - .find((o) => o.id === openedMenu) - ?.tabs.find((t) => t.id === activeTab)?.content - } -
+
{tabContent()}
+ { + setModalConfig({ ...modalConfig, value: '' }); + close(); + }} + onClose={close} + onConfirm={handleConfirmModal} + opened={opened} + title={`Modify ${modalConfig.mode}`} + > + ) => + setModalConfig({ ...modalConfig, value: e.target.value }) + } + value={modalConfig.value} + /> +
diff --git a/packages/storybook-pages/src/Pages/AgendaPage/AgendaPage.tsx b/packages/storybook-pages/src/Pages/AgendaPage/AgendaPage.tsx index e28feb11..6a8e2f37 100644 --- a/packages/storybook-pages/src/Pages/AgendaPage/AgendaPage.tsx +++ b/packages/storybook-pages/src/Pages/AgendaPage/AgendaPage.tsx @@ -494,6 +494,7 @@ export function AgendaPage(): ReactElement { initialState={{ pagination: { pageIndex: 0, pageSize: 6 }, }} + maxVisibleActions={2} paginationProps={{ itemsPerPageAriaLabel: texts.resutsNbr, itemsPerPageOptions: [ @@ -504,7 +505,6 @@ export function AgendaPage(): ReactElement { { label: texts.displaySpecificResultNbr(15), value: 15 }, ], }} - rowActionNumber={2} /> ); diff --git a/packages/storybook-pages/src/Pages/BrowsingPage/BrowsingPage.mock.tsx b/packages/storybook-pages/src/Pages/BrowsingPage/BrowsingPage.mock.tsx index 804b4cd4..279ea9f5 100644 --- a/packages/storybook-pages/src/Pages/BrowsingPage/BrowsingPage.mock.tsx +++ b/packages/storybook-pages/src/Pages/BrowsingPage/BrowsingPage.mock.tsx @@ -159,6 +159,7 @@ export const tableProps: ITableGridViewTableProps = { selectedCountOfRowCountRowsSelected: `{selectedCount} fichier(s) sélectionnés`, }, manualPagination: false, + maxVisibleActions: 3, menuLabel: 'Autres actions', paginationProps: { itemsPerPageAriaLabel: 'Nombre de résultats par page', @@ -170,7 +171,6 @@ export const tableProps: ITableGridViewTableProps = { { label: 'Afficher 15 résultats', value: 15 }, ], }, - rowActionNumber: 3, rowCount: 6, }; diff --git a/packages/storybook-pages/src/Pages/ThemePage/Code.mdx b/packages/storybook-pages/src/Pages/ThemePage/Code.mdx new file mode 100644 index 00000000..47bce00e --- /dev/null +++ b/packages/storybook-pages/src/Pages/ThemePage/Code.mdx @@ -0,0 +1,13 @@ +import { Meta, Source } from '@storybook/blocks'; + +import * as ThemePageStory from './ThemePage.stories'; + +import ThemePage from './ThemePage?raw'; + + + +# Code + +Code of ThemePage page + + diff --git a/packages/storybook-pages/src/Pages/ThemePage/ThemePage.mock.tsx b/packages/storybook-pages/src/Pages/ThemePage/ThemePage.mock.tsx new file mode 100644 index 00000000..f77a36e6 --- /dev/null +++ b/packages/storybook-pages/src/Pages/ThemePage/ThemePage.mock.tsx @@ -0,0 +1,28 @@ +export const texts = { + applicationInProgress: 'EN COURS DE CANDIDATURE', + beforeYesterday: 'Avant-hier', + beingSigned: 'en cours de signature', + cardListUploadText: 'Ma carte de tiers payant', + currentlyUnderConsultation: 'en cours de consultation', + duringTheProcedure: 'en cours de procédure', + fence: 'cloturé', + finish: 'terminé/signé', + firstDecember2023: 'Le 01 décembre 2023', + fiveTeenDecember2023: 'Le 15 décembre 2023', + headerSubTitle: 'sous-titre', + headerTitle: 'Element de text', + heightDecember2023: 'Le 08 décembre 2023', + iconCardSubtitle: 'sous-titre', + iconCardTitle: 'Mes documents', + inProgress: 'En cours', + myBill: 'Ma facture du 12/12/23', + myResidenceCertificate: 'Mon attestation de domicile', + notifications: 'Notifications', + onBreak: 'En pause', + tenDecember2023: 'Le 10 décembre 2023', + threeHoursAgo: 'Il y a 3h', + twentyDecember2023: 'Le 20 décembre 2023', + twentySeven2023: 'Le 27 Novembre 2023', + valid: 'Validé', + viewMore: 'Voir plus', +}; diff --git a/packages/storybook-pages/src/Pages/ThemePage/ThemePage.stories.tsx b/packages/storybook-pages/src/Pages/ThemePage/ThemePage.stories.tsx new file mode 100644 index 00000000..3ed7f15b --- /dev/null +++ b/packages/storybook-pages/src/Pages/ThemePage/ThemePage.stories.tsx @@ -0,0 +1,21 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import { sharedMeta } from '../pages-story'; + +import { ThemePage as Cmp } from './ThemePage'; + +const meta = { + ...sharedMeta, + component: Cmp, + parameters: { + layout: 'fullscreen', + }, + title: '3-custom/Pages/ThemePage', +} satisfies Meta; + +export default meta; +type IStory = StoryObj; + +export const ThemePage: IStory = { + args: {}, +}; diff --git a/packages/storybook-pages/src/Pages/ThemePage/ThemePage.tsx b/packages/storybook-pages/src/Pages/ThemePage/ThemePage.tsx new file mode 100644 index 00000000..69421b74 --- /dev/null +++ b/packages/storybook-pages/src/Pages/ThemePage/ThemePage.tsx @@ -0,0 +1,140 @@ +'use client'; + +import type { MantineThemeOverride } from '@mantine/core'; +import type { ReactElement } from 'react'; + +import { Provider } from '@smile/haring-react-shared'; + +import { DashboardPage } from '../DashboardPage/DashboardPage'; + +const dsfrColors = { + blueFrance850: '#e3e3fd', + blueFrance925: '#e3e3fd', + blueFrance950: '#ececfe', + blueFrance975: '#f5f5fe', + blueFranceMain525: '#6a6af4', + blueFranceSun113: '#000091', + greenBourgeon: '#688839', + greenBourgeonSun: '#E3EDDB', + grey1000: '#ffffff', + grey425: '#666666', + grey850: '#dddddd', + grey900: '#cecece', + grey925: '#e5e5e5', + grey950: '#eeeeee', + grey975: '#f6f6f6', + greyMain525: '#7b7b7b', + redMarianne425: '#c9191e', + redMarianne850: '#fcbfbf', + redMarianne925: '#fddede', + redMarianne950: '#fee9e9', + redMarianne975: '#fef4f4', + redMarianneMain472: '#e1000f', + white: '#FFFFFF', + yellowTournesol: '#FFD100', + yellowTournesolSun: '#FFF4CF', +}; + +const dsfr: MantineThemeOverride = { + breakpoints: { + lg: '992px', + md: '768px', + sm: '576px', + xl: '1248px', + }, + colors: { + brand: [ + dsfrColors.blueFrance975, + dsfrColors.blueFrance950, + dsfrColors.blueFrance925, + dsfrColors.blueFrance850, + dsfrColors.blueFranceMain525, + dsfrColors.blueFranceMain525, + dsfrColors.blueFranceSun113, + dsfrColors.blueFranceMain525, + dsfrColors.blueFranceSun113, + dsfrColors.blueFranceSun113, + ], + green: [ + dsfrColors.greenBourgeonSun, + dsfrColors.greenBourgeonSun, + dsfrColors.greenBourgeonSun, + dsfrColors.greenBourgeonSun, + dsfrColors.greenBourgeon, + dsfrColors.greenBourgeon, + dsfrColors.greenBourgeon, + dsfrColors.greenBourgeon, + dsfrColors.greenBourgeon, + dsfrColors.greenBourgeon, + ], + grey: [ + dsfrColors.grey975, + dsfrColors.grey950, + dsfrColors.grey925, + dsfrColors.grey900, + dsfrColors.grey850, + dsfrColors.greyMain525, + dsfrColors.grey425, + dsfrColors.grey425, + dsfrColors.grey850, + dsfrColors.grey1000, + ], + red: [ + dsfrColors.redMarianne975, + dsfrColors.redMarianne950, + dsfrColors.redMarianne925, + dsfrColors.redMarianne850, + dsfrColors.redMarianneMain472, + dsfrColors.redMarianneMain472, + dsfrColors.redMarianne425, + dsfrColors.redMarianne425, + dsfrColors.redMarianne425, + dsfrColors.redMarianne425, + ], + yellow: [ + dsfrColors.yellowTournesolSun, + dsfrColors.yellowTournesolSun, + dsfrColors.yellowTournesolSun, + dsfrColors.yellowTournesolSun, + dsfrColors.yellowTournesol, + dsfrColors.yellowTournesol, + dsfrColors.yellowTournesol, + dsfrColors.yellowTournesol, + dsfrColors.yellowTournesol, + dsfrColors.yellowTournesol, + ], + }, + components: { + Switch: { + defaultProps: { + color: dsfrColors.blueFranceSun113, + }, + }, + }, + defaultRadius: 0, + fontFamily: 'Marianne, Arial, sans-serif', + headings: { + fontFamily: 'Marianne, Arial, sans-serif', + sizes: { + h1: { fontSize: '40px', lineHeight: '48px' }, + h2: { fontSize: '32px', lineHeight: '40px' }, + h3: { fontSize: '28px', lineHeight: '36px' }, + h4: { fontSize: '24px', lineHeight: '32px' }, + h5: { fontSize: '22px', lineHeight: '28px' }, + h6: { fontSize: '20px', lineHeight: '28px' }, + }, + }, + other: { + dsfrColors, + }, + primaryColor: 'brand', + primaryShade: 6, +}; + +export function ThemePage(): ReactElement { + return ( + + + + ); +}