Skip to content

Commit 2146366

Browse files
committed
✨ [WIP] Implement the Task Manager drawer
Resolves: #1938 Add a queued tasks count badge plus and item drawer. - Use the standard `NotificationDrawer` attached to the `Page` layout to render the task manager item drawer. - Add a `TaskManagerContext` to control the count indicator and visibility of the drawer. This is a top level context so the task manager is available on all pages. __Still Needs__: - Query for hitting the queued count endpoint - Query for pulling queued task details at a set page size (pull just enough so the user needs to scroll to see more) - Task rows in the notification drawer - Individual task actions - Infinite scroll on the task list (or at least a load more link/icon, maybe a visual indicator that more can be fetched on scroll or click) Related changes: - Update the `HeaderApp` to handle visibility of masthead toolbar items at the `ToolbarGroup` level. - Rename `SSOMenu` to `SsoToolbarItem`. Signed-off-by: Scott J Dickerson <[email protected]>
1 parent 460fded commit 2146366

File tree

8 files changed

+248
-105
lines changed

8 files changed

+248
-105
lines changed

client/src/app/App.tsx

+6-3
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,19 @@ import { BrowserRouter } from "react-router-dom";
44
import { AppRoutes } from "./Routes";
55
import { DefaultLayout } from "./layout";
66
import { NotificationsProvider } from "./components/NotificationsContext";
7+
import { TaskManagerProvider } from "./components/task-manager/TaskManagerContext";
78

89
import "./app.css";
910

