Skip to content

Commit

Permalink
Merge pull request #6384 from MBelniak/master
Browse files Browse the repository at this point in the history
Feat: Stateful TreeTable
  • Loading branch information
nitrogenous authored May 16, 2024
2 parents f50cac4 + 9319de9 commit 98c25fb
Show file tree
Hide file tree
Showing 8 changed files with 498 additions and 24 deletions.
61 changes: 61 additions & 0 deletions components/doc/common/apidoc/index.json
Original file line number Diff line number Diff line change
Expand Up @@ -54868,6 +54868,22 @@
"default": "",
"description": "Order to sort the data by default."
},
{
"name": "stateKey",
"optional": true,
"readonly": false,
"type": "string",
"default": "",
"description": "Unique identifier of a stateful table to use in state storage."
},
{
"name": "stateStorage",
"optional": true,
"readonly": false,
"type": "\"custom\" | \"local\" | \"session\"",
"default": "session",
"description": "Defines where a stateful table keeps its state, valid values are \"session\" for sessionStorage, \"local\" for localStorage and \"custom\"."
},
{
"name": "stripedRows",
"optional": true,
Expand Down Expand Up @@ -54945,6 +54961,25 @@
"callbacks": {
"description": "Defines callbacks that determine the behavior of the component based on a given condition or report the actions that the component takes.",
"values": [
{
"name": "customRestoreState",
"parameters": [],
"returnType": "undefined | object",
"description": "A function to implement custom restoreState with stateStorage=\"custom\". Need to return state object."
},
{
"name": "customSaveState",
"parameters": [
{
"name": "state",
"optional": false,
"type": "object",
"description": "The object to be stored."
}
],
"returnType": "void",
"description": "A function to implement custom saveState with stateStorage=\"custom\"."
},
{
"name": "onCollapse",
"parameters": [
Expand Down Expand Up @@ -55127,6 +55162,32 @@
"returnType": "void",
"description": "Callback to invoke on sort."
},
{
"name": "onStateRestore",
"parameters": [
{
"name": "state",
"optional": false,
"type": "object",
"description": "Table state."
}
],
"returnType": "void",
"description": "Callback to invoke table state is restored."
},
{
"name": "onStateSave",
"parameters": [
{
"name": "state",
"optional": false,
"type": "object",
"description": "Table state."
}
],
"returnType": "void",
"description": "Callback to invoke table state is saved."
},
{
"name": "onToggle",
"parameters": [
Expand Down
125 changes: 125 additions & 0 deletions components/doc/treetable/statefuldoc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import { DocSectionCode } from '@/components/doc/common/docsectioncode';
import { DocSectionText } from '@/components/doc/common/docsectiontext';
import { Column } from '@/components/lib/column/Column';
import { TreeTable } from '@/components/lib/treetable/TreeTable';
import { useEffect, useState } from 'react';
import { NodeService } from '../../../service/NodeService';

export function StatefulDoc(props) {
const [nodes, setNodes] = useState([]);

useEffect(() => {
NodeService.getTreeTableNodes().then((data) => setNodes(data));
}, []); // eslint-disable-line react-hooks/exhaustive-deps

const code = {
basic: `
<TreeTable value={nodes} stateKey={'tree-table-state-demo-session'} stateStorage={'session'}>
<Column field="name" header="Name" expander filter filterPlaceholder="Filter by name"></Column>
<Column field="size" header="Size" filter filterPlaceholder="Filter by size"></Column>
<Column field="type" header="Type" filter filterPlaceholder="Filter by type"></Column>
</TreeTable>
`,
javascript: `
import React, { useState, useEffect } from 'react';
import { TreeTable } from 'primereact/treetable';
import { Column } from 'primereact/column';
import { NodeService } from './service/NodeService';
export default function StatefulDemo() {
const [nodes, setNodes] = useState([]);
useEffect(() => {
NodeService.getTreeTableNodes().then((data) => setNodes(data));
}, []);
return (
<div className="card">
<TreeTable value={nodes} tableStyle={{ minWidth: '50rem' }} stateKey={'tree-table-state-demo-session'} stateStorage={'session'}
paginator rows={5} rowsPerPageOptions={[5, 10, 25]}>
<Column field="name" header="Name" expander filter filterPlaceholder="Filter by name"></Column>
<Column field="size" header="Size" filter filterPlaceholder="Filter by size"></Column>
<Column field="type" header="Type" filter filterPlaceholder="Filter by type"></Column>
</TreeTable>
</div>
)
}
`,
typescript: `
import React, { useState, useEffect } from 'react';
import { TreeTable } from 'primereact/treetable';
import { Column } from 'primereact/column';
import { TreeNode } from 'primereact/treenode';
import { NodeService } from './service/NodeService';
export default function StatefulDemo() {
const [nodes, setNodes] = useState<TreeNode[]>([]);
useEffect(() => {
NodeService.getTreeTableNodes().then((data) => setNodes(data));
}, []);
return (
<div className="card">
<TreeTable value={nodes} tableStyle={{ minWidth: '50rem' }} stateKey={'tree-table-state-demo-session'} stateStorage={'session'}
paginator rows={5} rowsPerPageOptions={[5, 10, 25]}>
<Column field="name" header="Name" expander filter filterPlaceholder="Filter by name"></Column>
<Column field="size" header="Size" filter filterPlaceholder="Filter by size"></Column>
<Column field="type" header="Type" filter filterPlaceholder="Filter by type"></Column>
</TreeTable>
</div>
)
}
`,
data: `
{
key: '0',
label: 'Documents',
data: 'Documents Folder',
icon: 'pi pi-fw pi-inbox',
children: [
{
key: '0-0',
label: 'Work',
data: 'Work Folder',
icon: 'pi pi-fw pi-cog',
children: [
{ key: '0-0-0', label: 'Expenses.doc', icon: 'pi pi-fw pi-file', data: 'Expenses Document' },
{ key: '0-0-1', label: 'Resume.doc', icon: 'pi pi-fw pi-file', data: 'Resume Document' }
]
},
{
key: '0-1',
label: 'Home',
data: 'Home Folder',
icon: 'pi pi-fw pi-home',
children: [{ key: '0-1-0', label: 'Invoices.txt', icon: 'pi pi-fw pi-file', data: 'Invoices for this month' }]
}
]
},
...
`
};

return (
<>
<DocSectionText {...props}>
<p>Stateful table allows keeping the state such as page, sort and filtering either at local storage or session storage so that when the page is visited again, table would render the data using the last settings.</p>
<p>
Change the state of the table e.g paginate or expand rows, navigate away and then return to this table again to test this feature. The setting is set as <i>session</i> with the <i>stateStorage</i> property so that Table retains
the state until the browser is closed. Other alternative is <i>local</i> referring to <i>localStorage</i> for an extended lifetime.
</p>
</DocSectionText>
<div className="card">
<TreeTable value={nodes} tableStyle={{ minWidth: '50rem' }} stateKey={'tree-table-state-demo-session'} stateStorage={'session'} paginator rows={5} rowsPerPageOptions={[5, 10, 25]}>
<Column field="name" header="Name" expander filter filterPlaceholder="Filter by name" sortable />
<Column field="size" header="Size" filter filterPlaceholder="Filter by size" sortable />
<Column field="type" header="Type" filter filterPlaceholder="Filter by type" sortable />
</TreeTable>
</div>
<DocSectionCode code={code} service={['NodeService']} />
</>
);
}
25 changes: 5 additions & 20 deletions components/lib/datatable/DataTable.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { DataTableBase } from './DataTableBase';
import { TableBody } from './TableBody';
import { TableFooter } from './TableFooter';
import { TableHeader } from './TableHeader';
import { getStorage } from '../../utils/utils';

export const DataTable = React.forwardRef((inProps, ref) => {
const context = React.useContext(PrimeReactContext);
Expand Down Expand Up @@ -175,24 +176,8 @@ export const DataTable = React.forwardRef((inProps, ref) => {
return columns;
};

const getStorage = () => {
switch (props.stateStorage) {
case 'local':
return window.localStorage;

case 'session':
return window.sessionStorage;

case 'custom':
return null;

default:
throw new Error(props.stateStorage + ' is not a valid value for the state storage, supported values are "local", "session" and "custom".');
}
};

const saveState = () => {
let state = {};
const state = {};

if (props.paginator) {
state.first = getFirst();
Expand Down Expand Up @@ -237,7 +222,7 @@ export const DataTable = React.forwardRef((inProps, ref) => {
props.customSaveState(state);
}
} else {
const storage = getStorage();
const storage = getStorage(props.stateStorage);

if (ObjectUtils.isNotEmpty(state)) {
storage.setItem(props.stateKey, JSON.stringify(state));
Expand All @@ -250,7 +235,7 @@ export const DataTable = React.forwardRef((inProps, ref) => {
};

const clearState = () => {
const storage = getStorage();
const storage = getStorage(props.stateStorage);

if (storage && props.stateKey) {
storage.removeItem(props.stateKey);
Expand All @@ -265,7 +250,7 @@ export const DataTable = React.forwardRef((inProps, ref) => {
restoredState = props.customRestoreState();
}
} else {
const storage = getStorage();
const storage = getStorage(props.stateStorage);
const stateString = storage.getItem(props.stateKey);
const dateFormat = /\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z/;

Expand Down
Loading

0 comments on commit 98c25fb

Please sign in to comment.