Commit a3cfd20d authored by Matija Obreza's avatar Matija Obreza
Browse files

Show snackbars on filters change in Overview

parent c5784ca2
import * as React from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import {withStyles} from '@material-ui/core/styles';
import { withStyles } from '@material-ui/core/styles';
import { translate } from 'react-i18next';
import { showSnackbar } from 'actions/snackbar';
import Chip from '@material-ui/core/Chip';
import Paper from '@material-ui/core/Paper';
import * as flattenjs from 'flattenjs';
......@@ -26,24 +29,25 @@ import PrettyDate from 'ui/common/time/PrettyDate';
* }
*/
interface IPrettyFiltersProps extends React.ClassAttributes<any> {
classes: any;
onSubmit: (newFilter) => any;
filterObj: object;
lookups: object;
prefix: string;
t: any;
classes: any;
onSubmit: (newFilter) => any;
showSnackbar: (snack: string) => void;
filterObj: object;
lookups: object;
prefix: string;
t: any;
}
const styles = (theme) => ({
root: {
display: 'flex',
justifyContent: 'center' as 'center',
flexWrap: 'wrap' as 'wrap',
padding: theme.spacing.unit / 2,
},
chip: {
margin: theme.spacing.unit / 2,
},
root: {
display: 'flex',
justifyContent: 'center' as 'center',
flexWrap: 'wrap' as 'wrap',
padding: theme.spacing.unit / 2,
},
chip: {
margin: theme.spacing.unit / 2,
},
});
// Following keywords are skipped for showing
......@@ -56,20 +60,20 @@ const keywordsToSkip = ['eq', 'contains', 'sw'];
* @return IPrettyFiltersProps.filterObj.
*/
function handleFilterObj(filterObj, path) {
// clone original filter in order to not mutate it
const clone = _.cloneDeep(filterObj);
// array element
if (path.endsWith(']')) {
const lastIndex = path.lastIndexOf('[');
const arrayPath = path.substring(0, lastIndex);
const index = parseInt(path.substring(lastIndex).replace(/[\[\]']+/g, ''), 10);
// clone original filter in order to not mutate it
const clone = _.cloneDeep(filterObj);
// array element
if (path.endsWith(']')) {
const lastIndex = path.lastIndexOf('[');
const arrayPath = path.substring(0, lastIndex);
const index = parseInt(path.substring(lastIndex).replace(/[\[\]']+/g, ''), 10);
_.get(clone, arrayPath).splice(index, 1);
_.get(clone, arrayPath).splice(index, 1);
return clone;
}
return clone;
}
return _.omit(clone, [path]);
return _.omit(clone, [path]);
}
/**
......@@ -86,102 +90,107 @@ function handleFilterObj(filterObj, path) {
* @return IPrettyFiltersProps.filterObj.
*/
function getLabelName(path, value, lookups, prefix, t) {
const lastKey = path.replace(/\[(.+?)\]/g, '').split('.').pop();
let name = value;
if (lookups && lookups[lastKey]) {
name = lookups[lastKey][value] || value;
}
const prettyPath: string = path
// remove array indexes square brackets [0], [1]... from path.
.replace(/\[(.+?)\]/g, '')
// split path title.eq -> ['title', 'eq']
.split('.')
// skip for showing keywords such as 'contains', 'eq'...
.filter((e) => keywordsToSkip.indexOf(e) === -1)
// join
.join('.');
if (typeof name === 'boolean') {
if (name) {
return t(`f.${prefix}.${prettyPath}`);
}
return t('f.NOT', {what: t(`f.${prefix}.${prettyPath.replace(/^NOT\./, '')}`)});
const lastKey = path.replace(/\[(.+?)\]/g, '').split('.').pop();
let name = value;
if (lookups && lookups[lastKey]) {
name = lookups[lastKey][value] || value;
}
const prettyPath: string = path
// remove array indexes square brackets [0], [1]... from path.
.replace(/\[(.+?)\]/g, '')
// split path title.eq -> ['title', 'eq']
.split('.')
// skip for showing keywords such as 'contains', 'eq'...
.filter((e) => keywordsToSkip.indexOf(e) === -1)
// join
.join('.');
if (typeof name === 'boolean') {
if (name) {
return t(`f.${prefix}.${prettyPath}`);
}
return t('f.NOT', { what: t(`f.${prefix}.${prettyPath.replace(/^NOT\./, '')}`) });
}
const translatedPrettyPath = prettyPath.startsWith('NOT') ?
t('f.NOT', {what: t(`f.${prefix}.${prettyPath.replace(/^NOT\./, '')}`)})
:
t(`f.${prefix}.${prettyPath}`);
const translatedPrettyPath = prettyPath.startsWith('NOT') ?
t('f.NOT', { what: t(`f.${prefix}.${prettyPath.replace(/^NOT\./, '')}`) })
:
t(`f.${prefix}.${prettyPath}`);
const translatedPrettyName = t(`${name}`);
const translatedPrettyName = t(`${name}`);
if (prettyPath.includes('Date')) {
return <span>{ translatedPrettyPath }: <PrettyDate value={ new Date(name) }/></span>;
}
if (prettyPath.includes('Date')) {
return <span>{ translatedPrettyPath }: <PrettyDate value={ new Date(name) } /></span>;
}
return `${translatedPrettyPath}: ${translatedPrettyName}`;
return `${translatedPrettyPath}: ${translatedPrettyName}`;
}
class PrettyFilters extends React.Component<IPrettyFiltersProps, any> {
constructor(props: IPrettyFiltersProps, context: any) {
super(props, context);
this.state = {
chipData: flattenjs.convert(cleanFilters(props.filterObj)),
};
}
protected handleDelete = (path) => () => {
const {filterObj, onSubmit} = this.props;
const updated = handleFilterObj(filterObj, path);
onSubmit(updated);
}
public componentWillReceiveProps(nextProps) {
const chipData = flattenjs.convert(cleanFilters(nextProps.filterObj));
if (!_.isEqual(chipData, this.state.chipData)) {
this.setState({chipData});
}
}
public render() {
const {classes, lookups, prefix, t } = this.props;
const {chipData} = this.state;
const dataArr = Object.getOwnPropertyNames(chipData);
return dataArr.length > 0 && (
<Paper square className={ classes.root }>
{ dataArr.map((key) => {
return (
<Chip
key={ key }
label={ getLabelName(key, chipData[key], lookups, prefix, t) }
onDelete={ this.handleDelete(key) }
className={ classes.chip }
/>
);
}) }
</Paper>
);
constructor(props: IPrettyFiltersProps, context: any) {
super(props, context);
this.state = {
chipData: flattenjs.convert(cleanFilters(props.filterObj)),
};
}
protected handleDelete = (path) => () => {
const { filterObj, onSubmit, showSnackbar } = this.props;
const updated = handleFilterObj(filterObj, path);
showSnackbar('Applying filters...');
onSubmit(updated);
}
public componentWillReceiveProps(nextProps) {
const chipData = flattenjs.convert(cleanFilters(nextProps.filterObj));
if (!_.isEqual(chipData, this.state.chipData)) {
this.setState({ chipData });
}
}
public render() {
const { classes, lookups, prefix, t } = this.props;
const { chipData } = this.state;
const dataArr = Object.getOwnPropertyNames(chipData);
return dataArr.length > 0 && (
<Paper square className={ classes.root }>
{ dataArr.map((key) => {
return (
<Chip
key={ key }
label={ getLabelName(key, chipData[key], lookups, prefix, t) }
onDelete={ this.handleDelete(key) }
className={ classes.chip }
/>
);
}) }
</Paper>
);
}
}
const styled = withStyles(styles)(PrettyFilters);
const mapStateToProps = (state, ownProps) => ({
lookups: {
crop: [],
category: '',
sampStat: Accession.SAMPSTAT,
storage: Accession.STORAGE,
accessions: {
true: 'Yes',
false: 'No',
},
lookups: {
crop: [],
category: '',
sampStat: Accession.SAMPSTAT,
storage: Accession.STORAGE,
accessions: {
true: 'Yes',
false: 'No',
},
},
});
export default translate()(connect(mapStateToProps)(styled));
const mapDispatchToProps = (dispatch) => bindActionCreators({
showSnackbar,
}, dispatch);
export default translate()(connect(mapStateToProps, mapDispatchToProps)(styled));
......@@ -7,6 +7,7 @@ import * as _ from 'lodash';
// Actions
import {applyFilters, loadAccessionsPage, listAccessionsPromise, updateRoute, applyOverviewFilters, loadAccessionsOverviewPage} from 'actions/accessions';
import { showSnackbar } from 'actions/snackbar';
// Models
import Accession from 'model/Accession';
......@@ -18,6 +19,7 @@ import AccessionOverview from 'model/AccessionOverview';
import PageLayout, { PageContents } from 'ui/layout/PageLayout';
import GridLayout from 'ui/layout/GridLayout';
import ContentHeader from 'ui/common/heading/ContentHeader';
import Loading from 'ui/common/Loading';
import Tabs, {Tab} from 'ui/common/Tabs';
import PropertiesCard from 'ui/common/PropertiesCard';
import PrettyFilters from 'ui/common/filter/PrettyFilters';
......@@ -32,6 +34,7 @@ interface IOverviewPageProps extends React.ClassAttributes<any> {
overview: AccessionOverview;
paged: FilteredPage<Accession>;
filterCode: string;
showSnackbar: (snack: string) => void;
applyOverviewFilters: (filters: string | AccessionFilter, page?: IPageRequest) => void;
updateRoute: (paged: FilteredPage<Accession>, path?: string) => void;
currentTab: string;
......@@ -63,7 +66,7 @@ class BrowsePage extends React.Component<IOverviewPageProps, any> {
}
private addTerm = (property, term) => {
const { overview, applyOverviewFilters } = this.props;
const { overview, applyOverviewFilters, showSnackbar } = this.props;
const updatedFilter: AccessionFilter = { ...overview.filter };
switch (property) {
......@@ -89,6 +92,7 @@ class BrowsePage extends React.Component<IOverviewPageProps, any> {
}
// console.log(`Updated filter for ${property} +${term}`, updatedFilter);
showSnackbar('Applying filters...');
applyOverviewFilters(updatedFilter);
};
......@@ -147,7 +151,7 @@ class BrowsePage extends React.Component<IOverviewPageProps, any> {
filterObj={ overviewWrapper && overviewWrapper.filter || {} }
onSubmit={ applyOverviewFilters }
/>
{ overview &&
{ ! overview ? <Loading /> :
<div style={ {display: 'flex', marginTop: '1em', marginBottom: '1em'} }>
<PageContents>
<GridLayout>
......@@ -214,6 +218,7 @@ const mapStateToProps = (state, ownProps) => ({
});
const mapDispatchToProps = (dispatch) => bindActionCreators({
showSnackbar,
applyFilters,
loadDataPage: loadAccessionsPage,
loadDataPromise: listAccessionsPromise,
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment