Experimental filters: add clear button and bumps UI to support ranges (#3975)
This commit is contained in:
parent
10d30ca9a4
commit
d2074f4824
9 changed files with 120 additions and 82 deletions
5
.changeset/tough-frogs-remember.md
Normal file
5
.changeset/tough-frogs-remember.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
"saleor-dashboard": patch
|
||||
---
|
||||
|
||||
Experimental filters: add clear function and bumps UI to support ranges
|
14
package-lock.json
generated
14
package-lock.json
generated
|
@ -27,7 +27,7 @@
|
|||
"@material-ui/lab": "^4.0.0-alpha.61",
|
||||
"@material-ui/styles": "^4.11.4",
|
||||
"@reach/auto-id": "^0.16.0",
|
||||
"@saleor/macaw-ui": "0.8.0-pre.107",
|
||||
"@saleor/macaw-ui": "0.8.0-pre.109",
|
||||
"@saleor/sdk": "0.6.0",
|
||||
"@sentry/react": "^6.0.0",
|
||||
"@types/faker": "^5.1.6",
|
||||
|
@ -8260,9 +8260,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@saleor/macaw-ui": {
|
||||
"version": "0.8.0-pre.107",
|
||||
"resolved": "https://registry.npmjs.org/@saleor/macaw-ui/-/macaw-ui-0.8.0-pre.107.tgz",
|
||||
"integrity": "sha512-Z7xA1TM4KXjRJvz6gx6cKhw/pCB7q5BT+yYAAdGDKbfvMFRjh3mXilH8NOF3kkYwEe+3ykeLvijCyp1Bm7UH9w==",
|
||||
"version": "0.8.0-pre.109",
|
||||
"resolved": "https://registry.npmjs.org/@saleor/macaw-ui/-/macaw-ui-0.8.0-pre.109.tgz",
|
||||
"integrity": "sha512-lTIe2ha18IBQk+Fy8boo1USi0Z6ISPT2jOqBd2dAP+PkPwM5quAR8hfEICuOUmac8qzjZBCQ/uv2FLbue9rquA==",
|
||||
"dependencies": {
|
||||
"@dessert-box/react": "^0.4.0",
|
||||
"@floating-ui/react-dom-interactions": "^0.5.0",
|
||||
|
@ -41546,9 +41546,9 @@
|
|||
}
|
||||
},
|
||||
"@saleor/macaw-ui": {
|
||||
"version": "0.8.0-pre.107",
|
||||
"resolved": "https://registry.npmjs.org/@saleor/macaw-ui/-/macaw-ui-0.8.0-pre.107.tgz",
|
||||
"integrity": "sha512-Z7xA1TM4KXjRJvz6gx6cKhw/pCB7q5BT+yYAAdGDKbfvMFRjh3mXilH8NOF3kkYwEe+3ykeLvijCyp1Bm7UH9w==",
|
||||
"version": "0.8.0-pre.109",
|
||||
"resolved": "https://registry.npmjs.org/@saleor/macaw-ui/-/macaw-ui-0.8.0-pre.109.tgz",
|
||||
"integrity": "sha512-lTIe2ha18IBQk+Fy8boo1USi0Z6ISPT2jOqBd2dAP+PkPwM5quAR8hfEICuOUmac8qzjZBCQ/uv2FLbue9rquA==",
|
||||
"requires": {
|
||||
"@dessert-box/react": "^0.4.0",
|
||||
"@floating-ui/react-dom-interactions": "^0.5.0",
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
"@material-ui/lab": "^4.0.0-alpha.61",
|
||||
"@material-ui/styles": "^4.11.4",
|
||||
"@reach/auto-id": "^0.16.0",
|
||||
"@saleor/macaw-ui": "0.8.0-pre.107",
|
||||
"@saleor/macaw-ui": "0.8.0-pre.109",
|
||||
"@saleor/sdk": "0.6.0",
|
||||
"@sentry/react": "^6.0.0",
|
||||
"@types/faker": "^5.1.6",
|
||||
|
|
|
@ -2,8 +2,11 @@ import { ConditionalFilters } from "@dashboard/components/ConditionalFilter";
|
|||
import { Box, Button, CloseIcon, Popover, Text } from "@saleor/macaw-ui/next";
|
||||
import React from "react";
|
||||
|
||||
export const ExpressionFilters = () => (
|
||||
<Popover>
|
||||
export const ExpressionFilters = () => {
|
||||
const [open, setOpen] = React.useState(false);
|
||||
|
||||
return (
|
||||
<Popover open={open} onOpenChange={open => setOpen(open)}>
|
||||
<Popover.Trigger>
|
||||
<Button>Show filters</Button>
|
||||
</Popover.Trigger>
|
||||
|
@ -35,8 +38,9 @@ export const ExpressionFilters = () => (
|
|||
</Popover.Close>
|
||||
</Box>
|
||||
</Box>
|
||||
<ConditionalFilters />
|
||||
<ConditionalFilters onClose={() => setOpen(false)} />
|
||||
</Box>
|
||||
</Popover.Content>
|
||||
</Popover>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -6,11 +6,20 @@ import { FilterContainer } from "./FilterElement";
|
|||
import { FiltersArea } from "./FiltersArea";
|
||||
import { LoadingFiltersArea } from "./LoadingFiltersArea";
|
||||
|
||||
export const ConditionalFilters: FC = () => {
|
||||
const { valueProvider } = useConditionalFilterContext();
|
||||
export const ConditionalFilters: FC<{ onClose: () => void }> = ({
|
||||
onClose,
|
||||
}) => {
|
||||
const { valueProvider, containerState } = useConditionalFilterContext();
|
||||
|
||||
const handleConfirm = (value: FilterContainer) => {
|
||||
valueProvider.persist(value);
|
||||
onClose();
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
valueProvider.clear();
|
||||
containerState.clear();
|
||||
onClose();
|
||||
};
|
||||
|
||||
return valueProvider.loading ? (
|
||||
|
@ -22,7 +31,7 @@ export const ConditionalFilters: FC = () => {
|
|||
borderBottomLeftRadius={2}
|
||||
borderBottomRightRadius={2}
|
||||
>
|
||||
<FiltersArea onConfirm={handleConfirm} />
|
||||
<FiltersArea onConfirm={handleConfirm} onCancel={handleCancel} />
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -4,4 +4,5 @@ export interface FilterValueProvider {
|
|||
value: FilterContainer;
|
||||
loading: boolean;
|
||||
persist: (newValue: FilterContainer) => void;
|
||||
clear: () => void;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { _ExperimentalFilters, Box, FilterEvent } from "@saleor/macaw-ui/next";
|
||||
import React from "react";
|
||||
import React, { FC } from "react";
|
||||
|
||||
import { useConditionalFilterContext } from "./context";
|
||||
import { FilterContainer } from "./FilterElement";
|
||||
|
@ -8,9 +8,10 @@ import { useFilterContainer } from "./useFilterContainer";
|
|||
|
||||
interface FiltersAreaProps {
|
||||
onConfirm: (value: FilterContainer) => void;
|
||||
onCancel?: () => void;
|
||||
}
|
||||
|
||||
export const FiltersArea = ({ onConfirm }: FiltersAreaProps) => {
|
||||
export const FiltersArea: FC<FiltersAreaProps> = ({ onConfirm, onCancel }) => {
|
||||
const { apiProvider, leftOperandsProvider } = useConditionalFilterContext();
|
||||
|
||||
const {
|
||||
|
@ -72,7 +73,7 @@ export const FiltersArea = ({ onConfirm }: FiltersAreaProps) => {
|
|||
+ Add row
|
||||
</_ExperimentalFilters.AddRowButton>
|
||||
<Box display="flex" gap={3}>
|
||||
<_ExperimentalFilters.ClearButton>
|
||||
<_ExperimentalFilters.ClearButton onClick={onCancel}>
|
||||
Clear
|
||||
</_ExperimentalFilters.ClearButton>
|
||||
<_ExperimentalFilters.ConfirmButton onClick={() => onConfirm(value)}>
|
||||
|
|
|
@ -54,9 +54,17 @@ export const useUrlValueProvider = (
|
|||
setValue(filterValue);
|
||||
};
|
||||
|
||||
const clear = () => {
|
||||
router.history.replace({
|
||||
pathname: router.location.pathname,
|
||||
});
|
||||
setValue([]);
|
||||
};
|
||||
|
||||
return {
|
||||
value,
|
||||
loading,
|
||||
persist,
|
||||
clear,
|
||||
};
|
||||
};
|
||||
|
|
|
@ -6,48 +6,51 @@ import { FilterValueProvider } from "./FilterValueProvider";
|
|||
type StateCallback = (el: FilterElement) => void;
|
||||
type Element = FilterContainer[number];
|
||||
|
||||
|
||||
const isFilterElement = (el: unknown): el is FilterElement => typeof el !== "string" && !Array.isArray(el)
|
||||
const isFilterElement = (el: unknown): el is FilterElement =>
|
||||
typeof el !== "string" && !Array.isArray(el);
|
||||
|
||||
const removeConstraint = (container: FilterContainer) => {
|
||||
return container.map((el) => {
|
||||
if (!isFilterElement(el)) return el
|
||||
return container.map(el => {
|
||||
if (!isFilterElement(el)) return el;
|
||||
|
||||
if (!el.constraint?.existIn(container)) {
|
||||
el.clearConstraint()
|
||||
el.clearConstraint();
|
||||
}
|
||||
|
||||
return el
|
||||
})
|
||||
}
|
||||
return el;
|
||||
});
|
||||
};
|
||||
|
||||
const calculateIndexesToRemove = (container: FilterContainer, position: number) => {
|
||||
const next = position + 1
|
||||
const previous = position - 1
|
||||
const indexTuple = [position]
|
||||
const calculateIndexesToRemove = (
|
||||
container: FilterContainer,
|
||||
position: number,
|
||||
) => {
|
||||
const next = position + 1;
|
||||
const previous = position - 1;
|
||||
const indexTuple = [position];
|
||||
|
||||
if (typeof container[next] === "string") {
|
||||
indexTuple.push(next)
|
||||
indexTuple.push(next);
|
||||
|
||||
return indexTuple
|
||||
return indexTuple;
|
||||
}
|
||||
|
||||
if (typeof container[previous] === "string") {
|
||||
indexTuple.push(previous)
|
||||
}
|
||||
|
||||
return indexTuple
|
||||
indexTuple.push(previous);
|
||||
}
|
||||
|
||||
return indexTuple;
|
||||
};
|
||||
|
||||
const removeElement = (container: FilterContainer, position: number) => {
|
||||
const indexTuple = calculateIndexesToRemove(container, position)
|
||||
const indexTuple = calculateIndexesToRemove(container, position);
|
||||
|
||||
const newContainer = container
|
||||
.filter((_, elIndex) => !indexTuple.includes(elIndex))
|
||||
const newContainer = container.filter(
|
||||
(_, elIndex) => !indexTuple.includes(elIndex),
|
||||
);
|
||||
|
||||
return removeConstraint(newContainer)
|
||||
}
|
||||
return removeConstraint(newContainer);
|
||||
};
|
||||
|
||||
export const useContainerState = (valueProvider: FilterValueProvider) => {
|
||||
const [value, setValue] = useState<FilterContainer>([]);
|
||||
|
@ -81,13 +84,15 @@ export const useContainerState = (valueProvider: FilterValueProvider) => {
|
|||
};
|
||||
|
||||
const updateBySlug = (slug: string, cb: StateCallback) => {
|
||||
setValue(v => v.map((el) => {
|
||||
setValue(v =>
|
||||
v.map(el => {
|
||||
if (isFilterElement(el) && el.value.value === slug) {
|
||||
cb(el)
|
||||
cb(el);
|
||||
}
|
||||
|
||||
return el
|
||||
}))
|
||||
return el;
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
const removeAt = (position: string) => {
|
||||
|
@ -109,13 +114,17 @@ export const useContainerState = (valueProvider: FilterValueProvider) => {
|
|||
};
|
||||
|
||||
const exist = (slug: string) => {
|
||||
return value.some((entry) =>
|
||||
isFilterElement(entry) && entry.value.value === slug
|
||||
)
|
||||
}
|
||||
return value.some(
|
||||
entry => isFilterElement(entry) && entry.value.value === slug,
|
||||
);
|
||||
};
|
||||
|
||||
const createEmpty = () => {
|
||||
create(FilterElement.createEmpty())
|
||||
create(FilterElement.createEmpty());
|
||||
};
|
||||
|
||||
const clear = () => {
|
||||
setValue([]);
|
||||
};
|
||||
|
||||
return {
|
||||
|
@ -126,5 +135,6 @@ export const useContainerState = (valueProvider: FilterValueProvider) => {
|
|||
updateAt,
|
||||
removeAt,
|
||||
value,
|
||||
clear,
|
||||
};
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue