Skip to content

Commit 3d3ded2

Browse files
committed
feat: user documentation directly in the application
1 parent dc15f6c commit 3d3ded2

12 files changed

Lines changed: 1857 additions & 45 deletions

File tree

package-lock.json

Lines changed: 1501 additions & 42 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/diracx-web-components/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
"@types/react": "^18.3.12",
3636
"@types/react-dom": "^18.3.1",
3737
"dayjs": "^1.11.13",
38+
"mui-markdown": "^2.0.1",
3839
"react": "^18.3.1",
3940
"react-dom": "^18.3.1",
4041
"react-virtuoso": "^4.12.3",

packages/diracx-web-components/src/components/DashboardLayout/DashboardDrawer.tsx

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,15 @@ import {
1616
Toolbar,
1717
useTheme,
1818
} from "@mui/material";
19-
import { MenuBook, Add } from "@mui/icons-material";
19+
import { MenuBook, Add, TipsAndUpdates } from "@mui/icons-material";
2020
import React, { useContext, useEffect, useState } from "react";
2121
import { monitorForElements } from "@atlaskit/pragmatic-drag-and-drop/element/adapter";
2222
import { extractClosestEdge } from "@atlaskit/pragmatic-drag-and-drop-hitbox/closest-edge";
2323
import { ApplicationsContext } from "../../contexts/ApplicationsProvider";
2424
import { DashboardGroup } from "../../types";
2525
import DrawerItemGroup from "./DrawerItemGroup";
2626
import AppDialog from "./ApplicationDialog";
27+
import HelpOverlay from "./HelpOverlay";
2728

