Refactor multiple choice field widget
This commit is contained in:
parent
15470221c2
commit
0a58c3a5e1
4 changed files with 200 additions and 46 deletions
145
src/components/Filter/FilterAutocompleteField.tsx
Normal file
145
src/components/Filter/FilterAutocompleteField.tsx
Normal file
|
@ -0,0 +1,145 @@
|
||||||
|
import React from "react";
|
||||||
|
import FormControlLabel from "@material-ui/core/FormControlLabel";
|
||||||
|
import TextField from "@material-ui/core/TextField";
|
||||||
|
import makeStyles from "@material-ui/core/styles/makeStyles";
|
||||||
|
import { FormattedMessage } from "react-intl";
|
||||||
|
|
||||||
|
import { toggle } from "@saleor/utils/lists";
|
||||||
|
import { MultiAutocompleteChoiceType } from "../MultiAutocompleteSelectField";
|
||||||
|
import Link from "../Link";
|
||||||
|
import Checkbox from "../Checkbox";
|
||||||
|
import Hr from "../Hr";
|
||||||
|
import { IFilterElement } from "./types";
|
||||||
|
import { FilterReducerAction } from "./reducer";
|
||||||
|
|
||||||
|
interface FilterAutocompleteFieldProps {
|
||||||
|
displayValues: Record<string, MultiAutocompleteChoiceType[]>;
|
||||||
|
filterField: IFilterElement<string>;
|
||||||
|
setDisplayValues: (
|
||||||
|
values: Record<string, MultiAutocompleteChoiceType[]>
|
||||||
|
) => void;
|
||||||
|
onFilterPropertyChange: React.Dispatch<FilterReducerAction<string>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const useStyles = makeStyles(
|
||||||
|
theme => ({
|
||||||
|
hr: {
|
||||||
|
backgroundColor: theme.palette.primary.light,
|
||||||
|
margin: theme.spacing(1, 0)
|
||||||
|
},
|
||||||
|
input: {
|
||||||
|
padding: "12px 0 9px 12px"
|
||||||
|
},
|
||||||
|
inputContainer: {
|
||||||
|
marginBottom: theme.spacing(1)
|
||||||
|
},
|
||||||
|
option: {
|
||||||
|
left: -theme.spacing(0.5),
|
||||||
|
position: "relative"
|
||||||
|
},
|
||||||
|
showMore: {
|
||||||
|
display: "inline-block",
|
||||||
|
marginTop: theme.spacing(1)
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
{ name: "FilterAutocompleteField" }
|
||||||
|
);
|
||||||
|
|
||||||
|
const FilterAutocompleteField: React.FC<FilterAutocompleteFieldProps> = ({
|
||||||
|
displayValues,
|
||||||
|
filterField,
|
||||||
|
setDisplayValues,
|
||||||
|
onFilterPropertyChange
|
||||||
|
}) => {
|
||||||
|
const classes = useStyles({});
|
||||||
|
|
||||||
|
const fieldDisplayValues = displayValues[filterField.name];
|
||||||
|
const availableOptions = filterField.options.filter(option =>
|
||||||
|
fieldDisplayValues.every(
|
||||||
|
displayValue => displayValue.value !== option.value
|
||||||
|
)
|
||||||
|
);
|
||||||
|
const displayHr = !(
|
||||||
|
(fieldDisplayValues.length === 0 && availableOptions.length > 0) ||
|
||||||
|
(availableOptions.length === 0 && fieldDisplayValues.length > 0)
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleChange = (option: MultiAutocompleteChoiceType) => {
|
||||||
|
onFilterPropertyChange({
|
||||||
|
payload: {
|
||||||
|
name: filterField.name,
|
||||||
|
update: {
|
||||||
|
value: toggle(option.value, filterField.value, (a, b) => a === b)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
type: "set-property"
|
||||||
|
});
|
||||||
|
|
||||||
|
setDisplayValues({
|
||||||
|
...displayValues,
|
||||||
|
[filterField.name]: toggle(
|
||||||
|
option,
|
||||||
|
fieldDisplayValues,
|
||||||
|
(a, b) => a.value === b.value
|
||||||
|
)
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<TextField
|
||||||
|
className={classes.inputContainer}
|
||||||
|
fullWidth
|
||||||
|
name={filterField.name + "_autocomplete"}
|
||||||
|
InputProps={{
|
||||||
|
classes: {
|
||||||
|
input: classes.input
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onChange={event => filterField.onSearchChange(event.target.value)}
|
||||||
|
/>
|
||||||
|
{fieldDisplayValues.map(displayValue => (
|
||||||
|
<div className={classes.option} key={displayValue.value}>
|
||||||
|
<FormControlLabel
|
||||||
|
control={
|
||||||
|
<Checkbox
|
||||||
|
checked={filterField.value.includes(displayValue.value)}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
label={displayValue.label}
|
||||||
|
name={filterField.name}
|
||||||
|
onChange={() => handleChange(displayValue)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
{displayHr && <Hr className={classes.hr} />}
|
||||||
|
{availableOptions.map(option => (
|
||||||
|
<div className={classes.option} key={option.value}>
|
||||||
|
<FormControlLabel
|
||||||
|
control={
|
||||||
|
<Checkbox checked={filterField.value.includes(option.value)} />
|
||||||
|
}
|
||||||
|
label={option.label}
|
||||||
|
name={filterField.name}
|
||||||
|
onChange={() => handleChange(option)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
{filterField.hasMore && (
|
||||||
|
<Link
|
||||||
|
className={classes.showMore}
|
||||||
|
underline
|
||||||
|
onClick={filterField.onFetchMore}
|
||||||
|
>
|
||||||
|
<FormattedMessage
|
||||||
|
defaultMessage="Show more"
|
||||||
|
description="search results"
|
||||||
|
/>
|
||||||
|
</Link>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
FilterAutocompleteField.displayName = "FilterAutocompleteField";
|
||||||
|
export default FilterAutocompleteField;
|
|
@ -22,9 +22,11 @@ import FormSpacer from "../FormSpacer";
|
||||||
import MultiAutocompleteSelectField, {
|
import MultiAutocompleteSelectField, {
|
||||||
MultiAutocompleteChoiceType
|
MultiAutocompleteChoiceType
|
||||||
} from "../MultiAutocompleteSelectField";
|
} from "../MultiAutocompleteSelectField";
|
||||||
|
import Link from "../Link";
|
||||||
import { IFilter, FieldType, FilterType } from "./types";
|
import { IFilter, FieldType, FilterType } from "./types";
|
||||||
import Arrow from "./Arrow";
|
import Arrow from "./Arrow";
|
||||||
import { FilterReducerAction } from "./reducer";
|
import { FilterReducerAction } from "./reducer";
|
||||||
|
import FilterAutocompleteField from "./FilterAutocompleteField";
|
||||||
|
|
||||||
export interface FilterContentProps<T extends string = string> {
|
export interface FilterContentProps<T extends string = string> {
|
||||||
currencySymbol: string;
|
currencySymbol: string;
|
||||||
|
@ -428,51 +430,57 @@ const FilterContent: React.FC<FilterContentProps> = ({
|
||||||
))}
|
))}
|
||||||
{filterField.type === FieldType.autocomplete &&
|
{filterField.type === FieldType.autocomplete &&
|
||||||
filterField.multiple && (
|
filterField.multiple && (
|
||||||
<MultiAutocompleteSelectField
|
<FilterAutocompleteField
|
||||||
displayValues={
|
displayValues={autocompleteDisplayValues}
|
||||||
autocompleteDisplayValues[filterField.name]
|
filterField={filterField}
|
||||||
}
|
setDisplayValues={setAutocompleteDisplayValues}
|
||||||
label={filterField.label}
|
onFilterPropertyChange={onFilterPropertyChange}
|
||||||
choices={filterField.options}
|
|
||||||
name={filterField.name}
|
|
||||||
value={filterField.value}
|
|
||||||
// helperText={intl.formatMessage({
|
|
||||||
// defaultMessage:
|
|
||||||
// "*Optional. Adding product to collection helps users find it.",
|
|
||||||
// description: "field is optional"
|
|
||||||
// })}
|
|
||||||
onChange={createMultiAutocompleteSelectHandler(
|
|
||||||
event =>
|
|
||||||
onFilterPropertyChange({
|
|
||||||
payload: {
|
|
||||||
name: filterField.name,
|
|
||||||
update: {
|
|
||||||
value: toggle(
|
|
||||||
event.target.value,
|
|
||||||
filterField.value,
|
|
||||||
(a, b) => a === b
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
type: "set-property"
|
|
||||||
}),
|
|
||||||
value =>
|
|
||||||
setAutocompleteDisplayValues({
|
|
||||||
...autocompleteDisplayValues,
|
|
||||||
[filterField.name]: toggle(
|
|
||||||
value[0],
|
|
||||||
autocompleteDisplayValues[filterField.name],
|
|
||||||
(a, b) => a.value === b.value
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
[],
|
|
||||||
filterField.options
|
|
||||||
)}
|
|
||||||
fetchChoices={filterField.onSearchChange}
|
|
||||||
loading={filterField.loading}
|
|
||||||
data-tc={filterField.name}
|
|
||||||
key={filterField.name}
|
|
||||||
/>
|
/>
|
||||||
|
// <MultiAutocompleteSelectField
|
||||||
|
// displayValues={
|
||||||
|
// autocompleteDisplayValues[filterField.name]
|
||||||
|
// }
|
||||||
|
// label={filterField.label}
|
||||||
|
// choices={filterField.options}
|
||||||
|
// name={filterField.name}
|
||||||
|
// value={filterField.value}
|
||||||
|
// // helperText={intl.formatMessage({
|
||||||
|
// // defaultMessage:
|
||||||
|
// // "*Optional. Adding product to collection helps users find it.",
|
||||||
|
// // description: "field is optional"
|
||||||
|
// // })}
|
||||||
|
// onChange={createMultiAutocompleteSelectHandler(
|
||||||
|
// event =>
|
||||||
|
// onFilterPropertyChange({
|
||||||
|
// payload: {
|
||||||
|
// name: filterField.name,
|
||||||
|
// update: {
|
||||||
|
// value: toggle(
|
||||||
|
// event.target.value,
|
||||||
|
// filterField.value,
|
||||||
|
// (a, b) => a === b
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
// type: "set-property"
|
||||||
|
// }),
|
||||||
|
// value =>
|
||||||
|
// setAutocompleteDisplayValues({
|
||||||
|
// ...autocompleteDisplayValues,
|
||||||
|
// [filterField.name]: toggle(
|
||||||
|
// value[0],
|
||||||
|
// autocompleteDisplayValues[filterField.name],
|
||||||
|
// (a, b) => a.value === b.value
|
||||||
|
// )
|
||||||
|
// }),
|
||||||
|
// [],
|
||||||
|
// filterField.options
|
||||||
|
// )}
|
||||||
|
// fetchChoices={filterField.onSearchChange}
|
||||||
|
// loading={filterField.loading}
|
||||||
|
// data-tc={filterField.name}
|
||||||
|
// key={filterField.name}
|
||||||
|
// />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -26,7 +26,8 @@ function merge<T extends string>(
|
||||||
if (!!prevFilter) {
|
if (!!prevFilter) {
|
||||||
return {
|
return {
|
||||||
...newFilter,
|
...newFilter,
|
||||||
active: prevFilter.active
|
active: prevFilter.active,
|
||||||
|
value: prevFilter.value
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -44,7 +44,7 @@ const Link: React.FC<LinkProps> = props => {
|
||||||
return (
|
return (
|
||||||
<Typography
|
<Typography
|
||||||
component="a"
|
component="a"
|
||||||
className={classNames({
|
className={classNames(className, {
|
||||||
[classes.root]: true,
|
[classes.root]: true,
|
||||||
[classes[color]]: true,
|
[classes[color]]: true,
|
||||||
[classes.underline]: underline
|
[classes.underline]: underline
|
||||||
|
|
Loading…
Reference in a new issue