import * as React from 'react' import type { StoryFn, StoryObj, Meta } from '@storybook/react-webpack5' import { Grid } from '.' import type { Column, GridProps, GridActionsMenuFullProps } from '../../types' import { GridCellDefault, GridCellDropdownMenu } from '../../components' import { ButtonPrimary, EmptyState, ListItem, ListItemDivider, ListItemInfo, Menu, } from '@planview/pv-uikit' import { size } from '@planview/pv-utilities' import { Copy, Cut, DotsVertical, Trash } from '@planview/pv-icons' export default { title: 'pv-grid/Components/Grid', tags: ['autodocs'], component: Grid, parameters: { badges: ['intl'], }, args: { rowHeight: 'medium', selectionMode: 'multi', filterMode: 'default', sortMode: 'internal', }, argTypes: { loading: { control: { type: 'radio' }, options: [false, true, 3, 6], table: { category: 'Status', }, }, emptyContent: { table: { category: 'Status', }, }, columns: { control: false, }, columnGroups: { control: { type: 'radio' }, options: ['without groups', 'with groups'], mapping: { 'without groups': [], 'with groups': [ { id: 'top-group', label: 'Group', children: ['title', 'price'], }, ], }, }, rows: { control: false, }, ref: { control: false, description: 'This will return an api for interacting with the grid externally.', table: { category: 'Advanced', }, }, defaultSort: { control: false, table: { category: 'Sorting', }, }, sort: { control: false, table: { category: 'Sorting', }, }, sortMode: { table: { category: 'Sorting', }, }, multiColumnSort: { table: { category: 'Sorting', }, }, onSortChange: { table: { category: 'Sorting', }, }, selection: { table: { category: 'Selection', }, }, onSelectionChange: { table: { category: 'Selection', }, }, selectionMode: { table: { category: 'Selection', }, }, filter: { table: { category: 'Filtering', }, }, filterMode: { table: { category: 'Filtering', }, }, filteredIds: { control: { type: 'radio' }, options: ['not filtered', 'filtered', 'no matches'], mapping: { 'not filtered': undefined, filtered: ['1002'], 'no matches': [], }, table: { category: 'Filtering', }, }, preferencesAdapter: { control: false, table: { category: 'Advanced', }, }, expandedRows: { control: false, table: { category: 'Tree', }, }, onExpandedRowsChange: { table: { category: 'Tree', }, }, actionsMenu: { control: { type: 'radio' }, options: ['no actions menu', 'actions menu', 'actions menu async'], mapping: { 'no actions menu': undefined, 'actions menu': () => ( <> } label="Cut" /> } label="Copy" /> } label="Delete" /> ), 'actions menu async': { Menu({ row, menuProps }: GridActionsMenuFullProps) { const [loading, setLoading] = React.useState(true) React.useEffect(() => { const t = setTimeout(() => setLoading(false), 500) return () => clearTimeout(t) }, []) return ( {row.title} } label="Cut" /> } label="Copy" /> } label="Delete" /> ) }, }, }, table: { category: 'Context Menu', }, }, enableColumnVisibilityMenu: { control: { type: 'boolean' }, table: { category: 'Context Menu', }, }, rowDrag: { control: false, table: { category: 'Drag and Drop', }, }, onCellChange: { control: false, table: { category: 'Editing', }, }, }, } satisfies Meta type Row = { id: string title: string price?: number } const rows = [ { id: '1001', title: 'Journey to the Center of the Earth', price: 11.99 }, { id: '1002', title: 'Twenty Thousand Leagues Under the Sea', price: 35.65, }, { id: '1003', title: 'Around the World In Eighty Days', price: 29.98 }, ] const currencyFormatter = new Intl.NumberFormat(undefined, { style: 'currency', currency: 'USD', }) export const Default: StoryFn> = ({ columns: _columns, rows: _rows, ...rest }) => { const columns = React.useMemo[]>( () => [ { id: 'id', label: 'ID', width: 100, }, { id: 'title', label: 'Title', width: 400, }, { id: 'price', label: 'Price', width: 200, header: { align: 'right', }, cell: { label: ({ value }: { value: number }) => currencyFormatter.format(value), Renderer: ({ label, tabIndex }) => ( ), }, }, ], [] ) return } Default.parameters = { docs: { source: { code: ` type Book = { id: number title: string price: number } const rows = React.useMemo(() => [ { id: '1001', title: 'Journey to the Center of the Earth', price: 11.99 }, { id: '1002', title: 'Twenty Thousand Leagues Under the Sea', price: 35.65, }, { id: '1003', title: 'Around the World In Eighty Days', price: 29.98 }, ], []) const columns = React.useMemo[]>( () => [ { id: 'id', label: 'ID', width: 100, }, { id: 'title', label: 'Title', width: 400, }, { id: 'price', label: 'Price', width: 200, header: { align: 'right', }, cell: { label: ({ value }: { value: number }) => currencyFormatter.format(value), Renderer: ({ label, tabIndex }) => ( ), }, }, ], [] ) return `, }, }, } export const LoadingState: StoryFn> = ({ columns: _columns, rows: _rows, ...rest }) => { const columns = React.useMemo[]>( () => [ { id: 'id', label: 'ID', width: 100, }, { id: 'title', label: 'Title', width: 400, }, { id: 'price', label: 'Price', width: 200, header: { align: 'right', }, cell: { label: ({ value }: { value: number }) => currencyFormatter.format(value), Renderer: ({ label, tabIndex }) => ( ), }, }, ], [] ) return } LoadingState.args = { loading: 3, } export const LocaleSorting: StoryObj> = { args: { columns: [ { id: 'title', label: 'Title (Natural sort)', sortStrategy: 'natural', width: 200, }, { id: 'title-alt', label: 'Title (Fast sort)', cell: { value: ({ row }) => row.title }, sortStrategy: 'fast', width: 200, }, ], rows: [ { id: '1', title: 'Alpha 20' }, { id: '2', title: 'alpha 2' }, { id: '3', title: 'alpha 100' }, { id: '4', title: 'älpha 11' }, // spell-checker: disable-line { id: '5', title: 'Beta 99' }, ], selectionMode: 'none', defaultSort: [{ columnId: 'title', direction: 'asc' }], }, } export const StickyColumns: StoryObj> = { args: { columns: [ { id: 'id', label: 'ID', width: 100, sticky: 'left', }, { id: 'title', label: 'Title', width: 400, }, { id: 'desc', label: 'Description', width: 200, cell: { value: () => 'N/A', }, }, { id: 'extra', label: 'Extra', width: 200, cell: { value: () => 'N/A', }, }, { id: 'price', label: 'Price', width: 200, header: { align: 'right', }, cell: { label: ({ value }: { value: number }) => currencyFormatter.format(value), Renderer: ({ label, tabIndex }) => ( ), }, }, { id: 'actions', label: '', width: size.small, resizable: false, movable: false, sortable: false, sticky: 'right', cell: { Renderer({ tabIndex }) { return ( }} > ) }, }, }, ], rows, }, } export const NoData: StoryObj> = { args: { columns: [ { id: 'id', label: 'ID', width: 100, }, { id: 'title', label: 'Title', width: 400, }, { id: 'price', label: 'Price', width: 200, header: { align: 'right', }, }, { id: 'extra', label: 'Extra', width: 400, }, ], rows: [], selectionMode: 'none', defaultSort: [{ columnId: 'title', direction: 'asc' }], emptyContent: ( } callToAction={Create Issue} /> ), }, decorators: [(Story) =>
{Story()}
], } export const ActionsMenu: StoryFn> = ({ columns: _columns, rows: _rows, ...rest }) => { const columns = React.useMemo[]>( () => [ { id: 'id', label: 'ID', width: 100, }, { id: 'title', label: 'Title', width: 400, }, { id: 'price', label: 'Price', width: 200, header: { align: 'right', }, cell: { label: ({ value }: { value: number }) => currencyFormatter.format(value), Renderer: ({ label, tabIndex }) => ( ), }, }, ], [] ) return } ActionsMenu.args = { actionsMenu: 'actions menu' as any, } ActionsMenu.parameters = { docs: { source: { code: ` ( <> } label="Cut" /> } label="Copy" /> } label="Delete" /> )} /> `, }, }, } ActionsMenu.tags = ['hidden'] export const ActionsMenuAsync: StoryFn> = ({ columns: _columns, rows: _rows, ...rest }) => { const columns = React.useMemo[]>( () => [ { id: 'id', label: 'ID', width: 100, }, { id: 'title', label: 'Title', width: 400, }, { id: 'price', label: 'Price', width: 200, header: { align: 'right', }, cell: { label: ({ value }: { value: number }) => currencyFormatter.format(value), Renderer: ({ label, tabIndex }) => ( ), }, }, ], [] ) return } ActionsMenuAsync.args = { actionsMenu: 'actions menu async' as any, } ActionsMenuAsync.parameters = { docs: { source: { code: ` ) { const [loading, setLoading] = React.useState(true) React.useEffect(() => { const t = setTimeout(() => setLoading(false), 500) return () => clearTimeout(t) }, []) return ( {row.title} } label="Cut" /> } label="Copy" /> } label="Delete" /> ) }, }} /> `, }, }, } ActionsMenuAsync.tags = ['hidden'] export const Footer: StoryFn> = ({ columns: _columns, rows: _rows, ...rest }) => { const columns = React.useMemo[]>( () => [ { id: 'id', label: 'ID', width: 100, }, { id: 'title', label: 'Title', width: 400, }, { id: 'price', label: 'Price', width: 200, header: { align: 'right', }, footer: { aggregation: 'sum', label: (value) => typeof value === 'number' ? currencyFormatter.format(value) : '', }, cell: { label: ({ value }: { value: number }) => currencyFormatter.format(value), Renderer: ({ label, tabIndex }) => ( ), }, }, ], [] ) return } Footer.tags = ['hidden'] export const ShowHideColumns: StoryFn> = ({ columns: _columns, rows: _rows, ...rest }) => { const columns = React.useMemo[]>( () => [ { id: 'id', label: 'ID', width: 100, hideable: false, }, { id: 'title', label: 'Title', width: 400, }, { id: 'price', label: 'Price', width: 200, hidden: true, header: { align: 'right', }, cell: { label: ({ value }: { value: number }) => currencyFormatter.format(value), Renderer: ({ label, tabIndex }) => ( ), }, }, ], [] ) return ( ) } ShowHideColumns.tags = ['hidden']