UNPKG

@spaced-out/ui-design-system

Version:
536 lines (497 loc) 14.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.TABLE_DOCS = void 0; const TABLE_DOCS = exports.TABLE_DOCS = { argTypes: { headers: { type: { required: true }, description: 'Provide an array of objects to render the header, each object should consist of label(*), key(*), sortable, sticky and render function', control: { type: 'object' }, table: { type: { summary: '{label: React.Node, key: $Keys<T>, className?: string, filterIcon?: React.Node, filtered?: boolean, subtext?: string, sortable?: boolean, headerIconClassName?: string, sticky?: boolean, render?: React.ComponentType<{data: T, extras?: U, className?: string, selected?: boolean,}>, }' } } }, entries: { type: { required: true }, description: 'Provide an array of object. Each object belong on one row in the table. Each object should have id and all the keys with values that is being passed in headers array', control: { type: 'object' }, table: { type: { summary: 'Array<T>' } } }, classNames: { description: 'Provide optional classNames to be applied to the wrapper, table, tableHeader, tableBody, tableRow, or checkbox', control: { type: 'object' }, table: { type: { summary: '{wrapper?: string, table?: string, tableHeader?: string, tableBody?: string, tableRow?: string, checkbox?: string}' } } }, Row: { description: 'Custom Row component to be passed to table', table: { type: { summary: 'TableRow<T, U>' } } }, sortable: { description: 'Sort Icon will appear in right side of column\'s header with column level true sortable value', name: 'sortable', options: [false, true], control: 'boolean', table: { type: { summary: 'boolean' }, defaultValue: { summary: true } } }, showHeader: { description: 'Show or hide header', options: [false, true], control: 'boolean', table: { type: { summary: 'boolean' }, defaultValue: { summary: true } } }, defaultSortKey: { description: 'key name for which table should be sorted by default. headers array should have sortable true for that key object', control: { type: 'text' }, table: { type: { summary: 'string' } } }, // defaultSortDirection?: 'asc' | 'desc' | 'original', defaultSortDirection: { description: 'default sorting direction for the defaultSortKey column', control: { type: 'text' }, table: { type: { summary: '\'asc\' | \'desc\' | \'original\'' } } }, selectedKeys: { description: 'array of keys which are selected. This prop is also used to decide weather to show checkboxes or not. So, pass an empty array in case no row is selected.', control: { type: 'object' }, table: { type: { summary: 'string[]' } } }, disabledKeys: { description: 'array of keys which are disabled. This prop is used to disable the checkbox and its events for particular rows.', control: { type: 'object' }, table: { type: { summary: 'string[]' } } }, //onSelect?: (keys: string[]) => mixed onSelect: { description: 'callback function when any row is selected', action: 'selected', type: { summary: '(keys: string[]) => mixed' } }, idName: { description: 'name of key in row objects that has to be used as id' }, //onSort?: (key: $Keys<T> onSort: { description: 'callback function when any table is sorted', action: 'sorted', type: { summary: '(key: $Keys<T>, direction: SortDirection) => void' } }, isLoading: { description: 'to wait for data to populate in table and show loading state when entries array is empty.', options: [false, true], control: 'boolean', table: { type: { summary: 'boolean' }, defaultValue: { summary: false } } }, emptyText: { description: 'Provide component to be shown in case of empty data', table: { type: { summary: 'React.Component' } } }, customLoader: { description: 'Provide optional component to be shown in case of loading state. It will override the default loader.', table: { type: { summary: 'React.Component' } } }, disabled: { description: 'disable all the checkboxes of each row in the table', options: [false, true], control: 'boolean', table: { type: { summary: 'boolean' }, defaultValue: { summary: false } } }, borderRadius: { description: 'Border Radius of all four corners of the table. Default value is borderRadiusMedium i.e., 12px.', control: 'text', table: { type: { summary: 'string' }, defaultValue: { summary: '12px' } } }, stickyHeader: { description: 'Sticky header will stick the header to the top of the table when scrolling. This would only work if the table wrapper is scrollable.', control: 'boolean', table: { type: { summary: 'boolean' }, defaultValue: { summary: false } } }, enableInternalSorting: { description: 'Enable internal sorting for the table. This would sort the table data internally and not rely on the apis sorting', control: 'boolean', table: { type: { summary: '?boolean' }, defaultValue: { summary: true } } } }, parameters: { docs: { subtitle: 'Generates a Table component.', description: { component: ` \`\`\`js import { Table, StaticTable, DefaultRow, BasicSingleCell, DateCell, DoubleCell, SingleCell, MonogramCell, TableActionBar, ButtonCta, DropdownCta, TableTopBar, TableBottomBar } from "@spaced-out/ui-design-system/lib/components/Table"; \`\`\` Table component internally uses HTML Table, Thead, Tbody, Tr, Th and Td tags. It accepts entries and headers for showing table. The Table component takes care of lots of things for you but it's not a magical component, it wraps a lot of behavior, so you can have, for instance, a static table with no sorting options. StaticTables work just like regular tables, except they don't have interactive headers, so you lose sorting. But remember, a regular interactive table uses a static table under the hood, so how does it work? You can think of the StaticTable as a kind of controlled table component. except that traditionally we'd update the entries prop and rerender from that. That would work just fine, but instead we can just pass in the keys we care about. By using StaticTable, we can simplify filtering and sorting outside of the table while the table keeps a stable value reference to all the entities. To make any of rows selectable, we can do that by also passing in another property, selectedKeys. Let's say we want to also make any of the rows selectable, we can do that by also passing in another property, selectedKeys. ### Custom Cells It's possible you need to render out a custom cell, not just the default cell. There are two ways to do that. If you only need to modify a single cell, you can simply add a render: \`React.ComponentType<{data: T, extras?: U}>\` key to any given header item. This cell renderer receives the _whole row_ of data, not just the individual cell, it's up to you what to do with it. ### With Table Top, Bottom Bar and Actions You can attach a top, bottom and action bar to the table by adding \`TableTopBar\`, \`TableBottomBar\` and \`TableActionBar\` components. There are just semantic components which just provide padding and border standardization. You can add any component inside these components. These also accept **className** prop. ### Best Practices * A Data Table should always have a \`TableTopBar\` * Use \`stickyHeader\` prop to make the header sticky * Always assign a height to the table wrapper to make it scrollable * Manage the Table's Wrapper height effectively when using \`TableActionBar\` component * Always use local \`ButtonCta\` and \`DropdownCta\` for actions in Action bar. These components are just CSS Wrappers for Button and SimpleButtonDropdown components * Use ButtonCta component to add a button CTA in the \`TableActionBar\` * Use DropdownCta component to add a dropdown CTA button in the \`TableActionBar\` for screen width less than size1280(1280px) \`\`\`jsx <Table headers={TABLE_HEADERS} entries={TABLE_DATA} stickyHeader={true} classNames={{ wrapper: classify(css.tableWrapper, { [css.withActionBar]: getNumberOfEntriesSelected() > 0, }) }} /> \`\`\` \`\`\`css .tableWrapper { height: calc(sizeFullViewportHeight - size160); } .tableWrapper.withActionBar { height: calc((sizeFullViewportHeight - size160) - actionBarHeight); } \`\`\` ### Usage \`\`\`jsx const TABLE_HEADERS = [ { label: "Name", key: "name", sortable: true, className: css.mediumColumn }, { label: "Tel", key: "phone", sortable: true, className: css.mediumColumn }, ]; const TABLE_DATA = [ { id: "1", name: "Alice", phone: "123-456-7890", }, { id: "2", name: "Bob", phone: "987-654-3210", }, ]; export const _TableExample = (): React.Node => { return ( <div className={css.container}> {/* Top Bar with Filters */} <TableTopBar> <div className={css.left}> <SubTitleMedium className={css.title}>Basic Data Table</SubTitleMedium> <SearchInput /> </div> </TableTopBar> {/* Table */} <Table headers={TABLE_HEADERS} entries={TABLE_DATA} isLoading={isLoading} stickyHeader={true} borderRadius={'0'} selectedKeys={staticTableKeysAcrossPages[currPage] || []} onSelect={(keys) => { setSelectAllEntries(false); setStaticTableKeysAcrossPages({ ...staticTableKeysAcrossPages, [currPage]: keys, }); }} classNames={{ wrapper: classify(css.tableWrapper, { [css.withActionBar]: getNumberOfEntriesSelected() > 0, }) }} /> {/* Table Actions */} {getNumberOfEntriesSelected() > 0 && ( <TableActionBar className={css.actionBar}> <div className={css.leftActionSlot}> <ButtonCta onClick={() => { setSelectAllEntries(true); setStaticTableKeysAcrossPages({ ...staticTableKeysAcrossPages, [currPage]: tableEntries.map((entry) => entry.id), }); }} > Select all items </ButtonCta> {getNumberOfEntriesSelected() > 0 && ( <BodySmall color="inversePrimary"> {getNumberOfEntriesSelected() entries selected} </BodySmall> )} </div> <div className={classify(css.middleActionSlot, css.fullTable)}> <ButtonCta size="small" iconLeftName="folder-plus"> Add to group </ButtonCta> </div> <div className={classify(css.middleActionSlot, css.smallTable)}> <ButtonCta size="small" iconLeftName="folder-plus"> Add to group </ButtonCta> <DropdownCta ariaLabel="Icon Button Dropdown" iconLeftName="ellipsis" options={[ { iconLeft: 'layer-plus', key: '1', label: 'Create group', }, ]} selectedKeys={selectedKeys} onOptionSelect={() => setSelectedKeys([])} size="small" /> </div> <div className={css.rightActionSlot}> <ButtonCta iconLeftName="close" onClick={() => { setSelectAllEntries(false); setStaticTableKeysAcrossPages({}); }} > Close </ButtonCta> </div> </TableActionBar> )} {/* Bottom Bar with Pagination */} <TableBottomBar> <Pagination currentPage={currPage} totalPages={totalPages} onChange={(page) => { onMove(page || 1); }} > <SubTitleExtraSmall color="secondary"> Showing page {currPage} of {totalPages} </SubTitleExtraSmall> </Pagination> </TableBottomBar> </div> ); }; \`\`\` \`\`\`css @value actionBarHeight: size50; .container { width: sizeFluid; } .left { display: flex; align-items: center; } .title { margin-right: spaceMedium; } .mediumColumn { width: calc(sizeFluid / 5); } .tableWrapper { height: calc(sizeFullViewportHeight - size160); } .actionBar { display: flex; align-items: center; gap: spaceXSmall; } .tableWrapper.withActionBar { height: calc((sizeFullViewportHeight - size160) - actionBarHeight); } .leftActionSlot, .rightActionSlot, .middleActionSlot { display: flex; gap: spaceSmall; align-items: center; } .leftActionSlot, .rightActionSlot { width: calc(sizeFluid / 4); } .leftActionSlot { justify-content: flex-start; } .middleActionSlot { width: calc(sizeFluid / 2); justify-content: center; gap: spaceXXSmall; } .rightActionSlot { justify-content: flex-end; } .smallTable { display: none; } @media only screen and (max-width: size1280) { .fullTable { display: none; } .smallTable { display: flex; } } .dangerText { color: colorTextDanger; } .dangerText:hover, .dangerText:active, .dangerText:focus { color: colorTextDanger; } \`\`\` ` } }, storySource: { componentPath: '/src/components/Table/Table.js' } } };