Skip to content

Commit 5ae0185

Browse files
authored
Merge pull request #2596 from harnish7576/harnish7576/issue2486_subissue_2488_adding_abolitions_filter
Added abolitions filter in API Variable/Parameter Explorer
2 parents 5a821a2 + 3112357 commit 5ae0185

2 files changed

Lines changed: 298 additions & 11 deletions

File tree

Lines changed: 251 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,251 @@
1+
import React from "react";
2+
import { render, screen, fireEvent, waitFor } from "@testing-library/react";
3+
import "@testing-library/jest-dom";
4+
import {
5+
VariableParameterExplorer,
6+
APIResultCard,
7+
} from "../../pages/learn/APIGeneralContent";
8+
9+
// Mock the antd components
10+
jest.mock("antd", () => ({
11+
Card: ({ children, onClick, ...props }) => (
12+
<div data-testid="card" onClick={onClick} {...props}>
13+
{children}
14+
</div>
15+
),
16+
Tag: ({ children, color, ...props }) => (
17+
<span data-testid="tag" data-color={color} {...props}>
18+
{children}
19+
</span>
20+
),
21+
Tooltip: ({ children, title }) => (
22+
<div data-testid="tooltip" title={title}>
23+
{children}
24+
</div>
25+
),
26+
}));
27+
28+
// Mock the icons
29+
jest.mock("@ant-design/icons", () => ({
30+
QuestionCircleOutlined: () => <span data-testid="question-icon" />,
31+
}));
32+
33+
// Mock the style object
34+
jest.mock("../../style", () => ({
35+
colors: {
36+
DARK_GRAY: "#666666",
37+
},
38+
}));
39+
40+
describe("APIResultCard", () => {
41+
const mockSetSelectedCard = jest.fn();
42+
43+
const mockParameterMetadata = {
44+
label: "Test Parameter",
45+
parameter: "gov.test.parameter",
46+
};
47+
48+
const mockVariableMetadata = {
49+
label: "Test Variable",
50+
name: "test_variable",
51+
};
52+
53+
beforeEach(() => {
54+
mockSetSelectedCard.mockClear();
55+
});
56+
57+
it("renders parameter card when type is parameter", () => {
58+
render(
59+
<APIResultCard
60+
metadata={mockParameterMetadata}
61+
type="parameter"
62+
setSelectedCard={mockSetSelectedCard}
63+
/>,
64+
);
65+
66+
expect(screen.getByTestId("card")).toBeInTheDocument();
67+
expect(screen.getByText("Parameter")).toBeInTheDocument();
68+
expect(screen.getByText("Test Parameter")).toBeInTheDocument();
69+
});
70+
71+
it("renders variable card when type is variable", () => {
72+
render(
73+
<APIResultCard
74+
metadata={mockVariableMetadata}
75+
type="variable"
76+
setSelectedCard={mockSetSelectedCard}
77+
/>,
78+
);
79+
80+
expect(screen.getByTestId("card")).toBeInTheDocument();
81+
expect(screen.getByText("Variable")).toBeInTheDocument();
82+
expect(screen.getByText("Test Variable")).toBeInTheDocument();
83+
});
84+
85+
it("calls setSelectedCard with correct data when clicked", () => {
86+
render(
87+
<APIResultCard
88+
metadata={mockParameterMetadata}
89+
type="parameter"
90+
setSelectedCard={mockSetSelectedCard}
91+
/>,
92+
);
93+
94+
fireEvent.click(screen.getByTestId("card"));
95+
96+
expect(mockSetSelectedCard).toHaveBeenCalledWith({
97+
...mockParameterMetadata,
98+
type: "parameter",
99+
});
100+
});
101+
102+
it("has correct card styling attributes", () => {
103+
render(
104+
<APIResultCard
105+
metadata={mockParameterMetadata}
106+
type="parameter"
107+
setSelectedCard={mockSetSelectedCard}
108+
/>,
109+
);
110+
111+
const card = screen.getByTestId("card");
112+
expect(card).toHaveStyle({
113+
width: "100%",
114+
backgroundColor: "white",
115+
cursor: "pointer",
116+
});
117+
});
118+
});
119+
120+
describe("VariableParameterExplorer", () => {
121+
const mockMetadata = {
122+
parameters: {
123+
param1: {
124+
type: "parameter",
125+
label: "Parameter One",
126+
parameter: "gov.param.one",
127+
description: "First parameter",
128+
},
129+
param2: {
130+
type: "parameter",
131+
label: "Parameter Two",
132+
parameter: "gov.abolitions.param.two",
133+
description: "Abolition parameter",
134+
},
135+
},
136+
variables: {
137+
var1: {
138+
name: "variable_one",
139+
label: "Variable One",
140+
description: "First variable",
141+
},
142+
var2: {
143+
name: "variable_two",
144+
label: "Variable Two",
145+
description: "Second variable",
146+
},
147+
},
148+
};
149+
150+
it("renders search input and cards", () => {
151+
render(<VariableParameterExplorer metadata={mockMetadata} />);
152+
153+
expect(
154+
screen.getByPlaceholderText("Search parameters or variables"),
155+
).toBeInTheDocument();
156+
expect(screen.getByText("Parameter One")).toBeInTheDocument();
157+
expect(screen.getByText("Variable One")).toBeInTheDocument();
158+
expect(screen.getByText("Variable Two")).toBeInTheDocument();
159+
});
160+
161+
it("filters cards based on search query", async () => {
162+
render(<VariableParameterExplorer metadata={mockMetadata} />);
163+
164+
const searchInput = screen.getByPlaceholderText(
165+
"Search parameters or variables",
166+
);
167+
fireEvent.change(searchInput, { target: { value: "Parameter One" } });
168+
169+
await waitFor(() => {
170+
expect(screen.getByText("Parameter One")).toBeInTheDocument();
171+
expect(screen.queryByText("Variable One")).not.toBeInTheDocument();
172+
expect(screen.queryByText("Variable Two")).not.toBeInTheDocument();
173+
});
174+
});
175+
176+
it("filters cards based on python name search", async () => {
177+
render(<VariableParameterExplorer metadata={mockMetadata} />);
178+
179+
const searchInput = screen.getByPlaceholderText(
180+
"Search parameters or variables",
181+
);
182+
fireEvent.change(searchInput, { target: { value: "variable_one" } });
183+
184+
await waitFor(() => {
185+
expect(screen.getByText("Variable One")).toBeInTheDocument();
186+
expect(screen.queryByText("Parameter One")).not.toBeInTheDocument();
187+
expect(screen.queryByText("Variable Two")).not.toBeInTheDocument();
188+
});
189+
});
190+
191+
it("hides abolition parameters by default", () => {
192+
render(<VariableParameterExplorer metadata={mockMetadata} />);
193+
194+
expect(screen.getByText("Parameter One")).toBeInTheDocument();
195+
expect(screen.queryByText("Parameter Two")).not.toBeInTheDocument();
196+
});
197+
198+
it("shows abolition parameters when checkbox is checked", async () => {
199+
render(<VariableParameterExplorer metadata={mockMetadata} />);
200+
201+
const checkbox = screen.getByRole("checkbox");
202+
fireEvent.click(checkbox);
203+
204+
await waitFor(() => {
205+
expect(screen.getByText("Parameter One")).toBeInTheDocument();
206+
expect(screen.getByText("Parameter Two")).toBeInTheDocument();
207+
});
208+
});
209+
210+
it("displays tooltip for abolition parameters checkbox", () => {
211+
render(<VariableParameterExplorer metadata={mockMetadata} />);
212+
213+
const tooltip = screen.getByTestId("tooltip");
214+
expect(tooltip).toHaveAttribute(
215+
"title",
216+
"Abolition parameters are used to remove all impacts from a standard parameter, usually by setting its value to 0",
217+
);
218+
});
219+
220+
it("returns null when metadata is not provided", () => {
221+
const { container } = render(<VariableParameterExplorer metadata={null} />);
222+
expect(container.firstChild).toBeNull();
223+
});
224+
225+
it("sorts cards alphabetically by label", () => {
226+
const unsortedMetadata = {
227+
parameters: {},
228+
variables: {
229+
var1: {
230+
name: "variable_z",
231+
label: "Z Variable",
232+
description: "Last variable",
233+
},
234+
var2: {
235+
name: "variable_a",
236+
label: "A Variable",
237+
description: "First variable",
238+
},
239+
},
240+
};
241+
242+
render(<VariableParameterExplorer metadata={unsortedMetadata} />);
243+
244+
const cards = screen.getAllByTestId("card");
245+
const firstCard = cards[0];
246+
const secondCard = cards[1];
247+
248+
expect(firstCard).toHaveTextContent("A Variable");
249+
expect(secondCard).toHaveTextContent("Z Variable");
250+
});
251+
});

