@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,269 @@
|
|
|
1
|
+
import React, { useMemo } from 'react';
|
|
2
|
+
import { useState, useEffect } from 'react';
|
|
3
|
+
import { GoAFormStepper, GoAFormStep, GoAPages, GoAButton } from '@abgov/react-components-new';
|
|
4
|
+
import {
|
|
5
|
+
Categorization,
|
|
6
|
+
UISchemaElement,
|
|
7
|
+
deriveLabelForUISchemaElement,
|
|
8
|
+
Category,
|
|
9
|
+
StatePropsOfLayout,
|
|
10
|
+
isVisible,
|
|
11
|
+
isEnabled,
|
|
12
|
+
} from '@jsonforms/core';
|
|
13
|
+
|
|
14
|
+
import { TranslateProps, withJsonFormsLayoutProps, withTranslateProps, useJsonForms } from '@jsonforms/react';
|
|
15
|
+
import { AjvProps, withAjvProps } from '@jsonforms/material-renderers';
|
|
16
|
+
import { JsonFormsDispatch } from '@jsonforms/react';
|
|
17
|
+
import { Hidden } from '@mui/material';
|
|
18
|
+
import { Grid, GridItem } from '../../common/Grid';
|
|
19
|
+
import { ReviewItem, ReviewListItem, ReviewListWrapper } from './styled-components';
|
|
20
|
+
|
|
21
|
+
export interface CategorizationStepperLayoutRendererProps extends StatePropsOfLayout, AjvProps, TranslateProps {
|
|
22
|
+
// eslint-disable-next-line
|
|
23
|
+
data: any;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const FormStepper = ({
|
|
27
|
+
uischema,
|
|
28
|
+
data,
|
|
29
|
+
schema,
|
|
30
|
+
// eslint-disable-next-line
|
|
31
|
+
ajv,
|
|
32
|
+
path,
|
|
33
|
+
cells,
|
|
34
|
+
renderers,
|
|
35
|
+
config,
|
|
36
|
+
visible,
|
|
37
|
+
enabled,
|
|
38
|
+
t,
|
|
39
|
+
}: CategorizationStepperLayoutRendererProps) => {
|
|
40
|
+
const categorization = uischema as Categorization;
|
|
41
|
+
const [step, setStep] = useState(0);
|
|
42
|
+
const [isFormValid, setIsFormValid] = useState(false);
|
|
43
|
+
const [showNextBtn, setShowNextBtn] = useState(true);
|
|
44
|
+
const categories = useMemo(
|
|
45
|
+
() => categorization.elements.filter((category) => isVisible(category, data, '', ajv)),
|
|
46
|
+
[categorization, data, ajv]
|
|
47
|
+
);
|
|
48
|
+
const disabledCategoryMap: boolean[] = categories.map((c) => !isEnabled(c, data, '', ajv));
|
|
49
|
+
const handleSubmit = () => {
|
|
50
|
+
console.log('submitted', data);
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const CategoryLabels = useMemo(() => {
|
|
54
|
+
return categories.map((e: Category | Categorization) => deriveLabelForUISchemaElement(e, t));
|
|
55
|
+
}, [categories, t]);
|
|
56
|
+
|
|
57
|
+
useEffect(() => {}, [categories.length]);
|
|
58
|
+
|
|
59
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
60
|
+
const vslidateFormData = (formData: Array<UISchemaElement>) => {
|
|
61
|
+
const validate = ajv.compile(schema);
|
|
62
|
+
return validate(formData);
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
useEffect(() => {
|
|
66
|
+
const valid = vslidateFormData(data);
|
|
67
|
+
setIsFormValid(valid);
|
|
68
|
+
}, [data, vslidateFormData]);
|
|
69
|
+
|
|
70
|
+
if (categories?.length < 1) {
|
|
71
|
+
// eslint-disable-next-line
|
|
72
|
+
return <></>;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function nextPage(page: number, disabled: boolean[]) {
|
|
76
|
+
page++;
|
|
77
|
+
while (page <= disabled.length && disabled[page - 1]) {
|
|
78
|
+
page++;
|
|
79
|
+
}
|
|
80
|
+
setPage(page);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function prevPage(page: number, disabled: boolean[]) {
|
|
84
|
+
page--;
|
|
85
|
+
while (page >= 0 && disabled[page - 1]) {
|
|
86
|
+
page--;
|
|
87
|
+
}
|
|
88
|
+
setPage(page);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function setPage(page: number) {
|
|
92
|
+
setStep(page);
|
|
93
|
+
if (page < 1 || page > categories.length + 1) return;
|
|
94
|
+
if (categories.length + 1 === page) {
|
|
95
|
+
setShowNextBtn(false);
|
|
96
|
+
} else {
|
|
97
|
+
setShowNextBtn(true);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
101
|
+
useEffect(() => {
|
|
102
|
+
setStep(0);
|
|
103
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
104
|
+
}, []);
|
|
105
|
+
const renderStepElements = (category: Category | Categorization, indexOfCategory: number) => {
|
|
106
|
+
return (
|
|
107
|
+
/*
|
|
108
|
+
[Mar-04-2024][Paul Li] the GoAPages internal state cannot handle the hidden/display well. We need extra hide/display control to it appropriately.
|
|
109
|
+
*/
|
|
110
|
+
<Hidden xsUp={indexOfCategory !== step - 1}>
|
|
111
|
+
{category.elements.map((elementUiSchema, index) => {
|
|
112
|
+
return (
|
|
113
|
+
<JsonFormsDispatch
|
|
114
|
+
key={index}
|
|
115
|
+
schema={schema}
|
|
116
|
+
uischema={elementUiSchema}
|
|
117
|
+
renderers={renderers}
|
|
118
|
+
cells={cells}
|
|
119
|
+
path={path}
|
|
120
|
+
visible={visible}
|
|
121
|
+
enabled={enabled && !disabledCategoryMap[indexOfCategory]}
|
|
122
|
+
/>
|
|
123
|
+
);
|
|
124
|
+
})}
|
|
125
|
+
</Hidden>
|
|
126
|
+
);
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
return (
|
|
130
|
+
<Hidden xsUp={!visible}>
|
|
131
|
+
<div id={`${path || `goa`}-form-stepper`} className="formStepper">
|
|
132
|
+
<GoAFormStepper
|
|
133
|
+
testId="form-stepper-test"
|
|
134
|
+
step={step}
|
|
135
|
+
onChange={(step) => {
|
|
136
|
+
setPage(step);
|
|
137
|
+
}}
|
|
138
|
+
>
|
|
139
|
+
{categories?.map((category, index) => {
|
|
140
|
+
return (
|
|
141
|
+
<GoAFormStep
|
|
142
|
+
key={`${CategoryLabels[index]}-tab`}
|
|
143
|
+
text={`${CategoryLabels[index]}${disabledCategoryMap[index] ? ' (disabled)' : ''}`}
|
|
144
|
+
status={'incomplete'}
|
|
145
|
+
/>
|
|
146
|
+
);
|
|
147
|
+
})}
|
|
148
|
+
<GoAFormStep text="Review" status="incomplete" />
|
|
149
|
+
</GoAFormStepper>
|
|
150
|
+
<GoAPages current={step} mb="xl">
|
|
151
|
+
{categories?.map((category, index) => {
|
|
152
|
+
return (
|
|
153
|
+
<div data-testid={`step_${index}-content`} key={`${CategoryLabels[index]}`}>
|
|
154
|
+
{renderStepElements(category, index)}
|
|
155
|
+
</div>
|
|
156
|
+
);
|
|
157
|
+
})}
|
|
158
|
+
<div>
|
|
159
|
+
<h3 style={{ flex: 1 }}>Summary</h3>
|
|
160
|
+
|
|
161
|
+
<ReviewItem>
|
|
162
|
+
<div style={{ width: '100%' }}>
|
|
163
|
+
{data && Object.keys(data)?.length > 0 && (
|
|
164
|
+
<Grid>
|
|
165
|
+
{Object.keys(flattenObject(data)).map((key, ix) => {
|
|
166
|
+
return (
|
|
167
|
+
<GridItem key={ix} md={6} vSpacing={1} hSpacing={0.5}>
|
|
168
|
+
<b>{key}</b> : <PreventControlElement key={ix} value={flattenObject(data)[key]} />
|
|
169
|
+
</GridItem>
|
|
170
|
+
);
|
|
171
|
+
})}
|
|
172
|
+
</Grid>
|
|
173
|
+
)}
|
|
174
|
+
</div>
|
|
175
|
+
</ReviewItem>
|
|
176
|
+
</div>
|
|
177
|
+
</GoAPages>
|
|
178
|
+
{step && step !== 0 && (
|
|
179
|
+
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
|
180
|
+
{step !== 1 ? (
|
|
181
|
+
<GoAButton
|
|
182
|
+
type="secondary"
|
|
183
|
+
disabled={disabledCategoryMap[step - 1] || !enabled}
|
|
184
|
+
onClick={() => prevPage(step, disabledCategoryMap)}
|
|
185
|
+
>
|
|
186
|
+
Previous
|
|
187
|
+
</GoAButton>
|
|
188
|
+
) : (
|
|
189
|
+
<div></div>
|
|
190
|
+
)}
|
|
191
|
+
{step !== null && showNextBtn && (
|
|
192
|
+
<GoAButton
|
|
193
|
+
type="primary"
|
|
194
|
+
disabled={disabledCategoryMap[step - 1] || !enabled}
|
|
195
|
+
onClick={() => nextPage(step, disabledCategoryMap)}
|
|
196
|
+
>
|
|
197
|
+
Next
|
|
198
|
+
</GoAButton>
|
|
199
|
+
)}
|
|
200
|
+
{!showNextBtn && (
|
|
201
|
+
<div>
|
|
202
|
+
<GoAButton type="primary" onClick={handleSubmit} disabled={!isFormValid || !enabled}>
|
|
203
|
+
Submit
|
|
204
|
+
</GoAButton>
|
|
205
|
+
</div>
|
|
206
|
+
)}
|
|
207
|
+
</div>
|
|
208
|
+
)}
|
|
209
|
+
</div>
|
|
210
|
+
</Hidden>
|
|
211
|
+
);
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
interface PreventControlElement {
|
|
215
|
+
value: unknown;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const PreventControlElement = (props: PreventControlElement): JSX.Element => {
|
|
219
|
+
if (typeof props?.value === 'string') return <span>{props.value}</span>;
|
|
220
|
+
|
|
221
|
+
if (Array.isArray(props?.value)) {
|
|
222
|
+
return (
|
|
223
|
+
<div>
|
|
224
|
+
{props.value.map((item, index) => {
|
|
225
|
+
return (
|
|
226
|
+
<ReviewListWrapper key={index}>
|
|
227
|
+
{item &&
|
|
228
|
+
Object.keys(item).map((key, innerIndex) => {
|
|
229
|
+
if (typeof item[key] === 'string') {
|
|
230
|
+
return (
|
|
231
|
+
<ReviewListItem key={innerIndex}>
|
|
232
|
+
{key}: {item[key]}
|
|
233
|
+
</ReviewListItem>
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
return (
|
|
237
|
+
<ReviewListItem key={innerIndex}>
|
|
238
|
+
{key}: {String(item[key])}
|
|
239
|
+
</ReviewListItem>
|
|
240
|
+
);
|
|
241
|
+
})}
|
|
242
|
+
</ReviewListWrapper>
|
|
243
|
+
);
|
|
244
|
+
})}
|
|
245
|
+
</div>
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// eslint-disable-next-line
|
|
250
|
+
return <></>;
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
export const flattenObject = (obj: Record<string, string>): Record<string, string> => {
|
|
254
|
+
const flattened = {} as Record<string, string>;
|
|
255
|
+
|
|
256
|
+
Object.keys(obj || {}).forEach((key) => {
|
|
257
|
+
const value = obj[key];
|
|
258
|
+
|
|
259
|
+
if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
|
|
260
|
+
Object.assign(flattened, flattenObject(value));
|
|
261
|
+
} else {
|
|
262
|
+
flattened[key] = value;
|
|
263
|
+
}
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
return flattened;
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
export const FormStepperControl = withAjvProps(withTranslateProps(withJsonFormsLayoutProps(FormStepper)));
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { rankWith, RankedTester, uiTypeIs, and, optionIs, UISchemaElement, isCategorization } from '@jsonforms/core';
|
|
2
|
+
|
|
3
|
+
// Ensure that all children (Category) have valid elements or things tend
|
|
4
|
+
// to blow up. If not, the the error control will report the problem.
|
|
5
|
+
const categoriesAreValid = (uischema: UISchemaElement): boolean => {
|
|
6
|
+
let isValid = true;
|
|
7
|
+
if ('type' in uischema && uischema.type === 'Categorization' && 'elements' in uischema) {
|
|
8
|
+
(uischema.elements as UISchemaElement[]).forEach((e) => {
|
|
9
|
+
if (e.type !== 'Category' || !('elements' in e)) {
|
|
10
|
+
isValid = false;
|
|
11
|
+
}
|
|
12
|
+
});
|
|
13
|
+
} else {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
return isValid;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export const CategorizationRendererTester: RankedTester = rankWith(
|
|
20
|
+
2,
|
|
21
|
+
and(uiTypeIs('Categorization'), categoriesAreValid, optionIs('variant', 'stepper'))
|
|
22
|
+
);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import styled from 'styled-components';
|
|
2
|
+
|
|
3
|
+
export const ReviewItem = styled.div`
|
|
4
|
+
display: flex;
|
|
5
|
+
border: 1px solid grey;
|
|
6
|
+
border-radius: 5px;
|
|
7
|
+
margin: 5px;
|
|
8
|
+
padding: 10px;
|
|
9
|
+
`;
|
|
10
|
+
|
|
11
|
+
export const ReviewListItem = styled.div`
|
|
12
|
+
margin-left: 1rem;
|
|
13
|
+
`;
|
|
14
|
+
|
|
15
|
+
export const ReviewListWrapper = styled.div`
|
|
16
|
+
margin-bottom: 1rem;
|
|
17
|
+
`;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import React, { useContext } from 'react';
|
|
2
|
+
import { GoAFormItem } from '@abgov/react-components-new';
|
|
3
|
+
import { ControlProps } from '@jsonforms/core';
|
|
4
|
+
import { Hidden } from '@mui/material';
|
|
5
|
+
import { checkFieldValidity, getLabelText } from '../../util/stringUtils';
|
|
6
|
+
export type GoAInputType =
|
|
7
|
+
| 'text'
|
|
8
|
+
| 'password'
|
|
9
|
+
| 'email'
|
|
10
|
+
| 'number'
|
|
11
|
+
| 'date'
|
|
12
|
+
| 'datetime-local'
|
|
13
|
+
| 'month'
|
|
14
|
+
| 'range'
|
|
15
|
+
| 'search'
|
|
16
|
+
| 'tel'
|
|
17
|
+
| 'time'
|
|
18
|
+
| 'url'
|
|
19
|
+
| 'week';
|
|
20
|
+
|
|
21
|
+
export interface WithInput {
|
|
22
|
+
//eslint-disable-next-line
|
|
23
|
+
input: any;
|
|
24
|
+
noLabel?: boolean;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const GoAInputBaseControl = (props: ControlProps & WithInput): JSX.Element => {
|
|
28
|
+
// eslint-disable-next-line
|
|
29
|
+
const { id, description, errors, uischema, visible, config, label, input, required } = props;
|
|
30
|
+
const isValid = errors.length === 0;
|
|
31
|
+
const InnerComponent = input;
|
|
32
|
+
const labelToUpdate: string = getLabelText(uischema.scope, label || '');
|
|
33
|
+
|
|
34
|
+
let modifiedErrors = checkFieldValidity(props as ControlProps);
|
|
35
|
+
|
|
36
|
+
if (modifiedErrors === 'should be equal to one of the allowed values' && uischema?.options?.enumContext) {
|
|
37
|
+
modifiedErrors = '';
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return (
|
|
41
|
+
<Hidden xsUp={!visible}>
|
|
42
|
+
<GoAFormItem
|
|
43
|
+
requirement={required ? 'required' : undefined}
|
|
44
|
+
error={modifiedErrors}
|
|
45
|
+
label={props?.noLabel === true ? '' : labelToUpdate}
|
|
46
|
+
helpText={typeof uischema?.options?.help === 'string' ? uischema?.options?.help : ''}
|
|
47
|
+
>
|
|
48
|
+
<InnerComponent {...props} />
|
|
49
|
+
</GoAFormItem>
|
|
50
|
+
</Hidden>
|
|
51
|
+
);
|
|
52
|
+
};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { isBooleanControl, RankedTester, rankWith, ControlProps, isDescriptionHidden } from '@jsonforms/core';
|
|
3
|
+
import { withJsonFormsControlProps } from '@jsonforms/react';
|
|
4
|
+
import { Hidden } from '@mui/material';
|
|
5
|
+
import { GoACheckbox } from '@abgov/react-components-new';
|
|
6
|
+
import { GoAInputBaseControl } from './InputBaseControl';
|
|
7
|
+
import { checkFieldValidity } from '../../util/stringUtils';
|
|
8
|
+
|
|
9
|
+
export const BooleanComponent = ({
|
|
10
|
+
data,
|
|
11
|
+
visible,
|
|
12
|
+
enabled,
|
|
13
|
+
uischema,
|
|
14
|
+
handleChange,
|
|
15
|
+
path,
|
|
16
|
+
config,
|
|
17
|
+
label,
|
|
18
|
+
required,
|
|
19
|
+
errors,
|
|
20
|
+
description,
|
|
21
|
+
}: ControlProps) => {
|
|
22
|
+
const appliedUiSchemaOptions = { ...config, ...uischema.options };
|
|
23
|
+
|
|
24
|
+
const showDescription = !isDescriptionHidden(
|
|
25
|
+
visible,
|
|
26
|
+
description,
|
|
27
|
+
false,
|
|
28
|
+
appliedUiSchemaOptions.showUnfocusedDescription
|
|
29
|
+
);
|
|
30
|
+
const errorsFormInput = checkFieldValidity({
|
|
31
|
+
data,
|
|
32
|
+
uischema,
|
|
33
|
+
label,
|
|
34
|
+
required,
|
|
35
|
+
errors,
|
|
36
|
+
} as ControlProps);
|
|
37
|
+
|
|
38
|
+
let text = label + (required ? ' (required)' : '');
|
|
39
|
+
|
|
40
|
+
if (label && description) {
|
|
41
|
+
text = description;
|
|
42
|
+
if (required) {
|
|
43
|
+
text = `${description} ` + (required ? ' (required)' : '');
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return (
|
|
48
|
+
<GoACheckbox
|
|
49
|
+
error={errorsFormInput.length > 0}
|
|
50
|
+
testId={`${path}-checkbox-test-id`}
|
|
51
|
+
disabled={!enabled}
|
|
52
|
+
text={text}
|
|
53
|
+
name={`${path}`}
|
|
54
|
+
checked={data}
|
|
55
|
+
onChange={(name: string, checked: boolean, value: string) => {
|
|
56
|
+
handleChange(path, checked);
|
|
57
|
+
}}
|
|
58
|
+
{...uischema?.options?.componentProps}
|
|
59
|
+
></GoACheckbox>
|
|
60
|
+
);
|
|
61
|
+
};
|
|
62
|
+
export const BooleanControl = (props: ControlProps) => (
|
|
63
|
+
<GoAInputBaseControl {...{ ...props, noLabel: true }} input={BooleanComponent} />
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
export const GoABooleanControlTester: RankedTester = rankWith(2, isBooleanControl);
|
|
67
|
+
export const GoABooleanControl = withJsonFormsControlProps(BooleanControl);
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { isBooleanControl, RankedTester, rankWith, ControlProps, optionIs, and } from '@jsonforms/core';
|
|
3
|
+
import { withJsonFormsControlProps } from '@jsonforms/react';
|
|
4
|
+
import { Hidden } from '@mui/material';
|
|
5
|
+
import { GoARadioGroup, GoARadioItem, GoAFormItem } from '@abgov/react-components-new';
|
|
6
|
+
import { GoAInputBaseControl } from './InputBaseControl';
|
|
7
|
+
import { checkFieldValidity, getLabelText } from '../../util/stringUtils';
|
|
8
|
+
|
|
9
|
+
export const BooleanRadioComponent = ({
|
|
10
|
+
data,
|
|
11
|
+
visible,
|
|
12
|
+
enabled,
|
|
13
|
+
uischema,
|
|
14
|
+
handleChange,
|
|
15
|
+
path,
|
|
16
|
+
config,
|
|
17
|
+
label,
|
|
18
|
+
required,
|
|
19
|
+
errors,
|
|
20
|
+
description,
|
|
21
|
+
}: ControlProps) => {
|
|
22
|
+
const appliedUiSchemaOptions = { ...config, ...uischema?.options };
|
|
23
|
+
const TrueValue = appliedUiSchemaOptions?.textForTrue || 'Yes';
|
|
24
|
+
const FalseValue = appliedUiSchemaOptions?.textForFalse || 'No';
|
|
25
|
+
const EnableDescription = appliedUiSchemaOptions?.enableDescription === true;
|
|
26
|
+
const TrueDescription = description || appliedUiSchemaOptions?.descriptionForTrue;
|
|
27
|
+
const FalseDescription = description || appliedUiSchemaOptions?.descriptionForFalse;
|
|
28
|
+
const BaseTestId = appliedUiSchemaOptions?.testId || `${path}-boolean-radio-jsonform`;
|
|
29
|
+
const errorsFormInput = checkFieldValidity({
|
|
30
|
+
data,
|
|
31
|
+
uischema,
|
|
32
|
+
label,
|
|
33
|
+
required,
|
|
34
|
+
errors,
|
|
35
|
+
} as ControlProps);
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<Hidden xsUp={!visible}>
|
|
39
|
+
<GoARadioGroup
|
|
40
|
+
error={errorsFormInput.length > 0}
|
|
41
|
+
name={`${label}`}
|
|
42
|
+
value={data === true ? TrueValue : data === false ? FalseValue : null}
|
|
43
|
+
disabled={!enabled}
|
|
44
|
+
testId={BaseTestId}
|
|
45
|
+
onChange={(_name, value) => {
|
|
46
|
+
if (value === TrueValue) {
|
|
47
|
+
handleChange(path, true);
|
|
48
|
+
}
|
|
49
|
+
if (value === FalseValue) {
|
|
50
|
+
handleChange(path, false);
|
|
51
|
+
}
|
|
52
|
+
}}
|
|
53
|
+
{...uischema?.options?.componentProps}
|
|
54
|
+
>
|
|
55
|
+
<GoARadioItem
|
|
56
|
+
value={TrueValue}
|
|
57
|
+
testId={`${BaseTestId}-yes-option`}
|
|
58
|
+
description={EnableDescription ? TrueDescription : null}
|
|
59
|
+
/>
|
|
60
|
+
<GoARadioItem
|
|
61
|
+
value={FalseValue}
|
|
62
|
+
testId={`${BaseTestId}-no-option`}
|
|
63
|
+
description={EnableDescription ? FalseDescription : null}
|
|
64
|
+
/>
|
|
65
|
+
</GoARadioGroup>
|
|
66
|
+
</Hidden>
|
|
67
|
+
);
|
|
68
|
+
};
|
|
69
|
+
export const BooleanRadioControl = (props: ControlProps) => (
|
|
70
|
+
<GoAInputBaseControl {...{ ...props }} input={BooleanRadioComponent} />
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
export const GoABooleanRadioControlTester: RankedTester = rankWith(3, and(isBooleanControl, optionIs('radio', true)));
|
|
74
|
+
export const GoABooleanRadioControl = withJsonFormsControlProps(BooleanRadioControl);
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { CellProps, WithClassname, ControlProps, isDateControl, RankedTester, rankWith } from '@jsonforms/core';
|
|
3
|
+
import { GoAInputDate } from '@abgov/react-components-new';
|
|
4
|
+
import { WithInputProps } from './type';
|
|
5
|
+
import { withJsonFormsControlProps } from '@jsonforms/react';
|
|
6
|
+
import { GoAInputBaseControl } from './InputBaseControl';
|
|
7
|
+
import { checkFieldValidity, getLabelText, isValidDate } from '../../util/stringUtils';
|
|
8
|
+
import { MessageControl } from '../../ErrorHandling/MessageControl';
|
|
9
|
+
|
|
10
|
+
export type GoAInputDateProps = CellProps & WithClassname & WithInputProps;
|
|
11
|
+
export const errMalformedDate = (scope: string, type: string): string => {
|
|
12
|
+
return `${type}-date for variable '${scope}' has an incorrect format.`;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const standardizeDate = (date: Date | string): string | undefined => {
|
|
16
|
+
try {
|
|
17
|
+
const stdDate = new Date(date).toISOString().substring(0, 10);
|
|
18
|
+
return stdDate;
|
|
19
|
+
} catch (e) {
|
|
20
|
+
const err = e as Error;
|
|
21
|
+
return undefined;
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const isValidDateFormat = (date: string): boolean => {
|
|
26
|
+
const standardized = standardizeDate(date);
|
|
27
|
+
return standardized !== undefined;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const invalidDateFormat = (scope: string, type: string): JSX.Element => {
|
|
31
|
+
return MessageControl(errMalformedDate(scope, type));
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const reformatDateProps = (props: object): object => {
|
|
35
|
+
if (props) {
|
|
36
|
+
if ('min' in props && typeof props.min === 'string') {
|
|
37
|
+
props['min'] = standardizeDate(props.min);
|
|
38
|
+
}
|
|
39
|
+
if ('max' in props && typeof props.max === 'string') {
|
|
40
|
+
props['max'] = standardizeDate(props.max as string);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return props;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export const GoADateInput = (props: GoAInputDateProps): JSX.Element => {
|
|
47
|
+
const { data, config, id, enabled, uischema, path, handleChange, label } = props;
|
|
48
|
+
const appliedUiSchemaOptions = { ...config, ...uischema?.options };
|
|
49
|
+
|
|
50
|
+
const minDate = uischema?.options?.componentProps?.min;
|
|
51
|
+
if (minDate && !isValidDateFormat(minDate)) {
|
|
52
|
+
return invalidDateFormat(uischema.scope, 'Min');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const maxDate = uischema?.options?.componentProps?.max;
|
|
56
|
+
if (maxDate && !isValidDateFormat(maxDate)) {
|
|
57
|
+
return invalidDateFormat(uischema.scope, 'Max');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return (
|
|
61
|
+
<GoAInputDate
|
|
62
|
+
error={checkFieldValidity(props as ControlProps).length > 0}
|
|
63
|
+
width="100%"
|
|
64
|
+
name={appliedUiSchemaOptions?.name || `${id || label}-input`}
|
|
65
|
+
value={standardizeDate(data) || ''}
|
|
66
|
+
testId={appliedUiSchemaOptions?.testId || `${id}-input`}
|
|
67
|
+
disabled={!enabled}
|
|
68
|
+
// Don't use handleChange in the onChange event, use the keyPress or onBlur.
|
|
69
|
+
// If you use it onChange along with keyPress event it will cause a
|
|
70
|
+
// side effect that causes the validation to render when it shouldn't.
|
|
71
|
+
onChange={(name, value) => {}}
|
|
72
|
+
onKeyPress={(name: string, value: Date | string, key: string) => {
|
|
73
|
+
if (!(key === 'Tab' || key === 'Shift')) {
|
|
74
|
+
value = standardizeDate(value) || '';
|
|
75
|
+
handleChange(path, value);
|
|
76
|
+
}
|
|
77
|
+
}}
|
|
78
|
+
onBlur={(name: string, value: Date | string) => {
|
|
79
|
+
value = standardizeDate(value) || '';
|
|
80
|
+
handleChange(path, value);
|
|
81
|
+
}}
|
|
82
|
+
{...reformatDateProps(uischema?.options?.componentProps)}
|
|
83
|
+
/>
|
|
84
|
+
);
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
export const GoADateControl = (props: ControlProps) => <GoAInputBaseControl {...props} input={GoADateInput} />;
|
|
88
|
+
|
|
89
|
+
export const GoADateControlTester: RankedTester = rankWith(4, isDateControl);
|
|
90
|
+
export const GoAInputDateControl = withJsonFormsControlProps(GoADateControl);
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { CellProps, WithClassname, ControlProps, isDateTimeControl, RankedTester, rankWith } from '@jsonforms/core';
|
|
3
|
+
import { GoAInputDateTime } from '@abgov/react-components-new';
|
|
4
|
+
import { WithInputProps } from './type';
|
|
5
|
+
import { withJsonFormsControlProps } from '@jsonforms/react';
|
|
6
|
+
import { GoAInputBaseControl } from './InputBaseControl';
|
|
7
|
+
import { checkFieldValidity, isValidDate } from '../../util/stringUtils';
|
|
8
|
+
type GoAInputDateTimeProps = CellProps & WithClassname & WithInputProps;
|
|
9
|
+
|
|
10
|
+
export const GoADateTimeInput = (props: GoAInputDateTimeProps): JSX.Element => {
|
|
11
|
+
// eslint-disable-next-line
|
|
12
|
+
const { data, config, id, enabled, uischema, isValid, path, errors, handleChange, schema, label } = props;
|
|
13
|
+
|
|
14
|
+
const appliedUiSchemaOptions = { ...config, ...uischema?.options };
|
|
15
|
+
|
|
16
|
+
return (
|
|
17
|
+
<GoAInputDateTime
|
|
18
|
+
error={checkFieldValidity(props as ControlProps).length > 0}
|
|
19
|
+
width="100%"
|
|
20
|
+
name={appliedUiSchemaOptions?.name || `${id || label}-input`}
|
|
21
|
+
value={data ? new Date(data).toISOString() : ''}
|
|
22
|
+
testId={appliedUiSchemaOptions?.testId || `${id}-input`}
|
|
23
|
+
disabled={!enabled}
|
|
24
|
+
// Dont use handleChange in the onChange event, use the keyPress or onBlur.
|
|
25
|
+
// If you use it onChange along with keyPress event it will cause a
|
|
26
|
+
// side effect that causes the validation to render when it shouldnt.
|
|
27
|
+
onChange={(name, value) => {}}
|
|
28
|
+
onKeyPress={(name: string, value: string, key: string) => {
|
|
29
|
+
if (!(key === 'Tab' || key === 'Shift')) {
|
|
30
|
+
value = isValidDate(value) ? new Date(value)?.toISOString() : '';
|
|
31
|
+
handleChange(path, value);
|
|
32
|
+
}
|
|
33
|
+
}}
|
|
34
|
+
onBlur={(name: string, value: string) => {
|
|
35
|
+
value = isValidDate(value) ? new Date(value).toISOString() : '';
|
|
36
|
+
handleChange(path, value);
|
|
37
|
+
}}
|
|
38
|
+
{...uischema?.options?.componentProps}
|
|
39
|
+
/>
|
|
40
|
+
);
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export const GoADateTimeControl = (props: ControlProps) => <GoAInputBaseControl {...props} input={GoADateTimeInput} />;
|
|
44
|
+
|
|
45
|
+
export const GoADateTimeControlTester: RankedTester = rankWith(2, isDateTimeControl);
|
|
46
|
+
export const GoAInputDateTimeControl = withJsonFormsControlProps(GoADateTimeControl);
|