Skip to content

Commit 5ef569b

Browse files
committed
Add filter by topic component
Signed-off-by: hemahg <hhg@redhat.com>
1 parent 23267c7 commit 5ef569b

File tree

4 files changed

+178
-2
lines changed

4 files changed

+178
-2
lines changed

ui/components/ClusterOverview/components/FilterByTime.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,6 @@ export function FilterByTime({
6464
setIsTimeSelectOpen(false);
6565
};
6666

67-
// Menu toggle (new PF6 API)
6867
const toggle = (toggleRef: React.Ref<MenuToggleElement>) => (
6968
<MenuToggle
7069
ref={toggleRef}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import type { Meta, StoryObj } from "@storybook/nextjs";
2+
import { FilterByTopic } from "./FilterByTopic";
3+
4+
const meta: Meta<typeof FilterByTopic> = {
5+
component: FilterByTopic,
6+
args: {
7+
selectedTopic: undefined,
8+
topicList: ["lorem", "dolor", "ipsum"],
9+
disableToolbar: false,
10+
},
11+
} as Meta<typeof FilterByTopic>;
12+
13+
export default meta;
14+
type Story = StoryObj<typeof FilterByTopic>;
15+
16+
export const Default: Story = {};
17+
Default.args = {};
18+
19+
export const Disabled: Story = {};
20+
Disabled.args = {
21+
disableToolbar: true,
22+
selectedTopic: "lorem",
23+
};
24+
25+
export const NoTopics: Story = {};
26+
NoTopics.args = {
27+
topicList: undefined,
28+
};
29+
30+
export const MultipleTopicsWithCommonWords: Story = {};
31+
MultipleTopicsWithCommonWords.args = {
32+
topicList: ["lorem dolor", "lorem ipsum", "lorem foo", "dolor", "ipsum"],
33+
};
34+
35+
export const DoesNotBreakWithLongWords: Story = {};
36+
DoesNotBreakWithLongWords.args = {
37+
topicList: [
38+
"lorem dolor lorem dolor lorem dolor lorem dolor lorem dolor lorem dolor",
39+
"lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum ",
40+
"lorem foo",
41+
"dolor",
42+
"ipsum",
43+
],
44+
};
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
import {
2+
Menu,
3+
MenuList,
4+
MenuContent,
5+
MenuSearch,
6+
MenuSearchInput,
7+
SearchInput,
8+
SelectOption,
9+
MenuToggle,
10+
MenuContainer,
11+
ToolbarItem,
12+
SelectGroup,
13+
} from "@/libs/patternfly/react-core";
14+
import { FilterIcon } from "@/libs/patternfly/react-icons";
15+
import { useTranslations } from "next-intl";
16+
import { useRef, useState } from "react";
17+
18+
export function FilterByTopic({
19+
selectedTopic,
20+
topicList = [],
21+
disableToolbar,
22+
onSetSelectedTopic,
23+
}: {
24+
selectedTopic: string | undefined;
25+
topicList: string[];
26+
disableToolbar: boolean;
27+
onSetSelectedTopic: (value: string | undefined) => void;
28+
}) {
29+
const t = useTranslations("metrics");
30+
const allTopicsLabel = t("all_topics");
31+
32+
const [isOpen, setIsOpen] = useState(false);
33+
const [filter, setFilter] = useState("");
34+
35+
const toggleRef = useRef<any>();
36+
const menuRef = useRef<any>();
37+
38+
const filteredTopics = topicList.filter((topic) =>
39+
topic.toLowerCase().includes(filter.toLowerCase()),
40+
);
41+
42+
const onSelect = (_event: any, itemId: string | number | undefined) => {
43+
if (itemId === "all-topics") {
44+
onSetSelectedTopic(undefined);
45+
} else {
46+
onSetSelectedTopic(itemId as string);
47+
}
48+
setIsOpen(false);
49+
};
50+
51+
const menuItems = [
52+
<SelectOption
53+
key="all-topics"
54+
itemId="all-topics"
55+
isSelected={selectedTopic === undefined}
56+
>
57+
{allTopicsLabel}
58+
</SelectOption>,
59+
<SelectGroup label="Filter by topic" key="topic-filter-group">
60+
{filteredTopics.map((topic, index) => (
61+
<SelectOption
62+
key={`topic-filter-${index + 1}`}
63+
itemId={topic}
64+
title={topic}
65+
isSelected={selectedTopic === topic}
66+
>
67+
{topic}
68+
</SelectOption>
69+
))}
70+
</SelectGroup>,
71+
];
72+
73+
if (filter && menuItems.length === 1) {
74+
menuItems.push(
75+
<SelectOption isDisabled key="no-results">
76+
{t("common:no_results_found")}
77+
</SelectOption>,
78+
);
79+
}
80+
81+
const toggle = (
82+
<MenuToggle
83+
ref={toggleRef}
84+
onClick={() => setIsOpen(!isOpen)}
85+
isExpanded={isOpen}
86+
isDisabled={disableToolbar || topicList.length === 0}
87+
className="appserv-metrics-filterbytopic"
88+
>
89+
{selectedTopic || allTopicsLabel}
90+
</MenuToggle>
91+
);
92+
93+
const menu = (
94+
<Menu
95+
ref={menuRef}
96+
onSelect={onSelect}
97+
activeItemId={selectedTopic ?? "all-topics"}
98+
isScrollable
99+
>
100+
<MenuSearch>
101+
<MenuSearchInput>
102+
<SearchInput
103+
value={filter}
104+
onChange={(_, val) => setFilter(val)}
105+
onClear={(evt) => {
106+
evt.stopPropagation();
107+
setFilter("");
108+
}}
109+
/>
110+
</MenuSearchInput>
111+
</MenuSearch>
112+
113+
<MenuContent maxMenuHeight="200px">
114+
<MenuList>{menuItems}</MenuList>
115+
</MenuContent>
116+
</Menu>
117+
);
118+
119+
return (
120+
<ToolbarItem>
121+
<MenuContainer
122+
toggle={toggle}
123+
toggleRef={toggleRef}
124+
menu={menu}
125+
menuRef={menuRef}
126+
isOpen={isOpen}
127+
onOpenChange={setIsOpen}
128+
onOpenChangeKeys={["Escape"]}
129+
/>
130+
</ToolbarItem>
131+
);
132+
}

ui/messages/en.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -415,7 +415,8 @@
415415
"not_available": "n/a"
416416
},
417417
"metrics": {
418-
"all_brokers": "All Brokers"
418+
"all_brokers": "All Brokers",
419+
"all_topics": "All topics"
419420
},
420421
"ColumnsModal": {
421422
"title": "Manage columns",

0 commit comments

Comments
 (0)