src/pages/learn/APIGeneralContent.jsx

Lines changed: 47 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ import { useState } from "react";
22
import GeneralContent from "./GeneralContent";
33
import CodeBlock from "../../layout/CodeBlock";
44
import style from "../../style";
5-
import { Card, Tag } from "antd";
5+
import { Card, Tag, Tooltip } from "antd";
6+
import { QuestionCircleOutlined } from "@ant-design/icons";
67

78
// During front-end redesign, this page should be refactored
89
// to use design system layout components and improved best practices.
@@ -399,6 +400,8 @@ export function VariableParameterExplorer(props) {
399400
const { metadata } = props;
400401
const [query, setQuery] = useState("");
401402
const [selectedCardData, setSelectedCardData] = useState(null);
403+
const [showAbolitions, setShowAbolitions] = useState(false);
404+
402405
const [page, setPage] = useState(0);
403406

404407
const MAX_ROWS = 3;
@@ -408,26 +411,36 @@ export function VariableParameterExplorer(props) {
408411
if (!metadata) return null;
409412

410413
const filterByQuery = (item) => {
411-
if (!query) return true;
414+
if (!query && showAbolitions) return true;
412415

413-
// Skip items with numeric or empty labels
414416
const label = item.label || "";
415-
if (!label.trim() || /^\d+$/.test(label)) return false;
416417

417-
return label
418-
.replaceAll(" ", "")
419-
.toLowerCase()
420-
.includes(query.replaceAll(" ", "").toLowerCase());
418+
const pythonName = item.type === "parameter" ? item.parameter : item.name;
419+
420+
if (!showAbolitions) {
421+
if (pythonName?.startsWith("gov.abolitions")) return false;
422+
}
423+
424+
// Filter based on the query string
425+
if (query) {
426+
return (
427+
label
428+
.replaceAll(" ", "")
429+
.toLowerCase()
430+
.includes(query.replaceAll(" ", "").toLowerCase()) ||
431+
pythonName?.toLowerCase().includes(query.toLowerCase())
432+
);
433+
}
434+
435+
return true;
421436
};
422437

423438
const parameterCards = Object.values(metadata.parameters || {})
424439
.filter((p) => p.type === "parameter")
425-
.filter((p) => p.label && !/^\d+$/.test(p.label)) // Add this filter
426440
.filter(filterByQuery)
427441
.map((p) => ({ ...p, type: "parameter" }));
428442

429443
const variableCards = Object.values(metadata.variables || {})
430-
.filter((v) => v.label && !/^\d+$/.test(v.label)) // Add this filter
431444
.filter(filterByQuery)
432445
.map((v) => ({ ...v, type: "variable" }));
433446

@@ -458,7 +471,30 @@ export function VariableParameterExplorer(props) {
458471
padding: "8px",
459472
}}
460473
/>
461-
474+
<div style={{ marginBottom: 10 }}>
475+
<label style={{ fontSize: "14px", cursor: "pointer" }}>
476+
<input
477+
type="checkbox"
478+
checked={showAbolitions}
479+
onChange={() => {
480+
setShowAbolitions((prev) => !prev);
481+
setPage(0); // reset to first page
482+
}}
483+
style={{ marginRight: 8 }}
484+
/>
485+
Show abolition parameters
486+
<Tooltip title="Abolition parameters are used to remove all impacts from a standard parameter, usually by setting its value to 0">
487+
<QuestionCircleOutlined
488+
style={{
489+
marginLeft: 6,
490+
fontSize: "14px",
491+
cursor: "pointer",
492+
color: style.colors.DARK_GRAY,
493+
}}
494+
/>
495+
</Tooltip>
496+
</label>
497+
</div>
462498
<div
463499
style={{
464500
display: "grid",

0 commit comments

Comments
 (0)