import type { Meta, StoryObj } from '@storybook/react' import { useState, useEffect } from 'react' import { Tag } from '../Tag' import { Typography } from '../Typography' import { DataTable, type DataTableColumn, type PaginationConfig, } from './DataTable' type OracleResponse = { oracle: string value: string timestamp: string } const oracleData: OracleResponse[] = [ { oracle: 'Fiews', value: '22.1', timestamp: 'January 01, 14:52', }, { oracle: 'Chainlayer', value: '22.551', timestamp: 'January 01, 14:53', }, { oracle: 'LinkPool', value: '22.612348', timestamp: 'January 01, 14:54', }, { oracle: 'Omniscience', value: '22.50', timestamp: 'January 01, 14:54', }, { oracle: 'P2P.org', value: '23.1247', timestamp: 'January 01, 14:54', }, ] type DefaultResponse = { text: string code: string tag: string } const defaultData: DefaultResponse[] = [ { text: 'Sample Text', code: 'SAMPLE_CODE', tag: 'Default', }, { text: 'Another Text', code: 'ANOTHER_CODE', tag: 'Success', }, { text: 'More Text', code: 'MORE_CODE', tag: 'Warning', }, ] const defaultColumns: DataTableColumn[] = [ { accessorKey: 'text', header: 'Text', cell: ({ row }) => ( {row.getValue('text')} ), meta: { skeletonWidth: 80 }, }, { accessorKey: 'code', header: 'Code', cell: ({ row }) => ( {row.getValue('code')} ), meta: { skeletonWidth: 120 }, }, { accessorKey: 'tag', header: 'Tag', cell: ({ row }) => { const tagValue = (row.getValue('tag') as string).toLowerCase() return ( {row.getValue('tag')} ) }, meta: { skeletonWidth: 64 }, }, ] const oracleColumns: DataTableColumn[] = [ { accessorKey: 'oracle', header: 'Name', enableSorting: true, meta: { skeletonWidth: 100 }, }, { accessorKey: 'value', header: 'Latest answer', enableSorting: false, meta: { skeletonWidth: 80 }, }, { accessorKey: 'timestamp', header: 'Last updated', enableSorting: false, meta: { skeletonWidth: 150 }, }, ] type TokenResponse = { symbol: string name: string price: string } const tokenData: TokenResponse[] = [ { symbol: 'BTC', name: 'Bitcoin', price: '$43,250.75', }, { symbol: 'ETH', name: 'Ethereum', price: '$2,680.45', }, { symbol: 'SOL', name: 'Solana', price: '$98.32', }, { symbol: 'XRP', name: 'Ripple', price: '$0.62', }, { symbol: 'AVAX', name: 'Avalanche', price: '$34.56', }, { symbol: 'MATIC', name: 'Polygon', price: '$0.89', }, { symbol: 'ADA', name: 'Cardano', price: '$0.52', }, { symbol: 'DOT', name: 'Polkadot', price: '$7.23', }, { symbol: 'LINK', name: 'Chainlink', price: '$15.67', }, { symbol: 'UNI', name: 'Uniswap', price: '$8.45', }, { symbol: 'ATOM', name: 'Cosmos', price: '$9.12', }, { symbol: 'LTC', name: 'Litecoin', price: '$72.34', }, { symbol: 'BCH', name: 'Bitcoin Cash', price: '$245.67', }, { symbol: 'XLM', name: 'Stellar', price: '$0.13', }, { symbol: 'VET', name: 'VeChain', price: '$0.028', }, { symbol: 'FIL', name: 'Filecoin', price: '$5.89', }, { symbol: 'TRX', name: 'TRON', price: '$0.087', }, { symbol: 'ETC', name: 'Ethereum Classic', price: '$23.45', }, { symbol: 'XMR', name: 'Monero', price: '$165.78', }, { symbol: 'EOS', name: 'EOS', price: '$0.76', }, { symbol: 'AAVE', name: 'Aave', price: '$89.23', }, { symbol: 'ALGO', name: 'Algorand', price: '$0.18', }, ] const tokenColumns: DataTableColumn[] = [ { accessorKey: 'symbol', header: 'Symbol', enableSorting: true, cell: ({ row }) => ( {row.getValue('symbol')} ), meta: { skeletonWidth: 80 }, }, { accessorKey: 'name', header: 'Name', enableSorting: true, cell: ({ row }) => ( {row.getValue('name')} ), meta: { skeletonWidth: 120 }, }, { accessorKey: 'price', header: 'Price', enableSorting: true, cell: ({ row }) => ( {row.getValue('price')} ), meta: { skeletonWidth: 100 }, }, ] const meta: Meta = { component: DataTable, title: 'Blocks/Table/DataTable', parameters: { layout: 'padded', docs: { source: { type: 'code' }, }, }, argTypes: { data: { control: 'object', description: 'The data to be displayed in the table', }, columns: { control: 'object', description: 'The column definitions for the table', }, isLoading: { control: 'boolean', description: 'Whether the table is in a loading state', }, tableOptions: { control: false, description: 'TanStack Table configuration options. Check [TanStack Table Documentation](https://tanstack.com/table/latest/docs/api/core/table).', table: { type: { summary: 'TableOptions' }, }, }, }, } export default meta type Story = StoryObj> export const Default: Story = { args: { columns: defaultColumns, data: defaultData, isLoading: false, }, } export const Loading: Story = { args: { columns: defaultColumns, data: defaultData, isLoading: true, }, } export const LoadingDefault: Story = { args: { columns: defaultColumns, data: [], isLoading: true, }, } export const LoadingWithPlaceholdersFromPagination: Story = { args: { columns: defaultColumns, data: [], isLoading: true, pagination: { pageSize: 7, }, }, } export const LoadingWithCustomPlaceholderCount: Story = { args: { columns: defaultColumns, data: [], isLoading: true, placeholderCount: 3, }, } export const Empty: Story = { args: { columns: defaultColumns, data: [], isLoading: false, }, } export const OracleTableExample: Story = { args: { columns: oracleColumns, data: oracleData, isLoading: false, }, } export const WithPagination: Story = { args: { columns: tokenColumns, data: tokenData, isLoading: false, }, } export const WithCustomPagination: Story = { args: { columns: oracleColumns, data: oracleData, isLoading: false, pagination: { pageSize: 3, }, }, } export const WithSelectPagination: Story = { args: { columns: oracleColumns, data: oracleData, isLoading: false, pagination: { pageSize: 3, pageControlMode: 'select', }, }, } export const WithDisplayPagination: Story = { args: { columns: oracleColumns, data: oracleData, isLoading: false, pagination: { pageSize: 3, pageControlMode: 'display', showPaginationDisplay: false, }, }, } // Server-side pagination example type UserData = { id: string name: string email: string role: string status: 'active' | 'inactive' | 'pending' } // Mock API function that simulates server-side pagination const mockApiCall = async ( pageIndex: number, pageSize: number, ): Promise<{ data: UserData[] totalCount: number }> => { // Simulate network delay await new Promise((resolve) => setTimeout(resolve, 500)) // Generate mock data const allData: UserData[] = Array.from({ length: 150 }, (_, i) => ({ id: `user-${i + 1}`, name: `User ${i + 1}`, email: `user${i + 1}@example.com`, role: ['Admin', 'User', 'Moderator', 'Guest'][i % 4], status: ['active', 'inactive', 'pending'][i % 3] as | 'active' | 'inactive' | 'pending', })) const startIndex = pageIndex * pageSize const endIndex = startIndex + pageSize const data = allData.slice(startIndex, endIndex) return { data, totalCount: allData.length, } } const mockApiCallWithoutTotal = async ( pageIndex: number, pageSize: number, ): Promise<{ data: UserData[] hasMore: boolean }> => { await new Promise((resolve) => setTimeout(resolve, 500)) const allData: UserData[] = Array.from({ length: 47 }, (_, i) => ({ id: `user-${i + 1}`, name: `User ${i + 1}`, email: `user${i + 1}@example.com`, role: ['Admin', 'User', 'Moderator', 'Guest'][i % 4], status: ['active', 'inactive', 'pending'][i % 3] as | 'active' | 'inactive' | 'pending', })) const startIndex = pageIndex * pageSize const endIndex = startIndex + pageSize const data = allData.slice(startIndex, endIndex) return { data, hasMore: endIndex < allData.length, } } const userColumns: DataTableColumn[] = [ { accessorKey: 'name', header: 'Name', enableSorting: true, cell: ({ row }) => ( {row.getValue('name')} ), meta: { skeletonWidth: 120 }, }, { accessorKey: 'email', header: 'Email', enableSorting: true, cell: ({ row }) => ( {row.getValue('email')} ), meta: { skeletonWidth: 200 }, }, { accessorKey: 'role', header: 'Role', enableSorting: true, cell: ({ row }) => ( {row.getValue('role')} ), meta: { skeletonWidth: 100 }, }, { accessorKey: 'status', header: 'Status', enableSorting: true, cell: ({ row }) => { const status = row.getValue('status') as string return ( {status} ) }, meta: { skeletonWidth: 80 }, }, ] const ServerSidePaginationComponent = () => { const [data, setData] = useState([]) const [isLoading, setIsLoading] = useState(false) const [totalCount, setTotalCount] = useState(0) const fetchData = async (pageIndex: number, pageSize: number) => { setIsLoading(true) try { const result = await mockApiCall(pageIndex, pageSize) setData(result.data) setTotalCount(result.totalCount) } catch (error) { console.error('Failed to fetch data:', error) } finally { setIsLoading(false) } } const paginationConfig: PaginationConfig = { pageSize: 10, serverSide: true, totalCount, onPageChange: fetchData, } // Initial data fetch useEffect(() => { void fetchData(0, 10) }, []) return ( ) } export const ServerSidePagination: Story = { render: () => , parameters: { docs: { source: { code: `const paginationConfig: PaginationConfig = { pageSize: 10, serverSide: true, totalCount, onPageChange: fetchData, } `, }, }, }, } const ServerSidePaginationWithoutTotalComponent = () => { const [data, setData] = useState([]) const [isLoading, setIsLoading] = useState(false) const [pageIndex, setPageIndex] = useState(0) const [hasMore, setHasMore] = useState(false) const pageSize = 10 const fetchData = async (nextPageIndex: number, nextPageSize: number) => { setIsLoading(true) try { const result = await mockApiCallWithoutTotal(nextPageIndex, nextPageSize) setData(result.data) setPageIndex(nextPageIndex) setHasMore(result.hasMore) } catch (error) { console.error('Failed to fetch data:', error) } finally { setIsLoading(false) } } const paginationConfig: PaginationConfig = { pageIndex, pageSize, serverSide: true, currentPageCount: data.length, canNextPage: hasMore, onPageChange: fetchData, } useEffect(() => { void fetchData(0, pageSize) }, []) return ( ) } const ServerSidePaginationWithoutTotalDisplayComponent = () => { const [data, setData] = useState([]) const [isLoading, setIsLoading] = useState(false) const [pageIndex, setPageIndex] = useState(0) const [hasMore, setHasMore] = useState(false) const pageSize = 10 const fetchData = async (nextPageIndex: number, nextPageSize: number) => { setIsLoading(true) try { const result = await mockApiCallWithoutTotal(nextPageIndex, nextPageSize) setData(result.data) setPageIndex(nextPageIndex) setHasMore(result.hasMore) } catch (error) { console.error('Failed to fetch data:', error) } finally { setIsLoading(false) } } const paginationConfig: PaginationConfig = { pageIndex, pageSize, serverSide: true, currentPageCount: data.length, canNextPage: hasMore, onPageChange: fetchData, pageControlMode: 'display', showPaginationDisplay: false, } useEffect(() => { void fetchData(0, pageSize) }, []) return ( ) } export const ServerSidePaginationWithoutTotal: Story = { render: () => , parameters: { docs: { source: { code: `const paginationConfig: PaginationConfig = { pageIndex, pageSize: 10, serverSide: true, currentPageCount: data.length, canNextPage: hasMore, onPageChange: fetchData, } `, }, }, }, } export const ServerSidePaginationWithoutTotalDisplay: Story = { render: () => , parameters: { docs: { source: { code: `const paginationConfig: PaginationConfig = { pageIndex, pageSize: 10, serverSide: true, currentPageCount: data.length, canNextPage: hasMore, onPageChange: fetchData, pageControlMode: 'display', showPaginationDisplay: false, } `, }, }, }, }