diff --git a/packages/react-core/src/components/Toolbar/ToolbarToggleGroup.tsx b/packages/react-core/src/components/Toolbar/ToolbarToggleGroup.tsx index 3a69a25ad7b..05a2f40b1a2 100644 --- a/packages/react-core/src/components/Toolbar/ToolbarToggleGroup.tsx +++ b/packages/react-core/src/components/Toolbar/ToolbarToggleGroup.tsx @@ -149,7 +149,7 @@ class ToolbarToggleGroup extends React.Component { aria-label="Show Filters" {...(_isExpanded && { 'aria-expanded': true })} aria-haspopup={_isExpanded && this.isContentPopup()} - aria-controls={expandableContentId} + aria-controls={_isExpanded ? expandableContentId : undefined} ref={this.toggleRef} > {toggleIcon} diff --git a/packages/react-core/src/components/Toolbar/__tests__/__snapshots__/Toolbar.test.tsx.snap b/packages/react-core/src/components/Toolbar/__tests__/__snapshots__/Toolbar.test.tsx.snap index 13b971baed6..c5bdf52faec 100644 --- a/packages/react-core/src/components/Toolbar/__tests__/__snapshots__/Toolbar.test.tsx.snap +++ b/packages/react-core/src/components/Toolbar/__tests__/__snapshots__/Toolbar.test.tsx.snap @@ -69,7 +69,6 @@ exports[`Toolbar should render with custom chip content 1`] = ` class="pf-v5-c-toolbar__toggle" > + + } + onClose={handleModalToggle} + actions={[ + , + + ]} + > + + + + + + + + ]} + /> + + + + + + + + + ]} + /> + + + + + + + + + ]} + /> + + + + + + + + + ]} + /> + + + + + + + + + ]} + /> + + + + + + + + + ]} + /> + + + + + + + + + ]} + /> + + + + + + + + + ]} + /> + + + + + ); + + const renderLabel = (labelText: string): JSX.Element => { + switch (labelText) { + case 'Running': + return ; + case 'Stopped': + return ; + case 'Needs maintenance': + return ; + case 'Down': + return ; + default: + return <>; + } + }; + + const toolbarItems = ( + + + + + + + + + Name + + + + + + + + + + + + + + + + + {renderPagination('top', false)} + + + + ); + + return ( + + + + + {toolbarItems} + + + + {managedColumns.map((column, columnIndex) => ( + + ))} + + + + {paginatedRows.map((row, rowIndex) => ( + + <> + {Object.entries(row).map(([key, value]) => + // eslint-disable-next-line no-nested-ternary + key === 'status' ? ( + // eslint-disable-next-line react/jsx-key + + ) : key === 'url' ? ( + + ) : ( + + ) + )} + + + ))} + +
{column}
+ {renderLabel(value as string)} + + + {row.url} + + + {value as string} +
+ {renderPagination('bottom', false)} + {renderModal()} +
+
+
+
+ ); +}; diff --git a/packages/react-table/src/demos/examples/TableColumnManagementWithDraggable.tsx b/packages/react-table/src/demos/examples/TableColumnManagementWithDraggable.tsx new file mode 100644 index 00000000000..41123b47396 --- /dev/null +++ b/packages/react-table/src/demos/examples/TableColumnManagementWithDraggable.tsx @@ -0,0 +1,641 @@ +import React from 'react'; +import { + Button, + DataList, + DataListCheck, + DataListControl, + DataListDragButton, + DataListItem, + DataListItemRow, + DataListCell, + DataListItemCells, + Toolbar, + ToolbarContent, + ToolbarItem, + Modal, + OverflowMenu, + OverflowMenuGroup, + OverflowMenuItem, + Text, + TextContent, + TextVariants, + MenuToggle +} from '@patternfly/react-core'; +import { Table as TableDeprecated, TableHeader, TableBody } from '@patternfly/react-table/deprecated'; +import CodeIcon from '@patternfly/react-icons/dist/esm/icons/code-icon'; +import CodeBranchIcon from '@patternfly/react-icons/dist/esm/icons/code-branch-icon'; +import CubeIcon from '@patternfly/react-icons/dist/esm/icons/cube-icon'; +import FilterIcon from '@patternfly/react-icons/dist/esm/icons/filter-icon'; +import SortAmountDownIcon from '@patternfly/react-icons/dist/esm/icons/sort-amount-down-icon'; + +interface RowType { + cells: ( + | { + title: React.JSX.Element; + props: { + column: string; + }; + } + | { + title: string; + props: { + column: string; + }; + } + )[]; +} + +export const TableColumnManagementWithDraggable: React.FunctionComponent = () => { + const actions = [ + { + title: Link + }, + { + title: 'Action' + }, + { + isSeparator: true + }, + { + title: Separated link + } + ]; + const defaultColumns: string[] = ['Repositories', 'Branches', 'Pull requests', 'Workspaces', 'Last commit', '']; + const defaultRows = [ + { + cells: [ + { + title: ( + +
Node 1
+ siemur/test-space +
+ ), + props: { column: 'Repositories' } + }, + { + title: ( + + 10 + + ), + props: { column: 'Branches' } + }, + { + title: ( + + 25 + + ), + props: { column: 'Pull requests' } + }, + { + title: ( + + 5 + + ), + props: { column: 'Workspaces' } + }, + { + title: '2 days ago', + props: { column: 'Last commit' } + }, + { + title: ( + + Action link + + ), + props: { column: '' } + } + ] + }, + { + cells: [ + { + title: ( + +
Node 2
+ siemur/test-space +
+ ), + props: { column: 'Repositories' } + }, + { + title: ( + + 8 + + ), + props: { column: 'Branches' } + }, + { + title: ( + + 30 + + ), + props: { column: 'Pull requests' } + }, + { + title: ( + + 2 + + ), + props: { column: 'Workspaces' } + }, + { + title: '2 days ago', + props: { column: 'Last commit' } + }, + { + title: ( + + Action link + + ), + props: { column: '' } + } + ] + }, + { + cells: [ + { + title: ( + +
Node 3
+ siemur/test-space +
+ ), + props: { column: 'Repositories' } + }, + { + title: ( + + 12 + + ), + props: { column: 'Branches' } + }, + { + title: ( + + 48 + + ), + props: { column: 'Pull requests' } + }, + { + title: ( + + 13 + + ), + props: { column: 'Workspaces' } + }, + { + title: '30 days ago', + props: { column: 'Last commit' } + }, + { + title: ( + + Action link + + ), + props: { column: '' } + } + ] + }, + { + cells: [ + { + title: ( + +
Node 4
+ siemur/test-space +
+ ), + props: { column: 'Repositories' } + }, + { + title: ( + + 3 + + ), + props: { column: 'Branches' } + }, + { + title: ( + + 8 + + ), + props: { column: 'Pull requests' } + }, + { + title: ( + + 20 + + ), + props: { column: 'Workspaces' } + }, + { + title: '8 days ago', + props: { column: 'Last commit' } + }, + { + title: ( + + Action link + + ), + props: { column: '' } + } + ] + }, + { + cells: [ + { + title: ( + +
Node 5
+ siemur/test-space +
+ ), + props: { column: 'Repositories' } + }, + { + title: ( + + 34 + + ), + props: { column: 'Branches' } + }, + { + title: ( + + 21 + + ), + props: { column: 'Pull requests' } + }, + { + title: ( + + 26 + + ), + props: { column: 'Workspaces' } + }, + { + title: '2 days ago', + props: { column: 'Last commit' } + }, + { + title: ( + + Action link + + ), + props: { column: '' } + } + ] + } + ]; + + const [filters, setFilters] = React.useState([]); + const [filteredColumns, setFilteredColumns] = React.useState([]); + const [columns, setColumns] = React.useState(defaultColumns); + const [rows, setRows] = React.useState(defaultRows); + const [isModalOpen, setIsModalOpen] = React.useState(false); + const [check1, setCheck1] = React.useState(true); + const [check2, setCheck2] = React.useState(true); + const [check3, setCheck3] = React.useState(true); + const [check4, setCheck4] = React.useState(true); + const [check5, setCheck5] = React.useState(true); + + const matchCheckboxNameToColumn = (name: string) => { + switch (name) { + case 'check1': + return 'Repositories'; + case 'check2': + return 'Branches'; + case 'check3': + return 'Pull requests'; + case 'check4': + return 'Workspaces'; + case 'check5': + return 'Last commit'; + default: + return ''; + } + }; + + const filterData = (checked: boolean, name: string) => { + if (checked) { + const updatedFilters = filters.filter((item) => item !== name); + const updatedFilteredColumns = defaultColumns.filter((column) => !updatedFilters.includes(column)); + setFilters(updatedFilters); + setFilteredColumns(updatedFilteredColumns); + } else { + const updatedFilters = filters; + updatedFilters.push(name); + const updatedFilteredColumns = columns.filter((column) => !filters.includes(column)); + setFilters(updatedFilters); + setFilteredColumns(updatedFilteredColumns); + } + }; + const unfilterAllData = () => { + setFilters([]); + setFilteredColumns(defaultColumns); + }; + const handleChange = (event: React.FormEvent, checked: boolean) => { + const target = event.currentTarget; + const name = target.name; + + const value = target.type === 'checkbox' ? checked : !!target.value; + filterData(checked, matchCheckboxNameToColumn(target.name)); + switch (name) { + case 'check1': + setCheck1(value); + break; + case 'check2': + setCheck2(value); + break; + case 'check3': + setCheck3(value); + break; + case 'check4': + setCheck4(value); + break; + case 'check5': + setCheck5(value); + break; + } + }; + const handleModalToggle = (_event: KeyboardEvent | React.MouseEvent) => { + setIsModalOpen(!isModalOpen); + }; + const onSave = () => { + // concat empty string at the end for actions column + const filteredOrderedColumns: string[] = columns + .filter((col) => filteredColumns.length === 0 || filteredColumns.indexOf(col as string) > -1) + .concat(['']); + const orderedRows: RowType[] = []; + defaultRows.forEach((row) => { + const updatedCells = row.cells + .filter((cell) => filteredOrderedColumns.indexOf(cell.props.column) > -1) + .sort((cellA, cellB) => { + const indexA = filteredOrderedColumns.indexOf(cellA.props.column); + const indexB = filteredOrderedColumns.indexOf(cellB.props.column); + if (indexA < indexB) { + return -1; + } + if (indexA > indexB) { + return 1; + } + // a must be equal to b + return 0; + }); + orderedRows.push({ + cells: updatedCells + }); + }); + setColumns(filteredOrderedColumns as string[]); + setRows(orderedRows); + setIsModalOpen(!isModalOpen); + }; + + const selectAllColumns = () => { + unfilterAllData(); + setCheck1(true); + setCheck2(true); + setCheck3(true); + setCheck4(true); + setCheck5(true); + }; + + const renderModal = () => ( + + Selected categories will be displayed in the table. + + + } + onClose={handleModalToggle} + actions={[ + , + + ]} + > + + + + + + + + + + + ]} + /> + + + + + + + + + + + + ]} + /> + + + + + + + + + + + + ]} + /> + + + + + + + + + + + + ]} + /> + + + + + + + + + + + + ]} + /> + + + + + ); + + const toolbarItems = ( + + + + + + + + Name + + + + + + + + + + + + + + + + + + + ); + + return ( + + + {toolbarItems} + + } + aria-label="Column Management with Draggable Table" + cells={columns} + rows={rows} + actions={actions} + > + + + + {renderModal()} + + ); +}; diff --git a/packages/react-table/src/docs/demos/table-demos/Compact.jsx b/packages/react-table/src/demos/examples/TableCompact.tsx similarity index 80% rename from packages/react-table/src/docs/demos/table-demos/Compact.jsx rename to packages/react-table/src/demos/examples/TableCompact.tsx index 381fb629729..d31ca17a8e1 100644 --- a/packages/react-table/src/docs/demos/table-demos/Compact.jsx +++ b/packages/react-table/src/demos/examples/TableCompact.tsx @@ -1,9 +1,7 @@ import React from 'react'; - import { Button, Card, - Label, MenuToggle, MenuToggleElement, Pagination, @@ -13,29 +11,45 @@ import { Toolbar, ToolbarContent, ToolbarGroup, - ToolbarItem + ToolbarItem, + Label, + PaginationVariant } from '@patternfly/react-core'; import { Table, TableText, Thead, Tr, Th, Tbody, Td } from '@patternfly/react-table'; import FilterIcon from '@patternfly/react-icons/dist/esm/icons/filter-icon'; -import { DashboardWrapper } from '@patternfly/react-core/src/demos/DashboardWrapper'; -import { rows, columns } from '@patternfly/react-table/src/docs/demos/table-demos/sampleData'; +import { rows, columns } from '@patternfly/react-table/dist/esm/demos/sampleData'; +import { DashboardWrapper } from '@patternfly/react-core/dist/esm/demos/DashboardWrapper'; -export const CompactTable = () => { - const [isSelectOpen, setIsSelectOpen] = React.useState(false); - const [page, setPage] = React.useState(1); - const [perPage, setPerPage] = React.useState(10); +export const TableCompact: React.FunctionComponent = () => { + const [isSelectOpen, setIsSelectOpen] = React.useState(false); + const [page, setPage] = React.useState(1); + const [perPage, setPerPage] = React.useState(10); const [paginatedRows, setPaginatedRows] = React.useState(rows.slice(0, 10)); - const handleSetPage = (_evt, newPage, perPage, startIdx, endIdx) => { + + const handleSetPage = ( + _evt: React.MouseEvent | React.KeyboardEvent | MouseEvent, + newPage: number, + _perPage: number, + startIdx: number, + endIdx: number + ) => { setPaginatedRows(rows.slice(startIdx, endIdx)); setPage(newPage); }; - handlePerPageSelect = (_evt, newPerPage, newPage, startIdx, endIdx) => { + + const handlePerPageSelect = ( + _evt: React.MouseEvent | React.KeyboardEvent | MouseEvent, + newPerPage: number, + newPage: number, + startIdx: number, + endIdx: number + ) => { setPaginatedRows(rows.slice(startIdx, endIdx)); setPage(newPage); setPerPage(newPerPage); }; - const renderPagination = (variant, isCompact) => ( + const renderPagination = (variant: string, isCompact: boolean) => ( { perPage={perPage} onSetPage={handleSetPage} onPerPageSelect={handlePerPageSelect} - variant={variant} + variant={variant as PaginationVariant} titles={{ paginationAriaLabel: `${variant} pagination` }} @@ -63,7 +77,7 @@ export const CompactTable = () => { )} isOpen={isSelectOpen} - onOpenChange={(isOpen) => setIsSelectOpen(isOpen)} + onOpenChange={(isOpen: boolean) => setIsSelectOpen(isOpen)} onSelect={() => setIsSelectOpen(!isSelectOpen)} > {[ @@ -92,7 +106,7 @@ export const CompactTable = () => { ); - const renderLabel = (labelText) => { + const renderLabel = (labelText: string) => { switch (labelText) { case 'Running': return ; @@ -102,8 +116,11 @@ export const CompactTable = () => { return ; case 'Down': return ; + default: + return <>; } }; + return ( @@ -146,7 +163,6 @@ export const CompactTable = () => { ))} - {renderPagination('bottom', false)} diff --git a/packages/react-table/src/docs/demos/table-demos/CompoundExpansion.jsx b/packages/react-table/src/demos/examples/TableCompoundExpansion.tsx similarity index 89% rename from packages/react-table/src/docs/demos/table-demos/CompoundExpansion.jsx rename to packages/react-table/src/demos/examples/TableCompoundExpansion.tsx index c66ac7e2509..7554acad0ab 100644 --- a/packages/react-table/src/docs/demos/table-demos/CompoundExpansion.jsx +++ b/packages/react-table/src/demos/examples/TableCompoundExpansion.tsx @@ -14,16 +14,17 @@ import { Pagination, PageSection, Select, - SelectOption + SelectOption, + PaginationVariant } from '@patternfly/react-core'; import CodeBranchIcon from '@patternfly/react-icons/dist/esm/icons/code-branch-icon'; import CodeIcon from '@patternfly/react-icons/dist/esm/icons/code-icon'; import CubeIcon from '@patternfly/react-icons/dist/esm/icons/cube-icon'; -import { DashboardWrapper } from '@patternfly/react-core/src/demos/DashboardWrapper'; +import { DashboardWrapper } from '@patternfly/react-core/dist/esm/demos/DashboardWrapper'; import FilterIcon from '@patternfly/react-icons/dist/esm/icons/filter-icon'; import spacing from '@patternfly/react-styles/css/utilities/Spacing/spacing'; -export const CompoundExpandable = () => { +export const TableCompoundExpansion: React.FunctionComponent = () => { // In real usage, this data would come from some external source like an API via props. const [isSelectOpen, setIsSelectOpen] = React.useState(false); @@ -55,7 +56,7 @@ export const CompoundExpandable = () => { {items.map((item) => ( - + {item.description} {item.date} {item.status} @@ -69,7 +70,7 @@ export const CompoundExpandable = () => { ); }; - const renderPagination = (variant, isCompact) => ( + const renderPagination = (variant: 'top' | 'bottom' | PaginationVariant, isCompact: boolean) => ( { )} isOpen={isSelectOpen} - onOpenChange={(isOpen) => setIsSelectOpen(isOpen)} + onOpenChange={(isOpen: boolean) => setIsSelectOpen(isOpen)} onSelect={() => setIsSelectOpen(!isSelectOpen)} > {[ @@ -137,7 +138,19 @@ export const CompoundExpandable = () => { } ]; - const repositories = [ + interface Repo { + name: string; + branches: number; + prs: number; + workspaces: number; + lastCommit: string; + } + + interface IDictionary { + [Key: string]: T; + } + + const repositories: Repo[] = [ { name: 'siemur/test-space', branches: 10, @@ -148,7 +161,7 @@ export const CompoundExpandable = () => { { name: 'siemur/test-space-2', branches: 3, prs: 4, workspaces: 4, lastCommit: '20 minutes' } ]; - const columnNames = { + const columnNames: IDictionary = { name: 'Repositories', branches: 'Branches', prs: 'Pull requests', @@ -159,11 +172,11 @@ export const CompoundExpandable = () => { // In this example, expanded cells are tracked by the repo and property names from each row. This could be any pair of unique identifiers. // This is to prevent state from being based on row and column order index in case we later add sorting and rearranging columns. // Note that this behavior is very similar to selection state. - const [expandedCells, setExpandedCells] = React.useState({ + const [expandedCells, setExpandedCells] = React.useState>({ 'siemur/test-space': 'branches' // Default to the first cell of the first row being expanded }); - const setCellExpanded = (repo, columnKey, isExpanding = true) => { - const newExpandedCells = { ...expandedCells }; + const setCellExpanded = (repo: Repo, columnKey: string, isExpanding = true) => { + const newExpandedCells: IDictionary = { ...expandedCells }; if (isExpanding) { newExpandedCells[repo.name] = columnKey; } else { @@ -171,7 +184,7 @@ export const CompoundExpandable = () => { } setExpandedCells(newExpandedCells); }; - const compoundExpandParams = (repo, columnKey, rowIndex, columnIndex) => ({ + const compoundExpandParams = (repo: Repo, columnKey: string, rowIndex: number, columnIndex: number) => ({ isExpanded: expandedCells[repo.name] === columnKey, onToggle: () => setCellExpanded(repo, columnKey, expandedCells[repo.name] !== columnKey), expandId: 'compound-expandable-demo', diff --git a/packages/react-table/src/demos/examples/TableEmptyStateDefault.tsx b/packages/react-table/src/demos/examples/TableEmptyStateDefault.tsx new file mode 100644 index 00000000000..ab9febef731 --- /dev/null +++ b/packages/react-table/src/demos/examples/TableEmptyStateDefault.tsx @@ -0,0 +1,60 @@ +import React from 'react'; +import { Table, Thead, Tr, Th, Tbody, Td } from '@patternfly/react-table'; +import { + Bullseye, + Button, + Card, + EmptyState, + EmptyStateVariant, + EmptyStateIcon, + EmptyStateBody, + EmptyStateHeader, + EmptyStateFooter, + EmptyStateActions, + PageSection +} from '@patternfly/react-core'; +import SearchIcon from '@patternfly/react-icons/dist/esm/icons/search-icon'; +import { DashboardWrapper } from '@patternfly/react-core/dist/esm/demos/DashboardWrapper'; + +export const TableEmptyStateDefault: React.FunctionComponent = () => ( + + + + + + + + + + + + + + + + + + +
RepositoriesBranchesPull requestsWorkspacesLast commit
+ + + } + titleText="No results found" + headingLevel="h2" + /> + + No results match this filter criteria. Clear all filters and try again. + + + + + + + + +
+
+
+
+); diff --git a/packages/react-table/src/demos/examples/TableEmptyStateError.tsx b/packages/react-table/src/demos/examples/TableEmptyStateError.tsx new file mode 100644 index 00000000000..c291b619f2b --- /dev/null +++ b/packages/react-table/src/demos/examples/TableEmptyStateError.tsx @@ -0,0 +1,53 @@ +import React from 'react'; +import { Table, Thead, Tr, Th, Tbody, Td } from '@patternfly/react-table'; +import { + Bullseye, + Card, + EmptyState, + EmptyStateVariant, + EmptyStateIcon, + EmptyStateBody, + EmptyStateHeader, + PageSection +} from '@patternfly/react-core'; +import ExclamationCircleIcon from '@patternfly/react-icons/dist/esm/icons/exclamation-circle-icon'; +import globalDangerColor200 from '@patternfly/react-tokens/dist/esm/global_danger_color_200'; +import { DashboardWrapper } from '@patternfly/react-core/dist/esm/demos/DashboardWrapper'; + +export const TableEmptyStateError: React.FunctionComponent = () => ( + + + + + + + + + + + + + + + + + + +
RepositoriesBranchesPull requestsWorkspacesLast commit
+ + + } + headingLevel="h2" + /> + + There was an error retrieving data. Check your connection and reload the page. + + + +
+
+
+
+); diff --git a/packages/react-table/src/demos/examples/TableEmptyStateLoading.tsx b/packages/react-table/src/demos/examples/TableEmptyStateLoading.tsx new file mode 100644 index 00000000000..229a6a327fc --- /dev/null +++ b/packages/react-table/src/demos/examples/TableEmptyStateLoading.tsx @@ -0,0 +1,43 @@ +import React from 'react'; +import { Table, Thead, Tr, Th, Tbody, Td } from '@patternfly/react-table'; +import { + Bullseye, + Card, + EmptyState, + EmptyStateIcon, + EmptyStateHeader, + PageSection, + Spinner +} from '@patternfly/react-core'; +import { DashboardWrapper } from '@patternfly/react-core/dist/esm/demos/DashboardWrapper'; + +export const TableEmptyStateLoading: React.FunctionComponent = () => ( + + + + + + + + + + + + + + + + + + +
RepositoriesBranchesPull requestsWorkspacesLast commit
+ + + } /> + + +
+
+
+
+); diff --git a/packages/react-table/src/docs/demos/table-demos/ExpandCollapseAll.jsx b/packages/react-table/src/demos/examples/TableExpandCollapseAll.tsx similarity index 91% rename from packages/react-table/src/docs/demos/table-demos/ExpandCollapseAll.jsx rename to packages/react-table/src/demos/examples/TableExpandCollapseAll.tsx index f7d6d5f18fb..f8e903be6c8 100644 --- a/packages/react-table/src/docs/demos/table-demos/ExpandCollapseAll.jsx +++ b/packages/react-table/src/demos/examples/TableExpandCollapseAll.tsx @@ -1,11 +1,20 @@ -import React from 'react'; +import React, { ReactNode } from 'react'; import { Card, Label, PageSection, TextVariants, Text, TextContent } from '@patternfly/react-core'; import { Table, Thead, Tbody, Tr, Th, Td, ExpandableRowContent } from '@patternfly/react-table'; -import { DashboardWrapper } from '@patternfly/react-core/src/demos/DashboardWrapper'; +import { DashboardWrapper } from '@patternfly/react-core/dist/esm/demos/DashboardWrapper'; const expandableColumns = ['Servers', 'Threads', 'Applications', 'Workspaces', 'Status']; -const serverData = [ +interface Server { + name: string; + threads: number; + applications: number; + workspaces: number; + status: { title: ReactNode }; + details: ReactNode; +} + +const serverData: Server[] = [ { name: 'US-Node 1', threads: 18, @@ -110,7 +119,7 @@ const serverData = [ const initialExpandedServerNames = ['US-Node 2']; // Default to expanded -const ExpandCollapseAllTableDemo = () => { +export const TableExpandCollapseAll: React.FunctionComponent = () => { const [areAllExpanded, setAreAllExpanded] = React.useState(false); const [collapseAllAriaLabel, setCollapseAllAriaLabel] = React.useState('Expand all'); const [expandedServerNames, setExpandedServerNames] = React.useState(initialExpandedServerNames); @@ -121,20 +130,18 @@ const ExpandCollapseAllTableDemo = () => { setCollapseAllAriaLabel(allExpanded ? 'Collapse all' : 'Expand all'); }, [expandedServerNames]); - const setServerExpanded = (server, isExpanding) => { + const setServerExpanded = (server: Server, isExpanding: boolean) => { const otherExpandedServerNames = expandedServerNames.filter((r) => r !== server.name); setExpandedServerNames(isExpanding ? [...otherExpandedServerNames, server.name] : otherExpandedServerNames); }; - const isServerExpanded = (server) => { - return expandedServerNames.includes(server.name); - }; + const isServerExpanded = (server: Server) => expandedServerNames.includes(server.name); // We want to be able to reference the original data object based on row index. But because an expanded // row takes up two row indexes, servers[rowIndex] will not be accurate like it would in a normal table. // One solution to this is to create an array of data objects indexed by the displayed row index. - const onCollapseAll = (_event, _rowIndex, isOpen) => { + const onCollapseAll = (_event: any, _rowIndex: number, isOpen: boolean) => { setExpandedServerNames(isOpen ? [...serverData.map((server) => server.name)] : []); }; @@ -154,7 +161,7 @@ const ExpandCollapseAllTableDemo = () => { diff --git a/packages/react-table/src/demos/examples/TableFilterable.tsx b/packages/react-table/src/demos/examples/TableFilterable.tsx new file mode 100644 index 00000000000..0d40da804fe --- /dev/null +++ b/packages/react-table/src/demos/examples/TableFilterable.tsx @@ -0,0 +1,394 @@ +import React from 'react'; +import { + Badge, + Button, + Bullseye, + EmptyState, + EmptyStateActions, + EmptyStateBody, + EmptyStateIcon, + EmptyStateHeader, + EmptyStateFooter, + Label, + MenuToggle, + MenuToggleElement, + Toolbar, + ToolbarItem, + ToolbarContent, + ToolbarFilter, + ToolbarToggleGroup, + ToolbarGroup, + Select, + SelectOption, + SearchInput, + ToolbarChipGroup +} from '@patternfly/react-core'; +import SearchIcon from '@patternfly/react-icons/dist/esm/icons/search-icon'; +import FilterIcon from '@patternfly/react-icons/dist/esm/icons/filter-icon'; +import { Table, TableText, Thead, Tr, Th, Tbody, Td } from '@patternfly/react-table'; +import { rows, columns } from '@patternfly/react-table/dist/esm/demos/sampleData'; + +export const TableFilterable: React.FunctionComponent = () => { + const [filters, setFilters] = React.useState>({ location: [], name: [], status: [] }); + const [currentCategory, setCurrentCategory] = React.useState('Status'); + const [isFilterDropdownOpen, setIsFilterDropdownOpen] = React.useState(false); + const [isCategoryDropdownOpen, setIsCategoryDropdownOpen] = React.useState(false); + const [inputValue, setInputValue] = React.useState(''); + + const rowData = rows.slice(0, 10); + + const onDelete = (type: string | ToolbarChipGroup, id: string) => { + if (type === 'Location') { + setFilters({ + ...filters, + location: filters.location.filter((fil: string) => fil !== id) + }); + } else if (type === 'Name') { + setFilters({ + ...filters, + name: filters.name.filter((fil: string) => fil !== id) + }); + } else if (type === 'Status') { + setFilters({ + ...filters, + status: filters.status.filter((fil: string) => fil !== id) + }); + } else { + setFilters({ location: [], name: [], status: [] }); + } + }; + + const onCategoryToggle = () => { + setIsCategoryDropdownOpen(!isCategoryDropdownOpen); + }; + + const onCategorySelect = (event: React.MouseEvent, value: string) => { + setCurrentCategory(value); + setIsCategoryDropdownOpen(false); + }; + + const onFilterToggle = () => { + setIsFilterDropdownOpen(!isFilterDropdownOpen); + }; + + const onFilterSelect = () => { + setIsFilterDropdownOpen(!isFilterDropdownOpen); + }; + + const onInputChange = (newValue: string) => { + setInputValue(newValue); + }; + + const onStatusSelect = (event: React.MouseEvent, selection: string) => { + const checked = (event.target as HTMLInputElement).checked; + setFilters({ + ...filters, + status: checked ? [...filters.status, selection] : filters.status.filter((value) => value !== selection) + }); + setIsFilterDropdownOpen(false); + }; + + const onNameInput = (event: React.KeyboardEvent) => { + if (event.key && event.key !== 'Enter') { + return; + } + + const prevFilters = filters.name; + setFilters({ ...filters, name: prevFilters.includes(inputValue) ? prevFilters : [...prevFilters, inputValue] }); + }; + + const onLocationSelect = (event: React.MouseEvent, selection: string) => { + setFilters({ ...filters, location: [selection] }); + + setIsFilterDropdownOpen(false); + onFilterSelect(); + }; + + const buildCategoryDropdown = () => { + const categoryMenuItems = [ + + Location + , + + Name + , + + Status + + ]; + + return ( + + + + ); + }; + + const buildFilterDropdown = () => { + const locationMenuItems = [ + + Raleigh + , + + San Francisco + , + + Boston + , + + Atlanta + + ]; + + const statusMenuItems = [ + + Running + , + + Stopped + , + + Down + , + + Degraded + , + + Needs maintenance + + ]; + + return ( + + onDelete(category, chip as string)} + categoryName="Location" + showToolbarItem={currentCategory === 'Location'} + > + + + onDelete(category, chip as string)} + categoryName="Name" + showToolbarItem={currentCategory === 'Name'} + > + onInputChange(value)} + value={inputValue} + onClear={() => { + onInputChange(''); + }} + onSearch={onNameInput} + /> + + onDelete(category, chip as string)} + categoryName="Status" + showToolbarItem={currentCategory === 'Status'} + > + + + + ); + }; + + const renderToolbar = () => ( + setFilters({ location: [], name: [], status: [] })} + collapseListedFiltersBreakpoint="xl" + > + + } breakpoint="xl"> + + {buildCategoryDropdown()} + {buildFilterDropdown()} + + + + + ); + + const filteredRows = + filters.name.length > 0 || filters.location.length > 0 || filters.status.length > 0 + ? rowData.filter( + (row) => + (filters.name.length === 0 || + filters.name.some((name) => row.name.toLowerCase().includes(name.toLowerCase()))) && + (filters.location.length === 0 || filters.location.includes(row.location)) && + (filters.status.length === 0 || filters.status.includes(row.status)) + ) + : rowData; + + const emptyState = ( + + } + /> + No results match the filter criteria. Clear all filters and try again. + + + + + + + ); + + const renderLabel = (labelText: string) => { + switch (labelText) { + case 'Running': + return ; + case 'Stopped': + return ; + case 'Needs maintenance': + return ; + case 'Down': + return ; + } + }; + + return ( + + {renderToolbar()} + + + + + + + + + + + + + + + {filteredRows.slice(0, 10).map((row, rowIndex) => ( + + <> + + + + + + + + + + + ))} + {filteredRows.length === 0 && ( + + + + )} + +
{columns[0]}{columns[1]}{columns[2]}{columns[3]}{columns[4]}{columns[5]}{columns[6]} + {columns[7]} +
{row.name}{row.threads}{row.applications}{row.workspaces}{renderLabel(row.status)}{row.location}{row.lastModified} + + {row.url} + +
+ {emptyState} +
+
+ ); +}; diff --git a/packages/react-table/src/docs/demos/table-demos/SortableResponsive.jsx b/packages/react-table/src/demos/examples/TableSortableResponsive.tsx similarity index 88% rename from packages/react-table/src/docs/demos/table-demos/SortableResponsive.jsx rename to packages/react-table/src/demos/examples/TableSortableResponsive.tsx index bc7978746af..794aee7c78b 100644 --- a/packages/react-table/src/docs/demos/table-demos/SortableResponsive.jsx +++ b/packages/react-table/src/demos/examples/TableSortableResponsive.tsx @@ -7,19 +7,10 @@ import { DropdownList, Flex, FlexItem, - Label, MenuToggle, MenuToggleElement, - OverflowMenu, - OverflowMenuContent, - OverflowMenuControl, - OverflowMenuDropdownItem, - OverflowMenuGroup, - OverflowMenuItem, PageSection, - PageSectionVariants, Pagination, - Select, SelectOption, SelectList, SelectGroup, @@ -28,7 +19,17 @@ import { Toolbar, ToolbarContent, ToolbarGroup, - ToolbarItem + ToolbarItem, + OverflowMenuDropdownItem, + PaginationVariant, + Label, + Select, + OverflowMenu, + OverflowMenuContent, + OverflowMenuControl, + OverflowMenuGroup, + OverflowMenuItem, + PageSectionVariants } from '@patternfly/react-core'; import { Table, Thead, Tr, Th, Tbody, Td } from '@patternfly/react-table'; import CloneIcon from '@patternfly/react-icons/dist/esm/icons/clone-icon'; @@ -38,15 +39,17 @@ import CodeIcon from '@patternfly/react-icons/dist/esm/icons/code-icon'; import CodeBranchIcon from '@patternfly/react-icons/dist/esm/icons/code-branch-icon'; import SortAmountDownIcon from '@patternfly/react-icons/dist/esm/icons/sort-amount-down-icon'; import CubeIcon from '@patternfly/react-icons/dist/esm/icons/cube-icon'; -import { DashboardWrapper } from '@patternfly/react-core/src/demos/DashboardWrapper'; +import { DashboardWrapper } from '@patternfly/react-core/dist/esm/demos/DashboardWrapper'; import EllipsisVIcon from '@patternfly/react-icons/dist/esm/icons/ellipsis-v-icon'; -import { rows, columns } from '@patternfly/react-table/src/docs/demos/table-demos/sampleData'; +import { rows, columns, SampleDataRow } from '@patternfly/react-table/dist/esm/demos/sampleData'; -export const ComposableTableSortable = () => { +type Direction = 'asc' | 'desc' | undefined; + +export const TableSortableResponsive: React.FunctionComponent = () => { const [isKebabDropdownOpen, setIsKebabDropdownOpen] = React.useState(false); - const sortRows = (r, sortIndex, sortDirection) => { - return [...r].sort((a, b) => { + const sortRows = (rows: SampleDataRow[], sortIndex: number, sortDirection: Direction) => + [...rows].sort((a, b) => { let returnValue = 0; if (sortIndex === 0 || sortIndex === 7) { returnValue = 1; @@ -62,7 +65,6 @@ export const ComposableTableSortable = () => { } return returnValue; }); - }; const [sortedData, setSortedData] = React.useState([...sortRows(rows, 0, 'asc')]); const [sortedRows, setSortedRows] = React.useState([...sortedData]); @@ -72,11 +74,11 @@ export const ComposableTableSortable = () => { // index of the currently active column const [activeSortIndex, setActiveSortIndex] = React.useState(0); // sort direction of the currently active column - const [activeSortDirection, setActiveSortDirection] = React.useState('asc'); + const [activeSortDirection, setActiveSortDirection] = React.useState('asc'); // sort dropdown expansion const [isSortDropdownOpen, setIsSortDropdownOpen] = React.useState(false); - const onSort = (event, index, direction) => { + const onSort = (_event: any, index: number, direction: Direction) => { setActiveSortIndex(index); setActiveSortDirection(direction); @@ -89,17 +91,17 @@ export const ComposableTableSortable = () => { setSortedRows(sortedData.slice((page - 1) * perPage, page * perPage - 1)); }, [sortedData, page, perPage]); - const handleSetPage = (_evt, newPage) => { + const handleSetPage = (_evt: React.MouseEvent | React.KeyboardEvent | MouseEvent, newPage: number) => { setPage(newPage); }; - const handlePerPageSelect = (_evt, newPerPage) => { + const handlePerPageSelect = (_evt: React.MouseEvent | React.KeyboardEvent | MouseEvent, newPerPage: number) => { setPerPage(newPerPage); }; - const renderPagination = (variant) => ( + const renderPagination = (variant: 'top' | 'bottom' | PaginationVariant, isCompact: boolean) => ( { /> ); - const renderLabel = (labelText) => { + const renderLabel = (labelText: string) => { switch (labelText) { case 'Running': return ; @@ -138,12 +140,12 @@ export const ComposableTableSortable = () => { ) => ( - } - style={ - { - width: '100%', - verticalAlign: 'text-bottom' - } as React.CSSProperties - } - > - {currentCategory} - - )} - isOpen={isCategoryDropdownOpen} - > - {categoryMenuItems} - - - ); - } - - buildFilterDropdown() { - const { currentCategory, isFilterDropdownOpen, inputValue, filters } = this.state; - - const locationMenuItems = [ - Raleigh, - San Francisco, - Boston, - Atlanta, - ]; - - const statusMenuItems = [ - - Running - , - - Stopped - , - - Down - , - - Degraded - , - - Needs maintenance - - ]; - - return ( - - - - - - this.onInputChange(value)} - value={inputValue} - onClear={() => { - this.onInputChange(''); - }} - onSearch={this.onNameInput} - /> - - - - - - ); - } - - renderToolbar() { - return ( - - - } breakpoint="xl"> - - {this.buildCategoryDropdown()} - {this.buildFilterDropdown()} - - - - - ); - } - - render() { - const { loading, filters, rows } = this.state; - - const filteredRows = - filters.name.length > 0 || filters.location.length > 0 || filters.status.length > 0 - ? rows.filter((row) => { - return ( - (filters.name.length === 0 || - filters.name.some((name) => row.name.toLowerCase().includes(name.toLowerCase()))) && - (filters.location.length === 0 || filters.location.includes(row.location)) && - (filters.status.length === 0 || filters.status.includes(row.status)) - ); - }) - : rows; - - let tableRows = filteredRows; - if (!loading && filteredRows.length === 0) { - tableRows = [ - { - heightAuto: true, - cells: [ - { - props: { colSpan: 8 }, - title: ( - - - } - /> - - No results match this filter criteria. Remove all filters or clear all filters to show results. - - - - - - - - - ) - } - ] - } - ]; - } else if (loading) { - tableRows = [ - { - heightAuto: true, - cells: [ - { - props: { colSpan: 8 }, - title: ( - - Please wait while loading data - - ) - } - ] - } - ]; - } - - const renderLabel = labelText => { - switch (labelText) { - case 'Running': - return ; - case 'Stopped': - return ; - case 'Needs maintenance': - return ; - case 'Down': - return ; - } - }; - - return ( - - {this.renderToolbar()} - - - - - - - - - - - - - - - {filteredRows.slice(0, 10).map((row, rowIndex) => ( - - <> - - - - - - - - - - - ))} - -
{columns[0]}{columns[1]}{columns[2]}{columns[3]}{columns[4]}{columns[5]}{columns[6]} - {columns[7]} -
{row.name}{row.threads}{row.applications}{row.workspaces}{renderLabel(row.status)}{row.location}{row.lastModified} - - {row.url} - -
-
- ); - } -} -``` - -### Sortable - responsive - -```js isFullscreen file="./table-demos/SortableResponsive.jsx" - -``` - -### Automatic pagination - -The below example illustrates the `isLastFullPageShown` prop, which makes the following changes when the user sets the number of items to display per page to an amount that exceeds the remaining amount of data: - -- The component automatically changes the page back to the last full page of results, rather than defaulting to the final page of results. - -To demonstrate this, navigate to the last page of data below using the `>>` navigation arrows, then use the dropdown selector to change the view to 5 per page. - -- The default behavior would show the last page of results, which would only contain the last two rows (rows 11 - 12). -- The `isLastFullPageShown` prop navigates you back to the previous page which does contain a full page of 5 rows (rows 6 - 10). - -```js isFullscreen -import React from 'react'; -import { Pagination } from '@patternfly/react-core'; -import { Table, Thead, Tr, Th, Tbody, Td } from '@patternfly/react-table'; - -class ComplexPaginationTableDemo extends React.Component { - constructor(props) { - super(props); - (this.columns = { - firstColumn: 'First column', - secondColumn: 'Second column', - thirdColumn: 'Third column' - }), - (this.defaultRows = [ - { firstColumn: 'Row 1 column 1', secondColumn: 'Row 1 column 2', thirdColumn: 'Row 1 column 3' }, - { firstColumn: 'Row 2 column 1', secondColumn: 'Row 2 column 2', thirdColumn: 'Row 2 column 3' }, - { firstColumn: 'Row 3 column 1', secondColumn: 'Row 3 column 2', thirdColumn: 'Row 3 column 3' }, - { firstColumn: 'Row 4 column 1', secondColumn: 'Row 4 column 2', thirdColumn: 'Row 4 column 3' }, - { firstColumn: 'Row 5 column 1', secondColumn: 'Row 5 column 2', thirdColumn: 'Row 5 column 3' }, - { firstColumn: 'Row 6 column 1', secondColumn: 'Row 6 column 2', thirdColumn: 'Row 6 column 3' }, - { firstColumn: 'Row 7 column 1', secondColumn: 'Row 7 column 2', thirdColumn: 'Row 7 column 3' }, - { firstColumn: 'Row 8 column 1', secondColumn: 'Row 8 column 2', thirdColumn: 'Row 8 column 3' }, - { firstColumn: 'Row 9 column 1', secondColumn: 'Row 9 column 2', thirdColumn: 'Row 9 column 3' }, - { firstColumn: 'Row 10 column 1', secondColumn: 'Row 10 column 2', thirdColumn: 'Row 10 column 3' }, - { firstColumn: 'Row 11 column 1', secondColumn: 'Row 11 column 2', thirdColumn: 'Row 11 column 3' }, - { firstColumn: 'Row 12 column 1', secondColumn: 'Row 12 column 2', thirdColumn: 'Row 12 column 3' } - ]); - this.defaultPerPage = 10; - this.state = { - perPage: this.defaultPerPage, - page: 1, - rows: this.defaultRows.slice(0, this.defaultPerPage) - }; - this.handleSetPage = this.handleSetPage.bind(this); - this.handlePerPageSelect = this.handlePerPageSelect.bind(this); - } - - handleSetPage(_evt, newPage, perPage, startIdx, endIdx) { - this.setState({ - page: newPage, - rows: this.defaultRows.slice(startIdx, endIdx) - }); - } - - handlePerPageSelect(_evt, newPerPage, newPage, startIdx, endIdx) { - this.setState({ - perPage: newPerPage, - page: newPage, - rows: this.defaultRows.slice(startIdx, endIdx) - }); - } - - renderPagination(variant = 'top') { - const { page, perPage } = this.state; - return ( - - ); - } - - render() { - return ( - - {this.renderPagination()} - - - - - - - - - - {this.state.rows.map((row, rowIndex) => ( - - <> - - - - - - ))} - -
{this.columns.firstColumn}{this.columns.secondColumn}{this.columns.thirdColumn}
{row.firstColumn}{row.secondColumn}{row.thirdColumn}
-
- ); - } -} -``` - -### Static bottom pagination on mobile - -```js isFullscreen file="table-demos/StaticBottomPagination.jsx" - -``` - -### Sticky header - -```js isFullscreen file="./table-demos/StickyHeader.jsx" - -``` - -### Sticky first column - -```js isFullscreen file="./table-demos/StickyFirstColumn.jsx" - -``` - -### Sticky columns and header with toolbar - -A toolbar may be added above a sticky table either inside or outside the `OuterScrollContainer`. - -```ts isFullscreen file="../../components/Table/examples/TableStickyColumnsAndHeader.tsx" - -``` - -## Empty states - -These examples demonstrate the use of an [Empty State component](/components/empty-state) inside of a [Table](/components/table). Empty states are useful in a table when a filter returns no results, while data is loading, or when any type of error or exception condition occurs. - -### Empty - -```js isFullscreen -import React from 'react'; -import { Table, Thead, Tr, Th, Tbody, Td } from '@patternfly/react-table'; -import { - Bullseye, - Button, - Card, - EmptyState, - EmptyStateVariant, - EmptyStateIcon, - EmptyStateBody, - EmptyStateHeader, - EmptyStateFooter, - EmptyStateActions, - PageSection -} from '@patternfly/react-core'; -import SearchIcon from '@patternfly/react-icons/dist/esm/icons/search-icon'; -import { DashboardWrapper } from '@patternfly/react-core/src/demos/DashboardWrapper'; - -export const TableEmptyState: React.FunctionComponent = () => ( - - - - - - - - - - - - - - - - - - -
RepositoriesBranchesPull requestsWorkspacesLast commit
- - - } - titleText="No results found" - headingLevel="h2" - /> - - No results match this filter criteria. Clear all filters and try again. - - - - - - - - -
-
-
-
-); -``` - -### Loading - -```js isFullscreen -import React from 'react'; -import { Table, Thead, Tr, Th, Tbody, Td } from '@patternfly/react-table'; -import { - Bullseye, - Card, - EmptyState, - EmptyStateIcon, - EmptyStateBody, - EmptyStateHeader, - PageSection, - Spinner -} from '@patternfly/react-core'; -import { DashboardWrapper } from '@patternfly/react-core/src/demos/DashboardWrapper'; - -export const LoadingStateDemo: React.FunctionComponent = () => ( - - - - - - - - - - - - - - - - - - -
RepositoriesBranchesPull requestsWorkspacesLast commit
- - - } /> - - -
-
-
-
-); -``` - -### Error - -```js isFullscreen -import React from 'react'; -import { Table, Thead, Tr, Th, Tbody, Td } from '@patternfly/react-table'; -import { - Bullseye, - Card, - EmptyState, - EmptyStateVariant, - EmptyStateIcon, - EmptyStateBody, - EmptyStateHeader, - PageSection -} from '@patternfly/react-core'; -import ExclamationCircleIcon from '@patternfly/react-icons/dist/esm/icons/exclamation-circle-icon'; -import globalDangerColor200 from '@patternfly/react-tokens/dist/esm/global_danger_color_200'; -import { DashboardWrapper } from '@patternfly/react-core/src/demos/DashboardWrapper'; - -export const ErrorStateDemo: React.FunctionComponent = () => ( - - - - - - - - - - - - - - - - - - -
RepositoriesBranchesPull requestsWorkspacesLast commit
- - - } - headingLevel="h2" - /> - - There was an error retrieving data. Check your connection and reload the page. - - - -
-
-
-
-); -``` diff --git a/packages/react-table/src/docs/demos/table-demos/ColumnManagement.jsx b/packages/react-table/src/docs/demos/table-demos/ColumnManagement.jsx deleted file mode 100644 index e551df541db..00000000000 --- a/packages/react-table/src/docs/demos/table-demos/ColumnManagement.jsx +++ /dev/null @@ -1,471 +0,0 @@ -import React from 'react'; - -import { - Button, - Card, - DataList, - DataListCheck, - DataListItem, - DataListItemRow, - DataListCell, - DataListItemCells, - Label, - Toolbar, - ToolbarContent, - ToolbarItem, - MenuToggle, - Modal, - OverflowMenu, - OverflowMenuGroup, - OverflowMenuItem, - PageSection, - Pagination, - PaginationVariant, - Text, - TextContent, - TextVariants -} from '@patternfly/react-core'; -import { Table, TableText, Thead, Tr, Th, Tbody, Td } from '@patternfly/react-table'; -import FilterIcon from '@patternfly/react-icons/dist/esm/icons/filter-icon'; -import SortAmountDownIcon from '@patternfly/react-icons/dist/esm/icons/sort-amount-down-icon'; -import { DashboardWrapper } from '@patternfly/react-core/src/demos/DashboardWrapper.js'; -import { capitalize } from '@patternfly/react-table/src/components/Table/utils/utils'; -import { rows, columns } from '@patternfly/react-table/src/docs/demos/table-demos/sampleData'; - -export const ColumnManagementAction = () => { - const defaultColumns = columns; - const defaultRows = rows; - - const [filters, setFilters] = React.useState([]); - const [filteredColumns, setFilteredColumns] = React.useState([]); - const [filteredRows, setFilteredRows] = React.useState([]); - const [managedColumns, setManagedColumns] = React.useState(defaultColumns); - const [managedRows, setManagedRows] = React.useState(defaultRows); - const [isModalOpen, setIsModalOpen] = React.useState(false); - const [checkedState, setCheckedState] = React.useState(Array(columns.length).fill(true)); - const [page, setPage] = React.useState(1); - const [perPage, setPerPage] = React.useState(10); - const [paginatedRows, setPaginatedRows] = React.useState(rows); - - const matchCheckboxNameToColumn = (name) => { - switch (name) { - case 'check1': - return 'Servers'; - case 'check2': - return 'Threads'; - case 'check3': - return 'Applications'; - case 'check4': - return 'Workspaces'; - case 'check5': - return 'Status'; - case 'check6': - return 'Location'; - case 'check7': - return 'Last Modified'; - case 'check8': - return 'URL'; - } - }; - const matchSelectedColumnNameToAttr = (name) => { - switch (name) { - case 'Servers': - return 'name'; - case 'Threads': - return 'threads'; - case 'Applications': - return 'applications'; - case 'Workspaces': - return 'workspaces'; - case 'Status': - return 'status'; - case 'Location': - return 'location'; - case 'Last Modified': - return 'lastModified'; - case 'URL': - return 'url'; - } - }; - - // Pagination logic - const handleSetPage = (_evt, newPage) => { - setPage(newPage); - }; - - const handlePerPageSelect = (_evt, newPerPage) => { - setPerPage(newPerPage); - }; - - const renderPagination = (variant, isCompact) => ( - - ); - - React.useEffect(() => { - setPaginatedRows(managedRows.slice((page - 1) * perPage, page * perPage - 1)); - }, [managedRows, page, perPage]); - - // Removes attribute from each node object in Data.jsx - const removePropFromObject = (object, keys) => - keys.reduce((obj, prop) => { - const { [prop]: _, ...keep } = obj; - return keep; - }, object); - - // Filters columns out of table that are not selected in the column management modal - const filterData = (checked, name) => { - const selectedColumn = matchSelectedColumnNameToAttr(name); - - const filteredRows = []; - if (checked) { - const updatedFilters = filters.filter((item) => item !== selectedColumn); - - // Only show the names of columns that were selected in the modal - const filteredColumns = defaultColumns.filter( - (column) => !updatedFilters.includes(matchSelectedColumnNameToAttr(column)) - ); - - // Remove the attributes (i.e. "columns") that were not selected - defaultRows.forEach((item) => filteredRows.push(removePropFromObject(item, updatedFilters))); - - setFilters(updatedFilters); - setFilteredColumns(filteredColumns); - setFilteredRows(filteredRows); - } else { - let updatedFilters = filters; - updatedFilters.push(selectedColumn); - - // Only show the names of columns that were selected in the modal - const filteredColumns = managedColumns.filter( - (column) => !filters.includes(matchSelectedColumnNameToAttr(column)) - ); - - // Remove the attributes (i.e. "columns") that were not selected - managedRows.forEach((item) => filteredRows.push(removePropFromObject(item, updatedFilters))); - - setFilters(updatedFilters); - setFilteredColumns(filteredColumns); - setFilteredRows(filteredRows); - } - }; - const unfilterAllData = () => { - setFilters([]); - setFilteredColumns(defaultColumns); - setFilteredRows(defaultRows); - }; - - const handleChange = (event, checked) => { - const target = event.target; - const value = target.type === 'checkbox' ? target.checked : target.value; - - // Remove any columns from the table that aren't checked - filterData(checked, matchCheckboxNameToColumn(target.name)); - const checkedIndex = columns.findIndex((element) => element === matchCheckboxNameToColumn(target.name)); - - const updatedCheckedState = [...checkedState]; - updatedCheckedState[checkedIndex] = value; - setCheckedState(updatedCheckedState); - }; - - const handleModalToggle = (_event) => { - setIsModalOpen(!isModalOpen); - }; - - const onSave = () => { - setManagedColumns(filteredColumns); - setManagedRows(filteredRows); - setPaginatedRows(filteredRows); - setIsModalOpen(!isModalOpen); - }; - - const selectAllColumns = () => { - unfilterAllData(); - setCheckedState(Array(columns.length).fill(true)); - }; - - const renderModal = () => { - return ( - - Selected categories will be displayed in the table. - - - } - onClose={handleModalToggle} - actions={[ - , - - ]} - > - - - - - - - - ]} - /> - - - - - - - - - ]} - /> - - - - - - - - - ]} - /> - - - - - - - - - ]} - /> - - - - - - - - - ]} - /> - - - - - - - - - ]} - /> - - - - - - - - - ]} - /> - - - - - - - - - ]} - /> - - - - - ); - }; - - const renderLabel = (labelText) => { - switch (labelText) { - case 'Running': - return ; - case 'Stopped': - return ; - case 'Needs Maintenance': - return ; - case 'Down': - return ; - } - }; - - const toolbarItems = ( - - - - - - - - - Name - - - - - - - - - - - - - - - - - {renderPagination(PaginationVariant.top)} - - - - ); - - return ( - - - - - {toolbarItems} - - - - {managedColumns.map((column, columnIndex) => ( - - ))} - - - - {paginatedRows.map((row, rowIndex) => ( - - <> - {Object.entries(row).map(([key, value]) => - key === 'status' ? ( - - ) : key === 'url' ? ( - - ) : ( - - ) - )} - - - ))} - -
{column}
- {renderLabel(value)} - - - {row.url} - - - {value} -
- {renderPagination(PaginationVariant.bottom)} - {renderModal()} -
-
-
-
- ); -}; diff --git a/packages/react-table/src/index.ts b/packages/react-table/src/index.ts index 07635cbbc8e..1b0840c21fc 100644 --- a/packages/react-table/src/index.ts +++ b/packages/react-table/src/index.ts @@ -1 +1,2 @@ export * from './components'; +export * from './demos'; diff --git a/packages/react-table/tsconfig.json b/packages/react-table/tsconfig.json index 91adec36e9e..8a0c909ae50 100644 --- a/packages/react-table/tsconfig.json +++ b/packages/react-table/tsconfig.json @@ -1,17 +1,18 @@ { "extends": "../tsconfig.base.json", "compilerOptions": { + "jsx": "react", "rootDir": "./src", "outDir": "./dist/esm", "tsBuildInfoFile": "dist/esm.tsbuildinfo", "baseUrl": ".", "paths": { - "*/deprecated": ["./src/deprecated"], + "./deprecated": ["./src/deprecated"] } }, "include": [ "./src/*", - "./src/**/*" + "./src/**/*", ], "references": [ {