diff --git a/src/components/TableToolsTable/TableToolsTable.js b/src/components/TableToolsTable/TableToolsTable.js index c4df589..2b58a0d 100644 --- a/src/components/TableToolsTable/TableToolsTable.js +++ b/src/components/TableToolsTable/TableToolsTable.js @@ -1,23 +1,17 @@ -import React from 'react'; +import React, { useContext } from 'react'; import propTypes from 'prop-types'; import { Pagination, PaginationVariant } from '@patternfly/react-core'; -import { - Table, - TableBody, - TableHeader, -} from '@patternfly/react-table/deprecated'; -import { - SkeletonTable, - ColumnManagementModal, -} from '@patternfly/react-component-groups'; - -import PrimaryToolbar from '@redhat-cloud-services/frontend-components/PrimaryToolbar'; +import { ColumnManagementModal } from '@patternfly/react-component-groups'; import TableToolbar from '@redhat-cloud-services/frontend-components/TableToolbar'; import useTableTools from '~/hooks/useTableTools'; -import { TableStateProvider, FilterModal, TableViewToggle } from '~/components'; +import { TableContext } from '~/hooks/useTableContext/constants'; +import { TableStateProvider, FilterModal } from '~/components'; + +import { variants, queryClient } from './constants'; const TableToolsTable = ({ + tableToolsTableVariant = 'table', loading: externalLoading, items: externalItems, error: externalError, @@ -28,20 +22,16 @@ const TableToolsTable = ({ // TODO I'm not sure if we need this level of customisation. // It might actually hurt in the long run. Consider removing until we really have the case where we need this toolbarProps: toolbarPropsProp, - tableHeaderProps, - tableBodyProps, tableToolbarProps, paginationProps, ...tablePropsRest }) => { + const { TableComponent, ToolbarComponent } = variants[tableToolsTableVariant]; const { - view, - loading, toolbarProps, - tableProps, filterModalProps, columnManagerModalProps, - tableViewToggleProps, + ...tableToolsProps } = useTableTools( externalLoading, externalItems, @@ -59,26 +49,9 @@ const TableToolsTable = ({ return ( <> - - {toolbarProps?.children} - {tableViewToggleProps && } - + - { - // TODO This is a bit hackish. We should rather have an indicator if data necessary for the current view is loading. - (view === 'rows' || (view === 'tree' && !treeTable)) && loading ? ( - title)} - /> - ) : ( - - - -
- ) - } + {toolbarProps.pagination && ( @@ -101,6 +74,7 @@ const TableToolsTable = ({ }; TableToolsTable.propTypes = { + tableToolsTableVariant: propTypes.string, items: propTypes.oneOfType([propTypes.array, propTypes.func]).isRequired, columns: propTypes.arrayOf( propTypes.shape({ diff --git a/src/components/TableToolsTable/TableToolsTable.stories.js b/src/components/TableToolsTable/TableToolsTable.stories.js index 53179a4..6cef406 100644 --- a/src/components/TableToolsTable/TableToolsTable.stories.js +++ b/src/components/TableToolsTable/TableToolsTable.stories.js @@ -37,6 +37,7 @@ const defaultOptions = { const argProps = { debug: propTypes.bool, + composable: propTypes.bool, columns: propTypes.array, filters: propTypes.array, filtered: propTypes.bool, @@ -62,6 +63,7 @@ const meta = { title: 'TableToolsTable', args: { debug: true, + composable: false, columns, filters, filtered: true, @@ -105,6 +107,7 @@ const emptyRows = (_kind, colSpan) => [ const CommonExample = ({ debug, + composable, columns, filters, filtered, @@ -144,6 +147,11 @@ const CommonExample = ({ return ( { + const composableTableProps = useTableToolsForComposable(props); + const { + columns, + rows, + view, + loading, + tableTree, + pagination, + bulkSelect, + expandable, + total, + } = composableTableProps; + + useEffect(() => { + console.group('ComposableTable props'); + console.warn( + 'Note: The "composable" variant is considered experimental and not fully featured yet.', + ); + + console.log('props', props); + console.log('composableTableProps', composableTableProps); + + console.groupEnd(); + }, [composableTableProps, props]); + + return (view === 'rows' || (view === 'tree' && !tableTree)) && loading ? ( + ( + + {title} + + ))} + /> + ) : ( + + + +
+ ); +}; + +export default ComposableTable; diff --git a/src/components/TableToolsTable/components/ComposableTable/components/Cell.js b/src/components/TableToolsTable/components/ComposableTable/components/Cell.js new file mode 100644 index 0000000..0d301db --- /dev/null +++ b/src/components/TableToolsTable/components/ComposableTable/components/Cell.js @@ -0,0 +1,20 @@ +import React from 'react'; +import propTypes from 'prop-types'; +import { Td } from '@patternfly/react-table'; + +const Cell = ({ row, column: { title, key, Component } }) => ( + + {Component ? ( + + ) : ( + row.item[key || title?.toLowerCase()] + )} + +); + +Cell.propTypes = { + row: propTypes.object, + column: propTypes.object, +}; + +export default Cell; diff --git a/src/components/TableToolsTable/components/ComposableTable/components/TableBody.js b/src/components/TableToolsTable/components/ComposableTable/components/TableBody.js new file mode 100644 index 0000000..7312e83 --- /dev/null +++ b/src/components/TableToolsTable/components/ComposableTable/components/TableBody.js @@ -0,0 +1,61 @@ +import React from 'react'; +import propTypes from 'prop-types'; +import { Tbody, Td, Tr } from '@patternfly/react-table'; + +import NoResultsTable from '~/components/NoResultsTable'; + +import Cell from './Cell'; + +const TableBody = ({ total, columns, rows, bulkSelect, expandable }) => ( + + {total === 0 ? ( + + + + + + ) : ( + rows?.map((row, idx) => ( + + {!!expandable && ( + expandable.onToggle(row?.item?.itemId), + }, + } + : {})} + /> + )} + {!!bulkSelect && ( + bulkSelect.selectOne(row?.item), + isSelected: bulkSelect.isItemSelected(row?.item?.itemId), + }, + } + : {})} + /> + )} + + {columns.map((column) => ( + + ))} + + )) + )} + +); + +TableBody.propTypes = { + rows: propTypes.array, + columns: propTypes.array, +}; + +export default TableBody; diff --git a/src/components/TableToolsTable/components/ComposableTable/components/TableHead.js b/src/components/TableToolsTable/components/ComposableTable/components/TableHead.js new file mode 100644 index 0000000..afb020d --- /dev/null +++ b/src/components/TableToolsTable/components/ComposableTable/components/TableHead.js @@ -0,0 +1,37 @@ +import React from 'react'; +import propTypes from 'prop-types'; +import { Thead, Tr, Th } from '@patternfly/react-table'; + +const TableHead = ({ columns, hasBulkSelect, isExpandable }) => ( + + + {hasBulkSelect && } + {isExpandable && } + {columns.map(({ title, sortable }, idx) => ( + { + console.log(_event, index, direction, ...args); + }, + }, + } + : {})} + > + {title} + + ))} + + +); + +TableHead.propTypes = { + columns: propTypes.object, + hasBulkSelect: propTypes.bool, +}; + +export default TableHead; diff --git a/src/components/TableToolsTable/components/ComposableTable/hooks/useTableToolsForComposable.js b/src/components/TableToolsTable/components/ComposableTable/hooks/useTableToolsForComposable.js new file mode 100644 index 0000000..3272cd4 --- /dev/null +++ b/src/components/TableToolsTable/components/ComposableTable/hooks/useTableToolsForComposable.js @@ -0,0 +1,51 @@ +import { useMemo } from 'react'; + +/** + * This hook is an adapter to put required props into the right place and + * form to be consumable by the ComposableTable components + * + * @param {object} [options] AsyncTableTools options + * + * @returns {object} + * + * @group Hooks + * + */ +const useTableToolsForComposable = ({ + loading, + view, + total, + bulkSelect, + expandable, + tableProps: { cells: columns, rows, tableTree }, + toolbarProps: { pagination }, +}) => { + const composableReturn = useMemo( + () => ({ + columns, + rows, + tableTree, + loading, + view, + pagination, + bulkSelect, + expandable, + total, + }), + [ + columns, + rows, + tableTree, + loading, + view, + pagination, + bulkSelect, + total, + expandable, + ], + ); + + return composableReturn; +}; + +export default useTableToolsForComposable; diff --git a/src/components/TableToolsTable/components/Table/Table.js b/src/components/TableToolsTable/components/Table/Table.js new file mode 100644 index 0000000..c8ee25e --- /dev/null +++ b/src/components/TableToolsTable/components/Table/Table.js @@ -0,0 +1,49 @@ +import React, { useEffect } from 'react'; +import propTypes from 'prop-types'; +import { + Table as PatternflyTable, + TableBody, + TableHeader, +} from '@patternfly/react-table/deprecated'; +import { SkeletonTable } from '@patternfly/react-component-groups'; + +import useTableToolsForTable from './hooks/useTableToolsForTable'; + +const Table = (props) => { + const tableToolsProps = useTableToolsForTable(props); + const { view, loading, toolbarProps, tableProps, tableTree } = + tableToolsProps; + + useEffect(() => { + console.group('Table props'); + + console.log('props', props); + console.log('tableToolsProps', tableToolsProps); + + console.groupEnd(); + }, [tableToolsProps, props]); + + // TODO This is a bit hackish. We should rather have an indicator if data necessary for the current view is loading. + return (view === 'rows' || (view === 'tree' && !tableTree)) && loading ? ( + title)} + /> + ) : ( + + + + + ); +}; + +Table.propTypes = { + view: propTypes.string, + loading: propTypes.bool, + hasTreeTable: propTypes.bool, + toolbarProps: propTypes.object, + tableProps: propTypes.object, + tableTree: propTypes.object, +}; + +export default Table; diff --git a/src/components/TableToolsTable/components/Table/hooks/useBulkSelectProps.js b/src/components/TableToolsTable/components/Table/hooks/useBulkSelectProps.js new file mode 100644 index 0000000..038f589 --- /dev/null +++ b/src/components/TableToolsTable/components/Table/hooks/useBulkSelectProps.js @@ -0,0 +1,17 @@ +import { useMemo } from 'react'; + +const useBulkSelectProps = ({ total, selectOne }) => { + const props = useMemo( + () => ({ + tableProps: { + onSelect: total > 0 ? selectOne : undefined, + canSelectAll: false, + }, + }), + [total, selectOne], + ); + + return props; +}; + +export default useBulkSelectProps; diff --git a/src/components/TableToolsTable/components/Table/hooks/useTableToolsForTable.js b/src/components/TableToolsTable/components/Table/hooks/useTableToolsForTable.js new file mode 100644 index 0000000..d892cc8 --- /dev/null +++ b/src/components/TableToolsTable/components/Table/hooks/useTableToolsForTable.js @@ -0,0 +1,40 @@ +import { useMemo } from 'react'; + +import useBulkSelectProps from './useBulkSelectProps'; + +/** + * This hook is an adapter to put required props into the right place and + * form to be consumable by the ComposableTable components + * + * @param {object} [options] AsyncTableTools options + * + * @returns {object} + * + * @group Hooks + * + */ +const useTableToolsForTable = (tableToolsProps) => { + const { loading, total, bulkSelect, toolbarProps, tableProps } = + tableToolsProps; + console.log('ta', tableToolsProps); + const { tableProps: bulkSelectTableProps } = useBulkSelectProps({ + ...bulkSelect, + total, + }); + + const properProps = useMemo(() => { + return { + toolbarProps: { + ...toolbarProps, + }, + tableProps: { + ...bulkSelectTableProps, + ...tableProps, + }, + }; + }, [toolbarProps, tableProps, bulkSelectTableProps]); + + return properProps; +}; + +export default useTableToolsForTable; diff --git a/src/components/TableToolsTable/components/Toolbar.js b/src/components/TableToolsTable/components/Toolbar.js new file mode 100644 index 0000000..5dfbdd8 --- /dev/null +++ b/src/components/TableToolsTable/components/Toolbar.js @@ -0,0 +1,19 @@ +import React from 'react'; +import propTypes from 'prop-types'; + +import PrimaryToolbar from '@redhat-cloud-services/frontend-components/PrimaryToolbar'; + +import { TableViewToggle } from '~/components'; + +const Toolbar = ({ toolbarProps, tableViewToggleProps }) => ( + + {tableViewToggleProps && } + +); + +Toolbar.propTypes = { + toolbarProps: propTypes.object, + tableViewToggleProps: propTypes.object, +}; + +export default Toolbar; diff --git a/src/components/TableToolsTable/components/index.js b/src/components/TableToolsTable/components/index.js new file mode 100644 index 0000000..05877e5 --- /dev/null +++ b/src/components/TableToolsTable/components/index.js @@ -0,0 +1,3 @@ +export { default as Toolbar } from './Toolbar'; +export { default as ComposableTable } from './ComposableTable/ComposableTable'; +export { default as Table } from './Table/Table'; diff --git a/src/components/TableToolsTable/constants.js b/src/components/TableToolsTable/constants.js new file mode 100644 index 0000000..26c1a77 --- /dev/null +++ b/src/components/TableToolsTable/constants.js @@ -0,0 +1,16 @@ +import { QueryClient } from '@tanstack/react-query'; + +import { ComposableTable, Table, Toolbar } from './components'; + +export const variants = { + table: { + TableComponent: Table, + ToolbarComponent: Toolbar, + }, + composable: { + TableComponent: ComposableTable, + ToolbarComponent: Toolbar, + }, +}; + +export const queryClient = new QueryClient(); diff --git a/src/hooks/useBulkSelect/__snapshots__/useBulkSelect.test.js.snap b/src/hooks/useBulkSelect/__snapshots__/useBulkSelect.test.js.snap index 4bfb68b..0621d67 100644 --- a/src/hooks/useBulkSelect/__snapshots__/useBulkSelect.test.js.snap +++ b/src/hooks/useBulkSelect/__snapshots__/useBulkSelect.test.js.snap @@ -3,9 +3,9 @@ exports[`useBulkSelect returns a bulk select configuration 1`] = ` { "current": { - "tableProps": { - "canSelectAll": false, - "onSelect": undefined, + "bulkSelect": { + "isItemSelected": [Function], + "selectOne": [Function], }, "tableView": { "deselect": [Function], diff --git a/src/hooks/useBulkSelect/useBulkSelect.js b/src/hooks/useBulkSelect/useBulkSelect.js index 595d36f..0d37954 100644 --- a/src/hooks/useBulkSelect/useBulkSelect.js +++ b/src/hooks/useBulkSelect/useBulkSelect.js @@ -124,11 +124,15 @@ const useBulkSelect = ({ select, deselect, }, + ...(enableBulkSelect ? { - tableProps: { - onSelect: total > 0 ? selectOne : undefined, - canSelectAll: false, + bulkSelect: { + selectOne: (item) => { + console.log('onSelect', item); + return selectOne(undefined, undefined, undefined, { item }); + }, + isItemSelected, }, toolbarProps: { bulkSelect: { diff --git a/src/hooks/useExpandable/useExpandable.js b/src/hooks/useExpandable/useExpandable.js index 33a79bb..362ca6a 100644 --- a/src/hooks/useExpandable/useExpandable.js +++ b/src/hooks/useExpandable/useExpandable.js @@ -69,6 +69,12 @@ const useExpandable = (options) => { }, ...(enableExpandingRow ? { + expandable: { + onToggle: (itemId) => { + toggle(itemId); + }, + isExpanded: isItemOpen, + }, tableProps: { onCollapse, }, diff --git a/src/hooks/useTableTools/useTableTools.js b/src/hooks/useTableTools/useTableTools.js index 0ec7d02..dd3f2e2 100644 --- a/src/hooks/useTableTools/useTableTools.js +++ b/src/hooks/useTableTools/useTableTools.js @@ -75,6 +75,7 @@ const useTableTools = ( useFilterConfig(options); const { + expandable, tableProps: expandableTableProps, tableView: expandableTableViewOptions, } = useExpandable(options); @@ -85,6 +86,7 @@ const useTableTools = ( }); const { + bulkSelect, toolbarProps: bulkSelectToolbarProps, tableProps: bulkSelectTableProps, tableView: bulkSelectTableViewOptions, @@ -210,6 +212,9 @@ const useTableTools = ( return { view, loading, + total, + bulkSelect, + expandable, toolbarProps, tableProps, columnManagerModalProps,