Skip to content

Commit 2927444

Browse files
👻 WIP Storybook - Search page (#363)
1 parent bf6e836 commit 2927444

File tree

3 files changed

+1058
-0
lines changed

3 files changed

+1058
-0
lines changed
+125
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
import React from "react";
2+
3+
import type { Meta, StoryObj } from "@storybook/react";
4+
import {
5+
Nav,
6+
NavItem,
7+
NavList,
8+
Page,
9+
PageSidebar,
10+
SkipToContent,
11+
} from "@patternfly/react-core";
12+
13+
import { NotificationsProvider } from "@app/components/NotificationsContext";
14+
15+
import { HeaderApp } from "@app/layout/header";
16+
import { PageContentWithDrawerProvider } from "@app/components/PageDrawerContext";
17+
import { Notifications } from "@app/components/Notifications";
18+
19+
import { SearchPage } from "./pages/search";
20+
import { ProductsPage } from "./pages/products";
21+
22+
type Route = "dashboard" | "search" | "products";
23+
type RouteProps = {
24+
[key in Route]: {
25+
element: React.ReactNode;
26+
};
27+
};
28+
29+
type AppProps = {
30+
route: Route;
31+
};
32+
33+
const App: React.FC<AppProps> = ({ route }) => {
34+
const routeList: RouteProps = {
35+
dashboard: {
36+
element: <>Dashboard</>,
37+
},
38+
search: {
39+
element: <SearchPage />,
40+
},
41+
products: {
42+
element: <ProductsPage />,
43+
},
44+
};
45+
46+
return routeList[route].element;
47+
};
48+
49+
const SidebarApp: React.FC = () => {
50+
return (
51+
<PageSidebar>
52+
<Nav id="nav-sidebar" aria-label="Nav">
53+
<NavList>
54+
<NavItem>Dashboard</NavItem>
55+
<NavItem>Search</NavItem>
56+
<NavItem>Products</NavItem>
57+
<NavItem>Vulnerabilities</NavItem>
58+
<NavItem>Packages</NavItem>
59+
<NavItem>Data sources</NavItem>
60+
</NavList>
61+
</Nav>
62+
</PageSidebar>
63+
);
64+
};
65+
66+
interface DefaultLayoutProps {
67+
children?: React.ReactNode;
68+
}
69+
70+
const DefaultLayout: React.FC<DefaultLayoutProps> = ({ children }) => {
71+
const pageId = "main-content-page-layout-horizontal-nav";
72+
const PageSkipToContent = (
73+
<SkipToContent href={`#${pageId}`}>Skip to content</SkipToContent>
74+
);
75+
76+
return (
77+
<Page
78+
header={<HeaderApp />}
79+
sidebar={<SidebarApp />}
80+
isManagedSidebar
81+
skipToContent={PageSkipToContent}
82+
mainContainerId={pageId}
83+
>
84+
<PageContentWithDrawerProvider>
85+
{children}
86+
<Notifications />
87+
</PageContentWithDrawerProvider>
88+
</Page>
89+
);
90+
};
91+
92+
const meta = {
93+
title: "v2.1/App",
94+
component: App,
95+
decorators: [
96+
(Story) => (
97+
<NotificationsProvider>
98+
<DefaultLayout>
99+
<Story />
100+
</DefaultLayout>
101+
</NotificationsProvider>
102+
),
103+
],
104+
} satisfies Meta<typeof SearchPage>;
105+
106+
export default meta;
107+
type Story = StoryObj<typeof meta>;
108+
109+
export const Dashboard: Story = {
110+
args: {
111+
route: "dashboard",
112+
},
113+
};
114+
115+
export const Search: Story = {
116+
args: {
117+
route: "search",
118+
},
119+
};
120+
121+
export const Products: Story = {
122+
args: {
123+
route: "products",
124+
},
125+
};
+224
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
1+
import React from "react";
2+
3+
import {
4+
Card,
5+
CardBody,
6+
CardFooter,
7+
CardHeader,
8+
CardTitle,
9+
DescriptionList,
10+
DescriptionListDescription,
11+
DescriptionListGroup,
12+
DescriptionListTerm,
13+
DescriptionListTermHelpText,
14+
DescriptionListTermHelpTextButton,
15+
Divider,
16+
Flex,
17+
FlexItem,
18+
Gallery,
19+
Grid,
20+
GridItem,
21+
Icon,
22+
Label,
23+
List,
24+
ListItem,
25+
PageSection,
26+
PageSectionVariants,
27+
Popover,
28+
Split,
29+
SplitItem,
30+
Stack,
31+
StackItem,
32+
Text,
33+
TextContent,
34+
ToggleGroup,
35+
ToggleGroupItem,
36+
Toolbar,
37+
ToolbarContent,
38+
ToolbarItem,
39+
Tooltip,
40+
} from "@patternfly/react-core";
41+
42+
import {
43+
Chart,
44+
ChartAxis,
45+
ChartBar,
46+
ChartStack,
47+
ChartThemeColor,
48+
ChartTooltip,
49+
} from "@patternfly/react-charts";
50+
import { right } from "@patternfly/react-core/dist/esm/helpers/Popper/thirdparty/popper-core";
51+
import {
52+
BoxIcon,
53+
CircleNotchIcon,
54+
GithubIcon,
55+
ListIcon,
56+
ReceiptIcon,
57+
RedhatIcon,
58+
SeverityCriticalIcon,
59+
SeverityImportantIcon,
60+
SeverityMinorIcon,
61+
SeverityModerateIcon,
62+
SeverityNoneIcon,
63+
ShieldVirusIcon,
64+
} from "@patternfly/react-icons";
65+
66+
import { severityList } from "@app/api/model-utils";
67+
import { Severity } from "@app/client";
68+
import { FilterToolbar, FilterType } from "@app/components/FilterToolbar";
69+
import { SeverityShieldAndText } from "@app/components/SeverityShieldAndText";
70+
import { SimplePagination } from "@app/components/SimplePagination";
71+
import { useLocalTableControls } from "@app/hooks/table-controls";
72+
73+
interface Legend {
74+
severity: Severity;
75+
}
76+
77+
const LEGENDS: Legend[] = [
78+
{ severity: "critical" },
79+
{ severity: "high" },
80+
{ severity: "medium" },
81+
{ severity: "low" },
82+
{ severity: "none" },
83+
];
84+
85+
//
86+
87+
interface Data {
88+
type: "sbom" | "vulnerability" | "pkg";
89+
name: string;
90+
}
91+
92+
const sboms: Data[] = [...Array(4).keys()].map((item) => ({
93+
type: "sbom",
94+
name: `sbom-${item}`,
95+
}));
96+
const vulnerabilities: Data[] = [...Array(4).keys()].map((item) => ({
97+
type: "vulnerability",
98+
name: `CVE-${item}`,
99+
}));
100+
const pkgs: Data[] = [...Array(4).keys()].map((item) => ({
101+
type: "pkg",
102+
name: `package-${item}`,
103+
}));
104+
105+
export const ProductsPage: React.FC = () => {
106+
const tableControls = useLocalTableControls({
107+
tableName: "search-table",
108+
idProperty: "name",
109+
items: [...sboms, ...vulnerabilities, ...pkgs],
110+
columnNames: {
111+
name: "Name",
112+
type: "Type",
113+
description: "Description",
114+
source: "Source",
115+
period: "Period",
116+
state: "State",
117+
},
118+
hasActionsColumn: true,
119+
isSortEnabled: true,
120+
sortableColumns: ["name"],
121+
getSortValues: (item) => ({
122+
name: item.name,
123+
}),
124+
isPaginationEnabled: true,
125+
isExpansionEnabled: true,
126+
expandableVariant: "single",
127+
isFilterEnabled: true,
128+
filterCategories: [
129+
{
130+
categoryKey: "name",
131+
title: "Name",
132+
type: FilterType.search,
133+
placeholderText: "Search",
134+
getItemValue: (item) => item.name || "",
135+
},
136+
{
137+
categoryKey: "vulnerability",
138+
title: "Vulnerability",
139+
type: FilterType.multiselect,
140+
logicOperator: "OR",
141+
selectOptions: [...Array(vulnerabilities.length).keys()].map(
142+
(item) => ({
143+
value: `CVE-${item}`,
144+
label: `CVE-${item}`,
145+
})
146+
),
147+
placeholderText: "Vulnerability",
148+
matcher: (filter, item) => {
149+
return item.type !== "vulnerability" ? true : filter === item.name;
150+
},
151+
},
152+
{
153+
categoryKey: "sbom",
154+
title: "SBOM",
155+
type: FilterType.multiselect,
156+
logicOperator: "OR",
157+
selectOptions: [...Array(sboms.length).keys()].map((item) => ({
158+
value: `sbom-${item}`,
159+
label: `sbom-${item}`,
160+
})),
161+
placeholderText: "SBOM",
162+
matcher: (filter, item) => {
163+
return item.type !== "sbom" ? true : filter === item.name;
164+
},
165+
},
166+
{
167+
categoryKey: "pkg",
168+
title: "Package",
169+
type: FilterType.multiselect,
170+
logicOperator: "OR",
171+
selectOptions: [...Array(pkgs.length).keys()].map((item) => ({
172+
value: `package-${item}`,
173+
label: `package-${item}`,
174+
})),
175+
placeholderText: "Package",
176+
matcher: (filter, item) => {
177+
return item.type !== "pkg" ? true : filter === item.name;
178+
},
179+
},
180+
],
181+
initialItemsPerPage: 20,
182+
});
183+
184+
const {
185+
currentPageItems,
186+
numRenderedColumns,
187+
propHelpers: {
188+
toolbarProps,
189+
filterToolbarProps,
190+
paginationToolbarItemProps,
191+
paginationProps,
192+
tableProps,
193+
getThProps,
194+
getTrProps,
195+
getTdProps,
196+
},
197+
expansionDerivedState: { isCellExpanded },
198+
} = tableControls;
199+
200+
return (
201+
<>
202+
<PageSection variant={PageSectionVariants.light}>
203+
<TextContent>
204+
<Text component="h1">Products</Text>
205+
<Text component="p">Group your SBOMs under products</Text>
206+
</TextContent>
207+
<Toolbar {...toolbarProps}>
208+
<ToolbarContent>
209+
<FilterToolbar showFiltersSideBySide {...filterToolbarProps} />
210+
<ToolbarItem></ToolbarItem>
211+
<ToolbarItem {...paginationToolbarItemProps}>
212+
<SimplePagination
213+
idPrefix="products-table"
214+
isTop
215+
paginationProps={paginationProps}
216+
/>
217+
</ToolbarItem>
218+
</ToolbarContent>
219+
</Toolbar>
220+
</PageSection>
221+
<PageSection>table</PageSection>
222+
</>
223+
);
224+
};

0 commit comments

Comments
 (0)