Skip to content

Commit

Permalink
Merge pull request #278 from performant-software/feature/cdc157_list_…
Browse files Browse the repository at this point in the history
…columns

CDC #157 - List Columns
  • Loading branch information
dleadbetter authored May 14, 2024
2 parents f5943bf + ff4834b commit c181d7b
Show file tree
Hide file tree
Showing 17 changed files with 280 additions and 79 deletions.
6 changes: 3 additions & 3 deletions packages/controlled-vocabulary/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@performant-software/controlled-vocabulary",
"version": "2.2.1",
"version": "2.2.2",
"description": "A package of components to allow user to configure dropdown elements. Use with the \"controlled_vocabulary\" gem.",
"license": "MIT",
"main": "./dist/index.cjs.js",
Expand All @@ -23,8 +23,8 @@
"underscore": "^1.13.2"
},
"peerDependencies": {
"@performant-software/semantic-components": "^2.2.1",
"@performant-software/shared-components": "^2.2.1",
"@performant-software/semantic-components": "^2.2.2",
"@performant-software/shared-components": "^2.2.2",
"react": ">= 16.13.1 < 19.0.0",
"react-dom": ">= 16.13.1 < 19.0.0"
},
Expand Down
6 changes: 3 additions & 3 deletions packages/core-data/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@performant-software/core-data",
"version": "2.2.1",
"version": "2.2.2",
"description": "A package of components used with the Core Data platform.",
"license": "MIT",
"main": "./dist/index.cjs.js",
Expand Down Expand Up @@ -37,8 +37,8 @@
"underscore": "^1.13.2"
},
"peerDependencies": {
"@performant-software/shared-components": "^2.2.1",
"@performant-software/geospatial": "^2.2.1",
"@performant-software/shared-components": "^2.2.2",
"@performant-software/geospatial": "^2.2.2",
"@peripleo/maplibre": "^0.5.2",
"@peripleo/peripleo": "^0.5.2",
"react": ">= 16.13.1 < 19.0.0",
Expand Down
2 changes: 1 addition & 1 deletion packages/geospatial/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@performant-software/geospatial",
"version": "2.2.1",
"version": "2.2.2",
"description": "A package of components for all things map-related.",
"license": "MIT",
"main": "./dist/index.cjs.js",
Expand Down
4 changes: 2 additions & 2 deletions packages/semantic-ui/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@performant-software/semantic-components",
"version": "2.2.1",
"version": "2.2.2",
"description": "A package of shared components based on the Semantic UI Framework.",
"license": "MIT",
"main": "./dist/index.cjs.js",
Expand Down Expand Up @@ -35,7 +35,7 @@
"zotero-translation-client": "^5.0.1"
},
"peerDependencies": {
"@performant-software/shared-components": "^2.2.1",
"@performant-software/shared-components": "^2.2.2",
"@samvera/clover-iiif": "^2.3.2",
"react": ">= 16.13.1 < 19.0.0",
"react-dnd": "^11.1.3",
Expand Down
60 changes: 16 additions & 44 deletions packages/semantic-ui/src/components/DataList.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import uuid from 'react-uuid';
import _ from 'underscore';
import { Icon, Input, Message } from 'semantic-ui-react';
import i18n from '../i18n/i18n';
import ListSessionUtils from '../utils/ListSession';
import Toaster from './Toaster';

type Props = {
Expand Down Expand Up @@ -131,9 +132,6 @@ type State = {
sortDirection: ?string
};

const SESSION_KEY = 'DataList';
const SESSION_DEFAULT = '{}';

const SORT_ASCENDING = 'ascending';
const SORT_DESCENDING = 'descending';

Expand Down Expand Up @@ -285,19 +283,6 @@ const useDataList = (WrappedComponent: ComponentType<any>) => (
return { filters };
}

/**
* Returns the session storage key for the current list.
*
* @returns {string|null}
*/
getSessionKey() {
if (!this.props.session) {
return null;
}

return `${SESSION_KEY}.${this.props.session.key}`;
}

/**
* Initializes the state based on the passed props.
*
Expand All @@ -307,7 +292,8 @@ const useDataList = (WrappedComponent: ComponentType<any>) => (
* saved: boolean, count: number, filters: (*|{}), page: number, error: null, loading: boolean, items: *[]}}
*/
initializeState(props: Props) {
const session = this.restoreSession();
const { key, storage } = props.session || {};
const session = ListSessionUtils.restoreSession(key, storage) || {};

const filters = session.filters || this.getDefaultFilters(props);
const page = session.page || 1;
Expand Down Expand Up @@ -445,6 +431,14 @@ const useDataList = (WrappedComponent: ComponentType<any>) => (
});
}

/**
* When no columns are sortable, load data as is
*
*/
onInit(page?: number = 1) {
this.setState({ sortColumn: '', sortDirection: '', page }, this.fetchData.bind(this));
}

/**
* Sets the new active page and reloads the data.
*
Expand Down Expand Up @@ -516,14 +510,6 @@ const useDataList = (WrappedComponent: ComponentType<any>) => (
this.setState({ sortColumn, sortDirection, page }, this.fetchData.bind(this));
}

/**
* When no columns are sortable, load data as is
*
*/
onInit(page?: number = 1) {
this.setState({ sortColumn: '', sortDirection: '', page }, this.fetchData.bind(this));
}

/**
* Renders the DataList component.
*
Expand Down Expand Up @@ -565,7 +551,7 @@ const useDataList = (WrappedComponent: ComponentType<any>) => (
sortColumn={this.state.sortColumn}
sortDirection={this.state.sortDirection}
/>
{this.state.saved && (
{ this.state.saved && (
<Toaster
onDismiss={() => this.setState({ saved: false })}
type={Toaster.MessageTypes.positive}
Expand All @@ -578,7 +564,7 @@ const useDataList = (WrappedComponent: ComponentType<any>) => (
/>
</Toaster>
)}
{this.state.error && (
{ this.state.error && (
<Toaster
onDismiss={() => this.setState({ error: false })}
timeout={0}
Expand Down Expand Up @@ -633,25 +619,11 @@ const useDataList = (WrappedComponent: ComponentType<any>) => (
);
}

/**
* Restores the DataList session object.
*/
restoreSession() {
const key = this.getSessionKey();

if (!key) {
return {};
}

const session = sessionStorage.getItem(key) || SESSION_DEFAULT;
return JSON.parse(session);
}

/**
* Sets the DataList session object.
*/
setSession() {
const key = this.getSessionKey();
const { key, storage } = this.props.session || {};

if (!key) {
return;
Expand All @@ -666,14 +638,14 @@ const useDataList = (WrappedComponent: ComponentType<any>) => (
sortDirection
} = this.state;

sessionStorage.setItem(key, JSON.stringify({
ListSessionUtils.setSession(key, storage, {
filters,
page,
perPage,
search,
sortColumn,
sortDirection
}));
});
}
}
);
Expand Down
67 changes: 63 additions & 4 deletions packages/semantic-ui/src/components/DataTableColumnSelector.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import React, { Component, type ComponentType, type Element } from 'react';
import { Checkbox, Dropdown, Icon } from 'semantic-ui-react';
import _ from 'underscore';
import Draggable from './Draggable';
import ListSessionUtils from '../utils/ListSession';
import type { Props as ListProps } from './List';
import './DataTableColumnSelector.css';

