Commit 9b05bf4f authored by Viacheslav Pavlov's avatar Viacheslav Pavlov Committed by Matija Obreza
Browse files

Support for negative filters in the UI

parent e2534b45
......@@ -105,6 +105,7 @@
}
},
"f": {
"NOT": "Excluding {{what}}",
"accessions": {
"crops": "Crop name",
"acceNumb": "Accession number",
......
......@@ -33,7 +33,7 @@ class AccessionFilter {
public uuid: string[];
public version: number[];
public NOT: AccessionFilter;
}
export default AccessionFilter;
......@@ -10,6 +10,8 @@ class FaoInstituteFilter {
public accessions: boolean;
public name: StringFilter;
public country: CountryFilter;
public NOT: FaoInstituteFilter;
}
export default FaoInstituteFilter;
......@@ -17,7 +17,7 @@ class MaterialRequestFilter {
public uuid: string[];
public version: number[];
public NOT: MaterialRequestFilter;
}
export default MaterialRequestFilter;
......@@ -17,7 +17,7 @@ class MaterialSubRequestFilter {
public uuid: string[];
public version: number[];
public NOT: MaterialSubRequestFilter;
}
export default MaterialSubRequestFilter;
......@@ -21,7 +21,7 @@ class SubsetFilter {
public uuid: string[];
public version: number[];
public NOT: SubsetFilter;
}
export default SubsetFilter;
......@@ -103,9 +103,14 @@ function getLabelName(path, value, lookups, prefix, t) {
// join
.join('.');
const translatedPrettyPath = 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}`);
if (prettyPath.includes('Date')) {
return <span>{ translatedPrettyPath }: <PrettyDate value={ new Date(name) }/></span>;
}
......
import * as React from 'react';
import { Field } from 'redux-form';
import { Fields, change } from 'redux-form';
import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
import { translate } from 'react-i18next';
import * as _ from 'lodash';
import FormGroup from '@material-ui/core/FormGroup';
import FormControlLabel from '@material-ui/core/FormControlLabel';
......@@ -14,6 +17,103 @@ import InputLabel from '@material-ui/core/InputLabel';
import InputAdornment from '@material-ui/core/InputAdornment';
import PlusOne from '@material-ui/icons/PlusOne';
interface IStringListProps extends React.ClassAttributes<any> {
input: any;
notInput: any;
removeByIndex: (index: number, isNot: boolean) => void;
}
class StringList extends React.Component<IStringListProps> {
public state = {
renderList: [],
};
private addToNotList = (item, index) => {
const { input, notInput } = this.props;
const {renderList} = this.state;
renderList[index] = {value: item.value, state: false};
this.setState({renderList});
input.onChange(renderList.filter((renderItem) => renderItem.state).map((renderItem) => renderItem.value));
notInput.onChange(renderList.filter((renderItem) => !renderItem.state).map((renderItem) => renderItem.value));
}
private removeFromNotList = (item, index) => {
const { input, notInput } = this.props;
const {renderList} = this.state;
renderList[index] = {value: item.value, state: true};
this.setState({renderList});
input.onChange(renderList.filter((renderItem) => renderItem.state).map((renderItem) => renderItem.value));
notInput.onChange(renderList.filter((renderItem) => !renderItem.state).map((renderItem) => renderItem.value));
}
public componentWillMount() {
let {input: {value: doList}, notInput: {value: notList}} = this.props;
doList = doList || [];
notList = notList || [];
const {renderList} = this.state;
doList
.filter((doItem) => renderList.findIndex((renderItem) => renderItem.value === doItem) === -1)
.map((doItem) => renderList.push({value: doItem, state: true}));
notList
.filter((notItem) => renderList.findIndex((renderItem) => renderItem.value === notItem) === -1)
.map((notItem) => renderList.push({value: notItem, state: false}));
this.setState({renderList});
}
public componentWillReceiveProps(nextProps) {
let {input: {value: doList}, notInput: {value: notList}} = nextProps;
doList = doList || [];
notList = notList || [];
const {renderList} = this.state;
const newRenderList = [];
renderList.map((renderItem) => {
if (doList.indexOf(renderItem.value) !== -1 || notList.indexOf(renderItem.value) !== -1) {
newRenderList.push(renderItem);
}
});
doList
.filter((doItem) => newRenderList.findIndex((renderItem) => renderItem.value === doItem) === -1)
.map((doItem) => newRenderList.push({value: doItem, state: true}));
notList
.filter((notItem) => newRenderList.findIndex((renderItem) => renderItem.value === notItem) === -1)
.map((notItem) => newRenderList.push({value: notItem, state: false}));
this.setState({renderList: newRenderList});
}
public render() {
const {removeByIndex} = this.props;
const {renderList} = this.state;
return (
<div>
{ renderList.map((renderItem, index) => (
<div style={ { margin: '.2rem 0', padding: '.2rem 1rem', backgroundColor: '#e8e5e1', color: '#202222' } } key={ renderItem.value }>
{ renderItem.state ?
<span className="font-bold float-Left mr-5" onClick={ () => this.addToNotList(renderItem, index) }>+</span>
:
<span className="font-bold float-Left mr-5" onClick={ () => this.removeFromNotList(renderItem, index) }>-</span>
}
{ renderItem.value }
<div className="font-bold float-right" onClick={ () => removeByIndex(index, !renderItem.state) }>X</div>
</div>
)) }
</div>
);
}
}
interface IStringArrFilterInternal extends React.ClassAttributes<any> {
name: string;
input: any;
......@@ -21,31 +121,44 @@ interface IStringArrFilterInternal extends React.ClassAttributes<any> {
label?: string;
options?: { [key: string]: any; };
t: any;
addToNotList: (item: any) => void;
notList: any[];
}
class InternalStringArrField extends React.Component<IStringArrFilterInternal & any, any> {
private constructor(props, context) {
super(props, context);
const notValues = _.get(props, `${props.names[0]}.input.value`);
const values = _.get(props, `${props.names[1]}.input.value`);
this.state = {
values: [ ...props.input.value ],
values,
notValues,
text: '',
};
}
public componentWillMount() {
const value = typeof this.props.input.value[0] === 'number' ? this.props.input.value.map((key) => `${key}`) : this.props.input.value;
this.setState({
values: [ ...value ],
text: '',
});
const input = _.get(this.props, `${this.props.names[0]}.input`);
const value = typeof input.value[0] === 'number' ? input.value.map((key) => `${key}`) : input.value;
const notValue = _.get(this.props, `${this.props.names[1]}.input.value`);
this.setState({
values: [ ...value ],
notValues: [ ...notValue ],
text: '',
});
}
public componentWillReceiveProps(nextProps) {
const value = typeof nextProps.input.value[0] === 'number' ? nextProps.input.value.map((key) => `${key}`) : nextProps.input.value;
const input = _.get(nextProps, `${nextProps.names[0]}.input`);
const value = typeof input.value[0] === 'number' ? input.value.map((key) => `${key}`) : input.value;
const notValue = _.get(nextProps, `${nextProps.names[1]}.input.value`);
this.setState({
values: [ ...value ],
notValues: [...notValue],
text: '',
});
}
......@@ -82,7 +195,7 @@ class InternalStringArrField extends React.Component<IStringArrFilterInternal &
}
private handleKeyPres = (event) => {
const { input } = this.props;
const { input } = _.get(this.props, this.props.names[0]);
const { text } = this.state;
if (event.key === 'Enter') {
......@@ -98,14 +211,14 @@ class InternalStringArrField extends React.Component<IStringArrFilterInternal &
}
private handleCheckbox = (event) => {
const { input } = this.props;
const { input } = _.get(this.props, this.props.names[0]);
const values = event.target.checked ? this.maybeAdd(event.target.value) : this.maybeRemove(event.target.value);
input.onChange(values);
}
private removeByIndex = (index) => (event) => {
const { input } = this.props;
private removeByIndex = (index, isNot) => {
const { input } = _.get(this.props, this.props.names[isNot ? 1 : 0]);
const newValues = this.maybeRemove(this.state.values[index]);
input.onChange(newValues);
......@@ -117,7 +230,7 @@ class InternalStringArrField extends React.Component<IStringArrFilterInternal &
}
private handleAddCurrent = (event) => {
const { input } = this.props;
const { input } = _.get(this.props, this.props.names[0]);
const { text } = this.state;
event.preventDefault();
......@@ -126,8 +239,11 @@ class InternalStringArrField extends React.Component<IStringArrFilterInternal &
}
public render() {
const { placeholder, label, options, t } = this.props;
const { text, values } = this.state;
const { placeholder, label, options, t, names } = this.props;
const { input } = _.get(this.props, names[0]);
const { input: notInput } = _.get(this.props, names[1]);
const { text, values, notValues } = this.state;
const withOptions: boolean = options !== undefined && typeof options === 'object';
......@@ -167,13 +283,9 @@ class InternalStringArrField extends React.Component<IStringArrFilterInternal &
/>
</FormControl>
}
{ (! withOptions && values && values.length > 0) &&
values.map((val, index) => (
<div style={ { margin: '.2rem 0', padding: '.2rem 1rem', backgroundColor: '#e8e5e1', color: '#202222' } } key={ val }>
{ withOptions ? options[val] || val : val }
<div className="font-bold float-right" onClick={ this.removeByIndex(index) }>X</div>
</div>
)) }
{ (! withOptions && ((values && values.length > 0) || (notValues && notValues.length > 0))) &&
<StringList input={ input } notInput={ notInput } removeByIndex={ this.removeByIndex } />
}
</div>
);
}
......@@ -189,16 +301,13 @@ interface IStringArrFilter extends React.ClassAttributes<any> {
class StringArrFilter extends React.Component<IStringArrFilter, any> {
public constructor(props: any) {
super(props);
}
public render() {
const { name, label, placeholder, options, t } = this.props;
return (
<div>
<Field
name={ name }
<Fields
names={ [`${name}`, `NOT.${name}`] }
component={ InternalStringArrField }
label={ label }
placeholder={ placeholder }
......@@ -210,4 +319,10 @@ class StringArrFilter extends React.Component<IStringArrFilter, any> {
}
}
export default translate()(StringArrFilter);
const mapDispatchToProps = (dispatch) => bindActionCreators({
change,
}, dispatch);
export default connect(null, mapDispatchToProps)(translate()(StringArrFilter));
Supports Markdown
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