Skip to content

Commit f08dfaa

Browse files
authored
[OGUI-1838] Change raw view to be an editable modal (#3218)
* Replaced the previous raw value display with an editable modal
1 parent 133b6db commit f08dfaa

File tree

7 files changed

+364
-26
lines changed

7 files changed

+364
-26
lines changed

Configuration/webapp/app/components/form/Form.tsx

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,10 @@ import { useCallback, useState, type FC, type PropsWithChildren } from 'react';
1616
import Accordion from '@mui/material/Accordion';
1717
import AccordionDetails from '@mui/material/AccordionDetails';
1818
import Stack from '@mui/material/Stack';
19+
1920
import { Widget } from './components/Widget';
2021
import { AccordionHeader } from './components/AccordionHeader';
21-
import { Typography } from '@mui/material';
22+
import { RawViewModal } from './raw-view/RawViewModal';
2223
import type { Control } from 'react-hook-form';
2324
import { type InputsType } from '~/routes/configuration';
2425
import { KEY_SEPARATOR } from './constants';
@@ -67,7 +68,7 @@ export const Form: FC<FormProps> = ({
6768
itemsRestrictions,
6869
control,
6970
}) => {
70-
const [viewForm, setViewForm] = useState<boolean>(true);
71+
const [isRawModalOpen, setIsRawModalOpen] = useState<boolean>(false);
7172

7273
const renderItem = useCallback(
7374
(key: string, value: FormRestrictions[string]) =>
@@ -94,21 +95,19 @@ export const Form: FC<FormProps> = ({
9495
);
9596

9697
return (
97-
<Accordion defaultExpanded>
98-
<AccordionHeader
99-
title={sectionTitle}
100-
viewForm={viewForm}
101-
viewFormToggle={() => setViewForm((v) => !v)}
102-
/>
103-
<AccordionDetails>
104-
{viewForm ? (
98+
<>
99+
<Accordion defaultExpanded>
100+
<AccordionHeader title={sectionTitle} showRawViewModal={() => setIsRawModalOpen(true)} />
101+
<AccordionDetails>
105102
<Stack spacing={2}>
106103
{Object.entries(itemsRestrictions).map(([key, value]) => renderItem(key, value))}
107104
</Stack>
108-
) : (
109-
<Typography component="pre">{JSON.stringify(items, null, 2)}</Typography>
110-
)}
111-
</AccordionDetails>
112-
</Accordion>
105+
</AccordionDetails>
106+
</Accordion>
107+
108+
{isRawModalOpen && (
109+
<RawViewModal onClose={() => setIsRawModalOpen(false)} title={sectionTitle} data={items} />
110+
)}
111+
</>
113112
);
114113
};

Configuration/webapp/app/components/form/components/AccordionHeader.tsx

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,12 @@
1515
import { useCallback, type FC, type PropsWithChildren, type ReactElement } from 'react';
1616
import AccordionSummary from '@mui/material/AccordionSummary';
1717
import ExpandLessIcon from '@mui/icons-material/ExpandLess';
18-
import FormData from '@mui/icons-material/ListAlt';
19-
import RawData from '@mui/icons-material/EditNote';
18+
import RawData from '@mui/icons-material/DataObject';
2019
import { IconButton, Typography } from '@mui/material';
2120

2221
interface AccordionHeaderProps extends PropsWithChildren {
2322
title: string;
24-
viewForm: boolean;
25-
viewFormToggle: () => void;
23+
showRawViewModal: () => void;
2624
}
2725

2826
/**
@@ -35,15 +33,14 @@ interface AccordionHeaderProps extends PropsWithChildren {
3533
*/
3634
export const AccordionHeader: FC<AccordionHeaderProps> = ({
3735
title,
38-
viewForm,
39-
viewFormToggle,
36+
showRawViewModal,
4037
}): ReactElement => {
41-
const viewFormToggleCallback = useCallback(
38+
const showRawViewModalCallback = useCallback(
4239
(e: React.MouseEvent<HTMLButtonElement>) => {
4340
e.stopPropagation();
44-
viewFormToggle();
41+
showRawViewModal();
4542
},
46-
[viewFormToggle],
43+
[showRawViewModal],
4744
);
4845

4946
return (
@@ -57,8 +54,8 @@ export const AccordionHeader: FC<AccordionHeaderProps> = ({
5754
}}
5855
>
5956
<Typography sx={{ marginRight: 'auto', alignContent: 'center' }}>{title}</Typography>
60-
<IconButton onClick={viewFormToggleCallback}>
61-
{viewForm ? <RawData /> : <FormData />}
57+
<IconButton onClick={showRawViewModalCallback}>
58+
<RawData />
6259
</IconButton>
6360
</AccordionSummary>
6461
);
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/**
2+
* @license
3+
* Copyright 2019-2020 CERN and copyright holders of ALICE O2.
4+
* See http://alice-o2.web.cern.ch/copyright for details of the copyright holders.
5+
* All rights not expressly granted are reserved.
6+
*
7+
* This software is distributed under the terms of the GNU General Public
8+
* License v3 (GPL Version 3), copied verbatim in the file "COPYING".
9+
*
10+
* In applying this license CERN does not waive the privileges and immunities
11+
* granted to it by virtue of its status as an Intergovernmental Organization
12+
* or submit itself to any jurisdiction.
13+
*/
14+
15+
import { type FC } from 'react';
16+
import Editor from '@monaco-editor/react';
17+
import { Box } from '@mui/material';
18+
import { Spinner } from '~/ui/spinner';
19+
20+
interface RawEditorProps {
21+
intialData: string;
22+
onChange: (value: string | undefined) => void;
23+
}
24+
25+
export const RawEditor: FC<RawEditorProps> = ({ intialData, onChange }) => (
26+
<Box
27+
sx={{
28+
border: '1px solid #e0e0e0',
29+
borderRadius: '4px',
30+
overflow: 'hidden',
31+
height: '60vh',
32+
}}
33+
>
34+
<Editor
35+
height="100%"
36+
defaultLanguage="json"
37+
defaultValue={intialData}
38+
theme="light"
39+
onChange={onChange}
40+
options={{
41+
readOnly: false,
42+
minimap: { enabled: false },
43+
scrollBeyondLastLine: false,
44+
fontSize: 14,
45+
fontFamily: '"Fira Code", "Roboto Mono", monospace',
46+
wordWrap: 'on',
47+
lineNumbers: 'on',
48+
renderLineHighlight: 'none',
49+
contextmenu: false,
50+
}}
51+
loading={<Spinner />}
52+
/>
53+
</Box>
54+
);
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/**
2+
* @license
3+
* Copyright 2019-2020 CERN and copyright holders of ALICE O2.
4+
* See http://alice-o2.web.cern.ch/copyright for details of the copyright holders.
5+
* All rights not expressly granted are reserved.
6+
*
7+
* This software is distributed under the terms of the GNU General Public
8+
* License v3 (GPL Version 3), copied verbatim in the file "COPYING".
9+
*
10+
* In applying this license CERN does not waive the privileges and immunities
11+
* granted to it by virtue of its status as an Intergovernmental Organization
12+
* or submit itself to any jurisdiction.
13+
*/
14+
15+
import { useMemo, useState, type FC } from 'react';
16+
import Dialog from '@mui/material/Dialog';
17+
import DialogTitle from '@mui/material/DialogTitle';
18+
import DialogContent from '@mui/material/DialogContent';
19+
import IconButton from '@mui/material/IconButton';
20+
import Box from '@mui/material/Box';
21+
import Tooltip from '@mui/material/Tooltip';
22+
import CloseIcon from '@mui/icons-material/Close';
23+
import ContentCopyIcon from '@mui/icons-material/ContentCopy';
24+
import { RawEditor } from './RawEditor';
25+
26+
interface RawViewModalProps {
27+
onClose: () => void;
28+
title: string;
29+
data: unknown;
30+
}
31+
32+
export const RawViewModal: FC<RawViewModalProps> = ({ onClose, title, data }) => {
33+
const initialFormattedData = useMemo(() => JSON.stringify(data, null, 2), [data]);
34+
35+
const [currentData, setCurrentData] = useState(initialFormattedData);
36+
37+
const handleCopy = () => {
38+
void navigator.clipboard.writeText(currentData);
39+
};
40+
41+
const handleEditorChange = (value: string | undefined) => {
42+
setCurrentData(value ?? '');
43+
};
44+
45+
return (
46+
<Dialog open={true} onClose={onClose} maxWidth="md" fullWidth>
47+
<DialogTitle sx={{ m: 0, p: 2, paddingRight: 12 }}>
48+
{title}
49+
50+
<Box
51+
sx={{
52+
position: 'absolute',
53+
right: 8,
54+
top: 8,
55+
display: 'flex',
56+
gap: 0.5,
57+
}}
58+
>
59+
<Tooltip title="Copy Content to Clipboard">
60+
<IconButton onClick={handleCopy}>
61+
<ContentCopyIcon />
62+
</IconButton>
63+
</Tooltip>
64+
65+
<Tooltip title="Close">
66+
<IconButton onClick={onClose}>
67+
<CloseIcon />
68+
</IconButton>
69+
</Tooltip>
70+
</Box>
71+
</DialogTitle>
72+
73+
<DialogContent dividers>
74+
<RawEditor intialData={initialFormattedData} onChange={handleEditorChange} />
75+
</DialogContent>
76+
</Dialog>
77+
);
78+
};

Configuration/webapp/package-lock.json

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

Configuration/webapp/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
"@aliceo2/web-ui": "^2.7.4",
1919
"@emotion/react": "^11.14.0",
2020
"@emotion/styled": "^11.14.1",
21+
"@monaco-editor/react": "^4.7.0",
2122
"@mui/icons-material": "^7.3.5",
2223
"@mui/material": "^7.3.5",
2324
"@react-router/node": "7.5.3",

0 commit comments

Comments
 (0)