@abgov/jsonforms-components 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.babelrc +12 -0
- package/.eslintrc.json +36 -0
- package/.releaserc.json +25 -0
- package/README.md +251 -0
- package/jest.config.ts +11 -0
- package/package.json +17 -0
- package/project.json +55 -0
- package/rollup.config.js +14 -0
- package/src/index.ts +166 -0
- package/src/lib/Additional/HelpContent.tsx +95 -0
- package/src/lib/Additional/index.ts +1 -0
- package/src/lib/Additional/styled-components.ts +27 -0
- package/src/lib/Cells/DateCell.tsx +10 -0
- package/src/lib/Cells/IntegerCell.tsx +10 -0
- package/src/lib/Cells/NumberCell.tsx +10 -0
- package/src/lib/Cells/TextCell.tsx +10 -0
- package/src/lib/Cells/TimeCell.tsx +10 -0
- package/src/lib/Cells/index.tsx +14 -0
- package/src/lib/Context/index.tsx +172 -0
- package/src/lib/Controls/FileUploader/ContextMenu.tsx +50 -0
- package/src/lib/Controls/FileUploader/FileUploaderControl.tsx +131 -0
- package/src/lib/Controls/FileUploader/FileUploaderTester.tsx +3 -0
- package/src/lib/Controls/FileUploader/index.tsx +2 -0
- package/src/lib/Controls/FileUploader/styled-components.tsx +10 -0
- package/src/lib/Controls/FormStepper/FormStepperControl.tsx +269 -0
- package/src/lib/Controls/FormStepper/FormStepperTester.tsx +22 -0
- package/src/lib/Controls/FormStepper/index.tsx +2 -0
- package/src/lib/Controls/FormStepper/styled-components.tsx +17 -0
- package/src/lib/Controls/Inputs/InputBaseControl.tsx +52 -0
- package/src/lib/Controls/Inputs/InputBooleanControl.tsx +67 -0
- package/src/lib/Controls/Inputs/InputBooleanRadioControl.tsx +74 -0
- package/src/lib/Controls/Inputs/InputDateControl.tsx +90 -0
- package/src/lib/Controls/Inputs/InputDateTimeControl.tsx +46 -0
- package/src/lib/Controls/Inputs/InputEnum.tsx +74 -0
- package/src/lib/Controls/Inputs/InputEnumAutoComplete.tsx +73 -0
- package/src/lib/Controls/Inputs/InputEnumRadios.tsx +43 -0
- package/src/lib/Controls/Inputs/InputIntegerControl.tsx +63 -0
- package/src/lib/Controls/Inputs/InputMultiLineTextControl.tsx +63 -0
- package/src/lib/Controls/Inputs/InputNumberControl.tsx +63 -0
- package/src/lib/Controls/Inputs/InputTextControl.tsx +62 -0
- package/src/lib/Controls/Inputs/InputTimeControl.tsx +46 -0
- package/src/lib/Controls/Inputs/index.tsx +13 -0
- package/src/lib/Controls/Inputs/inputControl.spec.ts +84 -0
- package/src/lib/Controls/Inputs/type.ts +3 -0
- package/src/lib/Controls/ObjectArray/DeleteDialog.tsx +49 -0
- package/src/lib/Controls/ObjectArray/ObjectArray.tsx +59 -0
- package/src/lib/Controls/ObjectArray/ObjectArrayToolBar.tsx +51 -0
- package/src/lib/Controls/ObjectArray/ObjectListControl.tsx +368 -0
- package/src/lib/Controls/ObjectArray/index.tsx +1 -0
- package/src/lib/Controls/ObjectArray/styled-components.tsx +13 -0
- package/src/lib/Controls/index.tsx +4 -0
- package/src/lib/ErrorHandling/GoAErrorControl.tsx +53 -0
- package/src/lib/ErrorHandling/MessageControl.tsx +19 -0
- package/src/lib/ErrorHandling/categorizationValidation.spec.ts +98 -0
- package/src/lib/ErrorHandling/controlValildation.spec.ts +57 -0
- package/src/lib/ErrorHandling/errorCheck.spec.ts +185 -0
- package/src/lib/ErrorHandling/errorCheck.tsx +86 -0
- package/src/lib/ErrorHandling/layoutValildation.spec.ts +47 -0
- package/src/lib/ErrorHandling/otherValildation.spec.ts +74 -0
- package/src/lib/ErrorHandling/schemaValidation.ts +115 -0
- package/src/lib/common/Grid.tsx +55 -0
- package/src/lib/jsonforms-components.module.scss +7 -0
- package/src/lib/jsonforms-components.spec.tsx +10 -0
- package/src/lib/jsonforms-components.tsx +14 -0
- package/src/lib/layouts/GroupControl.tsx +25 -0
- package/src/lib/layouts/HorizontalLayoutControl.tsx +30 -0
- package/src/lib/layouts/VerticalLayoutControl.tsx +28 -0
- package/src/lib/layouts/index.ts +3 -0
- package/src/lib/util/layout.tsx +68 -0
- package/src/lib/util/schemaUtils.ts +9 -0
- package/src/lib/util/stringUtils.ts +98 -0
- package/src/lib/util/style-component.ts +8 -0
- package/tsconfig.json +20 -0
- package/tsconfig.lib.json +19 -0
- package/tsconfig.spec.json +20 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import styled from 'styled-components';
|
|
2
|
+
|
|
3
|
+
export const HelpContentDiv = styled.div`
|
|
4
|
+
.parent-label {
|
|
5
|
+
font-size: 24px;
|
|
6
|
+
margin-bottom: 1rem;
|
|
7
|
+
font-weight: bold;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.child-label {
|
|
11
|
+
font-size: 18px;
|
|
12
|
+
margin-bottom: 0.5rem;
|
|
13
|
+
font-weight: bold;
|
|
14
|
+
}
|
|
15
|
+
.parent-margin {
|
|
16
|
+
margin-bottom: 1.5rem;
|
|
17
|
+
}
|
|
18
|
+
.child-margin {
|
|
19
|
+
margin-bottom: 0.25rem;
|
|
20
|
+
}
|
|
21
|
+
ul {
|
|
22
|
+
margin: 0 0 0 0.5rem;
|
|
23
|
+
}
|
|
24
|
+
.single-line {
|
|
25
|
+
margin: 0.25rem 0 0.25rem 0;
|
|
26
|
+
}
|
|
27
|
+
`;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { CellProps, isDateControl, RankedTester, rankWith, WithClassname } from '@jsonforms/core';
|
|
3
|
+
import { withJsonFormsCellProps } from '@jsonforms/react';
|
|
4
|
+
import { GoADateInput } from '../Controls';
|
|
5
|
+
|
|
6
|
+
export const GoADateCell = (props: CellProps & WithClassname) => <GoADateInput {...props} />;
|
|
7
|
+
|
|
8
|
+
export const GoADateCellTester: RankedTester = rankWith(1, isDateControl);
|
|
9
|
+
|
|
10
|
+
export default withJsonFormsCellProps(GoADateCell);
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { CellProps, isIntegerControl, RankedTester, rankWith, WithClassname } from '@jsonforms/core';
|
|
3
|
+
import { withJsonFormsCellProps } from '@jsonforms/react';
|
|
4
|
+
import { GoAInputInteger } from '../Controls';
|
|
5
|
+
|
|
6
|
+
export const GoAIntegerCell = (props: CellProps & WithClassname) => <GoAInputInteger {...props} />;
|
|
7
|
+
|
|
8
|
+
export const GoAIntegerCellTester: RankedTester = rankWith(1, isIntegerControl);
|
|
9
|
+
|
|
10
|
+
export default withJsonFormsCellProps(GoAIntegerCell);
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { CellProps, isNumberControl, RankedTester, rankWith, WithClassname } from '@jsonforms/core';
|
|
3
|
+
import { withJsonFormsCellProps } from '@jsonforms/react';
|
|
4
|
+
import { GoANumberInput } from '../Controls';
|
|
5
|
+
|
|
6
|
+
export const GoANumberCell = (props: CellProps & WithClassname) => <GoANumberInput {...props} />;
|
|
7
|
+
|
|
8
|
+
export const GoANumberCellTester: RankedTester = rankWith(1, isNumberControl);
|
|
9
|
+
|
|
10
|
+
export default withJsonFormsCellProps(GoANumberCell);
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { CellProps, isStringControl, RankedTester, rankWith, WithClassname } from '@jsonforms/core';
|
|
3
|
+
import { withJsonFormsCellProps } from '@jsonforms/react';
|
|
4
|
+
import { GoAInputText } from '../Controls';
|
|
5
|
+
|
|
6
|
+
export const GoATextCell = (props: CellProps & WithClassname) => <GoAInputText {...props} />;
|
|
7
|
+
|
|
8
|
+
export const GoATextCellTester: RankedTester = rankWith(1, isStringControl);
|
|
9
|
+
|
|
10
|
+
export default withJsonFormsCellProps(GoATextCell);
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { CellProps, isTimeControl, RankedTester, rankWith, WithClassname } from '@jsonforms/core';
|
|
3
|
+
import { withJsonFormsCellProps } from '@jsonforms/react';
|
|
4
|
+
import { GoADateInput } from '../Controls';
|
|
5
|
+
|
|
6
|
+
export const GoATimeCell = (props: CellProps & WithClassname) => <GoADateInput {...props} />;
|
|
7
|
+
|
|
8
|
+
export const GoATimeCellTester: RankedTester = rankWith(2, isTimeControl);
|
|
9
|
+
|
|
10
|
+
export default withJsonFormsCellProps(GoATimeCell);
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { GoATextCell, GoATextCellTester } from './TextCell';
|
|
2
|
+
import { GoADateCell, GoADateCellTester } from './DateCell';
|
|
3
|
+
import { GoATimeCell, GoATimeCellTester } from './TimeCell';
|
|
4
|
+
import { GoANumberCell, GoANumberCellTester } from './NumberCell';
|
|
5
|
+
import { GoAIntegerCell, GoAIntegerCellTester } from './IntegerCell';
|
|
6
|
+
import { withJsonFormsCellProps } from '@jsonforms/react';
|
|
7
|
+
import { JsonFormsCellRendererRegistryEntry } from '@jsonforms/core';
|
|
8
|
+
export const InputCells: JsonFormsCellRendererRegistryEntry[] = [
|
|
9
|
+
{ tester: GoATextCellTester, cell: withJsonFormsCellProps(GoATextCell) },
|
|
10
|
+
{ tester: GoADateCellTester, cell: withJsonFormsCellProps(GoADateCell) },
|
|
11
|
+
{ tester: GoATimeCellTester, cell: withJsonFormsCellProps(GoATimeCell) },
|
|
12
|
+
{ tester: GoANumberCellTester, cell: withJsonFormsCellProps(GoANumberCell) },
|
|
13
|
+
{ tester: GoAIntegerCellTester, cell: withJsonFormsCellProps(GoAIntegerCell) },
|
|
14
|
+
];
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import React, { createContext } from 'react';
|
|
2
|
+
import axios, { AxiosRequestConfig, AxiosStatic } from 'axios';
|
|
3
|
+
|
|
4
|
+
interface enumerators {
|
|
5
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
6
|
+
data: Map<string, () => any>;
|
|
7
|
+
functions: Map<string, () => (file: File, propertyId: string) => void>;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface AllData {
|
|
11
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
12
|
+
[x: string]: any;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const getAxiosInterceptorConfig = (axios: AxiosStatic): [number, AxiosStatic] => {
|
|
16
|
+
const requestId = axios.interceptors.request.use((req) => {
|
|
17
|
+
if (req.data === undefined) {
|
|
18
|
+
throw new Error(`The URL: ${req.url} encountered a CORS error.`);
|
|
19
|
+
}
|
|
20
|
+
return req;
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
return [requestId, axios];
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export function addDataByUrl(key: string, url: string, processDataFunction: (url: string) => string[], token?: string) {
|
|
27
|
+
let header = {} as AxiosRequestConfig<unknown>;
|
|
28
|
+
const [requestId, axiosWithConfig] = getAxiosInterceptorConfig(axios);
|
|
29
|
+
if (token) {
|
|
30
|
+
header = { ...header, ...{ Authorization: `Bearer ${token}` } };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
axiosWithConfig
|
|
34
|
+
.get(url, header)
|
|
35
|
+
.then((response) => {
|
|
36
|
+
const processedData = processDataFunction(response.data);
|
|
37
|
+
enumValues.set(key, () => processedData);
|
|
38
|
+
})
|
|
39
|
+
.catch((err: Error) => {
|
|
40
|
+
if (err.message.includes('CORS')) {
|
|
41
|
+
console.warn(err.message);
|
|
42
|
+
} else {
|
|
43
|
+
console.warn(`addDataByUrl: ${err.message}`);
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
axiosWithConfig.interceptors.request.eject(requestId);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function addDataByOptions(key: string, url: string, location: string[], type: string, values: string[]) {
|
|
50
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
51
|
+
const dataFunction = (data: any) => {
|
|
52
|
+
let dataLink = data;
|
|
53
|
+
let returnData = [''];
|
|
54
|
+
|
|
55
|
+
const locationArray = location && !Array.isArray(location) ? [location] : location;
|
|
56
|
+
locationArray?.forEach((attribute) => {
|
|
57
|
+
dataLink = dataLink[attribute];
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const valuesArray = Array.isArray(values) ? values : [values];
|
|
61
|
+
|
|
62
|
+
if (type === 'keys') {
|
|
63
|
+
returnData = Object.keys(dataLink);
|
|
64
|
+
} else {
|
|
65
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
66
|
+
returnData = dataLink.map((entry: any) => {
|
|
67
|
+
let parse = '';
|
|
68
|
+
valuesArray.forEach((v, index) => {
|
|
69
|
+
parse += `${entry[v]}`;
|
|
70
|
+
|
|
71
|
+
if (index < valuesArray.length - 1) {
|
|
72
|
+
parse += ' ';
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
return parse;
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return returnData;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
addDataByUrl(key, url, dataFunction);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
interface FileManagement {
|
|
86
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
87
|
+
fileList?: any;
|
|
88
|
+
uploadFile?: (file: File, propertyId: string) => void;
|
|
89
|
+
downloadFile?: (file: File) => void;
|
|
90
|
+
deleteFile?: (file: File) => void;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
type Props = {
|
|
94
|
+
children?: React.ReactNode;
|
|
95
|
+
fileManagement?: FileManagement;
|
|
96
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
97
|
+
data?: any;
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
101
|
+
const enumValues: Map<string, () => Record<string, any> | string[]> = new Map<string, () => Record<string, any>>();
|
|
102
|
+
const enumFunctions: Map<string, () => (file: File, propertyId: string) => void> = new Map<
|
|
103
|
+
string,
|
|
104
|
+
() => (file: File, propertyId: string) => void
|
|
105
|
+
>();
|
|
106
|
+
|
|
107
|
+
const baseEnumerator = {
|
|
108
|
+
data: enumValues,
|
|
109
|
+
functions: enumFunctions,
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
export const JsonFormContext = createContext(baseEnumerator);
|
|
113
|
+
|
|
114
|
+
export function ContextProvider(props: Props): JSX.Element | null {
|
|
115
|
+
const outerTheme = React.useContext(JsonFormContext);
|
|
116
|
+
|
|
117
|
+
if (props.fileManagement) {
|
|
118
|
+
const { fileList, uploadFile, downloadFile, deleteFile } = props.fileManagement;
|
|
119
|
+
|
|
120
|
+
/* eslint-disable @typescript-eslint/no-empty-function */
|
|
121
|
+
const uploadFileFunction = uploadFile ? uploadFile : () => {};
|
|
122
|
+
const downloadFileFunction = downloadFile ? downloadFile : () => {};
|
|
123
|
+
const deleteFileFunction = deleteFile ? deleteFile : () => {};
|
|
124
|
+
|
|
125
|
+
enumValues.set('file-list', () => fileList);
|
|
126
|
+
enumFunctions.set('upload-file', () => uploadFileFunction);
|
|
127
|
+
enumFunctions.set('download-file', () => downloadFileFunction);
|
|
128
|
+
enumFunctions.set('delete-file', () => deleteFileFunction);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (props.data) {
|
|
132
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
133
|
+
props.data?.forEach((item: any) => {
|
|
134
|
+
enumValues.set(Object.keys(item)[0], () => item);
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (!props.children) {
|
|
139
|
+
return null;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return <JsonFormContext.Provider value={baseEnumerator}>{props.children}</JsonFormContext.Provider>;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Grabs data stored under a given key
|
|
147
|
+
*
|
|
148
|
+
*/
|
|
149
|
+
export function getData(key: string) {
|
|
150
|
+
const dataFunction = baseEnumerator.data.get(key);
|
|
151
|
+
return dataFunction && dataFunction();
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Grabs all data
|
|
156
|
+
*
|
|
157
|
+
*/
|
|
158
|
+
export function getAllData() {
|
|
159
|
+
const allData: AllData = [];
|
|
160
|
+
baseEnumerator.data.forEach((d, key) => {
|
|
161
|
+
allData.push({ [key]: d() });
|
|
162
|
+
});
|
|
163
|
+
return allData;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Allows additional data to be added under a given key
|
|
167
|
+
*
|
|
168
|
+
* This data will then be available inside the context
|
|
169
|
+
*/
|
|
170
|
+
export function addData(key: string, data: Record<string, unknown> | unknown[]) {
|
|
171
|
+
enumValues.set(key, () => data);
|
|
172
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import React, { FC } from 'react';
|
|
2
|
+
import { GoAButton, GoAIconButton, GoAIconType } from '@abgov/react-components-new';
|
|
3
|
+
import styled from 'styled-components';
|
|
4
|
+
|
|
5
|
+
interface ContextMenuIconProps {
|
|
6
|
+
type: GoAIconType;
|
|
7
|
+
testId?: string;
|
|
8
|
+
title?: string;
|
|
9
|
+
onClick?: () => void;
|
|
10
|
+
disabled?: boolean;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface ContextMenuTextProps {
|
|
14
|
+
type?: GoAIconType;
|
|
15
|
+
testId?: string;
|
|
16
|
+
onClick?: () => void;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export const GoAContextMenuIcon: FC<ContextMenuIconProps> = (props) => {
|
|
20
|
+
return (
|
|
21
|
+
<GoAIconButton
|
|
22
|
+
icon={props.type}
|
|
23
|
+
onClick={props.onClick}
|
|
24
|
+
title={props.title}
|
|
25
|
+
testId={props.testId}
|
|
26
|
+
size="small"
|
|
27
|
+
disabled={props.disabled}
|
|
28
|
+
/>
|
|
29
|
+
);
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export const GoAContextMenuText: FC<ContextMenuTextProps> = (props) => {
|
|
33
|
+
return <GoAButton type="tertiary" onClick={props.onClick} testId={props.testId} size="compact" />;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export const GoAContextMenu = styled.div`
|
|
37
|
+
display: flex;
|
|
38
|
+
align-items: center;
|
|
39
|
+
background-color: #fff;
|
|
40
|
+
gap: 0.25rem;
|
|
41
|
+
|
|
42
|
+
> .goa-icon-button {
|
|
43
|
+
cursor: pointer;
|
|
44
|
+
border-radius: 0.25rem;
|
|
45
|
+
padding: 0.25rem;
|
|
46
|
+
}
|
|
47
|
+
> .goa-icon-button + .goa-icon-button {
|
|
48
|
+
margin-left: 0rem;
|
|
49
|
+
}
|
|
50
|
+
`;
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import React, { useContext, useEffect } from 'react';
|
|
2
|
+
import { GoAFileUploadInput, GoAFormItem, GoACircularProgress, GoAModal } from '@abgov/react-components-new';
|
|
3
|
+
import { WithClassname, ControlProps } from '@jsonforms/core';
|
|
4
|
+
|
|
5
|
+
import styled from 'styled-components';
|
|
6
|
+
import { JsonFormContext } from '../../Context';
|
|
7
|
+
|
|
8
|
+
import { GoAContextMenu, GoAContextMenuIcon } from './ContextMenu';
|
|
9
|
+
|
|
10
|
+
type FileUploaderLayoutRendererProps = ControlProps & WithClassname;
|
|
11
|
+
|
|
12
|
+
export const FileUploader = ({ data, path, handleChange, uischema, ...props }: FileUploaderLayoutRendererProps) => {
|
|
13
|
+
const enumerators = useContext(JsonFormContext);
|
|
14
|
+
const uploadTriggerFunction = enumerators.functions.get('upload-file');
|
|
15
|
+
const uploadTrigger = uploadTriggerFunction && uploadTriggerFunction();
|
|
16
|
+
const downloadTriggerFunction = enumerators.functions.get('download-file');
|
|
17
|
+
const downloadTrigger = downloadTriggerFunction && downloadTriggerFunction();
|
|
18
|
+
const deleteTriggerFunction = enumerators.functions.get('delete-file');
|
|
19
|
+
const deleteTrigger = deleteTriggerFunction && deleteTriggerFunction();
|
|
20
|
+
const fileListValue = enumerators.data.get('file-list');
|
|
21
|
+
// eslint-disable-next-line
|
|
22
|
+
const fileList = fileListValue && (fileListValue() as Record<string, any>);
|
|
23
|
+
const { required, label, i18nKeyPrefix } = props;
|
|
24
|
+
|
|
25
|
+
const propertyId = i18nKeyPrefix as string;
|
|
26
|
+
|
|
27
|
+
const variant = uischema?.options?.variant || 'button';
|
|
28
|
+
|
|
29
|
+
function uploadFile(file: File) {
|
|
30
|
+
if (uploadTrigger) {
|
|
31
|
+
handleChange(propertyId, ['Loading', Array.isArray(data) ? data[1] : data, file?.name]);
|
|
32
|
+
uploadTrigger(file, propertyId);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function downloadFile(file: File) {
|
|
36
|
+
if (downloadTrigger) {
|
|
37
|
+
downloadTrigger(file, propertyId);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
function deleteFile(file: File) {
|
|
41
|
+
if (deleteTrigger) {
|
|
42
|
+
deleteTrigger(file, propertyId);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
useEffect(() => {
|
|
47
|
+
// UseEffect is required because not having it causes a react update error, but
|
|
48
|
+
// it doesn't function correctly within jsonforms unless there is a minor delay here
|
|
49
|
+
const delayedFunction = () => {
|
|
50
|
+
if (fileList && Array.isArray(data) && data[1] !== fileList[propertyId]?.urn) {
|
|
51
|
+
handleChange(propertyId, fileList && fileList[propertyId]?.urn);
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const timeoutId = setTimeout(delayedFunction, 1);
|
|
56
|
+
return () => clearTimeout(timeoutId);
|
|
57
|
+
}, [data, handleChange, fileList, propertyId]);
|
|
58
|
+
|
|
59
|
+
return (
|
|
60
|
+
<FileUploaderStyle id="file-upload" className="FileUploader">
|
|
61
|
+
{required ? (
|
|
62
|
+
<GoAFormItem label={label} requirement="required"></GoAFormItem>
|
|
63
|
+
) : (
|
|
64
|
+
<div className="label">{props.label}</div>
|
|
65
|
+
)}
|
|
66
|
+
|
|
67
|
+
<div className="file-upload">
|
|
68
|
+
<GoAFileUploadInput variant={variant} onSelectFile={uploadFile} />
|
|
69
|
+
</div>
|
|
70
|
+
<div>
|
|
71
|
+
{Array.isArray(data) && data[0] === 'Loading' ? (
|
|
72
|
+
<GoAModal open={Array.isArray(data) && data[0] === 'Loading'}>
|
|
73
|
+
<div className="align-center">
|
|
74
|
+
<GoACircularProgress visible={true} message={`Uploading ${data[2]}`} size="large" />
|
|
75
|
+
</div>
|
|
76
|
+
</GoAModal>
|
|
77
|
+
) : (
|
|
78
|
+
<div>
|
|
79
|
+
{fileList && fileList[props.i18nKeyPrefix as string] && (
|
|
80
|
+
<AttachmentBorder>
|
|
81
|
+
<div>{fileList && fileList[props.i18nKeyPrefix as string].filename}</div>
|
|
82
|
+
<GoAContextMenu>
|
|
83
|
+
<GoAContextMenuIcon
|
|
84
|
+
testId="download-icon"
|
|
85
|
+
title="Download"
|
|
86
|
+
type="download"
|
|
87
|
+
onClick={() => downloadFile(fileList && fileList[props.i18nKeyPrefix as string])}
|
|
88
|
+
/>
|
|
89
|
+
<GoAContextMenuIcon
|
|
90
|
+
data-testid="delete-icon"
|
|
91
|
+
title="Delete"
|
|
92
|
+
type="trash"
|
|
93
|
+
onClick={() => deleteFile(fileList && fileList[props.i18nKeyPrefix as string])}
|
|
94
|
+
/>
|
|
95
|
+
</GoAContextMenu>
|
|
96
|
+
</AttachmentBorder>
|
|
97
|
+
)}
|
|
98
|
+
</div>
|
|
99
|
+
)}
|
|
100
|
+
</div>
|
|
101
|
+
</FileUploaderStyle>
|
|
102
|
+
);
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
const AttachmentBorder = styled.div`
|
|
106
|
+
display: flex;
|
|
107
|
+
flex-direction: row;
|
|
108
|
+
border: 1px solid #dcdcdc;
|
|
109
|
+
border-radius: 0.25rem;
|
|
110
|
+
padding: 0.5rem;
|
|
111
|
+
width: fit-content;
|
|
112
|
+
margin-top: 5px;
|
|
113
|
+
`;
|
|
114
|
+
|
|
115
|
+
const FileUploaderStyle = styled.div`
|
|
116
|
+
.label {
|
|
117
|
+
display: block;
|
|
118
|
+
font-weight: var(--goa-font-weight-bold);
|
|
119
|
+
color: var(--goa-color-text-default);
|
|
120
|
+
font-size: var(--goa-font-size-4);
|
|
121
|
+
padding: 0 0 0.5rem 0;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.align-center {
|
|
125
|
+
text-align-last: center;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
.file-upload {
|
|
129
|
+
margin-bottom: 0.5rem;
|
|
130
|
+
}
|
|
131
|
+
`;
|