From a17560d774ff0ea63425e961466db4bb6062fcf1 Mon Sep 17 00:00:00 2001 From: alx-chernigin Date: Tue, 1 Oct 2024 15:32:03 +0300 Subject: [PATCH] feat(Table): table actions column added --- .../TableActions/DefaultRowActions.scss | 15 +++ .../TableActions/DefaultRowActions.tsx | 127 ++++++++++++++++++ .../__stories__/TableActions.stories.tsx | 25 ++++ .../TableActions/__stories__/constants.tsx | 64 +++++++++ .../stories/TableActionsColumnStory.tsx | 77 +++++++++++ ...leActionsColumnWithVirtualizationStory.tsx | 38 ++++++ .../TableSettingsWithActionsColumnStory.tsx | 39 ++++++ .../TableActions/__stories__/types.ts | 7 + .../TableActions/__stories__/utils.ts | 10 ++ src/components/TableActions/i18n/en.json | 3 + src/components/TableActions/i18n/index.ts | 10 ++ src/components/TableActions/i18n/ru.json | 3 + src/components/TableActions/index.ts | 2 + src/components/TableActions/types.ts | 34 +++++ src/components/index.ts | 1 + src/constants/actionsColumn.tsx | 48 +++++++ src/constants/index.ts | 2 + src/constants/settingsWithActionsColumn.ts | 26 ++++ 18 files changed, 531 insertions(+) create mode 100644 src/components/TableActions/DefaultRowActions.scss create mode 100644 src/components/TableActions/DefaultRowActions.tsx create mode 100644 src/components/TableActions/__stories__/TableActions.stories.tsx create mode 100644 src/components/TableActions/__stories__/constants.tsx create mode 100644 src/components/TableActions/__stories__/stories/TableActionsColumnStory.tsx create mode 100644 src/components/TableActions/__stories__/stories/TableActionsColumnWithVirtualizationStory.tsx create mode 100644 src/components/TableActions/__stories__/stories/TableSettingsWithActionsColumnStory.tsx create mode 100644 src/components/TableActions/__stories__/types.ts create mode 100644 src/components/TableActions/__stories__/utils.ts create mode 100644 src/components/TableActions/i18n/en.json create mode 100644 src/components/TableActions/i18n/index.ts create mode 100644 src/components/TableActions/i18n/ru.json create mode 100644 src/components/TableActions/index.ts create mode 100644 src/components/TableActions/types.ts create mode 100644 src/constants/actionsColumn.tsx create mode 100644 src/constants/settingsWithActionsColumn.ts diff --git a/src/components/TableActions/DefaultRowActions.scss b/src/components/TableActions/DefaultRowActions.scss new file mode 100644 index 0000000..137a596 --- /dev/null +++ b/src/components/TableActions/DefaultRowActions.scss @@ -0,0 +1,15 @@ +@use '@gravity-ui/uikit/styles/mixins'; +@use '../variables'; + +$block: '.#{variables.$ns}table'; +$popupBlock: '#{$block}-action-popup'; + +#{$popupBlock} { + &__menu { + @include mixins.max-height(200px); + + &-item { + @include mixins.max-text-width(250px); + } + } +} diff --git a/src/components/TableActions/DefaultRowActions.tsx b/src/components/TableActions/DefaultRowActions.tsx new file mode 100644 index 0000000..58dfc0a --- /dev/null +++ b/src/components/TableActions/DefaultRowActions.tsx @@ -0,0 +1,127 @@ +import React from 'react'; + +import {Ellipsis} from '@gravity-ui/icons'; +import type {PopupPlacement} from '@gravity-ui/uikit'; +import {Button, Icon, Menu, Popup, useUniqId} from '@gravity-ui/uikit'; + +import {block} from '../../utils'; + +import i18n from './i18n'; +import type {TableActionConfig, TableActionGroup, TableActionsSettings} from './types'; + +import './DefaultRowActions.scss'; + +type DefaultRowActionsProps = Pick< + TableActionsSettings, + 'getRowActions' | 'rowActionsSize' +> & { + item: I; + index: number; +}; + +const b = block('table'); +const actionsCn = b('actions'); +const actionsButtonCn = b('actions-button'); + +const bPopup = block('table-action-popup'); +const menuCn = bPopup('menu'); +const menuItemCn = bPopup('menu-item'); + +const DEFAULT_PLACEMENT: PopupPlacement = ['bottom-end', 'top-end', 'auto']; + +const isActionGroup = ( + config: TableActionConfig, +): config is TableActionGroup => { + return Array.isArray((config as TableActionGroup).items); +}; + +export const DefaultRowActions = ({ + index: rowIndex, + item, + getRowActions, + rowActionsSize, +}: DefaultRowActionsProps) => { + const [isPopupOpen, setIsPopupOpen] = React.useState(false); + const anchorRef = React.useRef(null); + const rowId = useUniqId(); + + if (getRowActions === undefined) { + return null; + } + + const renderPopupMenuItem = (action: TableActionConfig, index: number) => { + if (isActionGroup(action)) { + return ( + + {action.items.map(renderPopupMenuItem)} + + ); + } + + const {text, icon, handler, href, ...restProps} = action; + + return ( + { + event.stopPropagation(); + handler(item, index, event); + + setIsPopupOpen(false); + }} + href={typeof href === 'function' ? href(item, index) : href} + iconStart={icon} + className={menuItemCn} + {...restProps} + > + {text} + + ); + }; + + const actions = getRowActions(item, rowIndex); + + if (actions.length === 0) { + return null; + } + + return ( +
+ setIsPopupOpen(false)} + id={rowId} + > + {/* eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions */} +
{ + event.stopPropagation(); + }} + > + + {actions.map(renderPopupMenuItem)} + +
+
+ +
+ ); +}; diff --git a/src/components/TableActions/__stories__/TableActions.stories.tsx b/src/components/TableActions/__stories__/TableActions.stories.tsx new file mode 100644 index 0000000..1b4f16b --- /dev/null +++ b/src/components/TableActions/__stories__/TableActions.stories.tsx @@ -0,0 +1,25 @@ +import type {Meta, StoryObj} from '@storybook/react'; + +import {TableActionsColumnStory} from './stories/TableActionsColumnStory'; +import {TableActionsColumnWithVirtualizationStory} from './stories/TableActionsColumnWithVirtualizationStory'; +import {TableSettingsWithActionsColumnStory} from './stories/TableSettingsWithActionsColumnStory'; + +const meta: Meta = { + title: 'Table actions', +}; + +export default meta; + +export const ActionsColumn: StoryObj = { + render: TableActionsColumnStory, +}; + +export const SettingsWithActionsColumn: StoryObj = { + render: TableSettingsWithActionsColumnStory, +}; + +export const ActionsColumnWithVirtualizationStory: StoryObj< + typeof TableActionsColumnWithVirtualizationStory +> = { + render: TableActionsColumnWithVirtualizationStory, +}; diff --git a/src/components/TableActions/__stories__/constants.tsx b/src/components/TableActions/__stories__/constants.tsx new file mode 100644 index 0000000..db881b4 --- /dev/null +++ b/src/components/TableActions/__stories__/constants.tsx @@ -0,0 +1,64 @@ +import React from 'react'; + +import {Pencil} from '@gravity-ui/icons'; +import {Icon} from '@gravity-ui/uikit'; +import type {ColumnDef} from '@tanstack/react-table'; + +import type {TableActionsSettings} from '../types'; + +import type {Item} from './types'; + +export const actionsSettings: TableActionsSettings = { + getRowActions: (item, index) => [ + { + text: 'default', + handler: () => { + alert(JSON.stringify(item)); + }, + }, + { + text: 'with icon', + icon: , + handler: () => {}, + }, + { + text: 'disabled', + disabled: true, + handler: () => {}, + }, + { + text: 'danger theme', + theme: 'danger', + handler: () => { + alert(index); + }, + }, + { + text: 'with href', + theme: 'normal', + href: 'https://gravity-ui.com', + target: '_blank', + rel: 'noopener noreferrer', + handler: () => {}, + }, + ], +}; + +export const baseColumns: ColumnDef[] = [ + {accessorKey: 'id', header: 'Index', size: 50}, + {accessorKey: 'name', header: 'Name', size: 100}, + {accessorKey: 'age', header: 'Age', size: 100}, + { + id: 'name-age', + accessorFn: (item) => `${item.name}: ${item.age}`, + header: () => Name: Age, + cell: (info) => {info.getValue()}, + maxSize: 200, + minSize: 100, + }, + { + accessorKey: 'status', + header: 'Status', + size: 100, + }, +]; diff --git a/src/components/TableActions/__stories__/stories/TableActionsColumnStory.tsx b/src/components/TableActions/__stories__/stories/TableActionsColumnStory.tsx new file mode 100644 index 0000000..738b09f --- /dev/null +++ b/src/components/TableActions/__stories__/stories/TableActionsColumnStory.tsx @@ -0,0 +1,77 @@ +import React from 'react'; + +import {Select} from '@gravity-ui/uikit'; +import type {ColumnDef, RowSelectionState} from '@tanstack/react-table'; + +import {ACTIONS_COLUMN_ID, getActionsColumn, selectionColumn} from '../../../../constants'; +import {useTable} from '../../../../hooks'; +import {Table} from '../../../Table/Table'; +import {actionsSettings, baseColumns} from '../constants'; +import type {Item} from '../types'; +import {generateData} from '../utils'; + +const data = generateData(5); + +const defaultColumns: ColumnDef[] = [ + selectionColumn as ColumnDef, + ...baseColumns, + getActionsColumn(ACTIONS_COLUMN_ID, { + ...actionsSettings, + }), +]; + +const columnsWithRenderRowActions: ColumnDef[] = [ + selectionColumn as ColumnDef, + ...baseColumns, + getActionsColumn(ACTIONS_COLUMN_ID, { + ...actionsSettings, + renderRowActions: ({index}) => { + if (index % 2) { + return null; + } + + return ( +