Commit 042fdfac authored by Matija Obreza's avatar Matija Obreza

Merge branch '574-booleanarrfilter-component' into 'master'

BooleanArrFilter component

Closes #574

See merge request genesys-pgr/genesys-ui!567
parents 9b814921 017c0773
...@@ -15,7 +15,7 @@ import StringArrFilter from 'ui/common/filter/StringArrFilter'; ...@@ -15,7 +15,7 @@ import StringArrFilter from 'ui/common/filter/StringArrFilter';
import AutocompleteFilter from 'ui/common/filter/AutocompleteFilter'; import AutocompleteFilter from 'ui/common/filter/AutocompleteFilter';
import Accession from 'model/accession/Accession'; import Accession from 'model/accession/Accession';
import DateFilter from 'ui/common/filter/DateFilter'; import DateFilter from 'ui/common/filter/DateFilter';
import BooleanFilter from 'ui/common/filter/BooleanFilter'; import BooleanArrFilter from 'ui/common/filter/BooleanArrFilter';
import { autocomplete } from 'accessions/actions/public'; import { autocomplete } from 'accessions/actions/public';
import TextFilter from 'ui/common/filter/TextFilter'; import TextFilter from 'ui/common/filter/TextFilter';
import IconButton from '@material-ui/core/IconButton'; import IconButton from '@material-ui/core/IconButton';
...@@ -29,7 +29,7 @@ const AccessionFilters = ({handleSubmit, initialValues, initialize, filterCode, ...@@ -29,7 +29,7 @@ const AccessionFilters = ({handleSubmit, initialValues, initialize, filterCode,
return ( return (
<FiltersBlock title={ t('accessions.public.f.filtersTitle') } handleSubmit={ handleSubmit } initialize={ initialize } { ...other }> <FiltersBlock title={ t('accessions.public.f.filtersTitle') } handleSubmit={ handleSubmit } initialize={ initialize } { ...other }>
<CollapsibleComponentSearch sectionIndex={ sectionIndex++ } title={ t('accessions.public.f.historic') }> <CollapsibleComponentSearch sectionIndex={ sectionIndex++ } title={ t('accessions.public.f.historic') }>
<BooleanFilter <BooleanArrFilter
name="historic" name="historic"
terms={ terms && terms.get('historic') } terms={ terms && terms.get('historic') }
notNull notNull
...@@ -144,30 +144,30 @@ const AccessionFilters = ({handleSubmit, initialValues, initialize, filterCode, ...@@ -144,30 +144,30 @@ const AccessionFilters = ({handleSubmit, initialValues, initialize, filterCode,
terms={ terms && terms.get('storage') } terms={ terms && terms.get('storage') }
/> />
</CollapsibleComponentSearch> </CollapsibleComponentSearch>
<CollapsibleComponentSearch sectionIndex={ sectionIndex++ } title={ t('accessions.public.f.status') }> <CollapsibleComponentSearch title={ t('accessions.public.f.status') }>
<BooleanFilter <BooleanArrFilter
name="available" name="available"
label={ t('accessions.public.f.available') } label={ t('accessions.public.f.available') }
terms={ terms && terms.get('available') } terms={ terms && terms.get('available') }
/> />
<BooleanFilter <BooleanArrFilter
name="mlsStatus" name="mlsStatus"
label={ t('accessions.public.f.mlsStatus') } label={ t('accessions.public.f.mlsStatus') }
terms={ terms && terms.get('mlsStatus') } terms={ terms && terms.get('mlsStatus') }
/> />
<BooleanFilter <BooleanArrFilter
name="sgsv" name="sgsv"
label={ t('accessions.public.f.sgsv') } label={ t('accessions.public.f.sgsv') }
terms={ terms && terms.get('sgsv') } terms={ terms && terms.get('sgsv') }
notNull notNull
/> />
<BooleanFilter <BooleanArrFilter
name="images" name="images"
label={ t('accessions.public.f.images') } label={ t('accessions.public.f.images') }
terms={ terms && terms.get('images') } terms={ terms && terms.get('images') }
notNull notNull
/> />
<BooleanFilter <BooleanArrFilter
name="aegis" name="aegis"
label={ t('accessions.public.f.aegis') } label={ t('accessions.public.f.aegis') }
terms={ terms && terms.get('aegis') } terms={ terms && terms.get('aegis') }
......
import * as React from 'react';
import { translate } from 'react-i18next';
import FormControlLabel from '@material-ui/core/FormControlLabel';
import FormLabel from '@material-ui/core/FormLabel';
import FormControl from '@material-ui/core/FormControl';
import RadioGroup from '@material-ui/core/RadioGroup';
import { withStyles } from '@material-ui/core/styles';
import Checkbox from '@material-ui/core/Checkbox';
import { cleanFilters } from 'utilities';
/* tslint:disable */
const styles = (theme) =>({
label: {
// just mock for overriding
},
});
/* tslint:enable */
interface IBooleanArrCheckbox extends React.ClassAttributes<any> {
formLabel: string;
options: any[];
renderOptionLabel: any;
input: any;
classes: any;
t: any;
customHandleChange: any;
isNotNull: boolean;
isNull: boolean;
notNull: boolean;
}
class BooleanArrCheckbox extends React.Component<IBooleanArrCheckbox> {
public componentWillReceiveProps(nextProps) {
const { input, notNull } = this.props;
const { input: nextInput } = nextProps;
// notNull case, set initial value for filter in case when filter === null
if (notNull && input && input.value === '' && nextInput && nextInput.value === '') {
input.onChange(cleanFilters.DEFINED_NULL);
}
}
private onChangeHandler = (option) => () => {
const { customHandleChange, input } = this.props;
return customHandleChange(input, option);
}
private isChecked = (option) => {
const { input, isNull, isNotNull, notNull } = this.props;
if (notNull) {
// notNull case
return input.value === option || typeof input.value === 'string';
} else {
// notNull === false case
if (isNotNull) {
return typeof option === 'boolean';
}
if (isNull) {
return option === 'NULL';
}
return input.value === option;
}
}
public render() {
const { classes, formLabel, options, renderOptionLabel } = this.props;
return (
<FormControl className={ classes.root } fullWidth>
<FormLabel>{ formLabel }</FormLabel>
<RadioGroup>
{ options.map((option, i) => (
<FormControlLabel
key={ `radio_option_${i}` }
value={ option }
classes={ {label: classes.label} }
label={ renderOptionLabel(option) }
control={
<Checkbox
checked={ this.isChecked(option) }
onChange={ this.onChangeHandler(option) }
/>
}
/>
)) }
</RadioGroup>
</FormControl>
);
}
}
export default withStyles(styles)(translate()(BooleanArrCheckbox));
import * as React from 'react';
import { translate } from 'react-i18next';
import { Field } from 'redux-form';
import Number from 'ui/common/Number';
import BooleanArrCheckbox from './BooleanCheckbox';
import TextField from '@material-ui/core/TextField';
import { cleanFilters } from 'utilities';
interface IBooleanArrFilter extends React.ClassAttributes<any> {
name: string;
label?: string;
terms: Map<string, any>;
t: any;
notNull: boolean;
isNull: boolean;
isNotNull: boolean;
}
class NullableFilterInternal extends React.Component<any> {
public componentWillReceiveProps(nextProps): void {
const { input } = this.props;
const { nullChecked: newNullChecked, boolName } = nextProps;
const checked = input.value.indexOf(boolName) !== -1;
if (checked !== newNullChecked) {
if (newNullChecked) {
input.onChange([...input.value, boolName]);
} else {
const index = input.value.indexOf(boolName);
if (index !== -1) {
const newValue = input.value.filter((val) => val !== boolName);
input.onChange(newValue);
}
}
}
}
public render(): React.ReactNode {
return (<TextField style={ {display: 'none'} }/>);
}
}
class BooleanArrFilter extends React.Component<IBooleanArrFilter, any> {
public state = {
nullChecked: undefined,
notNullChecked: undefined,
};
public componentWillReceiveProps(nextProps, nextState) {
const { isNull } = this.props;
if (!nextProps.isNull && isNull && !nextState.nullChecked && this.state.nullChecked) {
this.setState({nullChecked: false});
}
if (!nextProps.isNotNull && this.props.isNotNull && !nextState.notNullChecked && this.state.notNullChecked) {
this.setState({notNullChecked: false});
}
}
private getLabel = (val) => val === 'true' ? this.props.t(`common:label.yes`) : val === 'false' ? this.props.t(`common:label.no`) : this.props.t(`common:f.NULL`);
private getTermsValue = (val) => {
switch (val) {
case 'true':
return this.props.terms.get('1');
case 'false':
return this.props.terms.get('0');
case 'NULL':
return this.props.terms.get('missing') || '0';
default:
return ((this.props.terms.get('1') || 0) + (this.props.terms.get('0') || 0) + (this.props.terms.get('missing') || 0)) || '0';
}
}
private handleChange = (input, value) => {
const { isNotNull, notNull } = this.props;
if (notNull) {
// notNull case
if (typeof input.value === 'string') {
input.onChange(!value);
} else {
if (input.value === value) {
return;
} else {
input.onChange(cleanFilters.DEFINED_NULL);
}
}
} else {
// notNull === false case
if (value === 'NULL') {
this.setState({nullChecked: true, notNullChecked: false});
input.onChange('');
return;
}
if (isNotNull) {
this.setState({nullChecked: false, notNullChecked: false});
input.onChange(!value);
return;
}
if (input.value !== value && typeof input.value === 'boolean' && typeof value === 'boolean') {
this.setState({nullChecked: false, notNullChecked: true});
input.onChange('');
return;
}
if (typeof input.value !== 'boolean') {
this.setState({nullChecked: false, notNullChecked: false});
input.onChange(value);
return;
}
}
}
public render() {
const {terms, name, label, isNull, isNotNull, notNull = false} = this.props;
const { nullChecked, notNullChecked } = this.state;
const options = notNull ? [true, false] : [true, false, 'NULL'];
return (
<div>
<Field
name={ name }
singleColumn
component={ BooleanArrCheckbox }
formLabel={ label }
options={ options }
classes={ {label: 'full-width'} }
customHandleChange={ this.handleChange }
notNull={ notNull }
isNull={ isNull }
isNotNull={ isNotNull }
renderOptionLabel={ (stat) =>
<span
className="full-width"
style={ {display: 'flex', justifyContent: 'space-between'} }
>
{ `${this.getLabel(`${stat}`)}` }
{ terms &&
<span className="float-right">
<Number value={ this.getTermsValue(`${stat}`) }/>
</span>
}
</span>
}
/>
{ !notNull &&
<span>
<Field
name="NULL"
boolName={ name }
nullChecked={ nullChecked ? true : nullChecked === false ? false : isNull }
component={ NullableFilterInternal }
/>
<Field
name="NOTNULL"
boolName={ name }
nullChecked={ notNullChecked ? true : notNullChecked === false ? false : isNotNull }
component={ NullableFilterInternal }
/>
</span>
}
</div>
);
}
}
export default translate()(BooleanArrFilter);
...@@ -34,7 +34,6 @@ const styles = (theme) => ({ ...@@ -34,7 +34,6 @@ const styles = (theme) => ({
// //
// Renders a standard filters block // Renders a standard filters block
class FiltersBlock extends React.Component<any> { class FiltersBlock extends React.Component<any> {
protected renderChild = (child) => { protected renderChild = (child) => {
const {values} = this.props; const {values} = this.props;
if (! values) { if (! values) {
...@@ -69,18 +68,17 @@ class FiltersBlock extends React.Component<any> { ...@@ -69,18 +68,17 @@ class FiltersBlock extends React.Component<any> {
} }
} }
private onReset = (e) => { private onReset = () => {
const {values, initialize, handleSubmit, showSnackbar} = this.props; const { reset, onSubmit, showSnackbar } = this.props;
showSnackbar('common.snackbar.resettingFilters'); showSnackbar('common.snackbar.resettingFilters');
log('Clearing form'); log('Clearing form');
const initialValues = {}; reset();
Object.keys(values).forEach((key) => {
initialValues[key] = null; setTimeout(() => {
}); onSubmit();
initialize(initialValues); }, 100);
setTimeout(handleSubmit, 100);
this.scrollToTop(); this.scrollToTop();
this.setState({initialized: false});
} }
private processSubmit = this.props.handleSubmit((data) => { private processSubmit = this.props.handleSubmit((data) => {
......
...@@ -284,7 +284,7 @@ class PrettyFilters extends React.Component<IPrettyFiltersProps, any> { ...@@ -284,7 +284,7 @@ class PrettyFilters extends React.Component<IPrettyFiltersProps, any> {
protected handleDelete = (path) => () => { protected handleDelete = (path) => () => {
const { filterObj, onSubmit, showSnackbar } = this.props; const { filterObj, onSubmit, showSnackbar } = this.props;
const updated = handleFilterObj(filterObj, path); const updated = cleanFilters(handleFilterObj(filterObj, path));
showSnackbar('common.snackbar.applyingFilters'); showSnackbar('common.snackbar.applyingFilters');
onSubmit(updated); onSubmit(updated);
} }
......
...@@ -45,34 +45,43 @@ const handleCheckboxChange = (fields, code) => { ...@@ -45,34 +45,43 @@ const handleCheckboxChange = (fields, code) => {
} }
}; };
const CheckboxSelection = ({formLabel, singleColumn = false, options, renderOptionLabel, valueField, fields, input, required = false, meta, classes, t, ...rest}) => ( const CheckboxSelection = ({formLabel, singleColumn = false, options, renderOptionLabel, valueField, fields, input, required = false, meta, classes, t, customHandleChange, selectedValue, ...rest}) => {
<FormControl required={ required } className={ classes.root } fullWidth> const onChangeHandler = (option) => (event, value) => {
<FormLabel className={ classes.formLabel }>{ formLabel }</FormLabel> const code = valueField ? option[valueField] : option;
<RadioGroup return customHandleChange ?
{ ...input } customHandleChange(fields, code) :
{ ...rest } handleCheckboxChange(fields, code);
className={ singleColumn ? classes.radioGroupSingleColumn : classes.radioGroupMultiColumn } };
>
{ options && options.length > 0 && return (
options.map((option, i) => ( <FormControl required={ required } className={ classes.root } fullWidth>
<FormControlLabel <FormLabel className={ classes.formLabel }>{ formLabel }</FormLabel>
key={ `radio_option_${i}` } <RadioGroup
value={ valueField ? option[valueField] : option } { ...input }
classes={ {label: classes.label} } { ...rest }
label={ renderOptionLabel ? renderOptionLabel(option) : valueField ? option[valueField] : option } className={ singleColumn ? classes.radioGroupSingleColumn : classes.radioGroupMultiColumn }
control={ >
<Checkbox { options && options.length > 0 &&
{ ...rest } options.map((option, i) => (
checked={ fields.getAll() && fields.getAll().indexOf(valueField ? option[valueField] : option) !== -1 } <FormControlLabel
onChange={ (event, value) => handleCheckboxChange(fields,valueField ? option[valueField] : option) } // tslint:disable-line key={ `radio_option_${i}` }
/> value={ selectedValue ? selectedValue : valueField ? option[valueField] : option }
} classes={ {label: classes.label} }
className={ classes.label } label={ renderOptionLabel ? renderOptionLabel(option) : valueField ? option[valueField] : option }
/> control={
)) <Checkbox
} { ...rest }
</RadioGroup> checked={ selectedValue === option || fields.getAll() && fields.getAll().indexOf(valueField ? option[valueField] : option) !== -1 }
</FormControl> onChange={ onChangeHandler(option) }
); />
}
className={ classes.label }
/>
))
}
</RadioGroup>
</FormControl>
);
};
export default withStyles(styles)(translate()(CheckboxSelection)); export default withStyles(styles)(translate()(CheckboxSelection));
...@@ -109,6 +109,8 @@ export function cleanFilters(filter, keysToSkip?): any { ...@@ -109,6 +109,8 @@ export function cleanFilters(filter, keysToSkip?): any {
result[k] = sub; result[k] = sub;
} }
} }
} else if (val === cleanFilters.DEFINED_NULL) {
result[k] = null;
} else if (val !== null) { } else if (val !== null) {
result[k] = val; result[k] = val;
} }
...@@ -116,6 +118,8 @@ export function cleanFilters(filter, keysToSkip?): any { ...@@ -116,6 +118,8 @@ export function cleanFilters(filter, keysToSkip?): any {
return result; return result;
} }
cleanFilters.DEFINED_NULL = 'DEFINED_NULL';
export const withPermissionCheck = (...permissions) => (promised) => (dispatch, getState) => { export const withPermissionCheck = (...permissions) => (promised) => (dispatch, getState) => {
return promised(dispatch, getState).then((res) => { return promised(dispatch, getState).then((res) => {
......
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