2829
interface DashboardDrawerProps {
2930
/** The variant of the drawer. Usually temporary if on mobile and permanent otherwise. */
@@ -78,6 +79,8 @@ export default function DashboardDrawer({
7879
const [renamingGroupId, setRenamingGroupId] = useState<string | null>(null);
7980
const [renameValue, setRenameValue] = useState("");
8081

82+
const [helpOpen, setHelpOpen] = useState(false);
83+
8184
// Define the applications that are accessible to users.
8285
// Each application has an associated icon and path.
8386
const [userDashboard, setUserDashboard, , , setCurrentAppId] =
@@ -456,6 +459,15 @@ export default function DashboardDrawer({
456459
<ListItemText primary={"Add application"} />
457460
</ListItemButton>
458461
</ListItem>
462+
<ListItem key={"User Guide"}>
463+
<ListItemButton
464+
onClick={() => setHelpOpen(true)}
465+
data-testid="user-guide-button"
466+
>
467+
<ListItemIcon>{<TipsAndUpdates />}</ListItemIcon>
468+
<ListItemText primary={"User Guide"} />
469+
</ListItemButton>
470+
</ListItem>
459471
<ListItem key={"Documentation"}>
460472
<ListItemButton target="_blank" href={docURL}>
461473
<ListItemIcon>{<MenuBook />}</ListItemIcon>
@@ -523,6 +535,8 @@ export default function DashboardDrawer({
523535
setAppDialogOpen={setAppDialogOpen}
524536
handleCreateApp={handleAppCreation}
525537
/>
538+
539+
<HelpOverlay isOpen={helpOpen} closeHelp={() => setHelpOpen(false)} />
526540
</>
527541
);
528542
}
Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
import { useState, useContext } from "react";
2+
import { MuiMarkdown, defaultOverrides } from "mui-markdown";
3+
4+
import {
5+
Box,
6+
Typography,
7+
ListItemButton,
8+
IconButton,
9+
List,
10+
ListItemText,
11+
Divider,
12+
Modal,
13+
Collapse,
14+
useTheme,
15+
} from "@mui/material";
16+
17+
import { grey } from "@mui/material/colors";
18+
19+
import CloseIcon from "@mui/icons-material/Close";
20+
21+
import ExpandLess from "@mui/icons-material/ExpandLess";
22+
import ExpandMore from "@mui/icons-material/ExpandMore";
23+
import { ApplicationsContext } from "../../contexts";
24+
25+
/**
26+
*
27+
* @param isOpen Boolean indicating if the help page is open
28+
* @param closeHelp Function to close the help page
29+
* @returns Component to display the help overlay with user documentation
30+
*/
31+
export default function HelpOverlay({
32+
isOpen,
33+
closeHelp,
34+
}: {
35+
isOpen: boolean;
36+
closeHelp: () => void;
37+
}) {
38+
const [selected, setSelected] = useState<string>("general");
39+
const [expandedApps, setExpandedApps] = useState<Record<string, boolean>>({});
40+
41+
const userDocumentation = useContext(ApplicationsContext)[5];
42+
const theme = useTheme();
43+
44+
const backgroundColor = theme.palette.mode === "dark" ? grey[900] : grey[50];
45+
46+
const customOverrides = {
47+
...defaultOverrides,
48+
h1: {
49+
component: Typography,
50+
props: {
51+
variant: "h3",
52+
gutterBottom: true,
53+
sx: {
54+
marginTop: "1.5rem",
55+
marginBottom: "1.5rem",
56+
},
57+
} as React.HTMLProps<HTMLHeadingElement>,
58+
},
59+
h2: {
60+
component: Typography,
61+
props: {
62+
variant: "h4",
63+
gutterBottom: true,
64+
sx: {
65+
marginTop: "1rem",
66+
marginBottom: "0.5rem",
67+
},
68+
} as React.HTMLProps<HTMLHeadingElement>,
69+
},
70+
h3: {
71+
component: Typography,
72+
props: {
73+
variant: "h5",
74+
gutterBottom: true,
75+
sx: {
76+
marginTop: "0.75rem",
77+
marginBottom: "0.25rem",
78+
},
79+
} as React.HTMLProps<HTMLHeadingElement>,
80+
},
81+
p: {
82+
component: Typography,
83+
props: {
84+
paragraph: true,
85+
} as React.HTMLProps<HTMLParagraphElement>,
86+
},
87+
};
88+
// Handle application section toggle
89+
const toggleApp = (appName: string) => {
90+
setExpandedApps((prev) => ({
91+
...prev,
92+
[appName]: !prev[appName],
93+
}));
94+
};
95+
96+
// Render content based on selected item
97+
const RenderContent = () => {
98+
if (selected === "general") {
99+
return (
100+
<>
101+
<MuiMarkdown overrides={customOverrides}>
102+
{userDocumentation.generalUsage}
103+
</MuiMarkdown>
104+
</>
105+
);
106+
}
107+
108+
const [appName, sectionTitle] = selected.split("::");
109+
const app = userDocumentation.applications.find(
110+
(a) => a.appName === appName,
111+
);
112+
const section = app?.sections.find((s) => s.title === sectionTitle);
113+
114+
return (
115+
<>
116+
<MuiMarkdown overrides={customOverrides}>
117+
{section?.content}
118+
</MuiMarkdown>
119+
</>
120+
);
121+
};
122+
123+
return (
124+
<Modal open={isOpen} onClose={closeHelp}>
125+
<Box
126+
sx={{
127+
position: "absolute",
128+
display: "flex",
129+
width: "80vw",
130+
height: "80vh",
131+
bgcolor: backgroundColor,
132+
borderRadius: 2,
133+
overflow: "hidden",
134+
top: "50%",
135+
left: "50%",
136+
transform: "translate(-50%, -50%)",
137+
}}
138+
>
139+
{/* Left vertical menu */}
140+
<IconButton
141+
onClick={closeHelp}
142+
sx={{ position: "absolute", top: 8, right: 8 }}
143+
aria-label="Close"
144+
>
145+
<CloseIcon />
146+
</IconButton>
147+
<Box
148+
sx={{ width: 250, borderRight: "1px solid #ddd", overflowY: "auto" }}
149+
>
150+
<List>
151+
<ListItemButton
152+
selected={selected === "general"}
153+
onClick={() => setSelected("general")}
154+
>
155+
<ListItemText primary="General Usage" />
156+
</ListItemButton>
157+
<Divider />
158+
{userDocumentation.applications.map((app, index) => (
159+
<Box key={index}>
160+
<ListItemButton onClick={() => toggleApp(app.appName)}>
161+
<ListItemText primary={app.appName} />
162+
{expandedApps[app.appName] ? <ExpandLess /> : <ExpandMore />}
163+
</ListItemButton>
164+
<Collapse
165+
in={expandedApps[app.appName]}
166+
timeout="auto"
167+
unmountOnExit
168+
>
169+
<List component="div" disablePadding>
170+
{app.sections.map((section) => (
171+
<ListItemButton
172+
key={section.title}
173+
sx={{ pl: 4 }}
174+
selected={
175+
selected === `${app.appName}::${section.title}`
176+
}
177+
onClick={() =>
178+
setSelected(`${app.appName}::${section.title}`)
179+
}
180+
>
181+
<ListItemText primary={section.title} />
182+
</ListItemButton>
183+
))}
184+
</List>
185+
</Collapse>
186+
</Box>
187+
))}
188+
</List>
189+
</Box>
190+
191+
{/* Right content pane */}
192+
<Box sx={{ flex: 1, p: 3, overflowY: "auto" }}>{RenderContent()}</Box>
193+
</Box>
194+
</Modal>
195+
);
196+
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import { UserDocumentation } from "../types/UserDocumentation";
2+
3+
/**
4+
* User documentation for DiracX web.
5+
*
6+
*/
7+
export const userDocumentation: UserDocumentation = {
8+
generalUsage: `
9+
# General usage
10+
11+
## Navigate between applications
12+
13+
You can click on the \`Add application\` button to create a new instance of an application. The list of open instances appears in the sidebar on the left. Clicking on one of these instances will display it in the center of the dashboard
14+
15+
## Share applications
16+
17+
To share applications, click the \`Export\` button at the top right of the screen. Then, select the applications you want to share and copy them to the clipboard.
18+
19+
## Import applications
20+
21+
To import applications, click the \`Import\` button at the top right of the screen. Paste the copied applications into the input field and click \`Import\`. The imported applications will appear in the sidebar.
22+
If the states are outdated, you can copy the updated version of them by clicking the \`Copy all updated states\` button.
23+
24+
## Save you dashoard
25+
To save your dashboard, clicke the \`Export\`button at the top right of the screen. Then, select the applications you want to save and click on the \`Save in the browser\` button. The saved applications will be stored in your browser's local storage.
26+
When you want to load the saved applications, click the \`Import\` button at the top right of the screen and select the \`Load from the browser\` option. The saved applications will be loaded into the sidebar.
27+
28+
`,
29+
applications: [
30+
{
31+
appName: "Job Monitor",
32+
sections: [
33+
{
34+
title: "General Usage",
35+
content: `
36+
# General Usage
37+
38+
The \`Job Monitor\` application allows you to track and manage jobs. It provides a search bar to filter jobs by criteria such as job type, status, and submission date. The application displays a list of jobs in a table format, with each job showing its status and other relevant information.`,
39+
},
40+
{
41+
title: "The Search Bar",
42+
content: `
43+
# How to use the search bar
44+
45+
The search bar allows you to filter jobs based on various criteria. The filters are represented as equations in the search bar, where each equation consists of a job attribute, an operator, and a value.
46+
47+
A resarch is automatically performed when all the equations in the search bar are valid.
48+
49+
## How to create a filter
50+
To create a filter, click on the search bar and start typing. Suggestions will appear based on the available job attributes. You can select a suggestion to choose the criterion. After that, you can either type or select an operator and a value to filter by.
51+
52+
The search bar only suggests attributes, operators, and values that are available in your current set of jobs.
53+
54+
## How to edit a filter
55+
To edit a filter, click on the filter in the search bar. You can change the operator or value by clicking on them. You can also use the arrow keys to navigate through the equations and edit them.
56+
57+
## How to remove a filter
58+
59+
To remove a filter, you can either press the \`Backspace\` key to remove the last token or right-click on the equation to remove the entire equation. `,
60+
},
61+
{
62+
title: "The table",
63+
content: `
64+
# How to use the table
65+
66+
The table displays the jobs that match the criteria specified in the search bar. Each row represents a job, and the columns show various attributes of the job, such as its ID, status, type, and submission date.
67+
68+
## Actions on the table
69+
70+
You can click on the eye icon to select more columns to display in the table.
71+
You can sort the table by clicking on the column headers. Clicking on a column header will sort the table in ascending order, and clicking again will sort it in descending order.
72+
You can set the page size by clicking on the \`Row per page\` dropdown at the bottom of the table. This allows you to control how many jobs are displayed per page.
73+
74+
## Actions on a job
75+
76+
You can do a right-click on a job to open the \`Job History\`.
77+
You can select one or more jobs by clicking on the checkboxes next to each job. Once you have selected jobs, you can perform actions on them using the buttons at the top of the table. The available actions include:
78+
- **Get IDs**: This button will copy the IDs of the selected jobs to the clipboard.
79+
- **Rechedule**: This button will reschedule the selected jobs.
80+
- **Kill**: This button will kill the selected jobs.
81+
- **Delete**: This button will delete the selected jobs.`,
82+
},
83+
],
84+
},
85+
],
86+
};

packages/diracx-web-components/src/components/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
// Application list
22
export { applicationList } from "./applicationList";
33

4+
// User Documentation
5+
export { userDocumentation } from "./UserDocumentation";
6+
47
// Dashboard Layout
58
export * from "./DashboardLayout";
69

0 commit comments

Comments
 (0)