Skip to content

Commit

Permalink
Add functionality to filter instances through labels
Browse files Browse the repository at this point in the history
  • Loading branch information
matborowczyk committed May 22, 2024
1 parent f97c50c commit e8729a2
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 3 deletions.
6 changes: 6 additions & 0 deletions changelogs/unreleased/5710-instance-filtering.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
description: Add functionality to filter instances through labels in the summary Pie Chart in Service Inventory
issue-nr: 5710
change-type: patch
destination-branches: [master, iso7]
sections:
minor-improvement: "{{description}}"
13 changes: 13 additions & 0 deletions src/Slices/ServiceInventory/UI/ServiceInventory.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,19 @@ export const ServiceInventory: React.FunctionComponent<{
route: "Inventory",
});

// event listener for filtering which will iterate through service model state to find all states that corresponds with given label string
document.addEventListener("group-filtering", (event) => {
const label =
(event as CustomEvent).detail !== "no label"
? (event as CustomEvent).detail
: null;

const states = service.lifecycle.states
.filter((state) => state.label === label)
.map((state) => state.name);
setFilter({ ...filter, state: states });
});

const [filter, setFilter] =
useUrlStateWithFilter<ServiceInstanceParams.Filter>({ route: "Inventory" });

Expand Down
19 changes: 18 additions & 1 deletion src/UI/Components/SummaryChart/SummaryChart.test.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import assert from "assert";
import React from "react";
import { render, screen } from "@testing-library/react";
import { act, render, screen } from "@testing-library/react";
import { userEvent } from "@testing-library/user-event";
import { Service } from "@/Test";
import { words } from "@/UI/words";
import { SummaryChart } from "./SummaryChart";
Expand Down Expand Up @@ -29,3 +30,19 @@ test("SummaryChart renders with no instances", () => {
screen.getByRole("img", { name: words("catalog.summary.title") }),
).toBeVisible();
});

test("SummaryChart renders with no instances", async () => {
const spyDispatch = jest.spyOn(document, "dispatchEvent");
render(
<SummaryChart
by_label={{ danger: 0, no_label: 0, warning: 0, info: 0, success: 0 }}
total="0"
/>,
);
await act(async () => {
await userEvent.click(screen.getByText("danger: 0"));
});
expect(spyDispatch).toHaveBeenCalledWith(
new CustomEvent("group-filtering", { detail: "danger" }),
);
});
65 changes: 63 additions & 2 deletions src/UI/Components/SummaryChart/SummaryChart.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from "react";
import { ChartDonut } from "@patternfly/react-charts";
import { ChartDonut, ChartLegend } from "@patternfly/react-charts";
import {
global_danger_color_100,
global_info_color_100,
Expand All @@ -10,11 +10,23 @@ import {
import { InstancesByLabel } from "@/Core";
import { words } from "@/UI/words";

/**
* @interface Props
* @desc The props for the SummaryChart component.
* @property {InstancesByLabel} by_label - The instances grouped by label.
* @property {string} total - The total number of instances.
*/
interface Props {
by_label: InstancesByLabel;
total: string;
}

/**
* @component SummaryChart
* @desc A donut chart component with a legend that displays the instances grouped by label.
* @param {Props} props - The component props.
* @returns {JSX.Element} - The rendered SummaryChart component.
*/
export const SummaryChart: React.FC<Props> = ({ by_label, total }) => {
const chartData = getChartData(by_label);

Expand All @@ -30,8 +42,44 @@ export const SummaryChart: React.FC<Props> = ({ by_label, total }) => {
ariaTitle={words("catalog.summary.title")}
constrainToVisibleArea={true}
data={chartData}
legendComponent={
<ChartLegend
name={"legend"}
data={legendData}
events={[
{
target: "labels",
eventHandlers: {
onClick: () => {
return [
{
target: "labels",
mutation: (props) => {
document.dispatchEvent(
new CustomEvent("group-filtering", {
detail: props.datum.name.split(":")[0],
}),
);
},
},
];
},
onMouseEnter: () => {
return [
{
target: "labels",
mutation: () => {
return { style: { cursor: "pointer" } };
},
},
];
},
},
},
]}
/>
}
labels={({ datum }) => `${datum.x}: ${datum.y}`}
legendData={legendData}
legendOrientation="vertical"
legendPosition="right"
padding={{
Expand All @@ -58,12 +106,25 @@ const colorsForChart = {

const orderedLabels = ["danger", "warning", "success", "info", "no_label"];

/**
* @interface ChartData
* @desc The data structure for each data point in the chart.
* @property {string} x - The label for the data point.
* @property {number} y - The value of the data point.
* @property {string} color - The color of the data point.
*/
interface ChartData {
x: string;
y: number;
color: string;
}

/**
* @function getChartData
* @desc Converts the instances grouped by label into chart data.
* @param {InstancesByLabel} by_label - The instances grouped by label.
* @returns {ChartData[]} - The chart data.
*/
function getChartData(by_label: InstancesByLabel): ChartData[] {
return orderedLabels.map((label) => ({
x: label === "no_label" ? words("catalog.summary.noLabel") : label,
Expand Down

0 comments on commit e8729a2

Please sign in to comment.