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, {
|
||||
MultiAutocompleteChoiceType
|
||||
} from "../MultiAutocompleteSelectField";
|
||||
import Link from "../Link";
|
||||
import { IFilter, FieldType, FilterType } from "./types";
|
||||
import Arrow from "./Arrow";
|
||||
import { FilterReducerAction } from "./reducer";
|
||||
import FilterAutocompleteField from "./FilterAutocompleteField";
|
||||
|
||||
export interface FilterContentProps<T extends string = string> {
|
||||
currencySymbol: string;
|
||||
|
@ -428,51 +430,57 @@ const FilterContent: React.FC<FilterContentProps> = ({
|
|||
))}
|
||||
{filterField.type === FieldType.autocomplete &&
|
||||
filterField.multiple && (
|
||||
<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}
|
||||
<FilterAutocompleteField
|
||||
displayValues={autocompleteDisplayValues}
|
||||
filterField={filterField}
|
||||
setDisplayValues={setAutocompleteDisplayValues}
|
||||
onFilterPropertyChange={onFilterPropertyChange}
|
||||
/>
|
||||
// <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>
|
||||
)}
|
||||
|
|
|
@ -26,7 +26,8 @@ function merge<T extends string>(
|
|||
if (!!prevFilter) {
|
||||
return {
|
||||
...newFilter,
|
||||
active: prevFilter.active
|
||||
active: prevFilter.active,
|
||||
value: prevFilter.value
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ const Link: React.FC<LinkProps> = props => {
|
|||
return (
|
||||
<Typography
|
||||
component="a"
|
||||
className={classNames({
|
||||
className={classNames(className, {
|
||||
[classes.root]: true,
|
||||
[classes[color]]: true,
|
||||
[classes.underline]: underline
|
||||
|
|
Loading…
Reference in a new issue