diff --git a/packages/react-table/src/components/Table/examples/TableStickyColumnsAndHeader.tsx b/packages/react-table/src/components/Table/examples/TableStickyColumnsAndHeader.tsx index 68d39559119..eb2b42bdbdb 100644 --- a/packages/react-table/src/components/Table/examples/TableStickyColumnsAndHeader.tsx +++ b/packages/react-table/src/components/Table/examples/TableStickyColumnsAndHeader.tsx @@ -53,10 +53,10 @@ export const TableStickyColumnsAndHeader: React.FunctionComponent = () => { // Index of the currently sorted column // Note: if you intend to make columns reorderable, you may instead want to use a non-numeric key // as the identifier of the sorted column. See the "Compound expandable" example. - const [activeSortIndex, setActiveSortIndex] = React.useState(null); + const [activeSortIndex, setActiveSortIndex] = React.useState(null); // Sort direction of the currently sorted column - const [activeSortDirection, setActiveSortDirection] = React.useState<'asc' | 'desc' | null>(null); + const [activeSortDirection, setActiveSortDirection] = React.useState<'asc' | 'desc' | null | undefined>(null); // Since OnSort specifies sorted columns by index, we need sortable values for our object by column index. // This example is trivial since our data objects just contain strings, but if the data was more complex @@ -69,7 +69,7 @@ export const TableStickyColumnsAndHeader: React.FunctionComponent = () => { // Note that we perform the sort as part of the component's render logic and not in onSort. // We shouldn't store the list of data in state because we don't want to have to sync that with props. let sortedFacts = facts; - if (activeSortIndex !== null) { + if (activeSortIndex) { sortedFacts = facts.sort((a, b) => { const aValue = getSortableRowValues(a)[activeSortIndex]; const bValue = getSortableRowValues(b)[activeSortIndex]; @@ -86,8 +86,8 @@ export const TableStickyColumnsAndHeader: React.FunctionComponent = () => { const getSortParams = (columnIndex: number): ThProps['sort'] => ({ sortBy: { - index: activeSortIndex, - direction: activeSortDirection + index: activeSortIndex as number, + direction: activeSortDirection as any }, onSort: (_event, index, direction) => { setActiveSortIndex(index); diff --git a/packages/react-table/src/docs/demos/Table.md b/packages/react-table/src/demos/Table.md similarity index 88% rename from packages/react-table/src/docs/demos/Table.md rename to packages/react-table/src/demos/Table.md index 6aebb44de5b..f3f5f2457f3 100644 --- a/packages/react-table/src/docs/demos/Table.md +++ b/packages/react-table/src/demos/Table.md @@ -25,7 +25,6 @@ TextContent, Text, Divider } from '@patternfly/react-core'; import { Table as TableDeprecated, TableHeader, TableBody } from '@patternfly/react-table/deprecated'; - import CheckIcon from '@patternfly/react-icons/dist/esm/icons/check-icon'; import CloneIcon from '@patternfly/react-icons/dist/esm/icons/clone-icon'; import CodeIcon from '@patternfly/react-icons/dist/esm/icons/code-icon'; @@ -49,37 +48,36 @@ import BarsIcon from '@patternfly/react-icons/dist/esm/icons/bars-icon'; import AttentionBellIcon from '@patternfly/react-icons/dist/esm/icons/attention-bell-icon'; import BlueprintIcon from '@patternfly/react-icons/dist/esm/icons/blueprint-icon'; 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 } from './sampleData'; ## Demos ### Bulk select -```js isFullscreen file="./table-demos/BulkSelect.jsx" - +```js isFullscreen file="./examples/BulkSelect.tsx" ``` ### Expand/collapse all -```js isFullscreen file="./table-demos/ExpandCollapseAll.jsx" +```js isFullscreen file="./examples/ExpandCollapseAll.tsx" ``` ### Compact -```js isFullscreen file="./table-demos/Compact.jsx" +```js isFullscreen file="./examples/Compact.tsx" ``` ### Compound expansion -```js isFullscreen file="./table-demos/CompoundExpansion.jsx" +```js isFullscreen file="./examples/CompoundExpansion.tsx" ``` ### Column management -```js isFullscreen file="./table-demos/ColumnManagement.jsx" +```js isFullscreen file="./examples/ColumnManagement.tsx" ``` @@ -855,7 +853,7 @@ import { 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/src/docs/demos/table-demos/sampleData'; +import { rows, columns } from './sampleData'; class FilterTableDemo extends React.Component { constructor(props) { @@ -1297,7 +1295,7 @@ class FilterTableDemo extends React.Component { ### Sortable - responsive -```js isFullscreen file="./table-demos/SortableResponsive.jsx" +```js isFullscreen file="./examples/SortableResponsive.tsx" ``` @@ -1420,19 +1418,19 @@ class ComplexPaginationTableDemo extends React.Component { ### Static bottom pagination on mobile -```js isFullscreen file="table-demos/StaticBottomPagination.jsx" +```ts isFullscreen file="./examples/StaticBottomPagination.tsx" ``` ### Sticky header -```js isFullscreen file="./table-demos/StickyHeader.jsx" +```js isFullscreen file="./examples/StickyHeader.tsx" ``` ### Sticky first column -```js isFullscreen file="./table-demos/StickyFirstColumn.jsx" +```js isFullscreen file="./examples/StickyFirstColumn.tsx" ``` @@ -1440,7 +1438,7 @@ class ComplexPaginationTableDemo extends React.Component { A toolbar may be added above a sticky table either inside or outside the `OuterScrollContainer`. -```ts isFullscreen file="../../components/Table/examples/TableStickyColumnsAndHeader.tsx" +```js isFullscreen file="../components/Table/examples/TableStickyColumnsAndHeader.tsx" ``` @@ -1450,172 +1448,15 @@ These examples demonstrate the use of an [Empty State component](/components/emp ### 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. - - - - - - - - -
-
-
-
-); +```js isFullscreen file="./examples/EmptyStateDefault.tsx" ``` ### 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
- - - } /> - - -
-
-
-
-); +```js isFullscreen file="./examples/EmptyStateLoading.tsx" ``` ### 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. - - - -
-
-
-
-); +```js isFullscreen file="./examples/EmptyStateError.tsx" ``` diff --git a/packages/react-table/src/docs/demos/table-demos/BulkSelect.jsx b/packages/react-table/src/demos/examples/BulkSelect.tsx similarity index 68% rename from packages/react-table/src/docs/demos/table-demos/BulkSelect.jsx rename to packages/react-table/src/demos/examples/BulkSelect.tsx index 7625126924e..55585336c94 100644 --- a/packages/react-table/src/docs/demos/table-demos/BulkSelect.jsx +++ b/packages/react-table/src/demos/examples/BulkSelect.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useState } from 'react'; import { Dropdown, @@ -6,44 +6,62 @@ import { DropdownItem, MenuToggle, MenuToggleCheckbox, + MenuToggleElement, PageSection, Pagination, Toolbar, ToolbarContent, ToolbarGroup, - ToolbarItem + ToolbarItem, + PaginationVariant } from '@patternfly/react-core'; -import { Table, Thead, Tr, Th, Tbody, Td } from '@patternfly/react-table'; +import { Table, Thead, Tr, Th, Tbody, Td, rows, columns, SampleDataRow } from '@patternfly/react-table'; import { DashboardWrapper } from '@patternfly/react-core/src/demos/DashboardWrapper'; -import { rows, columns } from '@patternfly/react-table/src/docs/demos/table-demos/sampleData'; -export const BulkSelectTableDemo = () => { - const [isBulkSelectDropdownOpen, setIsBulkSelectDropdownOpen] = React.useState(false); - const [bulkSelection, setBulkSelection] = React.useState(''); - const [page, setPage] = React.useState(1); - const [perPage, setPerPage] = React.useState(10); - const [paginatedRows, setPaginatedRows] = React.useState(rows.slice(0, 10)); - const [selectedRows, setSelectedRows] = React.useState([]); - const handleSetPage = (_evt, newPage, _perPage, startIdx, endIdx) => { +export const BulkSelectTableDemo: React.FunctionComponent = () => { + const [isBulkSelectDropdownOpen, setIsBulkSelectDropdownOpen] = useState(false); + const [bulkSelection, setBulkSelection] = useState(''); + const [page, setPage] = useState(1); + const [perPage, setPerPage] = useState(10); + const [paginatedRows, setPaginatedRows] = useState(rows.slice(0, 10)); + const [selectedRows, setSelectedRows] = useState([]); + + const handleSetPage = ( + _evt: MouseEvent | React.MouseEvent | React.KeyboardEvent, + newPage: number, + _perPage: number, + startIdx: number, + endIdx: number + ) => { setPaginatedRows(rows.slice(startIdx, endIdx)); setPage(newPage); }; - const handlePerPageSelect = (_evt, newPerPage, newPage, startIdx, endIdx) => { + + const handlePerPageSelect = ( + _evt: MouseEvent | React.MouseEvent | React.KeyboardEvent, + newPerPage: number, + newPage: number, + startIdx: number, + endIdx: number + ) => { setPaginatedRows(rows.slice(startIdx, endIdx)); setPage(newPage); setPerPage(newPerPage); }; - const setRowSelected = (row, isSelecting = true) => + const setRowSelected = (row: SampleDataRow, isSelecting: boolean) => setSelectedRows((prevSelected) => { const otherSelectedRows = prevSelected.filter((r) => r !== row.name); return isSelecting ? [...otherSelectedRows, row.name] : otherSelectedRows; }); - const selectAllRows = (isSelecting = true) => setSelectedRows(isSelecting ? rows.map((r) => r.name) : []); - const selectPageRows = (isSelecting = true) => setSelectedRows(isSelecting ? paginatedRows.map((r) => r.name) : []); - const isRowSelected = (row) => selectedRows.includes(row.name); - const buildPagination = (variant, isCompact) => ( + const selectAllRows = (isSelecting: boolean) => setSelectedRows(isSelecting ? rows.map((r) => r.name) : []); + + const selectPageRows = (isSelecting: boolean) => setSelectedRows(isSelecting ? paginatedRows.map((r) => r.name) : []); + + const isRowSelected = (row: any) => selectedRows.includes(row.name); + + const buildPagination = (variant: 'bottom' | 'top' | PaginationVariant, isCompact: boolean) => ( { Select all ({rows.length} items) ); + return ( { + onSelect={(_ev: React.MouseEvent, value: string | number) => { if (value === 'all') { selectAllRows(bulkSelection !== 'all'); } else if (value === 'page') { @@ -86,8 +105,8 @@ export const BulkSelectTableDemo = () => { setBulkSelection(value); }} isOpen={isBulkSelectDropdownOpen} - onOpenChange={(isOpen) => setIsBulkSelectDropdownOpen(isOpen)} - toggle={(toggleRef) => ( + onOpenChange={(isOpen: boolean) => setIsBulkSelectDropdownOpen(isOpen)} + toggle={(toggleRef: React.Ref) => ( { {buildBulkSelectDropdown()} - {buildPagination('top')} + {buildPagination('top', false)} ); @@ -147,7 +166,8 @@ export const BulkSelectTableDemo = () => { setRowSelected(row, isSelecting), + onSelect: (_event: React.FormEvent, isSelecting: boolean) => + setRowSelected(row, isSelecting), isSelected: isRowSelected(row) }} /> @@ -159,7 +179,7 @@ export const BulkSelectTableDemo = () => { ))} - {buildPagination('bottom')} + {buildPagination('bottom', true)} ); diff --git a/packages/react-table/src/demos/examples/ColumnManagement.tsx b/packages/react-table/src/demos/examples/ColumnManagement.tsx new file mode 100644 index 00000000000..3d23a282951 --- /dev/null +++ b/packages/react-table/src/demos/examples/ColumnManagement.tsx @@ -0,0 +1,486 @@ +import React, { useState, useEffect } 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'; +import { capitalize } from '@patternfly/react-table/src/components/Table/utils/utils'; +import { rows, columns } from '@patternfly/react-table/src/demos/examples/Table/sampleData'; + +export const ColumnManagementAction: React.FC = () => { + const defaultColumns = columns; + const defaultRows = rows; + + const [filters, setFilters] = useState([]); + const [filteredColumns, setFilteredColumns] = useState([]); + const [filteredRows, setFilteredRows] = useState([]); + const [managedColumns, setManagedColumns] = useState(defaultColumns); + const [managedRows, setManagedRows] = useState(defaultRows); + const [isModalOpen, setIsModalOpen] = useState(false); + const [checkedState, setCheckedState] = useState(Array(columns.length).fill(true)); + const [page, setPage] = useState(1); + const [perPage, setPerPage] = useState(10); + const [paginatedRows, setPaginatedRows] = useState(rows); + + const matchCheckboxNameToColumn = (name: string): string => { + 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'; + default: + return ''; + } + }; + + const matchSelectedColumnNameToAttr = (name: string): string => { + 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'; + default: + return ''; + } + }; + + // Pagination logic + const handleSetPage = ( + _evt: MouseEvent | React.MouseEvent | React.KeyboardEvent, + newPage: number + ) => { + setPage(newPage); + }; + + const handlePerPageSelect = ( + _evt: MouseEvent | React.MouseEvent | React.KeyboardEvent, + newPerPage: number + ) => { + setPerPage(newPerPage); + }; + + const renderPagination = (variant: 'top' | 'bottom' | PaginationVariant, isCompact: boolean) => ( + + ); + + 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: any, keys: string[]): any => + keys.reduce((obj, prop) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { [prop]: _, ...keep } = obj; + return keep; + }, object); + + // Filters columns out of table that are not selected in the column management modal + const filterData = (checked: boolean, name: string) => { + const selectedColumn: string = matchSelectedColumnNameToAttr(name); + + const filteredRows: any[] = []; + if (checked) { + const updatedFilters: string[] = filters.filter((item) => item !== selectedColumn); + + // Only show the names of columns that were selected in the modal + const filteredColumns: string[] = 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 { + const updatedFilters: string[] = [...filters]; + updatedFilters.push(selectedColumn); + + // Only show the names of columns that were selected in the modal + const filteredColumns: string[] = 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: React.FormEvent, checked: boolean) => { + const target = event.currentTarget; + 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: number = columns.findIndex((element) => element === matchCheckboxNameToColumn(target.name)); + + const updatedCheckedState: boolean[] = [...checkedState]; + updatedCheckedState[checkedIndex] = value as boolean; + setCheckedState(updatedCheckedState); + }; + + const handleModalToggle = (_event: React.MouseEvent | KeyboardEvent) => { + setIsModalOpen(!isModalOpen); + }; + + const onSave = () => { + setManagedColumns(filteredColumns); + setManagedRows(filteredRows); + setPaginatedRows(filteredRows); + setIsModalOpen(!isModalOpen); + }; + + const selectAllColumns = () => { + unfilterAllData(); + setCheckedState(Array(columns.length).fill(true)); + }; + + const renderModal = () => ( + + Selected categories will be displayed in the table. + + + } + 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(PaginationVariant.top)} + + + + ); + + 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} +
+ {renderPagination(PaginationVariant.bottom)} + {renderModal()} +
+
+
+
+ ); +}; diff --git a/packages/react-table/src/docs/demos/table-demos/Compact.jsx b/packages/react-table/src/demos/examples/Compact.tsx similarity index 82% rename from packages/react-table/src/docs/demos/table-demos/Compact.jsx rename to packages/react-table/src/demos/examples/Compact.tsx index 381fb629729..db841a6dad4 100644 --- a/packages/react-table/src/docs/demos/table-demos/Compact.jsx +++ b/packages/react-table/src/demos/examples/Compact.tsx @@ -1,9 +1,7 @@ -import React from 'react'; - +import React, { useState } from 'react'; import { Button, Card, - Label, MenuToggle, MenuToggleElement, Pagination, @@ -13,29 +11,33 @@ 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 { rows, columns } from '@patternfly/react-table/src/demos/examples/Table/sampleData'; import { DashboardWrapper } from '@patternfly/react-core/src/demos/DashboardWrapper'; -import { rows, columns } from '@patternfly/react-table/src/docs/demos/table-demos/sampleData'; -export const CompactTable = () => { - 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) => { +export const CompactTable: React.FC = () => { + const [isSelectOpen, setIsSelectOpen] = useState(false); + const [page, setPage] = useState(1); + const [perPage, setPerPage] = useState(10); + const [paginatedRows, setPaginatedRows] = useState(rows.slice(0, 10)); + + const handleSetPage = (_evt: any, newPage: number, startIdx: number, endIdx: number) => { setPaginatedRows(rows.slice(startIdx, endIdx)); setPage(newPage); }; - handlePerPageSelect = (_evt, newPerPage, newPage, startIdx, endIdx) => { + + const handlePerPageSelect = (_evt: any, 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 +65,7 @@ export const CompactTable = () => {
)} isOpen={isSelectOpen} - onOpenChange={(isOpen) => setIsSelectOpen(isOpen)} + onOpenChange={(isOpen: boolean) => setIsSelectOpen(isOpen)} onSelect={() => setIsSelectOpen(!isSelectOpen)} > {[ @@ -92,7 +94,7 @@ export const CompactTable = () => { ); - const renderLabel = (labelText) => { + const renderLabel = (labelText: string) => { switch (labelText) { case 'Running': return ; @@ -102,8 +104,11 @@ export const CompactTable = () => { return ; case 'Down': return ; + default: + return <>; } }; + return ( @@ -146,7 +151,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/CompoundExpansion.tsx similarity index 90% rename from packages/react-table/src/docs/demos/table-demos/CompoundExpansion.jsx rename to packages/react-table/src/demos/examples/CompoundExpansion.tsx index 1e0bf2a48b4..a867a69861e 100644 --- a/packages/react-table/src/docs/demos/table-demos/CompoundExpansion.jsx +++ b/packages/react-table/src/demos/examples/CompoundExpansion.tsx @@ -14,7 +14,8 @@ 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'; @@ -22,7 +23,7 @@ import CubeIcon from '@patternfly/react-icons/dist/esm/icons/cube-icon'; import { DashboardWrapper } from '@patternfly/react-core/src/demos/DashboardWrapper'; import FilterIcon from '@patternfly/react-icons/dist/esm/icons/filter-icon'; -export const CompoundExpandable = () => { +export const CompoundExpandable: React.FC = () => { // In real usage, this data would come from some external source like an API via props. const [isSelectOpen, setIsSelectOpen] = React.useState(false); @@ -54,7 +55,7 @@ export const CompoundExpandable = () => { {items.map((item) => ( - + {item.description} {item.date} {item.status} @@ -68,7 +69,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)} > {[ @@ -136,7 +137,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, @@ -147,7 +160,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', @@ -158,11 +171,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 { @@ -170,7 +183,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/EmptyStateDefault.tsx b/packages/react-table/src/demos/examples/EmptyStateDefault.tsx new file mode 100644 index 00000000000..92254c68bfa --- /dev/null +++ b/packages/react-table/src/demos/examples/EmptyStateDefault.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/src/demos/DashboardWrapper'; + +export const EmptyStateDefaultDemo: 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/EmptyStateError.tsx b/packages/react-table/src/demos/examples/EmptyStateError.tsx new file mode 100644 index 00000000000..355d5ac9d9b --- /dev/null +++ b/packages/react-table/src/demos/examples/EmptyStateError.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/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/demos/examples/EmptyStateLoading.tsx b/packages/react-table/src/demos/examples/EmptyStateLoading.tsx new file mode 100644 index 00000000000..834b0354371 --- /dev/null +++ b/packages/react-table/src/demos/examples/EmptyStateLoading.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/src/demos/DashboardWrapper'; + +export const EmptyStateLoadingDemo: 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/ExpandCollapseAll.tsx similarity index 92% rename from packages/react-table/src/docs/demos/table-demos/ExpandCollapseAll.jsx rename to packages/react-table/src/demos/examples/ExpandCollapseAll.tsx index f7d6d5f18fb..b4207b648ec 100644 --- a/packages/react-table/src/docs/demos/table-demos/ExpandCollapseAll.jsx +++ b/packages/react-table/src/demos/examples/ExpandCollapseAll.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'; 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 ExpandCollapseAllTableDemo: React.FC = () => { 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/docs/demos/table-demos/SortableResponsive.jsx b/packages/react-table/src/demos/examples/SortableResponsive.tsx similarity index 90% rename from packages/react-table/src/docs/demos/table-demos/SortableResponsive.jsx rename to packages/react-table/src/demos/examples/SortableResponsive.tsx index bc7978746af..4b4338e3dd8 100644 --- a/packages/react-table/src/docs/demos/table-demos/SortableResponsive.jsx +++ b/packages/react-table/src/demos/examples/SortableResponsive.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'; @@ -40,13 +41,15 @@ import SortAmountDownIcon from '@patternfly/react-icons/dist/esm/icons/sort-amou import CubeIcon from '@patternfly/react-icons/dist/esm/icons/cube-icon'; import { DashboardWrapper } from '@patternfly/react-core/src/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/src/demos/examples/Table/sampleData'; -export const ComposableTableSortable = () => { +type Direction = 'asc' | 'desc' | 'none'; + +export const ComposableTableSortable: React.FC = () => { 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,15 +91,15 @@ export const ComposableTableSortable = () => { setSortedRows(sortedData.slice((page - 1) * perPage, page * perPage - 1)); }, [sortedData, page, perPage]); - const handleSetPage = (_evt, newPage) => { + const handleSetPage = (_evt: any, newPage: number) => { setPage(newPage); }; - const handlePerPageSelect = (_evt, newPerPage) => { + const handlePerPageSelect = (_evt: any, newPerPage: number) => { setPerPage(newPerPage); }; - const renderPagination = (variant) => ( + const renderPagination = (variant: 'top' | 'bottom' | PaginationVariant) => ( { /> ); - const renderLabel = (labelText) => { + const renderLabel = (labelText: string) => { switch (labelText) { case 'Running': return ; @@ -138,12 +140,12 @@ export const ComposableTableSortable = () => {