Expand Down Expand Up @@ -75,20 +76,64 @@ const useColumnSelector = (WrappedComponent: ComponentType<any>) => (
constructor(props: Props) {
super(props);

this.state = {
columns: props.columns
};
this.state = this.initializeState(props);
}

/**
* Reset the columns on the state when the props change.
*
* @param prevProps
*/
componentDidUpdate(prevProps: Props): * {
componentDidUpdate(prevProps: Props, prevState: State): * {
if (!ObjectUtils.isEqual(prevProps.columns, this.props.columns)) {
this.setState({ columns: this.props.columns });
}

if (!ObjectUtils.isEqual(prevState.columns, this.state.columns)) {
this.setSession();
}
}

/**
* Sets the initial state from the session, if provided.
*
* @param props
*
* @returns {{columns}|{columns: *}}
*/
initializeState(props) {
const { key, storage } = props.session || {};
const session = ListSessionUtils.restoreSession(key, storage) || {};

// If the session does not have any stored columns, use the provided props
if (_.isEmpty(session.columns)) {
return {
columns: props.columns
};
}

const columns = [];

// Iterate over the session columns to preserve the ordering.
_.each(session.columns, (column) => {
const findColumn = _.findWhere(props.columns, { name: column.name });
if (findColumn) {
columns.push({ ...findColumn, ...column });
}
});

// Append any new columns not stored in the session
const columnNames = _.pluck(columns, 'name');

_.each(props.columns, (column) => {
if (!_.contains(columnNames, column.name)) {
columns.push(column);
}
});

return {
columns
};
}

/**
Expand Down Expand Up @@ -203,6 +248,20 @@ const useColumnSelector = (WrappedComponent: ComponentType<any>) => (
</>
);
}

/**
* Sets the list columns on the session.
*/
setSession() {
const { key, storage } = this.props.session || {};

if (!key) {
return;
}

const columns = _.map(this.state.columns, (column) => _.pick(column, 'name', 'hidden'));
ListSessionUtils.setSession(key, storage, { columns });
}
}
);

