From ae281cbc667c98bcf79d8945dcf41538884398fa Mon Sep 17 00:00:00 2001 From: Quentin Le Caignec Date: Mon, 6 Nov 2023 11:04:06 +0100 Subject: [PATCH 1/6] feat(redmine 1246968): added `TableGridView` --- .../src/types/actions.ts | 35 + .../src/Components/Table/Table.stories.tsx | 41 +- .../src/Components/Table/Table.tsx | 60 +- .../TableGridView/TableGridView.mock.tsx | 15 + .../TableGridView/TableGridView.stories.tsx | 17 + .../TableGridView/TableGridView.test.tsx | 17 + .../TableGridView/TableGridView.tsx | 132 ++ .../__snapshots__/TableGridView.test.tsx.snap | 2011 +++++++++++++++++ .../Components/ConfirmModal/ConfirmModal.tsx | 12 +- .../SwitchableView/SwitchableView.tsx | 2 +- .../Components/Thumbnail/Thumbnail.mock.tsx | 66 + .../Thumbnail/Thumbnail.stories.tsx | 60 +- .../Components/Thumbnail/Thumbnail.style.tsx | 3 +- .../Components/Thumbnail/Thumbnail.test.tsx | 2 +- .../src/Components/Thumbnail/Thumbnail.tsx | 57 +- .../__snapshots__/Thumbnail.test.tsx.snap | 51 + .../ThumbnailGrid/ThumbnailGrid.mock.ts | 31 + .../ThumbnailGrid/ThumbnailGrid.stories.tsx | 52 + .../ThumbnailGrid/ThumbnailGrid.test.tsx | 17 + .../ThumbnailGrid/ThumbnailGrid.tsx | 151 ++ .../__snapshots__/ThumbnailGrid.test.tsx.snap | 237 ++ .../react-front-kit/src/helpers/typeGuard.ts | 28 + packages/react-front-kit/src/index.tsx | 2 + 23 files changed, 2952 insertions(+), 147 deletions(-) create mode 100644 packages/react-front-kit-shared/src/types/actions.ts create mode 100644 packages/react-front-kit-table/src/Components/TableGridView/TableGridView.mock.tsx create mode 100644 packages/react-front-kit-table/src/Components/TableGridView/TableGridView.stories.tsx create mode 100644 packages/react-front-kit-table/src/Components/TableGridView/TableGridView.test.tsx create mode 100644 packages/react-front-kit-table/src/Components/TableGridView/TableGridView.tsx create mode 100644 packages/react-front-kit-table/src/Components/TableGridView/__snapshots__/TableGridView.test.tsx.snap create mode 100644 packages/react-front-kit/src/Components/Thumbnail/Thumbnail.mock.tsx create mode 100644 packages/react-front-kit/src/Components/ThumbnailGrid/ThumbnailGrid.mock.ts create mode 100644 packages/react-front-kit/src/Components/ThumbnailGrid/ThumbnailGrid.stories.tsx create mode 100644 packages/react-front-kit/src/Components/ThumbnailGrid/ThumbnailGrid.test.tsx create mode 100644 packages/react-front-kit/src/Components/ThumbnailGrid/ThumbnailGrid.tsx create mode 100644 packages/react-front-kit/src/Components/ThumbnailGrid/__snapshots__/ThumbnailGrid.test.tsx.snap create mode 100644 packages/react-front-kit/src/helpers/typeGuard.ts diff --git a/packages/react-front-kit-shared/src/types/actions.ts b/packages/react-front-kit-shared/src/types/actions.ts new file mode 100644 index 00000000..4ac140f6 --- /dev/null +++ b/packages/react-front-kit-shared/src/types/actions.ts @@ -0,0 +1,35 @@ +import type { MantineColor, ModalProps } from '@mantine/core'; +import type { ReactNode } from 'react'; + +export interface IConfirmModal extends Omit { + cancelColor?: MantineColor; + cancelLabel?: string; + confirmColor?: MantineColor; + confirmLabel?: string; + onCancel?: () => void; + onConfirm?: () => void; + title?: string; +} + +export interface IActionConfirmModalProps + extends Omit { + onCancel?: (item: Item) => false | void; + onClose?: () => void; + onConfirm?: (item: Item) => false | void; +} + +export interface IAction { + color?: string; + confirmModalProps?: IActionConfirmModalProps; + confirmation?: boolean; + icon: ReactNode; + id: number | string; + isItemAction?: boolean; + isMassAction?: boolean; + label: string; + onAction?: (item: Item) => void; +} + +export interface IConfirmAction extends IAction { + item: Item; +} diff --git a/packages/react-front-kit-table/src/Components/Table/Table.stories.tsx b/packages/react-front-kit-table/src/Components/Table/Table.stories.tsx index f1c9dde2..63613d42 100644 --- a/packages/react-front-kit-table/src/Components/Table/Table.stories.tsx +++ b/packages/react-front-kit-table/src/Components/Table/Table.stories.tsx @@ -28,40 +28,46 @@ export const Table: IStory = { actions: [ { icon: , + id: 'move', isMassAction: true, label: 'Move in tree', onAction: action('Move in tree'), }, { icon: , + id: 'open', label: 'Open document', onAction: action('Open document'), }, { icon: , + id: 'edit', label: 'Edit document', onAction: action('Edit document'), }, { icon: , + id: 'favorite', label: 'Add to favorites', onAction: action('Add to favorites'), }, { confirmation: true, icon: , + id: 'share', label: 'Share', onAction: action('Share'), }, { icon: , + id: 'download', label: 'Download', onAction: action('Download'), }, { color: 'red', confirmModalProps: { - children: 'Are you sur you want to delete ?', + children: 'Are you sure you want to delete ?', confirmColor: 'red', confirmLabel: 'Delete', onCancel: action('Delete:Cancel'), @@ -70,6 +76,7 @@ export const Table: IStory = { }, confirmation: true, icon: , + id: 'delete', isMassAction: true, label: 'Delete', onAction: action('Delete'), @@ -100,38 +107,52 @@ export const Table: IStory = { data: [ { creator: 'Valentin Perello', - date: '20/05/2022', + date: '16/05/2022', format: 'SVG', id: 1, - title: 'Doc test', + title: 'Doc test 1', }, { creator: 'Valentin Perello', - date: '20/05/2022', + date: '17/05/2022', format: 'PDF', id: 2, - title: 'Doc test', + title: 'Doc test 2', }, { creator: 'Valentin Perello', - date: '20/05/2022', + date: '18/05/2022', format: 'PDF', id: 3, - title: 'Doc test', + title: 'Doc test 3', }, { creator: 'Valentin Perello', - date: '20/05/2022', + date: '19/05/2022', format: 'PDF', id: 4, - title: 'Doc test', + title: 'Doc test 4', }, { creator: 'Valentin Perello', date: '20/05/2022', format: 'PDF', id: 5, - title: 'Doc test', + title: 'Doc test 5', + }, + { + creator: 'Valentin Perello', + date: '21/05/2022', + format: 'PDF', + id: 6, + title: 'Doc test 6', + }, + { + creator: 'Valentin Perello', + date: '22/05/2022', + format: 'PDF', + id: 7, + title: 'Doc test 7', }, ], rowActionNumber: 3, diff --git a/packages/react-front-kit-table/src/Components/Table/Table.tsx b/packages/react-front-kit-table/src/Components/Table/Table.tsx index 6b5d3c0c..cb50713a 100644 --- a/packages/react-front-kit-table/src/Components/Table/Table.tsx +++ b/packages/react-front-kit-table/src/Components/Table/Table.tsx @@ -6,12 +6,13 @@ 'use client'; import type { FloatingPosition } from '@mantine/core/lib/Floating'; +import type { IPaginationProps } from '@smile/react-front-kit'; import type { - IConfirmModalProps, - IPaginationProps, -} from '@smile/react-front-kit'; + IAction, + IConfirmAction, +} from '@smile/react-front-kit-shared/src/types/actions'; import type { MRT_Row, MRT_TableOptions } from 'mantine-react-table'; -import type { ReactElement, ReactNode } from 'react'; +import type { ReactElement } from 'react'; import { ActionIcon, Button, Flex, Menu, Space, Tooltip } from '@mantine/core'; import { @@ -35,35 +36,17 @@ import { useState } from 'react'; import { useStyles } from './Table.style'; -export interface IActionConfirmModalProps> - extends Omit< - IConfirmModalProps, - 'onCancel' | 'onClose' | 'onConfirm' | 'opened' - > { - onCancel?: (row: MRT_Row | MRT_Row[]) => false | void; - onClose?: () => void; - onConfirm?: (row: MRT_Row | MRT_Row[]) => false | void; -} - -export interface IAction> { - color?: string; - confirmModalProps?: IActionConfirmModalProps; - confirmation?: boolean; - icon: ReactNode; - isMassAction?: boolean; - isRowAction?: boolean; - label: string; - onAction?: (row: MRT_Row | MRT_Row[]) => void; -} +type ITableAction> = IAction< + MRT_Row | MRT_Row[] +>; -export interface IConfirmAction> - extends IAction { - row: MRT_Row | MRT_Row[]; -} +type ITableConfirmAction> = IConfirmAction< + MRT_Row | MRT_Row[] +>; export interface ITableProps> extends MRT_TableOptions { - actions?: IAction[]; + actions?: ITableAction[]; menuLabel?: string; paginationProps?: Partial; rowActionNumber?: number; @@ -94,14 +77,14 @@ export function Table>( const { enablePagination = true, manualPagination } = mantineTableProps; const { classes } = useStyles(); const [confirmAction, setConfirmAction] = - useState | null>(null); + useState | null>(null); const [openedMenuRowIndex, setOpenedMenuRowIndex] = useState( null, ); // Calculated values const massActions = actions.filter(({ isMassAction }) => isMassAction); - const rowActions = actions.filter(({ isRowAction = true }) => isRowAction); + const rowActions = actions.filter(({ isItemAction = true }) => isItemAction); const visibleRowActions = rowActions.slice(0, rowActionNumber); const menuRowActions = rowActions.slice(rowActionNumber); @@ -115,19 +98,19 @@ export function Table>( } function handleAction( - row: MRT_Row | MRT_Row[], - action: IAction, + item: MRT_Row | MRT_Row[], + action: ITableAction, ): void { if (action.confirmation) { - setConfirmAction({ ...action, row }); + setConfirmAction({ ...action, item }); } else { - action.onAction?.(row); + action.onAction?.(item); } } function handleCancel(): void { if ( - confirmAction?.confirmModalProps?.onCancel?.(confirmAction.row) !== false + confirmAction?.confirmModalProps?.onCancel?.(confirmAction.item) !== false ) { setConfirmAction(null); } @@ -140,10 +123,11 @@ export function Table>( function handleConfirm(): void { if ( - confirmAction?.confirmModalProps?.onConfirm?.(confirmAction.row) !== false + confirmAction?.confirmModalProps?.onConfirm?.(confirmAction.item) !== + false ) { setConfirmAction(null); - confirmAction?.onAction?.(confirmAction.row); + confirmAction?.onAction?.(confirmAction.item); } } diff --git a/packages/react-front-kit-table/src/Components/TableGridView/TableGridView.mock.tsx b/packages/react-front-kit-table/src/Components/TableGridView/TableGridView.mock.tsx new file mode 100644 index 00000000..e83dbe01 --- /dev/null +++ b/packages/react-front-kit-table/src/Components/TableGridView/TableGridView.mock.tsx @@ -0,0 +1,15 @@ +import type { ITableGridViewProps } from './TableGridView'; + +import { ThumbnailGrid } from '@smile/react-front-kit/src/Components/ThumbnailGrid/ThumbnailGrid.stories'; + +import { Table } from '../Table/Table.stories'; + +const { data, ...tableProps } = Table.args; +const { thumbnails, ...gridProps } = ThumbnailGrid.args; + +export const tableGridViewProps: ITableGridViewProps> = + { + data, + gridProps: { ...gridProps, idFieldName: 'id', labelFieldName: 'title' }, + tableProps, + }; diff --git a/packages/react-front-kit-table/src/Components/TableGridView/TableGridView.stories.tsx b/packages/react-front-kit-table/src/Components/TableGridView/TableGridView.stories.tsx new file mode 100644 index 00000000..4e678cd6 --- /dev/null +++ b/packages/react-front-kit-table/src/Components/TableGridView/TableGridView.stories.tsx @@ -0,0 +1,17 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import { TableGridView as Cmp } from './TableGridView'; +import { tableGridViewProps } from './TableGridView.mock'; + +const meta = { + component: Cmp, + tags: ['autodocs'], + title: '3-custom/Components/TableGridView', +} satisfies Meta; + +export default meta; +type IStory = StoryObj; + +export const TableGridView: IStory = { + args: { ...tableGridViewProps }, +}; diff --git a/packages/react-front-kit-table/src/Components/TableGridView/TableGridView.test.tsx b/packages/react-front-kit-table/src/Components/TableGridView/TableGridView.test.tsx new file mode 100644 index 00000000..ec044ffa --- /dev/null +++ b/packages/react-front-kit-table/src/Components/TableGridView/TableGridView.test.tsx @@ -0,0 +1,17 @@ +import { renderWithProviders } from '@smile/react-front-kit-shared/src/test-utils'; + +import { TableGridView } from './TableGridView'; +import { tableGridViewProps } from './TableGridView.mock'; + +describe('TableGridView', () => { + beforeEach(() => { + // Prevent mantine random ID + Math.random = () => 0.42; + }); + it('matches snapshot', () => { + const { container } = renderWithProviders( + , + ); + expect(container).toMatchSnapshot(); + }); +}); diff --git a/packages/react-front-kit-table/src/Components/TableGridView/TableGridView.tsx b/packages/react-front-kit-table/src/Components/TableGridView/TableGridView.tsx new file mode 100644 index 00000000..aa816e10 --- /dev/null +++ b/packages/react-front-kit-table/src/Components/TableGridView/TableGridView.tsx @@ -0,0 +1,132 @@ +import type { ITableProps } from '../Table/Table'; +import type { IThumbnail, IThumbnailGridProps } from '@smile/react-front-kit'; +import type { + IDataView, + ISwitchableViewProps, +} from '@smile/react-front-kit/src/Components/SwitchableView/SwitchableView'; +import type { MRT_RowSelectionState } from 'mantine-react-table'; +import type { ReactElement } from 'react'; + +import { createStyles } from '@mantine/styles'; +import { ListBullets, SquaresFour } from '@phosphor-icons/react'; +import { isNotNullNorEmpty } from '@smile/react-front-kit'; +import defaultImage from '@smile/react-front-kit/assets/defaultImage.jpg'; +import { SwitchableView, ThumbnailGrid } from '@smile/react-front-kit/src'; +import { typeGuard } from '@smile/react-front-kit/src/helpers/typeGuard'; +import { useState } from 'react'; + +import { Table } from '../Table/Table'; + +const useStyles = createStyles(() => ({ + tablePaper: { + border: 'none', + borderRadius: '4px', + }, +})); + +export interface ITableGridViewGridProps + extends Omit { + idFieldName: string; + imageFieldName?: string; + labelFieldName: string; +} + +export interface ITableGridViewProps> + extends Omit { + data: Data[]; + defaultView?: 'grid' | 'table'; + gridProps: ITableGridViewGridProps; + tableProps: Omit, 'data'>; +} + +export function TableGridView>( + props: ITableGridViewProps, +): ReactElement { + const { + data, + defaultView = 'table', + gridProps, + tableProps, + ...switchableViewProps + } = props; + const { idFieldName, labelFieldName, imageFieldName, ...otherGridProps } = + gridProps; + const [rowSelection, setRowSelection] = useState({}); + const { classes } = useStyles(); + + const extendedTableProps: ITableProps = { + data, + enableBottomToolbar: false, + enableRowSelection: true, + mantinePaperProps: { + className: classes.tablePaper, + }, + onRowSelectionChange: setRowSelection, + state: { rowSelection }, + ...tableProps, + }; + + const thumbnails: IThumbnail[] = data + .map((item, index) => { + const id = item[gridProps.idFieldName]; + const label = item[gridProps.labelFieldName]; + const image = ( + gridProps.imageFieldName !== undefined + ? item[gridProps.imageFieldName] + : defaultImage + ) as string; + const isSelected = Object.entries(rowSelection) + .map((entry) => (entry[1] ? entry[0] : null)) + .includes(index.toString()); + + if ( + (typeGuard(id, 'number') || typeGuard(id, 'string')) && + typeGuard(label, 'string') + ) { + const thumbnail: IThumbnail = { + ...item, + id, + image, + label, + selected: isSelected, + }; + return thumbnail; + } + return null; + }) + .filter(isNotNullNorEmpty); + + function handleThumbnailSelect(index: number): void { + const newRowSelection = { ...rowSelection }; + newRowSelection[index] = !newRowSelection[index]; + setRowSelection(newRowSelection); + } + + const views: IDataView[] = [ + { + dataView: , + label: , + value: 'table', + }, + { + dataView: ( + handleThumbnailSelect(i)} + /> + ), + label: , + value: 'grid', + }, + ]; + + return ( + + ); +} diff --git a/packages/react-front-kit-table/src/Components/TableGridView/__snapshots__/TableGridView.test.tsx.snap b/packages/react-front-kit-table/src/Components/TableGridView/__snapshots__/TableGridView.test.tsx.snap new file mode 100644 index 00000000..2f647731 --- /dev/null +++ b/packages/react-front-kit-table/src/Components/TableGridView/__snapshots__/TableGridView.test.tsx.snap @@ -0,0 +1,2011 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`TableGridView matches snapshot 1`] = ` +
+
+
+ +
+ +
+ + +
+
+ + +
+
+
+
+
+
+
+
+ +
+
+
+ + +
+
+
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+ +
+
+ +
+
+ +
+ +
+
+ +
+
+ +
+ +
+
+ +
+
+ +
+ +
+
+ +
+
+ +
+ +
+
+ +
+
+
+ +
+
+
+ + + + +
+
+
+
+ +
+ 1 + + SVG + + Doc test 1 + + Valentin Perello + + 16/05/2022 + +
+ + + + +
+
+ +
+
+
+ + + + +
+
+
+
+ +
+ 2 + + PDF + + Doc test 2 + + Valentin Perello + + 17/05/2022 + +
+ + + + +
+
+ +
+
+
+ + + + +
+
+
+
+ +
+ 3 + + PDF + + Doc test 3 + + Valentin Perello + + 18/05/2022 + +
+ + + + +
+
+ +
+
+
+ + + + +
+
+
+
+ +
+ 4 + + PDF + + Doc test 4 + + Valentin Perello + + 19/05/2022 + +
+ + + + +
+
+ +
+
+
+ + + + +
+
+
+
+ +
+ 5 + + PDF + + Doc test 5 + + Valentin Perello + + 20/05/2022 + +
+ + + + +
+
+ +
+
+
+ + + + +
+
+
+
+ +
+ 6 + + PDF + + Doc test 6 + + Valentin Perello + + 21/05/2022 + +
+ + + + +
+
+ +
+
+
+ + + + +
+
+
+
+ +
+ 7 + + PDF + + Doc test 7 + + Valentin Perello + + 22/05/2022 + +
+ + + + +
+
+ + + + + +`; diff --git a/packages/react-front-kit/src/Components/ConfirmModal/ConfirmModal.tsx b/packages/react-front-kit/src/Components/ConfirmModal/ConfirmModal.tsx index ff2e1042..5b8e710a 100644 --- a/packages/react-front-kit/src/Components/ConfirmModal/ConfirmModal.tsx +++ b/packages/react-front-kit/src/Components/ConfirmModal/ConfirmModal.tsx @@ -1,21 +1,13 @@ 'use client'; -import type { MantineColor, ModalProps } from '@mantine/core'; +import type { IConfirmModal } from '@smile/react-front-kit-shared/src/types/actions'; import type { ReactElement } from 'react'; import { Button, Modal } from '@mantine/core'; import { useStyles } from './ConfirmModal.style'; -export interface IConfirmModalProps extends ModalProps { - cancelColor?: MantineColor; - cancelLabel?: string; - confirmColor?: MantineColor; - confirmLabel?: string; - onCancel?: () => void; - onConfirm?: () => void; - title?: string; -} +type IConfirmModalProps = IConfirmModal; export function ConfirmModal(props: IConfirmModalProps): ReactElement { const { diff --git a/packages/react-front-kit/src/Components/SwitchableView/SwitchableView.tsx b/packages/react-front-kit/src/Components/SwitchableView/SwitchableView.tsx index f6ffd2e7..2afa1287 100644 --- a/packages/react-front-kit/src/Components/SwitchableView/SwitchableView.tsx +++ b/packages/react-front-kit/src/Components/SwitchableView/SwitchableView.tsx @@ -45,7 +45,7 @@ export interface IDataView extends SegmentedControlItem { dataView: ReactNode; } -interface ISwitchableViewProps extends PaperProps { +export interface ISwitchableViewProps extends PaperProps { /** Default index of the active view */ defaultValue?: number; /** Called when active view changes */ diff --git a/packages/react-front-kit/src/Components/Thumbnail/Thumbnail.mock.tsx b/packages/react-front-kit/src/Components/Thumbnail/Thumbnail.mock.tsx new file mode 100644 index 00000000..285b153e --- /dev/null +++ b/packages/react-front-kit/src/Components/Thumbnail/Thumbnail.mock.tsx @@ -0,0 +1,66 @@ +import type { IThumbnailAction } from './Thumbnail'; + +import { + DownloadSimple, + Eye, + PencilSimple, + ShareNetwork, + Star, + Trash, +} from '@phosphor-icons/react'; +import { FolderMove } from '@smile/react-front-kit-shared'; +import { action } from '@storybook/addon-actions'; + +export const thumbnailActions: IThumbnailAction[] = [ + { + icon: , + id: 'move', + label: 'Move in tree', + onAction: action('Move in tree'), + }, + { + icon: , + id: 'open', + label: 'Open document', + onAction: action('Open document'), + }, + { + icon: , + id: 'edit', + label: 'Edit document', + onAction: action('Edit document'), + }, + { + icon: , + id: 'favorite', + label: 'Add to favorites', + onAction: action('Add to favorites'), + }, + { + icon: , + id: 'share', + label: 'Share', + onAction: action('Share'), + }, + { + icon: , + id: 'download', + label: 'Download', + onAction: action('Download'), + }, + { + color: 'red', + confirmModalProps: { + cancelLabel: 'Abort', + children:

Are you sure ?

, + confirmColor: 'red', + confirmLabel: 'Remove', + title: 'Remove File', + }, + confirmation: true, + icon: , + id: 'delete', + label: 'Delete', + onAction: action('Delete'), + }, +]; diff --git a/packages/react-front-kit/src/Components/Thumbnail/Thumbnail.stories.tsx b/packages/react-front-kit/src/Components/Thumbnail/Thumbnail.stories.tsx index 6b259858..896445c1 100644 --- a/packages/react-front-kit/src/Components/Thumbnail/Thumbnail.stories.tsx +++ b/packages/react-front-kit/src/Components/Thumbnail/Thumbnail.stories.tsx @@ -1,17 +1,7 @@ import type { Meta, StoryObj } from '@storybook/react'; -import { - DownloadSimple, - Eye, - PencilSimple, - ShareNetwork, - Star, - Trash, -} from '@phosphor-icons/react'; -import { FolderMove } from '@smile/react-front-kit-shared'; -import { action } from '@storybook/addon-actions'; - import { Thumbnail as Cmp } from './Thumbnail'; +import { thumbnailActions } from './Thumbnail.mock'; const meta = { component: Cmp, @@ -24,53 +14,9 @@ type IStory = StoryObj; export const Thumbnail: IStory = { args: { - action: [ - { - icon: , - label: 'Move in tree', - onAction: action('Move in tree'), - }, - { - icon: , - label: 'Open document', - onAction: action('Open document'), - }, - { - icon: , - label: 'Edit document', - onAction: action('Edit document'), - }, - { - icon: , - label: 'Add to favorites', - onAction: action('Add to favorites'), - }, - { - icon: , - label: 'Share', - onAction: action('Share'), - }, - { - icon: , - label: 'Download', - onAction: action('Download'), - }, - { - color: 'red', - confirmModalProps: { - cancelLabel: 'Abord', - children:

Are you sur ?

, - confirmColor: 'red', - confirmLabel: 'Remove', - title: 'Remove File', - }, - confirmation: true, - icon: , - label: 'Delete', - onAction: action('Delete'), - }, - ], + actions: thumbnailActions, iconType: 'PDF', + id: '1', label: 'Debit_Suivi_PREV', selected: false, }, diff --git a/packages/react-front-kit/src/Components/Thumbnail/Thumbnail.style.tsx b/packages/react-front-kit/src/Components/Thumbnail/Thumbnail.style.tsx index 80b3190a..d47f02a7 100644 --- a/packages/react-front-kit/src/Components/Thumbnail/Thumbnail.style.tsx +++ b/packages/react-front-kit/src/Components/Thumbnail/Thumbnail.style.tsx @@ -50,7 +50,8 @@ export const useStyles = createStyles((theme) => ({ root: { background: theme.colors.gray[1], borderRadius: '16px', - heigh: 'auto', + cursor: 'pointer', + height: 'auto', padding: '16px', width: 'auto', }, diff --git a/packages/react-front-kit/src/Components/Thumbnail/Thumbnail.test.tsx b/packages/react-front-kit/src/Components/Thumbnail/Thumbnail.test.tsx index f8d32278..a16ebb00 100644 --- a/packages/react-front-kit/src/Components/Thumbnail/Thumbnail.test.tsx +++ b/packages/react-front-kit/src/Components/Thumbnail/Thumbnail.test.tsx @@ -4,7 +4,7 @@ import { Thumbnail } from './Thumbnail'; describe('Thumbnail', () => { it('matches snapshot', () => { - const { container } = renderWithProviders(); + const { container } = renderWithProviders(); expect(container).toMatchSnapshot(); }); }); diff --git a/packages/react-front-kit/src/Components/Thumbnail/Thumbnail.tsx b/packages/react-front-kit/src/Components/Thumbnail/Thumbnail.tsx index cf7c4a1f..7affaaf0 100644 --- a/packages/react-front-kit/src/Components/Thumbnail/Thumbnail.tsx +++ b/packages/react-front-kit/src/Components/Thumbnail/Thumbnail.tsx @@ -1,7 +1,10 @@ 'use client'; -import type { IConfirmModalProps } from '../ConfirmModal/ConfirmModal'; -import type { ReactElement, ReactNode } from 'react'; +import type { + IAction, + IActionConfirmModalProps, +} from '@smile/react-front-kit-shared/src/types/actions'; +import type { ReactElement } from 'react'; import { ActionIcon, @@ -21,35 +24,28 @@ import { ConfirmModal } from '../ConfirmModal/ConfirmModal'; import { useStyles } from './Thumbnail.style'; -export type IActionConfirmModalProps = Omit< - IConfirmModalProps, - 'onClose' | 'opened' ->; - -export interface IThumbnailAction { - color?: string; - confirmModalProps?: IActionConfirmModalProps; - confirmation?: boolean; - icon: ReactNode; - label: string; - onAction: () => void; -} - -export interface IThumbnailProps { - action?: IThumbnailAction[]; +export interface IThumbnail extends Record { iconType?: string; + id: number | string; image?: string; label?: string; onClick?: () => void; selected?: boolean; } +export type IThumbnailAction = IAction; + +export interface IThumbnailProps extends IThumbnail { + actions?: IThumbnailAction[]; +} + export function Thumbnail(props: IThumbnailProps): ReactElement { const { classes } = useStyles(); const theme = useMantineTheme(); const { - action = [], + actions = [], iconType, + id, image = defaultImage, label, onClick, @@ -57,9 +53,9 @@ export function Thumbnail(props: IThumbnailProps): ReactElement { } = props; const [confirmAction, setConfirmAction] = - useState(null); + useState | null>(null); - function clearconfirmAction(): void { + function clearConfirmAction(): void { setConfirmAction(null); } @@ -75,7 +71,7 @@ export function Thumbnail(props: IThumbnailProps): ReactElement { children: action.confirmModalProps?.children, confirmColor: action.confirmModalProps?.confirmColor, confirmLabel: action.confirmModalProps?.confirmLabel, - onConfirm: action.onAction, + onConfirm: () => action.onAction?.(props), title: action.confirmModalProps?.title, }); } @@ -84,15 +80,16 @@ export function Thumbnail(props: IThumbnailProps): ReactElement { if (action.confirmation) { setModal(action); } else { - action.onAction(); + action.onAction?.(props); } } function handleClose(): void { - clearconfirmAction(); + clearConfirmAction(); } - function handleModalButton(onAction?: () => void): void { - onAction && onAction(); + + function handleModalButton(onAction?: (item: IThumbnail) => void): void { + onAction && onAction(props); handleClose(); } @@ -101,6 +98,7 @@ export function Thumbnail(props: IThumbnailProps): ReactElement { @@ -118,13 +116,14 @@ export function Thumbnail(props: IThumbnailProps): ReactElement {
- {action.length > 0 && ( + {actions.length > 0 && ( e.stopPropagation()} radius={4} type="button" > @@ -137,8 +136,8 @@ export function Thumbnail(props: IThumbnailProps): ReactElement {
- - {action.map((action, index) => ( + e.stopPropagation()}> + {actions.map((action, index) => ( +
+
+
+ + + +

+

+
+
+
+
+
+ +
+
+
+
+
+`; + +exports[`ThumbnailGrid matches snapshot 1`] = `
= { + actions: thumbnailActions, + iconType: 'PDF', +}; + +export const thumbnails: IThumbnail[] = [ + { + id: '1', + label: 'Debit_Suivi_PREV', + ...baseThumbnail, + }, + { + id: '2', + label: 'Debit_Suivi_PREV_2', + ...baseThumbnail, + selected: true, + }, + { + id: '3', + label: 'Debit_Suivi_PREV_3', + ...baseThumbnail, + }, +]; + +export function getModalTitle(n: number): string { + return `Remove ${n} file${n > 1 ? 's' : ''}`; +} diff --git a/packages/react-front-kit/src/Components/ThumbnailGrid/ThumbnailGrid.stories.tsx b/packages/react-front-kit/src/Components/ThumbnailGrid/ThumbnailGrid.stories.tsx new file mode 100644 index 00000000..2efe723f --- /dev/null +++ b/packages/react-front-kit/src/Components/ThumbnailGrid/ThumbnailGrid.stories.tsx @@ -0,0 +1,52 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import { Trash } from '@phosphor-icons/react'; +import { FolderMove } from '@smile/react-front-kit-shared'; +import { action } from '@storybook/addon-actions'; + +import { ThumbnailGrid as Cmp } from './ThumbnailGrid'; +import { getModalTitle, thumbnails } from './ThumbnailGrid.mock'; + +const meta = { + component: Cmp, + tags: ['autodocs'], + title: '3-custom/Components/ThumbnailGrid', +} satisfies Meta; + +export default meta; +type IStory = StoryObj; + +export const ThumbnailGrid: IStory = { + args: { + cols: 5, + gridActions: [ + { + icon: , + id: 'move', + label: 'Move in tree', + onAction: action('Move selected in tree'), + }, + { + color: 'red', + confirmModalProps: { + cancelLabel: 'Abort', + children:

Are you sure ?

, + confirmColor: 'red', + confirmLabel: 'Remove', + title: getModalTitle( + thumbnails.filter((thumbnail) => thumbnail.selected).length, + ), + }, + confirmation: true, + icon: , + id: 'delete', + label: 'Delete', + onAction: action('Delete selected'), + }, + ], + onThumbnailClick: () => action('Thumbnail clicked'), + spacing: 25, + thumbnails, + verticalSpacing: 25, + }, +}; diff --git a/packages/react-front-kit/src/Components/ThumbnailGrid/ThumbnailGrid.test.tsx b/packages/react-front-kit/src/Components/ThumbnailGrid/ThumbnailGrid.test.tsx new file mode 100644 index 00000000..c3c9c385 --- /dev/null +++ b/packages/react-front-kit/src/Components/ThumbnailGrid/ThumbnailGrid.test.tsx @@ -0,0 +1,17 @@ +import { renderWithProviders } from '@smile/react-front-kit-shared/src/test-utils'; + +import { ThumbnailGrid } from './ThumbnailGrid'; +import { thumbnails } from './ThumbnailGrid.mock'; + +describe('ThumbnailGrid', () => { + beforeEach(() => { + // Prevent mantine random ID + Math.random = () => 0.42; + }); + it('matches snapshot', () => { + const { container } = renderWithProviders( + , + ); + expect(container).toMatchSnapshot(); + }); +}); diff --git a/packages/react-front-kit/src/Components/ThumbnailGrid/ThumbnailGrid.tsx b/packages/react-front-kit/src/Components/ThumbnailGrid/ThumbnailGrid.tsx new file mode 100644 index 00000000..832925a4 --- /dev/null +++ b/packages/react-front-kit/src/Components/ThumbnailGrid/ThumbnailGrid.tsx @@ -0,0 +1,151 @@ +'use client'; + +import type { IThumbnail } from '../Thumbnail/Thumbnail'; +import type { SimpleGridProps } from '@mantine/core'; +import type { + IAction, + IActionConfirmModalProps, +} from '@smile/react-front-kit-shared/src/types/actions'; +import type { ReactElement } from 'react'; + +import { Button, Group, SimpleGrid } from '@mantine/core'; +import { createStyles } from '@mantine/styles'; +import { useState } from 'react'; + +import { ConfirmModal } from '../ConfirmModal/ConfirmModal'; +import { Thumbnail } from '../Thumbnail/Thumbnail'; + +const useStyles = createStyles((theme) => ({ + container: { + display: 'flex', + flexDirection: 'column', + gap: 24, + }, + topBar: { + alignItems: 'center', + background: theme.fn.primaryColor(), + borderRadius: 4, + color: 'white', + display: 'inline-flex', + justifyContent: 'space-between', + padding: '16px 24px', + }, +})); + +type IGridAction = IAction; +type IGridActionConfirmModalProps = IActionConfirmModalProps; + +export interface IThumbnailGridProps extends SimpleGridProps { + gridActions?: IGridAction[]; + onThumbnailClick?: (item: IThumbnail, index: number) => void; + selectedElementsText?: (numberOfSelectedElements: number) => string; + thumbnails: IThumbnail[]; +} + +/** Additional props will be forwarded to the [Mantine SimpleGrid component](https://mantine.dev/core/simple-grid) */ +export function ThumbnailGrid(props: IThumbnailGridProps): ReactElement { + function defaultSelectedElementsText(n: number): string { + return `${n} selected file${n > 1 ? 's' : ''}`; + } + + const { + gridActions = [], + onThumbnailClick, + selectedElementsText = defaultSelectedElementsText, + thumbnails, + ...simpleGridProps + } = props; + + const selectedElements = thumbnails.filter((thumbnail) => thumbnail.selected); + const numberOfSelectedElements = selectedElements.length; + const [confirmAction, setConfirmAction] = + useState(null); + + const { classes } = useStyles(); + + function handleSelect(index: number): void { + onThumbnailClick?.(thumbnails[index], index); + } + + function setModal(action: IGridAction): void { + setConfirmAction({ + cancelColor: action.confirmModalProps?.cancelColor, + cancelLabel: action.confirmModalProps?.cancelLabel, + children: action.confirmModalProps?.children, + confirmColor: action.confirmModalProps?.confirmColor, + confirmLabel: action.confirmModalProps?.confirmLabel, + onConfirm: () => action.onAction?.(selectedElements), + title: action.confirmModalProps?.title, + }); + } + + function handleGridAction(action: IGridAction): void { + if (action.confirmation) { + setModal(action); + } else { + action.onAction?.(selectedElements); + } + } + + function clearConfirmAction(): void { + setConfirmAction(null); + } + + function handleClose(): void { + clearConfirmAction(); + } + + function handleModalButton(onAction?: (item: IThumbnail[]) => void): void { + onAction && onAction(selectedElements); + handleClose(); + } + + return ( + <> +
+ {numberOfSelectedElements > 0 && ( +
+ {selectedElementsText(numberOfSelectedElements)} + {gridActions.length > 0 && ( + + {gridActions.map((action) => ( + + ))} + + )} +
+ )} + + {thumbnails.map((thumbnail, index) => ( + handleSelect(index)} + {...thumbnail} + /> + ))} + +
+ + handleModalButton(confirmAction?.onCancel && confirmAction.onCancel) + } + onClose={handleClose} + onConfirm={() => + handleModalButton(confirmAction?.onConfirm && confirmAction.onConfirm) + } + opened={Boolean(confirmAction)} + > + {confirmAction?.children} + + + ); +} diff --git a/packages/react-front-kit/src/Components/ThumbnailGrid/__snapshots__/ThumbnailGrid.test.tsx.snap b/packages/react-front-kit/src/Components/ThumbnailGrid/__snapshots__/ThumbnailGrid.test.tsx.snap new file mode 100644 index 00000000..d7ab6c41 --- /dev/null +++ b/packages/react-front-kit/src/Components/ThumbnailGrid/__snapshots__/ThumbnailGrid.test.tsx.snap @@ -0,0 +1,237 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`ThumbnailGrid matches snapshot 1`] = ` +
+
+
+ + 1 selected file + +
+
+
+
+
+ + + +

+ Debit_Suivi_PREV +

+
+
+ +
+
+
+
+
+ +
+
+
+
+
+
+
+ + + +

+ Debit_Suivi_PREV_2 +

+
+
+ +
+
+
+
+
+ +
+
+
+
+
+
+
+ + + +

+ Debit_Suivi_PREV_3 +

+
+
+ +
+
+
+
+
+ +
+
+
+
+
+
+
+`; diff --git a/packages/react-front-kit/src/helpers/typeGuard.ts b/packages/react-front-kit/src/helpers/typeGuard.ts new file mode 100644 index 00000000..25b432a5 --- /dev/null +++ b/packages/react-front-kit/src/helpers/typeGuard.ts @@ -0,0 +1,28 @@ +export interface ITypeMap { + boolean: boolean; + number: number; + string: string; +} + +export type IPrimitiveOrConstructor = // 'string' | 'number' | 'boolean' | constructor + keyof ITypeMap | (new (...args: unknown[]) => unknown); + +// infer the guarded type from a specific case of PrimitiveOrConstructor +export type IGuardedType = T extends new ( + ...args: unknown[] +) => infer U + ? U + : T extends keyof ITypeMap + ? ITypeMap[T] + : never; + +export function typeGuard( + o: unknown, + className: T, +): o is IGuardedType { + const localPrimitiveOrConstructor: IPrimitiveOrConstructor = className; + if (typeof localPrimitiveOrConstructor === 'string') { + return typeof o === localPrimitiveOrConstructor; + } + return o instanceof localPrimitiveOrConstructor; +} diff --git a/packages/react-front-kit/src/index.tsx b/packages/react-front-kit/src/index.tsx index 9818e859..a20faa6c 100644 --- a/packages/react-front-kit/src/index.tsx +++ b/packages/react-front-kit/src/index.tsx @@ -13,7 +13,9 @@ export * from './Components/Header/Header'; export * from './Components/HeaderSearch/HeaderSearch'; export * from './Components/Pagination/Pagination'; export * from './Components/SidebarMenu/SidebarMenu'; +export * from './Components/SwitchableView/SwitchableView'; export * from './Components/Thumbnail/Thumbnail'; +export * from './Components/ThumbnailGrid/ThumbnailGrid'; // layout exports export * from './Layouts/FoldableColumnLayout/FoldableColumnLayout'; // page exports From 6ad8ffb8d169cb96f8b59e20e72270896ac08c8d Mon Sep 17 00:00:00 2001 From: Quentin Le Caignec Date: Mon, 13 Nov 2023 16:27:33 +0100 Subject: [PATCH 2/6] feat: pull request --- .changeset/giant-planes-juggle.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .changeset/giant-planes-juggle.md diff --git a/.changeset/giant-planes-juggle.md b/.changeset/giant-planes-juggle.md new file mode 100644 index 00000000..b29cfbb1 --- /dev/null +++ b/.changeset/giant-planes-juggle.md @@ -0,0 +1,7 @@ +--- +'@smile/react-front-kit-shared': minor +'@smile/react-front-kit-table': minor +'@smile/react-front-kit': minor +--- + +Reworked action/confirm types and moved then in `react-front-kit-shared` then refactored types in `Table`, `Thumbnail` and `ConfirmModal` , Added `ThumbnailGrid` and `TableGridView` components, From 003722f9eb264b8109f052c5043ae2e5f0cc572a Mon Sep 17 00:00:00 2001 From: Quentin Le Caignec Date: Mon, 13 Nov 2023 16:32:08 +0100 Subject: [PATCH 3/6] feat: tests --- .../__snapshots__/TableGridView.test.tsx.snap | 21 ++++++++ .../__snapshots__/Thumbnail.test.tsx.snap | 50 ------------------- 2 files changed, 21 insertions(+), 50 deletions(-) diff --git a/packages/react-front-kit-table/src/Components/TableGridView/__snapshots__/TableGridView.test.tsx.snap b/packages/react-front-kit-table/src/Components/TableGridView/__snapshots__/TableGridView.test.tsx.snap index 2f647731..32d5cb5b 100644 --- a/packages/react-front-kit-table/src/Components/TableGridView/__snapshots__/TableGridView.test.tsx.snap +++ b/packages/react-front-kit-table/src/Components/TableGridView/__snapshots__/TableGridView.test.tsx.snap @@ -2005,6 +2005,27 @@ exports[`TableGridView matches snapshot 1`] = `
+
+
+
+ +
+
diff --git a/packages/react-front-kit/src/Components/Thumbnail/__snapshots__/Thumbnail.test.tsx.snap b/packages/react-front-kit/src/Components/Thumbnail/__snapshots__/Thumbnail.test.tsx.snap index 57a169da..4e2e51c8 100644 --- a/packages/react-front-kit/src/Components/Thumbnail/__snapshots__/Thumbnail.test.tsx.snap +++ b/packages/react-front-kit/src/Components/Thumbnail/__snapshots__/Thumbnail.test.tsx.snap @@ -50,53 +50,3 @@ exports[`Thumbnail matches snapshot 1`] = ` `; - -exports[`ThumbnailGrid matches snapshot 1`] = ` -
-
-
-
- - - -

-

-
-
-
-
-
- -
-
-
-
-
-`; From 1bcb9bf0d3522bd8b95258778615d8caa958b74d Mon Sep 17 00:00:00 2001 From: Quentin Le Caignec Date: Tue, 14 Nov 2023 14:41:53 +0100 Subject: [PATCH 4/6] fix: code review --- .../react-front-kit-shared/src/types/index.ts | 1 + .../src/Components/Table/Table.mock.tsx | 146 ++++++++++++++++++ .../src/Components/Table/Table.stories.tsx | 144 +---------------- .../src/Components/Table/Table.tsx | 5 +- .../TableGridView/TableGridView.mock.tsx | 62 +++++++- .../TableGridView/TableGridView.tsx | 29 ++-- .../ThumbnailGrid/ThumbnailGrid.mock.ts | 31 ---- .../ThumbnailGrid/ThumbnailGrid.mock.tsx | 63 ++++++++ .../ThumbnailGrid/ThumbnailGrid.stories.tsx | 39 +---- .../ThumbnailGrid/ThumbnailGrid.tsx | 20 +-- packages/react-front-kit/src/helpers/index.ts | 1 + 11 files changed, 298 insertions(+), 243 deletions(-) create mode 100644 packages/react-front-kit-table/src/Components/Table/Table.mock.tsx delete mode 100644 packages/react-front-kit/src/Components/ThumbnailGrid/ThumbnailGrid.mock.ts create mode 100644 packages/react-front-kit/src/Components/ThumbnailGrid/ThumbnailGrid.mock.tsx diff --git a/packages/react-front-kit-shared/src/types/index.ts b/packages/react-front-kit-shared/src/types/index.ts index 5f30ef38..b914401a 100644 --- a/packages/react-front-kit-shared/src/types/index.ts +++ b/packages/react-front-kit-shared/src/types/index.ts @@ -1 +1,2 @@ +export * from './actions'; export * from './options'; diff --git a/packages/react-front-kit-table/src/Components/Table/Table.mock.tsx b/packages/react-front-kit-table/src/Components/Table/Table.mock.tsx new file mode 100644 index 00000000..e192cfe9 --- /dev/null +++ b/packages/react-front-kit-table/src/Components/Table/Table.mock.tsx @@ -0,0 +1,146 @@ +import type { ITableProps } from './Table'; + +import { + DownloadSimple, + Eye, + PencilSimple, + ShareNetwork, + Star, + Trash, +} from '@phosphor-icons/react'; +import { FolderMove } from '@smile/react-front-kit-shared'; +import { action } from '@storybook/addon-actions'; + +export const tableMock: ITableProps> = { + actions: [ + { + icon: , + id: 'move', + isMassAction: true, + label: 'Move in tree', + onAction: action('Move in tree'), + }, + { + icon: , + id: 'open', + label: 'Open document', + onAction: action('Open document'), + }, + { + icon: , + id: 'edit', + label: 'Edit document', + onAction: action('Edit document'), + }, + { + icon: , + id: 'favorite', + label: 'Add to favorites', + onAction: action('Add to favorites'), + }, + { + confirmation: true, + icon: , + id: 'share', + label: 'Share', + onAction: action('Share'), + }, + { + icon: , + id: 'download', + label: 'Download', + onAction: action('Download'), + }, + { + color: 'red', + confirmModalProps: { + children: 'Are you sure you want to delete ?', + confirmColor: 'red', + confirmLabel: 'Delete', + onCancel: action('Delete:Cancel'), + onConfirm: action('Delete:Confirm'), + title: 'Delete ?', + }, + confirmation: true, + icon: , + id: 'delete', + isMassAction: true, + label: 'Delete', + onAction: action('Delete'), + }, + ], + columns: [ + { + accessorKey: 'id', + header: 'id', + }, + { + accessorKey: 'format', + header: 'Format', + }, + { + accessorKey: 'title', + header: 'Titre', + }, + { + accessorKey: 'creator', + header: 'Créateur', + }, + { + accessorKey: 'date', + header: 'Date publication', + }, + ], + data: [ + { + creator: 'Valentin Perello', + date: '16/05/2022', + format: 'SVG', + id: 1, + title: 'Doc test 1', + }, + { + creator: 'Valentin Perello', + date: '17/05/2022', + format: 'PDF', + id: 2, + title: 'Doc test 2', + }, + { + creator: 'Valentin Perello', + date: '18/05/2022', + format: 'PDF', + id: 3, + title: 'Doc test 3', + }, + { + creator: 'Valentin Perello', + date: '19/05/2022', + format: 'PDF', + id: 4, + title: 'Doc test 4', + }, + { + creator: 'Valentin Perello', + date: '20/05/2022', + format: 'PDF', + id: 5, + title: 'Doc test 5', + }, + { + creator: 'Valentin Perello', + date: '21/05/2022', + format: 'PDF', + id: 6, + title: 'Doc test 6', + }, + { + creator: 'Valentin Perello', + date: '22/05/2022', + format: 'PDF', + id: 7, + title: 'Doc test 7', + }, + ], + rowActionNumber: 3, +}; diff --git a/packages/react-front-kit-table/src/Components/Table/Table.stories.tsx b/packages/react-front-kit-table/src/Components/Table/Table.stories.tsx index 63613d42..52ae231e 100644 --- a/packages/react-front-kit-table/src/Components/Table/Table.stories.tsx +++ b/packages/react-front-kit-table/src/Components/Table/Table.stories.tsx @@ -1,17 +1,7 @@ import type { Meta, StoryObj } from '@storybook/react'; -import { - DownloadSimple, - Eye, - PencilSimple, - ShareNetwork, - Star, - Trash, -} from '@phosphor-icons/react'; -import { FolderMove } from '@smile/react-front-kit-shared'; -import { action } from '@storybook/addon-actions'; - import { Table as Cmp } from './Table'; +import { tableMock } from './Table.mock'; const meta = { component: Cmp, @@ -25,136 +15,6 @@ type IStory = StoryObj; export const Table: IStory = { args: { - actions: [ - { - icon: , - id: 'move', - isMassAction: true, - label: 'Move in tree', - onAction: action('Move in tree'), - }, - { - icon: , - id: 'open', - label: 'Open document', - onAction: action('Open document'), - }, - { - icon: , - id: 'edit', - label: 'Edit document', - onAction: action('Edit document'), - }, - { - icon: , - id: 'favorite', - label: 'Add to favorites', - onAction: action('Add to favorites'), - }, - { - confirmation: true, - icon: , - id: 'share', - label: 'Share', - onAction: action('Share'), - }, - { - icon: , - id: 'download', - label: 'Download', - onAction: action('Download'), - }, - { - color: 'red', - confirmModalProps: { - children: 'Are you sure you want to delete ?', - confirmColor: 'red', - confirmLabel: 'Delete', - onCancel: action('Delete:Cancel'), - onConfirm: action('Delete:Confirm'), - title: 'Delete ?', - }, - confirmation: true, - icon: , - id: 'delete', - isMassAction: true, - label: 'Delete', - onAction: action('Delete'), - }, - ], - columns: [ - { - accessorKey: 'id', - header: 'id', - }, - { - accessorKey: 'format', - header: 'Format', - }, - { - accessorKey: 'title', - header: 'Titre', - }, - { - accessorKey: 'creator', - header: 'Créateur', - }, - { - accessorKey: 'date', - header: 'Date publication', - }, - ], - data: [ - { - creator: 'Valentin Perello', - date: '16/05/2022', - format: 'SVG', - id: 1, - title: 'Doc test 1', - }, - { - creator: 'Valentin Perello', - date: '17/05/2022', - format: 'PDF', - id: 2, - title: 'Doc test 2', - }, - { - creator: 'Valentin Perello', - date: '18/05/2022', - format: 'PDF', - id: 3, - title: 'Doc test 3', - }, - { - creator: 'Valentin Perello', - date: '19/05/2022', - format: 'PDF', - id: 4, - title: 'Doc test 4', - }, - { - creator: 'Valentin Perello', - date: '20/05/2022', - format: 'PDF', - id: 5, - title: 'Doc test 5', - }, - { - creator: 'Valentin Perello', - date: '21/05/2022', - format: 'PDF', - id: 6, - title: 'Doc test 6', - }, - { - creator: 'Valentin Perello', - date: '22/05/2022', - format: 'PDF', - id: 7, - title: 'Doc test 7', - }, - ], - rowActionNumber: 3, + ...tableMock, }, }; diff --git a/packages/react-front-kit-table/src/Components/Table/Table.tsx b/packages/react-front-kit-table/src/Components/Table/Table.tsx index cb50713a..9b1605de 100644 --- a/packages/react-front-kit-table/src/Components/Table/Table.tsx +++ b/packages/react-front-kit-table/src/Components/Table/Table.tsx @@ -7,10 +7,7 @@ 'use client'; import type { FloatingPosition } from '@mantine/core/lib/Floating'; import type { IPaginationProps } from '@smile/react-front-kit'; -import type { - IAction, - IConfirmAction, -} from '@smile/react-front-kit-shared/src/types/actions'; +import type { IAction, IConfirmAction } from '@smile/react-front-kit-shared'; import type { MRT_Row, MRT_TableOptions } from 'mantine-react-table'; import type { ReactElement } from 'react'; diff --git a/packages/react-front-kit-table/src/Components/TableGridView/TableGridView.mock.tsx b/packages/react-front-kit-table/src/Components/TableGridView/TableGridView.mock.tsx index e83dbe01..aaea5eac 100644 --- a/packages/react-front-kit-table/src/Components/TableGridView/TableGridView.mock.tsx +++ b/packages/react-front-kit-table/src/Components/TableGridView/TableGridView.mock.tsx @@ -1,11 +1,65 @@ import type { ITableGridViewProps } from './TableGridView'; +import type { IThumbnail } from '@smile/react-front-kit'; +import type { HandlerFunction } from '@storybook/addon-actions'; -import { ThumbnailGrid } from '@smile/react-front-kit/src/Components/ThumbnailGrid/ThumbnailGrid.stories'; +import { Trash } from '@phosphor-icons/react'; +import { FolderMove, baseThumbnail } from '@smile/react-front-kit'; +import { action } from '@storybook/addon-actions'; -import { Table } from '../Table/Table.stories'; +import { tableMock } from '../Table/Table.mock'; -const { data, ...tableProps } = Table.args; -const { thumbnails, ...gridProps } = ThumbnailGrid.args; +const thumbnailsMock: IThumbnail[] = [ + { + id: '1', + label: 'Debit_Suivi_PREV', + ...baseThumbnail, + }, + { + id: '2', + label: 'Debit_Suivi_PREV_2', + ...baseThumbnail, + selected: true, + }, + { + id: '3', + label: 'Debit_Suivi_PREV_3', + ...baseThumbnail, + }, +]; + +const thumbnailGridMock = { + cols: 5, + gridActions: [ + { + icon: , + id: 'move', + label: 'Move in tree', + onAction: action('Move selected in tree'), + }, + { + color: 'red', + confirmModalProps: { + cancelLabel: 'Abort', + children:

Are you sure ?

, + confirmColor: 'red', + confirmLabel: 'Remove', + title: 'remove x files ?', + }, + confirmation: true, + icon: , + id: 'delete', + label: 'Delete', + onAction: action('Delete selected'), + }, + ], + onThumbnailClick: (): HandlerFunction => action('Thumbnail clicked'), + spacing: 25, + thumbnails: thumbnailsMock, + verticalSpacing: 25, +}; + +const { data, ...tableProps } = tableMock; +const { thumbnails, ...gridProps } = thumbnailGridMock; export const tableGridViewProps: ITableGridViewProps> = { diff --git a/packages/react-front-kit-table/src/Components/TableGridView/TableGridView.tsx b/packages/react-front-kit-table/src/Components/TableGridView/TableGridView.tsx index aa816e10..804dace7 100644 --- a/packages/react-front-kit-table/src/Components/TableGridView/TableGridView.tsx +++ b/packages/react-front-kit-table/src/Components/TableGridView/TableGridView.tsx @@ -1,18 +1,21 @@ import type { ITableProps } from '../Table/Table'; -import type { IThumbnail, IThumbnailGridProps } from '@smile/react-front-kit'; import type { IDataView, ISwitchableViewProps, -} from '@smile/react-front-kit/src/Components/SwitchableView/SwitchableView'; + IThumbnail, + IThumbnailGridProps, +} from '@smile/react-front-kit'; import type { MRT_RowSelectionState } from 'mantine-react-table'; import type { ReactElement } from 'react'; import { createStyles } from '@mantine/styles'; import { ListBullets, SquaresFour } from '@phosphor-icons/react'; -import { isNotNullNorEmpty } from '@smile/react-front-kit'; -import defaultImage from '@smile/react-front-kit/assets/defaultImage.jpg'; -import { SwitchableView, ThumbnailGrid } from '@smile/react-front-kit/src'; -import { typeGuard } from '@smile/react-front-kit/src/helpers/typeGuard'; +import { + SwitchableView, + ThumbnailGrid, + isNotNullNorEmpty, + typeGuard, +} from '@smile/react-front-kit'; import { useState } from 'react'; import { Table } from '../Table/Table'; @@ -52,6 +55,9 @@ export function TableGridView>( const { idFieldName, labelFieldName, imageFieldName, ...otherGridProps } = gridProps; const [rowSelection, setRowSelection] = useState({}); + const selectedIndexes = Object.entries(rowSelection).map((entry) => + entry[1] ? entry[0] : null, + ); const { classes } = useStyles(); const extendedTableProps: ITableProps = { @@ -70,14 +76,11 @@ export function TableGridView>( .map((item, index) => { const id = item[gridProps.idFieldName]; const label = item[gridProps.labelFieldName]; - const image = ( + const image = gridProps.imageFieldName !== undefined - ? item[gridProps.imageFieldName] - : defaultImage - ) as string; - const isSelected = Object.entries(rowSelection) - .map((entry) => (entry[1] ? entry[0] : null)) - .includes(index.toString()); + ? (item[gridProps.imageFieldName] as string) + : undefined; + const isSelected = selectedIndexes.includes(index.toString()); if ( (typeGuard(id, 'number') || typeGuard(id, 'string')) && diff --git a/packages/react-front-kit/src/Components/ThumbnailGrid/ThumbnailGrid.mock.ts b/packages/react-front-kit/src/Components/ThumbnailGrid/ThumbnailGrid.mock.ts deleted file mode 100644 index a485f3f4..00000000 --- a/packages/react-front-kit/src/Components/ThumbnailGrid/ThumbnailGrid.mock.ts +++ /dev/null @@ -1,31 +0,0 @@ -import type { IThumbnail } from '../Thumbnail/Thumbnail'; - -import { thumbnailActions } from '../Thumbnail/Thumbnail.mock'; - -export const baseThumbnail: Omit = { - actions: thumbnailActions, - iconType: 'PDF', -}; - -export const thumbnails: IThumbnail[] = [ - { - id: '1', - label: 'Debit_Suivi_PREV', - ...baseThumbnail, - }, - { - id: '2', - label: 'Debit_Suivi_PREV_2', - ...baseThumbnail, - selected: true, - }, - { - id: '3', - label: 'Debit_Suivi_PREV_3', - ...baseThumbnail, - }, -]; - -export function getModalTitle(n: number): string { - return `Remove ${n} file${n > 1 ? 's' : ''}`; -} diff --git a/packages/react-front-kit/src/Components/ThumbnailGrid/ThumbnailGrid.mock.tsx b/packages/react-front-kit/src/Components/ThumbnailGrid/ThumbnailGrid.mock.tsx new file mode 100644 index 00000000..6c20c00f --- /dev/null +++ b/packages/react-front-kit/src/Components/ThumbnailGrid/ThumbnailGrid.mock.tsx @@ -0,0 +1,63 @@ +import type { IThumbnail } from '../Thumbnail/Thumbnail'; +import type { HandlerFunction } from '@storybook/addon-actions'; + +import { Trash } from '@phosphor-icons/react'; +import { FolderMove } from '@smile/react-front-kit-shared'; +import { action } from '@storybook/addon-actions'; + +import { thumbnailActions } from '../Thumbnail/Thumbnail.mock'; + +export const baseThumbnail: Omit = { + actions: thumbnailActions, + iconType: 'PDF', +}; + +export const thumbnails: IThumbnail[] = [ + { + id: '1', + label: 'Debit_Suivi_PREV', + ...baseThumbnail, + }, + { + id: '2', + label: 'Debit_Suivi_PREV_2', + ...baseThumbnail, + selected: true, + }, + { + id: '3', + label: 'Debit_Suivi_PREV_3', + ...baseThumbnail, + }, +]; + +export const thumbnailGridMock = { + cols: 5, + gridActions: [ + { + icon: , + id: 'move', + label: 'Move in tree', + onAction: action('Move selected in tree'), + }, + { + color: 'red', + confirmModalProps: { + cancelLabel: 'Abort', + children:

Are you sure ?

, + confirmColor: 'red', + confirmLabel: 'Remove', + title: 'remove x files ?', + }, + confirmation: true, + icon: , + id: 'delete', + label: 'Delete', + onAction: action('Delete selected'), + }, + ], + onThumbnailClick: (): HandlerFunction => action('Thumbnail clicked'), + spacing: 25, + thumbnails, + verticalSpacing: 25, +}; diff --git a/packages/react-front-kit/src/Components/ThumbnailGrid/ThumbnailGrid.stories.tsx b/packages/react-front-kit/src/Components/ThumbnailGrid/ThumbnailGrid.stories.tsx index 2efe723f..93eb6303 100644 --- a/packages/react-front-kit/src/Components/ThumbnailGrid/ThumbnailGrid.stories.tsx +++ b/packages/react-front-kit/src/Components/ThumbnailGrid/ThumbnailGrid.stories.tsx @@ -1,11 +1,7 @@ import type { Meta, StoryObj } from '@storybook/react'; -import { Trash } from '@phosphor-icons/react'; -import { FolderMove } from '@smile/react-front-kit-shared'; -import { action } from '@storybook/addon-actions'; - import { ThumbnailGrid as Cmp } from './ThumbnailGrid'; -import { getModalTitle, thumbnails } from './ThumbnailGrid.mock'; +import { thumbnailGridMock } from './ThumbnailGrid.mock'; const meta = { component: Cmp, @@ -17,36 +13,5 @@ export default meta; type IStory = StoryObj; export const ThumbnailGrid: IStory = { - args: { - cols: 5, - gridActions: [ - { - icon: , - id: 'move', - label: 'Move in tree', - onAction: action('Move selected in tree'), - }, - { - color: 'red', - confirmModalProps: { - cancelLabel: 'Abort', - children:

Are you sure ?

, - confirmColor: 'red', - confirmLabel: 'Remove', - title: getModalTitle( - thumbnails.filter((thumbnail) => thumbnail.selected).length, - ), - }, - confirmation: true, - icon: , - id: 'delete', - label: 'Delete', - onAction: action('Delete selected'), - }, - ], - onThumbnailClick: () => action('Thumbnail clicked'), - spacing: 25, - thumbnails, - verticalSpacing: 25, - }, + args: { ...thumbnailGridMock }, }; diff --git a/packages/react-front-kit/src/Components/ThumbnailGrid/ThumbnailGrid.tsx b/packages/react-front-kit/src/Components/ThumbnailGrid/ThumbnailGrid.tsx index 832925a4..81339ddf 100644 --- a/packages/react-front-kit/src/Components/ThumbnailGrid/ThumbnailGrid.tsx +++ b/packages/react-front-kit/src/Components/ThumbnailGrid/ThumbnailGrid.tsx @@ -5,7 +5,7 @@ import type { SimpleGridProps } from '@mantine/core'; import type { IAction, IActionConfirmModalProps, -} from '@smile/react-front-kit-shared/src/types/actions'; +} from '@smile/react-front-kit-shared'; import type { ReactElement } from 'react'; import { Button, Group, SimpleGrid } from '@mantine/core'; @@ -32,6 +32,10 @@ const useStyles = createStyles((theme) => ({ }, })); +function defaultSelectedElementsText(n: number): string { + return `${n} selected file${n > 1 ? 's' : ''}`; +} + type IGridAction = IAction; type IGridActionConfirmModalProps = IActionConfirmModalProps; @@ -44,10 +48,6 @@ export interface IThumbnailGridProps extends SimpleGridProps { /** Additional props will be forwarded to the [Mantine SimpleGrid component](https://mantine.dev/core/simple-grid) */ export function ThumbnailGrid(props: IThumbnailGridProps): ReactElement { - function defaultSelectedElementsText(n: number): string { - return `${n} selected file${n > 1 ? 's' : ''}`; - } - const { gridActions = [], onThumbnailClick, @@ -96,7 +96,7 @@ export function ThumbnailGrid(props: IThumbnailGridProps): ReactElement { } function handleModalButton(onAction?: (item: IThumbnail[]) => void): void { - onAction && onAction(selectedElements); + onAction?.(selectedElements); handleClose(); } @@ -135,13 +135,9 @@ export function ThumbnailGrid(props: IThumbnailGridProps): ReactElement {
- handleModalButton(confirmAction?.onCancel && confirmAction.onCancel) - } + onCancel={() => handleModalButton(confirmAction?.onCancel)} onClose={handleClose} - onConfirm={() => - handleModalButton(confirmAction?.onConfirm && confirmAction.onConfirm) - } + onConfirm={() => handleModalButton(confirmAction?.onConfirm)} opened={Boolean(confirmAction)} > {confirmAction?.children} diff --git a/packages/react-front-kit/src/helpers/index.ts b/packages/react-front-kit/src/helpers/index.ts index 53af472e..a22f1944 100644 --- a/packages/react-front-kit/src/helpers/index.ts +++ b/packages/react-front-kit/src/helpers/index.ts @@ -1 +1,2 @@ export * from './nestedObject'; +export * from './typeGuard'; From cea18e5aa2cf97eb8752829a1870200e41a9ec18 Mon Sep 17 00:00:00 2001 From: Quentin Le Caignec Date: Tue, 14 Nov 2023 15:26:41 +0100 Subject: [PATCH 5/6] fix: code review --- .../TableGridView/TableGridView.mock.tsx | 77 +++++++++++++++++-- 1 file changed, 72 insertions(+), 5 deletions(-) diff --git a/packages/react-front-kit-table/src/Components/TableGridView/TableGridView.mock.tsx b/packages/react-front-kit-table/src/Components/TableGridView/TableGridView.mock.tsx index aaea5eac..bbc15e0e 100644 --- a/packages/react-front-kit-table/src/Components/TableGridView/TableGridView.mock.tsx +++ b/packages/react-front-kit-table/src/Components/TableGridView/TableGridView.mock.tsx @@ -1,29 +1,96 @@ import type { ITableGridViewProps } from './TableGridView'; import type { IThumbnail } from '@smile/react-front-kit'; +import type { IThumbnailAction } from '@smile/react-front-kit/src'; import type { HandlerFunction } from '@storybook/addon-actions'; -import { Trash } from '@phosphor-icons/react'; -import { FolderMove, baseThumbnail } from '@smile/react-front-kit'; +import { + DownloadSimple, + Eye, + PencilSimple, + ShareNetwork, + Star, + Trash, +} from '@phosphor-icons/react'; +import { FolderMove } from '@smile/react-front-kit'; import { action } from '@storybook/addon-actions'; import { tableMock } from '../Table/Table.mock'; +const thumbnailActionsMock: IThumbnailAction[] = [ + { + icon: , + id: 'move', + label: 'Move in tree', + onAction: action('Move in tree'), + }, + { + icon: , + id: 'open', + label: 'Open document', + onAction: action('Open document'), + }, + { + icon: , + id: 'edit', + label: 'Edit document', + onAction: action('Edit document'), + }, + { + icon: , + id: 'favorite', + label: 'Add to favorites', + onAction: action('Add to favorites'), + }, + { + icon: , + id: 'share', + label: 'Share', + onAction: action('Share'), + }, + { + icon: , + id: 'download', + label: 'Download', + onAction: action('Download'), + }, + { + color: 'red', + confirmModalProps: { + cancelLabel: 'Abort', + children:

Are you sure ?

, + confirmColor: 'red', + confirmLabel: 'Remove', + title: 'Remove File', + }, + confirmation: true, + icon: , + id: 'delete', + label: 'Delete', + onAction: action('Delete'), + }, +]; + +const baseThumbnailMock: Omit = { + actions: thumbnailActionsMock, + iconType: 'PDF', +}; + const thumbnailsMock: IThumbnail[] = [ { id: '1', label: 'Debit_Suivi_PREV', - ...baseThumbnail, + ...baseThumbnailMock, }, { id: '2', label: 'Debit_Suivi_PREV_2', - ...baseThumbnail, + ...baseThumbnailMock, selected: true, }, { id: '3', label: 'Debit_Suivi_PREV_3', - ...baseThumbnail, + ...baseThumbnailMock, }, ]; From 5bdaccb5ca6b19a23c8aa03ce670574ec03ae871 Mon Sep 17 00:00:00 2001 From: Quentin Le Caignec Date: Tue, 14 Nov 2023 15:52:18 +0100 Subject: [PATCH 6/6] fix: code review --- .../react-front-kit/src/Components/Thumbnail/Thumbnail.tsx | 2 -- .../Components/Thumbnail/__snapshots__/Thumbnail.test.tsx.snap | 1 - .../ThumbnailGrid/__snapshots__/ThumbnailGrid.test.tsx.snap | 3 --- 3 files changed, 6 deletions(-) diff --git a/packages/react-front-kit/src/Components/Thumbnail/Thumbnail.tsx b/packages/react-front-kit/src/Components/Thumbnail/Thumbnail.tsx index 7affaaf0..c107ca38 100644 --- a/packages/react-front-kit/src/Components/Thumbnail/Thumbnail.tsx +++ b/packages/react-front-kit/src/Components/Thumbnail/Thumbnail.tsx @@ -45,7 +45,6 @@ export function Thumbnail(props: IThumbnailProps): ReactElement { const { actions = [], iconType, - id, image = defaultImage, label, onClick, @@ -98,7 +97,6 @@ export function Thumbnail(props: IThumbnailProps): ReactElement { diff --git a/packages/react-front-kit/src/Components/Thumbnail/__snapshots__/Thumbnail.test.tsx.snap b/packages/react-front-kit/src/Components/Thumbnail/__snapshots__/Thumbnail.test.tsx.snap index 4e2e51c8..478c41c2 100644 --- a/packages/react-front-kit/src/Components/Thumbnail/__snapshots__/Thumbnail.test.tsx.snap +++ b/packages/react-front-kit/src/Components/Thumbnail/__snapshots__/Thumbnail.test.tsx.snap @@ -4,7 +4,6 @@ exports[`Thumbnail matches snapshot 1`] = `