1011
const App: React.FC = () => {
1112
return (
1213
<BrowserRouter>
1314
<NotificationsProvider>
14-
<DefaultLayout>
15-
<AppRoutes />
16-
</DefaultLayout>
15+
<TaskManagerProvider>
16+
<DefaultLayout>
17+
<AppRoutes />
18+
</DefaultLayout>
19+
</TaskManagerProvider>
1720
</NotificationsProvider>
1821
</BrowserRouter>
1922
);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import React, { useContext, useState } from "react";
2+
3+
interface TaskManagerContextProps {
4+
queuedCount: number;
5+
setQueuedCount: (value: number) => void;
6+
7+
isExpanded: boolean;
8+
setIsExpanded: (value: boolean) => void;
9+
}
10+
11+
const TaskManagerContext = React.createContext<TaskManagerContextProps>({
12+
queuedCount: 0,
13+
setQueuedCount: () => undefined,
14+
15+
isExpanded: false,
16+
setIsExpanded: () => undefined,
17+
});
18+
19+
export const useTaskManagerContext = () => {
20+
const values = useContext(TaskManagerContext);
21+
22+
return values;
23+
};
24+
25+
export const TaskManagerProvider: React.FC<{ children: React.ReactNode }> = ({
26+
children,
27+
}) => {
28+
const [queuedCount, setQueuedCount] = useState(0);
29+
const [isExpanded, setIsExpanded] = useState(false);
30+
31+
// TODO: Setup the count query here!
32+
setTimeout(() => {
33+
setQueuedCount(queuedCount + 5);
34+
}, 5000);
35+
36+
return (
37+
<TaskManagerContext.Provider
38+
value={{
39+
queuedCount,
40+
setQueuedCount,
41+
isExpanded,
42+
setIsExpanded,
43+
}}
44+
>
45+
{children}
46+
</TaskManagerContext.Provider>
47+
);
48+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import React, { forwardRef } from "react";
2+
import { Link } from "react-router-dom";
3+
import {
4+
NotificationDrawer,
5+
NotificationDrawerBody,
6+
NotificationDrawerHeader,
7+
NotificationDrawerList,
8+
} from "@patternfly/react-core";
9+
import { useTaskManagerContext } from "./TaskManagerContext";
10+
11+
interface TaskManagerDrawerProps {
12+
ref?: React.ForwardedRef<HTMLElement>;
13+
}
14+
15+
export const TaskManagerDrawer: React.FC<TaskManagerDrawerProps> = forwardRef(
16+
(_props, ref) => {
17+
const { isExpanded, setIsExpanded, queuedCount } = useTaskManagerContext();
18+
19+
const closeDrawer = () => {
20+
setIsExpanded(!isExpanded);
21+
};
22+
23+
return (
24+
<NotificationDrawer ref={ref}>
25+
<NotificationDrawerHeader
26+
title="Task Manager"
27+
customText={`${queuedCount} queued`}
28+
onClose={closeDrawer}
29+
>
30+
{/* TODO: proper link to task page */}
31+
<Link to=".">View All Tasks</Link>
32+
</NotificationDrawerHeader>
33+
<NotificationDrawerBody>
34+
<NotificationDrawerList></NotificationDrawerList>
35+
</NotificationDrawerBody>
36+
</NotificationDrawer>
37+
);
38+
}
39+
);
40+
41+
TaskManagerDrawer.displayName = "TaskManagerDrawer";
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import React from "react";
2+
import { NotificationBadge } from "@patternfly/react-core";
3+
import { useTaskManagerContext } from "./TaskManagerContext";
4+
5+
export const TaskNotificationBadge: React.FC = () => {
6+
const { isExpanded, setIsExpanded, queuedCount } = useTaskManagerContext();
7+
8+
const badgeClick = () => {
9+
setIsExpanded(!isExpanded);
10+
};
11+
12+
return (
13+
<NotificationBadge
14+
aria-label="Count of queued tasks"
15+
variant={queuedCount > 0 ? "unread" : "read"}
16+
count={queuedCount}
17+
onClick={badgeClick}
18+
isExpanded={isExpanded}
19+
/>
20+
);
21+
};

client/src/app/layout/DefaultLayout/DefaultLayout.tsx

+10-1
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1-
import React from "react";
1+
import React, { useRef } from "react";
22
import { Page, SkipToContent } from "@patternfly/react-core";
33

44
import { HeaderApp } from "../HeaderApp";
55
import { SidebarApp } from "../SidebarApp";
66
import { Notifications } from "@app/components/Notifications";
77
import { PageContentWithDrawerProvider } from "@app/components/PageDrawerContext";
8+
import { TaskManagerDrawer } from "@app/components/task-manager/TaskManagerDrawer";
9+
import { useTaskManagerContext } from "@app/components/task-manager/TaskManagerContext";
810

911
export interface DefaultLayoutProps {}
1012

@@ -14,13 +16,20 @@ export const DefaultLayout: React.FC<DefaultLayoutProps> = ({ children }) => {
1416
<SkipToContent href={`#${pageId}`}>Skip to content</SkipToContent>
1517
);
1618

19+
// TODO: Task manager ref, use to focus on the drawer when it opens
20+
const drawerRef = useRef<HTMLElement | null>(null);
21+
22+
const { isExpanded } = useTaskManagerContext();
23+
1724
return (
1825
<Page
1926
header={<HeaderApp />}
2027
sidebar={<SidebarApp />}
2128
isManagedSidebar
2229
skipToContent={PageSkipToContent}
2330
mainContainerId={pageId}
31+
isNotificationDrawerExpanded={isExpanded}
32+
notificationDrawer={<TaskManagerDrawer ref={drawerRef} />}
2433
>
2534
<PageContentWithDrawerProvider>
2635
{children}

client/src/app/layout/HeaderApp/HeaderApp.tsx

+35-8
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,9 @@ import HelpIcon from "@patternfly/react-icons/dist/esm/icons/help-icon";
1919
import BarsIcon from "@patternfly/react-icons/dist/js/icons/bars-icon";
2020

2121
import useBranding from "@app/hooks/useBranding";
22+
import { TaskNotificationBadge } from "@app/components/task-manager/TaskNotificaitonBadge";
2223
import { AppAboutModalState } from "../AppAboutModalState";
23-
import { SSOMenu } from "./SSOMenu";
24+
import { SsoToolbarItem } from "./SsoToolbarItem";
2425
import { MobileDropdown } from "./MobileDropdown";
2526

2627
import "./header.css";
@@ -33,9 +34,21 @@ export const HeaderApp: React.FC = () => {
3334
const toolbar = (
3435
<Toolbar isFullHeight isStatic>
3536
<ToolbarContent>
37+
{/* toolbar items to always show */}
3638
<ToolbarGroup
39+
id="header-toolbar-tasks"
3740
variant="icon-button-group"
3841
align={{ default: "alignRight" }}
42+
>
43+
<ToolbarItem>
44+
<TaskNotificationBadge />
45+
</ToolbarItem>
46+
</ToolbarGroup>
47+
48+
{/* toolbar items to show at desktop sizes */}
49+
<ToolbarGroup
50+
id="header-toolbar-desktop"
51+
variant="icon-button-group"
3952
spacer={{ default: "spacerNone", md: "spacerMd" }}
4053
visibility={{
4154
default: "hidden",
@@ -62,16 +75,30 @@ export const HeaderApp: React.FC = () => {
6275
</AppAboutModalState>
6376
</ToolbarItem>
6477
</ToolbarGroup>
65-
<ToolbarGroup>
66-
<ToolbarItem
67-
visibility={{
68-
lg: "hidden",
69-
}} /** this kebab dropdown replaces the icon buttons and is hidden for desktop sizes */
70-
>
78+
79+
{/* toolbar items to show at mobile sizes */}
80+
<ToolbarGroup
81+
id="header-toolbar-mobile"
82+
variant="icon-button-group"
83+
spacer={{ default: "spacerNone", md: "spacerMd" }}
84+
visibility={{ lg: "hidden" }}
85+
>
86+
<ToolbarItem>
7187
<MobileDropdown />
7288
</ToolbarItem>
73-
<SSOMenu />
7489
</ToolbarGroup>
90+
91+
{/* Show the SSO menu at desktop sizes */}
92+
<ToolbarGroup
93+
id="header-toolbar-sso"
94+
visibility={{
95+
default: "hidden",
96+
md: "visible",
97+
}}
98+
>
99+
<SsoToolbarItem />
100+
</ToolbarGroup>
101+
75102
{rightBrand ? (
76103
<ToolbarGroup>
77104
<ToolbarItem>

client/src/app/layout/HeaderApp/SSOMenu.tsx

-93
This file was deleted.

0 commit comments

Comments
 (0)