Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: ✨ adds filter CANList Pills (Tags) #2917

Merged
merged 14 commits into from
Oct 11, 2024
15 changes: 15 additions & 0 deletions frontend/cypress/e2e/canList.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,16 @@ describe("CAN List", () => {
// click the button that has text Apply
cy.get("button").contains("Apply").click();

// check that the correct tags are displayed
cy.get("div").contains("Filters Applied:").should("exist");
cy.get("svg[id='filter-tag-activePeriod']").should("exist");
cy.get("svg[id='filter-tag-transfer']").should("exist");
cy.get("svg[id='filter-tag-portfolio']").should("exist");

cy.get("span").contains("1 Year").should("exist");
cy.get("span").contains("Direct").should("exist");
cy.get("span").contains("HMRF").should("exist");

// check that the table is filtered correctly
// table should contain 6 rows

Expand All @@ -77,6 +87,11 @@ describe("CAN List", () => {

// check that the table is filtered correctly
// table should have more than 5 rows
/// check that the correct tags are displayed
cy.get("div").contains("Filters Applied:").should("not.exist");
cy.get("svg[id='filter-tag-activePeriod']").should("not.exist");
cy.get("svg[id='filter-tag-transfer']").should("not.exist");
cy.get("svg[id='filter-tag-portfolio']").should("not.exist");

cy.get("tbody").find("tr").should("have.length.greaterThan", 3);
});
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/components/UI/FilterTags/FilterTags.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import Tag from "../Tag";
* A filter tags.
* @param {Object} props - The component props.
* @param {Function} props.removeFilter - A function to call to remove a filter/tag.
* @param {Array<string>} props.tagsList - An array of tags to display.
* @returns {React.JSX.Element} - The procurement shop select element.
* @param {string[]} props.tagsList - An array of tags to display.
* @returns {JSX.Element} - The filter tags component. (Pills with an 'x' to remove them)
*/
export const FilterTags = ({ removeFilter, tagsList }) => {
const FilterTag = ({ tag }) => (
Expand Down
89 changes: 89 additions & 0 deletions frontend/src/pages/cans/list/CANFilterTags/CANFilterTags.hooks.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { useState, useEffect, useCallback } from "react";
/**
* @typedef {Object} FilterItem
* @property {string} title
*/

/**
* @typedef {Object} Filters
* @property {FilterItem[]} activePeriod
* @property {FilterItem[]} portfolio
* @property {FilterItem[]} transfer
*/

/**
* @typedef {Object} Tag
* @property {string} tagText
* @property {string} filter
*/

/**
* Custom hook for managing tags list
* @param {Filters} filters
* @returns {Tag[]}
*/
export const useTagsList = (filters) => {
const [tagsList, setTagsList] = useState([]);

/**
* @param {keyof Filters} filterKey
* @param {string} filterName
*/
const updateTags = useCallback(
(filterKey, filterName) => {
if (!Array.isArray(filters[filterKey])) return;

const selectedTags = filters[filterKey].map((item) => ({
tagText: item.title,
filter: filterName
}));

setTagsList((prevState) => [...prevState.filter((t) => t.filter !== filterName), ...selectedTags]);
},
[filters]
);

useEffect(() => {
updateTags("activePeriod", "activePeriod");
}, [filters.activePeriod, updateTags]);

useEffect(() => {
updateTags("portfolio", "portfolio");
}, [filters.portfolio, updateTags]);

useEffect(() => {
updateTags("transfer", "transfer");
}, [filters.transfer, updateTags]);

return tagsList;
};

/**
* Removes a filter tag
* @param {Tag} tag - The tag to remove
* @param {function(function(Filters): Filters): void} setFilters - Function to update filters
*/
export const removeFilter = (tag, setFilters) => {
switch (tag.filter) {
case "activePeriod":
setFilters((prevState) => ({
...prevState,
activePeriod: prevState.activePeriod.filter((period) => period.title !== tag.tagText)
}));
break;
case "portfolio":
setFilters((prevState) => ({
...prevState,
portfolio: prevState.portfolio.filter((portfolio) => portfolio.title !== tag.tagText)
}));
break;
case "transfer":
setFilters((prevState) => ({
...prevState,
transfer: prevState.transfer.filter((transfer) => transfer.title !== tag.tagText)
}));
break;
default:
console.warn(`Unknown filter type: ${tag.filter}`);
}
};
35 changes: 35 additions & 0 deletions frontend/src/pages/cans/list/CANFilterTags/CANFilterTags.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import _ from "lodash";
import FilterTags from "../../../../components/UI/FilterTags";
import FilterTagsWrapper from "../../../../components/UI/FilterTags/FilterTagsWrapper";
import { useTagsList, removeFilter } from "./CANFilterTags.hooks";

/**
* A filter tags component.
* @param {Object} props - The component props.
* @param {import('./CANFilterTags.hooks').Filters} props.filters - The current filters.
* @param {() => void} props.setFilters - A function to call to set the filters.
* @returns {JSX.Element|null} The filter tags component or null if no tags.
*/
export const CANFilterTags = ({ filters, setFilters }) => {
const tagsList = useTagsList(filters);

const tagsListByFilter = _.groupBy(tagsList, "filter");
const tagsListByFilterMerged = Object.values(tagsListByFilter)
.flat()
.sort((a, b) => a.tagText.localeCompare(b.tagText));

if (tagsList.length === 0) {
return null;
}

return (
<FilterTagsWrapper>
<FilterTags
removeFilter={(tag) => removeFilter(tag, setFilters)}
tagsList={tagsListByFilterMerged}
/>
</FilterTagsWrapper>
);
};

export default CANFilterTags;
1 change: 1 addition & 0 deletions frontend/src/pages/cans/list/CANFilterTags/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from "./CANFilterTags";
9 changes: 8 additions & 1 deletion frontend/src/pages/cans/list/CanList.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ import FiscalYear from "../../../components/UI/FiscalYear";
import { setSelectedFiscalYear } from "../../../pages/cans/detail/canDetailSlice";
import ErrorPage from "../../ErrorPage";
import CANFilterButton from "./CANFilterButton";
import { sortAndFilterCANs, getPortfolioOptions } from "./CanList.helpers";
import CANFilterTags from "./CANFilterTags";
import { getPortfolioOptions, sortAndFilterCANs } from "./CanList.helpers";

/**
* Page for the CAN List.
Expand Down Expand Up @@ -79,6 +80,12 @@ const CanList = () => {
/>
}
FYSelect={<CANFiscalYearSelect />}
FilterTags={
<CANFilterTags
filters={filters}
setFilters={setFilters}
/>
}
/>
</App>
)
Expand Down