diff --git a/govtool/frontend/src/components/atoms/ChipButton.tsx b/govtool/frontend/src/components/atoms/ChipButton.tsx new file mode 100644 index 000000000..ca11c4db0 --- /dev/null +++ b/govtool/frontend/src/components/atoms/ChipButton.tsx @@ -0,0 +1,64 @@ +import * as React from "react"; +import { Chip, ChipProps, IconButton } from "@mui/material"; +import { IconX } from "@intersect.mbo/intersectmbo.org-icons-set"; + +interface ChipButtonProps + extends Omit { + label: React.ReactNode; + onDelete: () => void; + bgColor?: string; + deleteIconPosition?: "left" | "right"; + iconSize?: number; + testId?: string; +} + +const ChipButton: React.FC = ({ + label, + onDelete, + bgColor = "#B9CCF5", + deleteIconPosition = "left", + iconSize = 14, + testId, + sx, + ...rest +}) => ( + + + + } + sx={{ + backgroundColor: bgColor, + borderRadius: 999, + height: "auto", + py: 0.75, + pl: 1.75, + pr: 2.25, + display: "flex", + flexDirection: deleteIconPosition === "right" ? "row" : "row-reverse", + gap: 0.5, + "& .MuiChip-label": { + fontSize: 12, + fontWeight: 400, + color: "#000", + whiteSpace: "nowrap", + overflow: "hidden", + textOverflow: "ellipsis", + px: 0, + py: 0, + }, + "& .MuiChip-deleteIcon": { m: 0 }, + ...(sx as object), + }} + /> +); + +export default ChipButton; diff --git a/govtool/frontend/src/components/molecules/DataActionsBar.tsx b/govtool/frontend/src/components/molecules/DataActionsBar.tsx index 17183f3e0..7839af965 100644 --- a/govtool/frontend/src/components/molecules/DataActionsBar.tsx +++ b/govtool/frontend/src/components/molecules/DataActionsBar.tsx @@ -1,11 +1,11 @@ -import { Dispatch, FC, SetStateAction } from "react"; +import { Dispatch, FC, SetStateAction, useMemo, useState } from "react"; import { Box, InputBase, IconButton } from "@mui/material"; import Search from "@mui/icons-material/Search"; import CloseIcon from "@mui/icons-material/Close"; import { DataActionsFilters, DataActionsSorting } from "@molecules"; import { OrderActionsChip } from "./OrderActionsChip"; -import { theme } from "@/theme"; +import ChipButton from "../atoms/ChipButton"; type DataActionsBarProps = { chosenFilters?: string[]; @@ -13,10 +13,7 @@ type DataActionsBarProps = { chosenSorting: string; closeFilters?: () => void; closeSorts: () => void; - filterOptions?: { - key: string; - label: string; - }[]; + filterOptions?: { key: string; label: string }[]; filtersOpen?: boolean; filtersTitle?: string; isFiltering?: boolean; @@ -26,107 +23,155 @@ type DataActionsBarProps = { setChosenSorting: Dispatch>; setFiltersOpen?: Dispatch>; setSearchText: Dispatch>; - setSortOpen: Dispatch>; - sortOpen: boolean; - sortOptions?: { - key: string; - label: string; - }[]; + setSortOpen?: Dispatch>; + sortOpen?: boolean; + sortOptions?: { key: string; label: string }[]; }; -export const DataActionsBar: FC = ({ ...props }) => { - const { - chosenFilters = [], - chosenFiltersLength, - chosenSorting, - closeFilters = () => {}, - closeSorts, - filterOptions = [], - filtersOpen, - filtersTitle, - isFiltering = true, - searchText, - setChosenFilters = () => {}, - setChosenSorting, - setFiltersOpen, - setSearchText, - setSortOpen, - sortOpen, - sortOptions = [], - placeholder = "Search...", - } = props; - const { - palette: { boxShadow2 }, - } = theme; +export const DataActionsBar: FC = ({ + chosenFilters = [], + chosenFiltersLength, + chosenSorting, + closeFilters = () => {}, + closeSorts, + filterOptions = [], + filtersOpen, + filtersTitle, + isFiltering = true, + searchText, + setChosenFilters = () => {}, + setChosenSorting, + setFiltersOpen, + setSearchText, + setSortOpen, + sortOpen, + sortOptions = [], + placeholder = "Search...", +}) => { + const [localFiltersOpen, setLocalFiltersOpen] = useState(false); + const [localSortOpen, setLocalSortOpen] = useState(false); + + const effectiveFiltersOpen = filtersOpen ?? localFiltersOpen; + const effectiveSortOpen = sortOpen ?? localSortOpen; + const setEffectiveFiltersOpen = setFiltersOpen ?? setLocalFiltersOpen; + const setEffectiveSortOpen = setSortOpen ?? setLocalSortOpen; + + const selectedFilterItems = useMemo( + () => + (chosenFilters ?? []).map((key) => ({ + key, + label: (filterOptions ?? []).find((o) => o.key === key)?.label ?? key, + })), + [chosenFilters, filterOptions], + ); + + const handleRemoveFilter = (key: string) => + setChosenFilters?.((prev) => (prev ?? []).filter((k) => k !== key)); return ( - - setSearchText(e.target.value)} - placeholder={placeholder} - value={searchText} - startAdornment={ - - } - endAdornment={ - searchText && ( - setSearchText("")} - sx={{ ml: 1 }} - > - - - ) - } - sx={{ - bgcolor: "white", - border: 1, - borderColor: "secondaryBlue", - borderRadius: 50, - boxShadow: `2px 2px 20px 0px ${boxShadow2}`, - fontSize: 11, - fontWeight: 500, - height: 48, - padding: "16px 24px", - maxWidth: 500, - }} - /> - + - {filtersOpen && ( - - )} - {sortOpen && ( - - )} - + setSearchText(e.target.value)} + placeholder={placeholder} + value={searchText} + startAdornment={ + + } + endAdornment={ + searchText && ( + setSearchText("")} + sx={{ ml: 1 }} + > + + + ) + } + sx={{ + bgcolor: "white", + border: 1, + borderColor: "#6E87D9", + borderRadius: 50, + boxShadow: "2px 2px 20px 0 rgba(0,0,0,0.05)", + fontSize: 11, + fontWeight: 500, + height: 48, + padding: "16px 24px", + maxWidth: 500, + flex: "0 0 auto", + }} + /> + + + {effectiveFiltersOpen && ( + + )} + {effectiveSortOpen && ( + + )} + + + + {selectedFilterItems.length > 0 && ( + *": { flex: "0 0 auto", alignSelf: "flex-start" }, + }} + > + {selectedFilterItems.map(({ key, label }) => ( + handleRemoveFilter(key)} + deleteIconPosition="left" + bgColor="#B9CCF5" + size="small" + /> + ))} + + )} ); }; + +export default DataActionsBar;