Expand Down
8 changes: 7 additions & 1 deletion packages/semantic-ui/src/components/ItemList.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ type Sort = {
};

type Props = DataListProps & ItemsProps & {
/**
* If `true`, a dimmer will be displayed over the list component.
*/
dimmable?: boolean,

/**
* Callback fired when the sort dropdown is changed. This prop is provided by the <code>DataList</code>
* higher-order component.
Expand Down Expand Up @@ -94,7 +99,7 @@ const ItemList = useDataList((props: Props) => {
return (
<>
<Dimmer
active={props.loading}
active={props.dimmable && props.loading}
inverted
>
<Loader
Expand All @@ -116,6 +121,7 @@ const ItemList = useDataList((props: Props) => {
});

ItemList.defaultProps = {
dimmable: true,
filters: {},
searchable: true,
};
Expand Down
12 changes: 7 additions & 5 deletions packages/semantic-ui/src/components/ListTable.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Hooks, ObjectJs as ObjectUtils } from '@performant-software/shared-comp
import React, { useEffect } from 'react';
import _ from 'underscore';
import DataTable from './DataTable';
import ListSessionUtils from '../utils/ListSession';
import type { Props as DataTableProps } from './DataTable';
import useDataList, { SORT_ASCENDING, SORT_DESCENDING, type Props as DataListProps } from './DataList';
import './ListTable.css';
Expand Down Expand Up @@ -88,11 +89,12 @@ const ListTable = useDataList((props: Props) => {
*/
useEffect(() => {
if (!ObjectUtils.isEqual(props.columns, prevColumns)) {
const {
page,
defaultSort,
defaultSortDirection = SORT_ASCENDING
} = props;
const { key, storage } = props.session || {};
const session = ListSessionUtils.restoreSession(key, storage);

const defaultSort = session.sortColumn || props.defaultSort;
const defaultSortDirection = session.sortDirection || props.defaultSortDirection || SORT_ASCENDING;
const { page } = props;

if (defaultSort) {
props.onSort(defaultSort, defaultSortDirection, page);
Expand Down
41 changes: 41 additions & 0 deletions packages/semantic-ui/src/utils/ListSession.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// @flow

const SESSION_DEFAULT = '{}';

/**
* Restores the list session from the passed storage object.
*
* @param key
* @param storage
*
* @returns {{}|any}
*/
const restoreSession = (key, storage) => {
if (!(key && storage)) {
return {};
}

const session = storage.getItem(key) || SESSION_DEFAULT;
return JSON.parse(session);
};

/**
* Sets the passed list session on the passed storage object.
*
* @param key
* @param storage
* @param session
*/
const setSession = (key, storage, session) => {
if (!(key && storage)) {
return;
}

const currentSession = restoreSession(key, storage);
storage.setItem(key, JSON.stringify({ ...currentSession, ...session }));
};

export default {
restoreSession,
setSession
};
Loading

0 comments on commit c181d7b

Please sign in to comment.