This is an automated email from the ASF dual-hosted git repository.
ljmotta pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-kie-tools.git
The following commit(s) were added to refs/heads/main by this push:
new c243a16dbf5 NO-ISSUE: `form-code-generator-patternfly-theme` doesn't
support nested ListField (#2958)
c243a16dbf5 is described below
commit c243a16dbf5384d20013f6bf238ccf3132ff312d
Author: Luiz João Motta <[email protected]>
AuthorDate: Thu Mar 6 19:20:55 2025 -0300
NO-ISSUE: `form-code-generator-patternfly-theme` doesn't support nested
ListField (#2958)
---
.../src/api/types.ts | 4 +-
.../src/resources/checkboxGroupFunctions.txt | 10 -
.../src/resources/dateFunctions.txt | 37 --
.../src/resources/multipleSelectFunctions.txt | 19 -
.../src/resources/resources.d.ts | 20 -
.../src/resources/selectFunctions.txt | 16 -
.../src/resources/timeFunctions.txt | 17 -
.../src/uniforms/AutoForm.tsx | 3 +-
.../src/uniforms/BoolField.tsx | 5 +-
.../src/uniforms/CheckBoxGroupField.tsx | 43 +-
.../src/uniforms/DateField.tsx | 123 ++++-
.../src/uniforms/ListField.tsx | 79 +++-
.../src/uniforms/NestField.tsx | 8 +-
.../src/uniforms/NumField.tsx | 1 +
.../src/uniforms/RadioField.tsx | 1 +
.../src/uniforms/SelectField.tsx | 169 ++++++-
.../src/uniforms/TextField.tsx | 2 +
.../src/uniforms/UnsupportedField.tsx | 3 +
.../src/uniforms/rendering/ListItemField.tsx | 27 +-
.../src/uniforms/staticCode/staticCodeBlocks.ts | 44 +-
.../src/uniforms/utils/Utils.tsx | 18 +-
.../tests/AutoField.test.tsx | 23 +-
.../tests/AutoForm.test.tsx | 2 +
.../tests/BoolField.test.tsx | 8 +-
.../tests/CheckBoxGroupField.test.tsx | 31 +-
.../tests/DateField.test.tsx | 19 +-
.../tests/ListField.test.tsx | 27 +-
.../tests/NestField.test.tsx | 4 +-
.../tests/NumField.test.tsx | 12 +-
.../tests/RadioField.test.tsx | 25 +-
.../tests/SelectField.test.tsx | 44 +-
.../tests/TextField.test.tsx | 16 +-
.../tests/UnsupportedField.test.tsx | 4 +-
.../tests/__snapshots__/AutoForm.test.tsx.snap | 304 ++++++++++---
.../tests/__snapshots__/BoolField.test.tsx.snap | 26 +-
.../__snapshots__/CheckBoxGroupField.test.tsx.snap | 66 ++-
.../tests/__snapshots__/DateField.test.tsx.snap | 134 +++++-
.../tests/__snapshots__/ListField.test.tsx.snap | 495 ++++++++++++++++++++-
.../tests/__snapshots__/NestField.test.tsx.snap | 146 +++++-
.../tests/__snapshots__/NumField.test.tsx.snap | 57 ++-
.../tests/__snapshots__/RadioField.test.tsx.snap | 78 +++-
.../tests/__snapshots__/SelectField.test.tsx.snap | 67 ++-
.../tests/__snapshots__/TextField.test.tsx.snap | 68 ++-
.../__snapshots__/UnsupportedField.test.tsx.snap | 14 +-
.../webpack.config.js | 1 -
45 files changed, 1858 insertions(+), 462 deletions(-)
diff --git a/packages/form-code-generator-patternfly-theme/src/api/types.ts
b/packages/form-code-generator-patternfly-theme/src/api/types.ts
index a8a859e3401..c0f673c9f85 100644
--- a/packages/form-code-generator-patternfly-theme/src/api/types.ts
+++ b/packages/form-code-generator-patternfly-theme/src/api/types.ts
@@ -21,7 +21,7 @@ export interface FormElement {
reactImports: string[];
pfImports: string[];
pfIconImports?: string[];
- requiredCode?: string[];
+ requiredCode: string[] | undefined;
ref: InputReference;
stateCode: string;
jsxCode: string;
@@ -33,7 +33,7 @@ abstract class AbstractFormElement implements FormElement {
pfImports: string[];
pfIconImports?: string[];
reactImports: string[];
- requiredCode?: string[];
+ requiredCode: string[] | undefined;
ref: InputReference;
stateCode: string;
isReadonly: boolean;
diff --git
a/packages/form-code-generator-patternfly-theme/src/resources/checkboxGroupFunctions.txt
b/packages/form-code-generator-patternfly-theme/src/resources/checkboxGroupFunctions.txt
deleted file mode 100644
index 6851bedf209..00000000000
---
a/packages/form-code-generator-patternfly-theme/src/resources/checkboxGroupFunctions.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-const handleCheckboxGroupChange = (checkboxValue:string, groupValue:string[],
setter: (val:string[]) => void): void => {
- const newValues = [...groupValue];
- const index = newValues.indexOf(checkboxValue);
- if(index != -1) {
- newValues.splice(index, 1);
- } else {
- newValues.push(checkboxValue);
- }
- setter(newValues);
-}
\ No newline at end of file
diff --git
a/packages/form-code-generator-patternfly-theme/src/resources/dateFunctions.txt
b/packages/form-code-generator-patternfly-theme/src/resources/dateFunctions.txt
deleted file mode 100644
index 6b736c72d91..00000000000
---
a/packages/form-code-generator-patternfly-theme/src/resources/dateFunctions.txt
+++ /dev/null
@@ -1,37 +0,0 @@
-const parseDate = (date?: string): string => {
- if (!date) {
- return '';
- }
- const dateValue: Date = new Date(Date.parse(date));
- return dateValue.toISOString().slice(0, -14);
-};
-
-const onDateChange = (newValue: string, setter: (newValue: string) => void,
previousValue?: string ) => {
- if (newValue) {
- const newDate = new Date(newValue);
- const time = parseTime(previousValue);
- if (time !== '') {
- newDate.setHours(parseInt(time && time.split(':')[0]));
- newDate.setMinutes(parseInt(time && time.split(':')[1].split('
')[0]));
- }
- setter(newDate.toISOString());
- }
-};
-
-const parseTime = (date?: string): string => {
- if (!date) {
- return '';
- }
- const dateValue: Date = new Date(Date.parse(date));
- let isAm = true;
- let hours = dateValue.getHours();
- if (hours > 12) {
- hours %= 12;
- isAm = false;
- }
- let minutes = dateValue.getMinutes().toString();
- if (minutes.length == 1) {
- minutes = '0' + minutes;
- }
- return `${hours}:${minutes} ${isAm ? 'AM' : 'PM'}`;
-};
\ No newline at end of file
diff --git
a/packages/form-code-generator-patternfly-theme/src/resources/multipleSelectFunctions.txt
b/packages/form-code-generator-patternfly-theme/src/resources/multipleSelectFunctions.txt
deleted file mode 100644
index f37310beb57..00000000000
---
a/packages/form-code-generator-patternfly-theme/src/resources/multipleSelectFunctions.txt
+++ /dev/null
@@ -1,19 +0,0 @@
-const handleMultipleSelect = (
- newSelection: string | SelectOptionObject,
- isPlaceHolder: boolean,
- currentValue: string[],
- setSelection: (val: string[]) => void,
-) => {
- if (isPlaceHolder) {
- setSelection([]);
- } else {
- const parseSelection = (): string[] => {
- const selectedValue = newSelection.toString ? newSelection.toString() :
newSelection as string;
- if (currentValue.indexOf(selectedValue) != -1) {
- return currentValue.filter((s) => s !== selectedValue);
- }
- return [selectedValue, ...currentValue];
- };
- setSelection(parseSelection());
- }
-};
\ No newline at end of file
diff --git
a/packages/form-code-generator-patternfly-theme/src/resources/resources.d.ts
b/packages/form-code-generator-patternfly-theme/src/resources/resources.d.ts
deleted file mode 100644
index 6d2799f3b54..00000000000
--- a/packages/form-code-generator-patternfly-theme/src/resources/resources.d.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-declare module "*.txt";
diff --git
a/packages/form-code-generator-patternfly-theme/src/resources/selectFunctions.txt
b/packages/form-code-generator-patternfly-theme/src/resources/selectFunctions.txt
deleted file mode 100644
index 6789920ec3c..00000000000
---
a/packages/form-code-generator-patternfly-theme/src/resources/selectFunctions.txt
+++ /dev/null
@@ -1,16 +0,0 @@
-const handleSelect = (
- newSelection: string | SelectOptionObject,
- isPlaceHolder: boolean,
- currentSelection: string,
- setSelection: (val: string) => void,
- setExpanded: (expanded: boolean) => void
-) => {
- if (isPlaceHolder) {
- setSelection('');
- setExpanded(false);
- } else {
- const parsedSelection = newSelection.toString ? newSelection.toString() :
newSelection as string;
- setSelection(parsedSelection || '');
- setExpanded(false);
- }
-};
\ No newline at end of file
diff --git
a/packages/form-code-generator-patternfly-theme/src/resources/timeFunctions.txt
b/packages/form-code-generator-patternfly-theme/src/resources/timeFunctions.txt
deleted file mode 100644
index 33c2a2912ce..00000000000
---
a/packages/form-code-generator-patternfly-theme/src/resources/timeFunctions.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-const onTimeChange = (time: string, setter: (newValue: string) => void,
previousValue?: string, hours?: number, minutes?: number) => {
- if (previousValue) {
- const newDate = new Date(Date.parse(previousValue));
- if (hours && minutes) {
- newDate.setHours(hours);
- newDate.setMinutes(minutes);
- } else if (time !== '') {
- const localeHours = parseInt(time && time.split(':')[0]);
- const localeMinutes = parseInt(time && time.split(':')[1].split('
')[0]);
- if (!isNaN(localeHours) && !isNaN(localeMinutes)) {
- newDate.setHours(localeHours);
- newDate.setMinutes(localeMinutes);
- }
- }
- setter(newDate.toISOString());
- }
-};
\ No newline at end of file
diff --git
a/packages/form-code-generator-patternfly-theme/src/uniforms/AutoForm.tsx
b/packages/form-code-generator-patternfly-theme/src/uniforms/AutoForm.tsx
index 272d042a2fd..5018795dc9a 100644
--- a/packages/form-code-generator-patternfly-theme/src/uniforms/AutoForm.tsx
+++ b/packages/form-code-generator-patternfly-theme/src/uniforms/AutoForm.tsx
@@ -60,8 +60,7 @@ const AutoForm: React.FC<AutoFormProps> = (props) => {
const formName = `Form${formId ? `${NS_SEPARATOR}${formId}` : ""}`;
const hooks = inputs.map((input) => input.stateCode).join("\n");
const elements = inputs.map((input) => input.jsxCode).join("\n");
- const staticCodeStr: string = staticCodeArray.map((id) =>
JSON.stringify(getStaticCodeBlock(id))).join("\n");
-
+ const staticCodeStr: string = staticCodeArray.map((id) =>
getStaticCodeBlock(id)).join("\n");
const formTemplate = `
import React, { ${reactImports.join(", ")} } from "react";
import { ${pfImports.join(", ")} } from "@patternfly/react-core";
diff --git
a/packages/form-code-generator-patternfly-theme/src/uniforms/BoolField.tsx
b/packages/form-code-generator-patternfly-theme/src/uniforms/BoolField.tsx
index 043e106dbd1..7885b2353f3 100644
--- a/packages/form-code-generator-patternfly-theme/src/uniforms/BoolField.tsx
+++ b/packages/form-code-generator-patternfly-theme/src/uniforms/BoolField.tsx
@@ -39,8 +39,6 @@ export type BoolFieldProps = HTMLFieldProps<
const Bool: React.FC<BoolFieldProps> = (props: BoolFieldProps) => {
const ref: InputReference = getInputReference(props.name,
DEFAULT_DATA_TYPE_BOOLEAN);
- const stateCode = getStateCodeFromRef(ref);
-
const jsxCode = `<FormGroup fieldId='${props.id}'>
<Checkbox
isChecked={${props.itemProps?.isListItem ? getListItemValue({ itemProps:
props.itemProps, name: props.name }) : ref.stateName}}
@@ -56,8 +54,9 @@ const Bool: React.FC<BoolFieldProps> = (props:
BoolFieldProps) => {
ref,
pfImports: ["Checkbox", "FormGroup"],
reactImports: ["useState"],
+ requiredCode: undefined,
jsxCode,
- stateCode,
+ stateCode: props.itemProps ? "" : getStateCodeFromRef(ref),
isReadonly: props.disabled,
};
diff --git
a/packages/form-code-generator-patternfly-theme/src/uniforms/CheckBoxGroupField.tsx
b/packages/form-code-generator-patternfly-theme/src/uniforms/CheckBoxGroupField.tsx
index 196b7fb2d9a..b9c2a77f624 100644
---
a/packages/form-code-generator-patternfly-theme/src/uniforms/CheckBoxGroupField.tsx
+++
b/packages/form-code-generator-patternfly-theme/src/uniforms/CheckBoxGroupField.tsx
@@ -22,9 +22,13 @@ import { connectField, HTMLFieldProps } from "uniforms/cjs";
import { buildDefaultInputElement, getInputReference, renderField } from
"./utils/Utils";
import { FormInput, InputReference } from "../api";
import { useAddFormElementToContext } from "./CodeGenContext";
-import { CHECKBOX_GROUP_FUNCTIONS } from "./staticCode/staticCodeBlocks";
import { DEFAULT_DATA_TYPE_STRING_ARRAY } from "./utils/dataTypes";
-import { getListItemName, getListItemOnChange, getListItemValue, ListItemProps
} from "./rendering/ListItemField";
+import {
+ getItemNameAndWithIsNested,
+ getListItemName,
+ getListItemValue,
+ ListItemProps,
+} from "./rendering/ListItemField";
export type CheckBoxGroupProps = HTMLFieldProps<
string[],
@@ -42,6 +46,35 @@ export type CheckBoxGroupProps = HTMLFieldProps<
const CheckBoxGroup: React.FC<CheckBoxGroupProps> = (props:
CheckBoxGroupProps) => {
const ref: InputReference = getInputReference(props.name,
DEFAULT_DATA_TYPE_STRING_ARRAY);
+ function getOnChange(value: string) {
+ if (props.itemProps?.isListItem) {
+ const { itemName, isNested } = getItemNameAndWithIsNested(props.name);
+ const propertyPath =
props.itemProps?.listStateName.split(".").splice(1).join(".");
+ const path =
`${propertyPath}[${props.itemProps?.indexVariableName}]${isNested ?
`.${itemName}` : ""}`;
+ return `
+ ${props.itemProps?.listStateSetter}(prev => {
+ const newState = [...prev];
+ const newValue = [...newState${path}]
+ if(newValue.indexOf('${value}') != -1) {
+ newValue.splice(index, 1);
+ } else {
+ newValue.push('${value}');
+ }
+ newState${path} = newValue
+ return newState;
+ })`;
+ }
+ return `${ref.stateSetter}(prev => {
+ const newState = [...prev];
+ if(newState.indexOf('${value}') != -1) {
+ newState.splice(index, 1);
+ } else {
+ newState.push('${value}');
+ }
+ return newState;
+ })`;
+ }
+
const jsxCode = props.allowedValues
?.map((value) => {
return `<Checkbox
@@ -51,8 +84,8 @@ const CheckBoxGroup: React.FC<CheckBoxGroupProps> = (props:
CheckBoxGroupProps)
aria-label={'${props.name}'}
label={'${props.transform ? props.transform(value) : value}'}
isDisabled={${props.disabled || false}}
- isChecked={${ref.stateName}.indexOf('${value}') !== -1}
- onChange={${props.itemProps?.isListItem ? getListItemOnChange({ itemProps:
props.itemProps, name: props.name, callback: (internalValue: string) =>
`handleCheckboxGroupChange(${internalValue}, ${ref.stateName},
${ref.stateSetter})`, overrideNewValue: `'${value}'` }) : `() =>
handleCheckboxGroupChange('${value}', ${ref.stateName}, ${ref.stateSetter})`}}
+ isChecked={${props.itemProps?.isListItem ? `${getListItemValue({ itemProps:
props.itemProps, name: props.name })}.indexOf('${value}') !== -1` :
`${ref.stateName}.indexOf('${value}') !== -1`}}
+ onChange={${getOnChange(value)}}
value={${props.itemProps?.isListItem ? getListItemValue({ itemProps:
props.itemProps, name: props.name }) : `'${value}'`}}
/>`;
})
@@ -62,13 +95,13 @@ const CheckBoxGroup: React.FC<CheckBoxGroupProps> = (props:
CheckBoxGroupProps)
pfImports: ["Checkbox"],
inputJsxCode: jsxCode || "",
ref: ref,
- requiredCode: [CHECKBOX_GROUP_FUNCTIONS],
wrapper: {
id: props.id,
label: props.label,
required: props.required,
},
disabled: props.disabled,
+ itemProps: props.itemProps,
});
useAddFormElementToContext(element);
diff --git
a/packages/form-code-generator-patternfly-theme/src/uniforms/DateField.tsx
b/packages/form-code-generator-patternfly-theme/src/uniforms/DateField.tsx
index 591dbd4e59c..5e0283294bc 100644
--- a/packages/form-code-generator-patternfly-theme/src/uniforms/DateField.tsx
+++ b/packages/form-code-generator-patternfly-theme/src/uniforms/DateField.tsx
@@ -23,9 +23,14 @@ import { connectField, HTMLFieldProps } from "uniforms/cjs";
import { FormInput, InputReference } from "../api";
import { buildDefaultInputElement, getInputReference, renderField } from
"./utils/Utils";
import { useAddFormElementToContext } from "./CodeGenContext";
-import { DATE_FUNCTIONS, TIME_FUNCTIONS } from "./staticCode/staticCodeBlocks";
+import { DATE_FUNCTIONS } from "./staticCode/staticCodeBlocks";
import { DEFAULT_DATA_TYPE_DATE } from "./utils/dataTypes";
-import { getListItemName, getListItemOnChange, getListItemValue, ListItemProps
} from "./rendering/ListItemField";
+import {
+ getItemNameAndWithIsNested,
+ getListItemName,
+ getListItemValue,
+ ListItemProps,
+} from "./rendering/ListItemField";
export type DateFieldProps = HTMLFieldProps<
Date,
@@ -45,6 +50,96 @@ const Date: React.FC<DateFieldProps> = (props:
DateFieldProps) => {
const pfImports = ["DatePicker", "Flex", "FlexItem", "InputGroup",
"TimePicker"];
+ function getOnDateChange() {
+ if (props.itemProps?.isListItem) {
+ const { itemName, isNested } = getItemNameAndWithIsNested(props.name);
+ const propertyPath =
props.itemProps?.listStateName.split(".").splice(1).join(".");
+ const path =
`${propertyPath}[${props.itemProps?.indexVariableName}]${isNested ?
`.${itemName}` : ""}`;
+ return `newDate => {
+ ${props.itemProps?.listStateSetter}(prev => {
+ if (newDate) {
+ const newState = [...prev];
+ const currentDate = newState${path}
+ const newDate = new Date(newDate);
+ const time = parseTime(currentDate);
+ if (time !== '') {
+ newDate.setHours(parseInt(time && time.split(':')[0]));
+ newDate.setMinutes(parseInt(time && time.split(':')[1].split('
')[0]));
+ }
+ newState${path} = newDate.toISOString();
+ return newState;
+ }
+ return prev;
+ })
+ }`;
+ }
+ return `newDate => {
+ ${ref.stateSetter}(prev => {
+ if (newDate) {
+ const newDate = new Date(newDate);
+ const time = parseTime(prev);
+ if (time !== '') {
+ newDate.setHours(parseInt(time && time.split(':')[0]));
+ newDate.setMinutes(parseInt(time && time.split(':')[1].split('
')[0]));
+ }
+ return newDate.toISOString();
+ }
+ return prev;
+ })
+ }
+ `;
+ }
+
+ function getOnTimeChange() {
+ if (props.itemProps?.isListItem) {
+ const { itemName, isNested } = getItemNameAndWithIsNested(props.name);
+ const propertyPath =
props.itemProps?.listStateName.split(".").splice(1).join(".");
+ const path =
`${propertyPath}[${props.itemProps?.indexVariableName}]${isNested ?
`.${itemName}` : ""}`;
+ return `(time, hours?, minutes?) =>
${props.itemProps?.listStateSetter}(prev => {
+ const newState = [...prev];
+ const currentDate = newState${path}
+ if (currentDate) {
+ const newDate = new Date(Date.parse(currentDate));
+ if (hours && minutes) {
+ newDate.setHours(hours);
+ newDate.setMinutes(minutes);
+ } else if (time !== '') {
+ const localeHours = parseInt(time && time.split(':')[0]);
+ const localeMinutes = parseInt(time && time.split(':')[1].split('
')[0]);
+ if (!isNaN(localeHours) && !isNaN(localeMinutes)) {
+ newDate.setHours(localeHours);
+ newDate.setMinutes(localeMinutes);
+ }
+ }
+ newState${path} = newDate.toISOString();
+ return newState;
+ }
+ return prev;
+ })`;
+ }
+ return `(time, hours?, minutes?) => {
+ ${ref.stateSetter}(prev => {
+ if (prev) {
+ const newDate = new Date(Date.parse(prev));
+ if (hours && minutes) {
+ newDate.setHours(hours);
+ newDate.setMinutes(minutes);
+ } else if (time !== '') {
+ const localeHours = parseInt(time && time.split(':')[0]);
+ const localeMinutes = parseInt(time && time.split(':')[1].split('
')[0]);
+ if (!isNaN(localeHours) && !isNaN(localeMinutes)) {
+ newDate.setHours(localeHours);
+ newDate.setMinutes(localeMinutes);
+ }
+ }
+ return newDate.toISOString();
+ }
+ return prev;
+ })
+ }
+ `;
+ }
+
const jsxCode = `<Flex
direction={{ default: 'column' }}
id={'${props.id}'}
@@ -55,31 +150,14 @@ const Date: React.FC<DateFieldProps> = (props:
DateFieldProps) => {
id={'date-picker-${props.id}'}
isDisabled={${props.disabled || false}}
name={${props.itemProps?.isListItem ? getListItemName({ itemProps:
props.itemProps, name: props.name }) : `'${props.name}'`}}
- onChange={${
- props.itemProps?.isListItem
- ? getListItemOnChange({
- itemProps: props.itemProps,
- name: props.name,
- callback: (value) => `onDateChange(${value},
${ref.stateSetter}, ${ref.stateName})`,
- })
- : `newDate => onDateChange(newDate, ${ref.stateSetter},
${ref.stateName})`
- }}
+ onChange={${getOnDateChange()}}
value={${props.itemProps?.isListItem ? getListItemValue({ itemProps:
props.itemProps, name: props.name, callback: (value: string) =>
`parseDate(${value})` }) : `parseDate(${ref.stateName})`}}
/>
<TimePicker
id={'time-picker-${props.id}'}
isDisabled={${props.disabled || false}}
name={${props.itemProps?.isListItem ? getListItemName({ itemProps:
props.itemProps, name: props.name }) : `'${props.name}'`}}
- onChange={${
- props.itemProps?.isListItem
- ? getListItemOnChange({
- itemProps: props.itemProps,
- name: props.name,
- callback: (_) => `onTimeChange(time, ${ref.stateSetter},
${ref.stateName}, hours, minutes)`,
- overrideParam: "(time, hours?, minutes?)",
- })
- : `(time, hours?, minutes?) => onTimeChange(time,
${ref.stateSetter}, ${ref.stateName}, hours, minutes)`
- }}
+ onChange={${getOnTimeChange()}}
style={{ width: '120px' }}
time={${props.itemProps?.isListItem ? getListItemValue({ itemProps:
props.itemProps, name: props.name, callback: (value: string) =>
`parseTime(${value})` }) : `parseTime(${ref.stateName})`}}
/>
@@ -91,13 +169,14 @@ const Date: React.FC<DateFieldProps> = (props:
DateFieldProps) => {
pfImports,
inputJsxCode: jsxCode,
ref,
- requiredCode: [DATE_FUNCTIONS, TIME_FUNCTIONS],
+ requiredCode: [DATE_FUNCTIONS],
wrapper: {
id: props.id,
label: props.label,
required: props.required,
},
disabled: props.disabled,
+ itemProps: props.itemProps,
});
useAddFormElementToContext(element);
diff --git
a/packages/form-code-generator-patternfly-theme/src/uniforms/ListField.tsx
b/packages/form-code-generator-patternfly-theme/src/uniforms/ListField.tsx
index 420301b3b94..9b457afc198 100644
--- a/packages/form-code-generator-patternfly-theme/src/uniforms/ListField.tsx
+++ b/packages/form-code-generator-patternfly-theme/src/uniforms/ListField.tsx
@@ -17,7 +17,7 @@
* under the License.
*/
-import React, { useContext, useCallback } from "react";
+import React, { useContext } from "react";
import { connectField, context, HTMLFieldProps } from "uniforms/cjs";
import { getInputReference, getStateCode, renderField } from "./utils/Utils";
import { codeGenContext } from "./CodeGenContext";
@@ -30,7 +30,7 @@ import {
DEFAULT_DATA_TYPE_STRING_ARRAY,
} from "./utils/dataTypes";
import { renderListItemFragmentWithContext } from "./rendering/RenderingUtils";
-import { ListItemProps } from "./rendering/ListItemField";
+import { getNextIndexVariableName, ListItemProps } from
"./rendering/ListItemField";
export type ListFieldProps = HTMLFieldProps<
unknown[],
@@ -43,17 +43,18 @@ export type ListFieldProps = HTMLFieldProps<
>;
const List: React.FC<ListFieldProps> = (props: ListFieldProps) => {
- const ref: InputReference = getInputReference(props.name,
DEFAULT_DATA_TYPE_ANY_ARRAY);
+ const ref: InputReference = getInputReference(props.name,
DEFAULT_DATA_TYPE_ANY_ARRAY, props.itemProps);
const uniformsContext = useContext(context);
const codegenCtx = useContext(codeGenContext);
+ const indexVariableName = getNextIndexVariableName(props.itemProps);
const listItem = renderListItemFragmentWithContext(
uniformsContext,
"$",
{
isListItem: true,
- indexVariableName: "itemIndex",
+ indexVariableName,
listName: props.name,
listStateName: ref.stateName,
listStateSetter: ref.stateSetter,
@@ -86,6 +87,52 @@ const List: React.FC<ListFieldProps> = (props:
ListFieldProps) => {
};
const listItemValue = getDefaultItemValue();
+ const addElementsIsDisabled = `${
+ props.maxCount === undefined
+ ? props.disabled
+ : `${props.disabled} || !(${props.maxCount} <= (${ref.stateName}?.length
?? -1))`
+ }`;
+
+ const onAddElementCallback = (prefix: string) => {
+ return props.itemProps
+ ? `${prefix}${ref.stateSetter}((s) => {
+ const newState = [...s];
+ (newState${ref.stateName.split(".").splice(1).join(".")}) =
[...(newState${ref.stateName.split(".").splice(1).join(".")} ?? []),
${listItemValue}];
+ return newState;
+})`
+ : `${prefix}${ref.stateSetter}((${ref.stateName} ??
[]).concat([${listItemValue}]))`;
+ };
+
+ const onAddElement = `!${props.disabled} &&
+ ${
+ props.maxCount === undefined
+ ? onAddElementCallback("")
+ : onAddElementCallback(`!(${props.maxCount} <=
(${ref.stateName}?.length ?? -1)) && `)
+ };`;
+
+ const removeElementIsDisabled = `${
+ props.minCount === undefined
+ ? props.disabled
+ : `${props.disabled} || (${props.minCount} >= (${ref.stateName}?.length
?? -1))`
+ }`;
+
+ const onRemoveElementCallback = (prefix: string) => {
+ return props.itemProps
+ ? `${prefix}${ref.stateSetter}((s) => {
+ const newState = [...s];
+ (newState${ref.stateName.split(".").splice(1).join(".")}) = value;
+ return newState;
+})`
+ : `${prefix}${ref.stateSetter}(value)`;
+ };
+
+ const onRemoveElement = `!${props.disabled} &&
+ ${
+ props.minCount === undefined
+ ? onRemoveElementCallback("")
+ : onRemoveElementCallback(`!(${props.minCount} >=
(${ref.stateName}?.length ?? -1)) && `)
+ };`;
+
const jsxCode = `<div>
<Split hasGutter>
<SplitItem>
@@ -103,9 +150,9 @@ const List: React.FC<ListFieldProps> = (props:
ListFieldProps) => {
name='$'
variant='plain'
style={{ paddingLeft: '0', paddingRight: '0' }}
- disabled={${props.maxCount === undefined ? props.disabled :
`${props.disabled} || !(${props.maxCount} <= (${ref.stateName}?.length ??
-1))`}}
+ disabled={${addElementsIsDisabled}}
onClick={() => {
- !${props.disabled} && ${props.maxCount === undefined ?
`${ref.stateSetter}((${ref.stateName} ?? []).concat([${listItemValue}]))` :
`!(${props.maxCount} <= (${ref.stateName}?.length ?? -1)) &&
${ref.stateSetter}((${ref.stateName} ?? []).concat([]))`};
+ ${onAddElement}
}}
>
<PlusCircleIcon color='#0088ce' />
@@ -113,9 +160,9 @@ const List: React.FC<ListFieldProps> = (props:
ListFieldProps) => {
</SplitItem>
</Split>
<div>
- {${ref.stateName}?.map((_, itemIndex) =>
+ {${ref.stateName}?.map((_, ${indexVariableName}) =>
(<div
- key={itemIndex}
+ key={${indexVariableName}}
style={{
marginBottom: '1rem',
display: 'flex',
@@ -125,13 +172,13 @@ const List: React.FC<ListFieldProps> = (props:
ListFieldProps) => {
<div style={{ width: '100%', marginRight: '10px'
}}>${listItem?.jsxCode}</div>
<div>
<Button
- disabled={${props.minCount === undefined ? props.disabled :
`${props.disabled} || (${props.minCount} >= (${ref.stateName}?.length ?? -1))`}}
+ disabled={${removeElementIsDisabled}}
variant='plain'
style={{ paddingLeft: '0', paddingRight: '0' }}
onClick={() => {
const value = [...${ref.stateName}]
- value.splice(itemIndex, 1);
- !${props.disabled} && ${props.minCount === undefined ?
`${ref.stateSetter}(value)` : `!(${props.minCount} >= (${ref.stateName}?.length
?? -1)) && ${ref.stateSetter}(value)`};
+ value.splice(${indexVariableName}, 1);
+ ${onRemoveElement}
}}
>
<MinusCircleIcon color='#cc0000' />
@@ -142,13 +189,21 @@ const List: React.FC<ListFieldProps> = (props:
ListFieldProps) => {
</div>
</div>`;
+ function getListStateCode() {
+ let stateCode = getStateCode(ref.stateName, ref.stateSetter,
ref.dataType.name, "[]");
+ stateCode = stateCode.includes("?.[itemIndex]") ? "" : stateCode;
+ stateCode = stateCode + "\n" + (listItem?.stateCode ?? "");
+ return stateCode;
+ }
+
const element: FormInput = {
ref,
pfImports: [...new Set(["Split", "SplitItem", "Button",
...(listItem?.pfImports ?? [])])],
pfIconImports: [...new Set(["PlusCircleIcon", "MinusCircleIcon",
...(listItem?.pfIconImports ?? [])])],
reactImports: [...new Set([...(listItem?.reactImports ?? [])])],
+ requiredCode: [...new Set([...(listItem?.requiredCode ?? [])])],
jsxCode,
- stateCode: getStateCode(ref.stateName, ref.stateSetter, ref.dataType.name,
"[]"),
+ stateCode: getListStateCode(),
isReadonly: props.disabled,
};
diff --git
a/packages/form-code-generator-patternfly-theme/src/uniforms/NestField.tsx
b/packages/form-code-generator-patternfly-theme/src/uniforms/NestField.tsx
index f0a85159240..d6ab1cd6dc8 100644
--- a/packages/form-code-generator-patternfly-theme/src/uniforms/NestField.tsx
+++ b/packages/form-code-generator-patternfly-theme/src/uniforms/NestField.tsx
@@ -46,7 +46,7 @@ const Nest: React.FunctionComponent<NestFieldProps> = ({
const codegenCtx = useContext(codeGenContext);
const nestedRefs: InputReference[] = [];
- const nestedStates: string[] = [];
+ const nestedStates: Set<string> = new Set();
const nestedJsx: string[] = [];
let pfImports: string[] = ["Card", "CardBody"];
@@ -59,7 +59,7 @@ const Nest: React.FunctionComponent<NestFieldProps> = ({
const renderedInput =
renderNestedInputFragmentWithContext(uniformsContext, field, itemProps,
disabled);
if (renderedInput) {
- nestedStates.push(renderedInput.stateCode);
+ nestedStates.add(renderedInput.stateCode.trim());
nestedJsx.push(renderedInput.jsxCode);
nestedRefs.push(renderedInput.ref);
if (renderedInput.ref.dataType === DEFAULT_DATA_TYPE_OBJECT) {
@@ -80,7 +80,7 @@ const Nest: React.FunctionComponent<NestFieldProps> = ({
const bodyLabel = label && !itemProps?.isListItem ?
`<label><b>${label}</b></label>` : "";
- const stateCode = nestedStates.join("\n");
+ const stateCode = [...nestedStates].join("\n");
const jsxCode = `<Card>
<CardBody className="pf-c-form">
${bodyLabel}
@@ -91,7 +91,7 @@ const Nest: React.FunctionComponent<NestFieldProps> = ({
pfImports,
pfIconImports,
reactImports,
- requiredCode: requiredCode,
+ requiredCode,
stateCode,
jsxCode,
ref: getInputReference(name, DEFAULT_DATA_TYPE_OBJECT),
diff --git
a/packages/form-code-generator-patternfly-theme/src/uniforms/NumField.tsx
b/packages/form-code-generator-patternfly-theme/src/uniforms/NumField.tsx
index 0ffac632aad..6f82c4e7267 100644
--- a/packages/form-code-generator-patternfly-theme/src/uniforms/NumField.tsx
+++ b/packages/form-code-generator-patternfly-theme/src/uniforms/NumField.tsx
@@ -66,6 +66,7 @@ const Num: React.FC<NumFieldProps> = (props: NumFieldProps)
=> {
required: props.required,
},
disabled: props.disabled,
+ itemProps: props.itemProps,
});
useAddFormElementToContext(element);
diff --git
a/packages/form-code-generator-patternfly-theme/src/uniforms/RadioField.tsx
b/packages/form-code-generator-patternfly-theme/src/uniforms/RadioField.tsx
index ef979702b76..80d0a145bbd 100644
--- a/packages/form-code-generator-patternfly-theme/src/uniforms/RadioField.tsx
+++ b/packages/form-code-generator-patternfly-theme/src/uniforms/RadioField.tsx
@@ -70,6 +70,7 @@ const Radio = (props: RadioFieldProps) => {
required: props.required,
},
disabled: props.disabled,
+ itemProps: props.itemProps,
});
useAddFormElementToContext(element);
diff --git
a/packages/form-code-generator-patternfly-theme/src/uniforms/SelectField.tsx
b/packages/form-code-generator-patternfly-theme/src/uniforms/SelectField.tsx
index 981226bd62c..84e48cf4587 100644
--- a/packages/form-code-generator-patternfly-theme/src/uniforms/SelectField.tsx
+++ b/packages/form-code-generator-patternfly-theme/src/uniforms/SelectField.tsx
@@ -22,9 +22,14 @@ import { connectField, HTMLFieldProps } from "uniforms/cjs";
import { useAddFormElementToContext } from "./CodeGenContext";
import { FormInput, InputReference } from "../api";
import { getInputReference, getStateCode, getStateCodeFromRef, NS_SEPARATOR,
renderField } from "./utils/Utils";
-import { MULTIPLE_SELECT_FUNCTIONS, SELECT_FUNCTIONS } from
"./staticCode/staticCodeBlocks";
import { DEFAULT_DATA_TYPE_STRING_ARRAY, DEFAULT_DATA_TYPE_STRING } from
"./utils/dataTypes";
-import { getListItemName, getListItemOnChange, getListItemValue, ListItemProps
} from "./rendering/ListItemField";
+import {
+ getItemNameAndWithIsNested,
+ getListItemName,
+ getListItemValue,
+ ListItemProps,
+} from "./rendering/ListItemField";
+import { REFUSED } from "dns";
export type SelectInputProps = HTMLFieldProps<
string | string[],
@@ -60,20 +65,145 @@ const Select: React.FC<SelectInputProps> = (props:
SelectInputProps) => {
selectedOptions.unshift(placeHolderOption);
}
- let stateCode = getStateCodeFromRef(ref);
+ function getExpandedStateCode() {
+ if (props.itemProps?.isListItem) {
+ const [, stateName] = props.itemProps.listStateSetter.split("set__");
+ return [`${stateName}${NS_SEPARATOR}expanded`,
`${props.itemProps.listStateSetter}${NS_SEPARATOR}expanded`];
+ }
+ return [`${ref.stateName}${NS_SEPARATOR}expanded`,
`${ref.stateSetter}${NS_SEPARATOR}expanded`];
+ }
+ const [expandedStateName, expandedStateNameSetter] = getExpandedStateCode();
+
+ // Creating a new item on the List will not add a new property to the
`expandedStateName` for List items
+ // The code above will ensure the `expandedStateName` has the property
before attributing it in a set state
+ function getVariableInitializer(path: string) {
+ // captures all `properties` and `indexNames` from
"<property1>[<indexName2>].<property2>[<indexName2>]"
+ const parts = [...path.matchAll(/(\w*)(?:\[(\w*)\])?/g)];
+ let propertyPath = "newState";
+ return parts
+ .map(([_, property, indexName]) => {
+ if (property === "" && indexName !== undefined) {
+ propertyPath += `[${indexName}]`;
+ return `${propertyPath} ??= {};`;
+ }
+ if (property !== "" && indexName !== undefined) {
+ propertyPath += `.${property}`;
+ return `${propertyPath} ??= [];`;
+ }
+ return "";
+ })
+ .join("\n");
+ }
- const expandedStateName = `${ref.stateName}${NS_SEPARATOR}expanded`;
- const expandedStateNameSetter = `${ref.stateSetter}${NS_SEPARATOR}expanded`;
- const expandedState = getStateCode(expandedStateName,
expandedStateNameSetter, "boolean", "false");
+ function getHandleSelect() {
+ if (props.itemProps?.isListItem) {
+ const { itemName, isNested } = getItemNameAndWithIsNested(props.name);
+ const propertyPath =
props.itemProps?.listStateName.split(".").splice(1).join(".");
+ const path =
`${propertyPath}[${props.itemProps?.indexVariableName}]${isNested ?
`.${itemName}` : ""}`;
+ if (isArray) {
+ return `(event, value, isPlaceHolder) => {
+ if (isPlaceHolder) {
+ ${props.itemProps?.listStateSetter}(prev => {
+ const newState = [...prev];
+ newState${path} = [];
+ return newState;
+ })
+ } else {
+ const selectedValue = newSelection.toString ?
newSelection.toString() : newSelection as string;
+ ${props.itemProps?.listStateSetter}(prev => {
+ const newState = [...prev];
+ if (newState${path}.indexOf(selectedValue) != -1) {
+ const filtered = newState${path}.filter((s) => s !==
selectedValue);
+ return newState${path} = filtered;
+ }
+ newState${path} = [selectedValue, ...newState${path}];
+ return newState;
+ })
+ }
+ }`;
+ } else {
+ return `(event, value, isPlaceHolder) => {
+ if (isPlaceHolder) {
+ ${props.itemProps?.listStateSetter}(prev => {
+ const newState = [...prev];
+ newState${path} = "";
+ return newState;
+ })
+ ${expandedStateNameSetter}(prev => {
+ const newState = [...prev];
+ ${getVariableInitializer(path)}
+ newState${path} = false;
+ return newState;
+ });
+ } else {
+ const parsedSelection = value.toString ? value.toString() : value
as string;
+ ${props.itemProps?.listStateSetter}(prev => {
+ const newState = [...prev];
+ newState${path} = parsedSelection || '';
+ return newState;
+ })
+ ${expandedStateNameSetter}(prev => {
+ const newState = [...prev];
+ ${getVariableInitializer(path)}
+ newState${path} = false;
+ return newState;
+ });
+ }
+ }`;
+ }
+ }
+ if (isArray) {
+ return `(event, value, isPlaceHolder) => {
+ if (isPlaceHolder) {
+ ${ref.stateSetter}([]);
+ } else {
+ ${ref.stateSetter}(prev => {
+ const selectedValue = value.toString ? value.toString() : value as
string;
+ if (prev.indexOf(selectedValue) != -1) {
+ return prev.filter((s) => s !== selectedValue);
+ }
+ return [selectedValue, ...prev];
+ });
+ }
+ }`;
+ } else {
+ return `(event, value, isPlaceHolder) => {
+ if (isPlaceHolder) {
+ ${ref.stateSetter}('');
+ ${expandedStateNameSetter}(false);
+ } else {
+ const parsedSelection = value.toString ? value.toString() : value as
string;
+ ${ref.stateSetter}(parsedSelection || '');
+ ${expandedStateNameSetter}(false);
+ }
+ }`;
+ }
+ }
- stateCode += `\n${expandedState}`;
+ function getIsOpen() {
+ if (props.itemProps?.isListItem) {
+ const { itemName, isNested } = getItemNameAndWithIsNested(props.name);
+ const propertyPath =
props.itemProps?.listStateName.split(".").splice(1).join(".").replaceAll("].",
"]?.");
+ return
`${expandedStateName}${propertyPath}?.[${props.itemProps?.indexVariableName}]${isNested
? `?.${itemName}` : ""} ?? false`;
+ }
+ return expandedStateName;
+ }
- let hanldeSelect;
- if (isArray) {
- hanldeSelect = `handleMultipleSelect(value, isPlaceHolder,
${ref.stateName}, ${ref.stateSetter})`;
- } else {
- hanldeSelect = `handleSelect(value, isPlaceHolder, ${ref.stateName},
${ref.stateSetter}, ${expandedStateNameSetter})`;
+ function getOnToggle() {
+ if (props.itemProps?.isListItem) {
+ const { itemName, isNested } = getItemNameAndWithIsNested(props.name);
+ const propertyPath =
props.itemProps?.listStateName.split(".").splice(1).join(".");
+ const path =
`${propertyPath}[${props.itemProps?.indexVariableName}]${isNested ?
`.${itemName}` : ""}`;
+ return `(isOpen) => ${expandedStateNameSetter}(prev => {
+ const newState = [...prev];
+ ${getVariableInitializer(path)}
+ newState${path} = isOpen
+ return newState;
+ })`;
+ }
+ return `(isOpen) => ${expandedStateNameSetter}(isOpen)`;
}
+
const jsxCode = `<FormGroup
fieldId={'${props.id}'}
label={'${props.label}'}
@@ -84,10 +214,10 @@ const Select: React.FC<SelectInputProps> = (props:
SelectInputProps) => {
variant={SelectVariant.${isArray ? "typeaheadMulti" : "single"}}
isDisabled={${props.disabled || false}}
placeholderText={'${props.placeholder || ""}'}
- isOpen={${expandedStateName}}
- selections={${ref.stateName}}
- onToggle={(isOpen) => ${expandedStateNameSetter}(isOpen)}
- onSelect={${props.itemProps?.isListItem ? getListItemOnChange({
itemProps: props.itemProps, name: props.name, callback: (value: string) =>
`${hanldeSelect}`, overrideParam: "(event, value, isPlaceHolder)" }) : `(event,
value, isPlaceHolder) => ${hanldeSelect}`}}
+ isOpen={${getIsOpen()}}
+ selections={${props.itemProps?.isListItem ? getListItemValue({
itemProps: props.itemProps, name: props.name }) : ref.stateName}}
+ onToggle={${getOnToggle()}}
+ onSelect={${getHandleSelect()}}
value={${props.itemProps?.isListItem ? getListItemValue({ itemProps:
props.itemProps, name: props.name }) : ref.stateName}}
>
${selectedOptions.join("\n")}
@@ -97,9 +227,12 @@ const Select: React.FC<SelectInputProps> = (props:
SelectInputProps) => {
ref,
pfImports: SELECT_IMPORTS,
reactImports: ["useState"],
- requiredCode: [isArray ? MULTIPLE_SELECT_FUNCTIONS : SELECT_FUNCTIONS],
jsxCode,
- stateCode,
+ requiredCode: undefined,
+ stateCode: props.itemProps?.isListItem
+ ? getStateCode(expandedStateName, expandedStateNameSetter, "object[]",
"[]")
+ : `${getStateCodeFromRef(ref)}
+ ${getStateCode(expandedStateName, expandedStateNameSetter, "boolean",
"false")}`,
isReadonly: props.disabled,
};
diff --git
a/packages/form-code-generator-patternfly-theme/src/uniforms/TextField.tsx
b/packages/form-code-generator-patternfly-theme/src/uniforms/TextField.tsx
index 564ca19f50d..eee5e60dc34 100644
--- a/packages/form-code-generator-patternfly-theme/src/uniforms/TextField.tsx
+++ b/packages/form-code-generator-patternfly-theme/src/uniforms/TextField.tsx
@@ -61,6 +61,7 @@ const Text: React.FC<TextFieldProps> = (props:
TextFieldProps) => {
required: props.required,
},
disabled: props.disabled,
+ itemProps: props.itemProps,
});
};
@@ -85,6 +86,7 @@ const Text: React.FC<TextFieldProps> = (props:
TextFieldProps) => {
required: props.required,
},
disabled: props.disabled,
+ itemProps: props.itemProps,
});
};
diff --git
a/packages/form-code-generator-patternfly-theme/src/uniforms/UnsupportedField.tsx
b/packages/form-code-generator-patternfly-theme/src/uniforms/UnsupportedField.tsx
index ec278640a1d..6f783ab0d38 100644
---
a/packages/form-code-generator-patternfly-theme/src/uniforms/UnsupportedField.tsx
+++
b/packages/form-code-generator-patternfly-theme/src/uniforms/UnsupportedField.tsx
@@ -23,6 +23,7 @@ import { buildDefaultInputElement, getInputReference,
renderField } from "./util
import { connectField, HTMLFieldProps } from "uniforms/cjs";
import { useAddFormElementToContext } from "./CodeGenContext";
import { DEFAULT_DATA_TYPE_ANY_ARRAY, DEFAULT_DATA_TYPE_OBJECT } from
"./utils/dataTypes";
+import { ListItemProps } from "./rendering/ListItemField";
export type UnsupportedFieldProps = HTMLFieldProps<
any,
@@ -30,6 +31,7 @@ export type UnsupportedFieldProps = HTMLFieldProps<
{
label: string;
required: boolean;
+ itemProps?: ListItemProps;
}
>;
@@ -56,6 +58,7 @@ const Unsupported: React.FC<UnsupportedFieldProps> = (props:
UnsupportedFieldPro
required: props.required,
},
disabled: props.disabled,
+ itemProps: props.itemProps,
});
useAddFormElementToContext(element);
diff --git
a/packages/form-code-generator-patternfly-theme/src/uniforms/rendering/ListItemField.tsx
b/packages/form-code-generator-patternfly-theme/src/uniforms/rendering/ListItemField.tsx
index ba170e07acf..c8c3ba7fec6 100644
---
a/packages/form-code-generator-patternfly-theme/src/uniforms/rendering/ListItemField.tsx
+++
b/packages/form-code-generator-patternfly-theme/src/uniforms/rendering/ListItemField.tsx
@@ -21,6 +21,9 @@ import { Context } from "uniforms";
import * as React from "react";
import { CodeGenContext, CodeGenContextProvider } from "../CodeGenContext";
import AutoField from "../AutoField";
+import { NS_SEPARATOR } from "../utils/Utils";
+
+export const DEFAULT_ITEM_INDEX_NAME = "itemIndex";
export interface ListItemProps {
isListItem: boolean;
@@ -34,7 +37,7 @@ export interface ListItemProps {
* The list item can be nested or not (be part of an object).
* For non-nested items the `itemName` will have value "$", for nested items
it will have its property name
*/
-function getItemNameAndWithIsNested(name: string) {
+export function getItemNameAndWithIsNested(name: string) {
const itemName = name.split(".").pop() ?? "$";
const isNested = itemName !== "$";
return { itemName, isNested };
@@ -42,12 +45,15 @@ function getItemNameAndWithIsNested(name: string) {
/**
* This function can either return:
- * `listName.$`
+ * `listName.${index}`
* `listName.${index}.itemName`
*/
export const getListItemName = ({ itemProps, name }: { itemProps:
ListItemProps; name: string }) => {
const { itemName, isNested } = getItemNameAndWithIsNested(name);
- return `\`${itemProps?.listName}${isNested ?
`.$\{${itemProps?.indexVariableName}}.${itemName}` :
`.$\{${itemProps?.indexVariableName}}`}\``;
+ if (isNested) {
+ return
`\`${itemProps?.listStateName}.$\{${itemProps?.indexVariableName}}.${itemName}\``;
+ }
+ return `\`${itemProps?.listStateName}.$\{${itemProps?.indexVariableName}}\``;
};
/**
@@ -65,7 +71,7 @@ export const getListItemValue = ({
callback?: (value: string) => string;
}) => {
const { itemName, isNested } = getItemNameAndWithIsNested(name);
- const property =
`${itemProps?.listStateName}[${itemProps?.indexVariableName}]${isNested ?
`.${itemName}` : ""}`;
+ const property =
`${itemProps?.listStateName}?.[${itemProps?.indexVariableName}]${isNested ?
`.${itemName}` : ""}`;
return `${callback ? callback(property) : property}`;
};
@@ -96,16 +102,23 @@ export const getListItemOnChange = ({
overrideNewValue?: string;
}) => {
const { itemName, isNested } = getItemNameAndWithIsNested(name);
- return `
- ${overrideParam ? overrideParam : "newValue"} => {
+ const propertyPath = itemProps?.listStateName.split(".").splice(1).join(".");
+ return `${overrideParam ? overrideParam : "newValue"} => {
${itemProps?.listStateSetter}(s => {
const newState = [...s];
- newState[${itemProps?.indexVariableName}]${isNested ? `.${itemName}` :
""} = ${callback ? callback(overrideNewValue ? overrideNewValue : "newValue") :
overrideNewValue ? overrideNewValue : "newValue"};
+ newState${propertyPath}[${itemProps?.indexVariableName}]${isNested ?
`.${itemName}` : ""} = ${callback ? callback(overrideNewValue ?
overrideNewValue : "newValue") : overrideNewValue ? overrideNewValue :
"newValue"};
return newState;
})
}`;
};
+export function getNextIndexVariableName(itemProps?: ListItemProps) {
+ if (itemProps === undefined) {
+ return DEFAULT_ITEM_INDEX_NAME;
+ }
+ return `nested${NS_SEPARATOR}${itemProps.indexVariableName}`;
+}
+
export interface Props {
codegenCtx: CodeGenContext;
uniformsContext: Context<any>;
diff --git
a/packages/form-code-generator-patternfly-theme/src/uniforms/staticCode/staticCodeBlocks.ts
b/packages/form-code-generator-patternfly-theme/src/uniforms/staticCode/staticCodeBlocks.ts
index 64ba4019cb7..9c3def2f9a3 100644
---
a/packages/form-code-generator-patternfly-theme/src/uniforms/staticCode/staticCodeBlocks.ts
+++
b/packages/form-code-generator-patternfly-theme/src/uniforms/staticCode/staticCodeBlocks.ts
@@ -17,25 +17,39 @@
* under the License.
*/
-import dateFunctions from "!!raw-loader!../../resources/dateFunctions.txt";
-import timeFunctions from "!!raw-loader!../../resources/timeFunctions.txt";
-import selectFunctions from "!!raw-loader!../../resources/selectFunctions.txt";
-import multipleSelectFunctions from
"!!raw-loader!../../resources/multipleSelectFunctions.txt";
-import checkboxGroupFunctions from
"!!raw-loader!../../resources/checkboxGroupFunctions.txt";
-
export const DATE_FUNCTIONS = "date_functions";
-export const TIME_FUNCTIONS = "time_functions";
-export const SELECT_FUNCTIONS = "select_functions";
-export const MULTIPLE_SELECT_FUNCTIONS = "multiple_select_functions";
-export const CHECKBOX_GROUP_FUNCTIONS = "checkbox_group_functions";
const _staticBlocks: Map<string, string> = new Map<string, string>();
-_staticBlocks.set(DATE_FUNCTIONS, dateFunctions);
-_staticBlocks.set(TIME_FUNCTIONS, timeFunctions);
-_staticBlocks.set(SELECT_FUNCTIONS, selectFunctions);
-_staticBlocks.set(MULTIPLE_SELECT_FUNCTIONS, multipleSelectFunctions);
-_staticBlocks.set(CHECKBOX_GROUP_FUNCTIONS, checkboxGroupFunctions);
+_staticBlocks.set(
+ DATE_FUNCTIONS,
+ `
+const parseDate = (date?: string): string => {
+ if (!date) {
+ return '';
+ }
+ const dateValue: Date = new Date(Date.parse(date));
+ return dateValue.toISOString().slice(0, -14);
+}
+
+const parseTime = (date?: string): string => {
+ if (!date) {
+ return '';
+ }
+ const dateValue: Date = new Date(Date.parse(date));
+ let isAm = true;
+ let hours = dateValue.getHours();
+ if (hours > 12) {
+ hours %= 12;
+ isAm = false;
+ }
+ let minutes = dateValue.getMinutes().toString();
+ if (minutes.length == 1) {
+ minutes = '0' + minutes;
+ }
+ return \`\${hours}:\${minutes} \${isAm ? 'AM' : 'PM'}\`;
+}`
+);
export const getStaticCodeBlock = (blockName: string): string | undefined => {
return _staticBlocks.get(blockName);
diff --git
a/packages/form-code-generator-patternfly-theme/src/uniforms/utils/Utils.tsx
b/packages/form-code-generator-patternfly-theme/src/uniforms/utils/Utils.tsx
index 0b8ee0bbe80..be5bfc7b571 100644
--- a/packages/form-code-generator-patternfly-theme/src/uniforms/utils/Utils.tsx
+++ b/packages/form-code-generator-patternfly-theme/src/uniforms/utils/Utils.tsx
@@ -20,11 +20,21 @@
import * as React from "react";
import { DataType, FormElement, FormInput, InputReference, InputsContainer }
from "../../api";
import { DEFAULT_DATA_TYPE_OBJECT } from "./dataTypes";
+import { ListItemProps } from "../rendering/ListItemField";
export const NS_SEPARATOR = "__";
export const FIELD_SET_PREFFIX = `set`;
-export const getInputReference = (binding: string, dataType: DataType):
InputReference => {
+export const getInputReference = (binding: string, dataType: DataType,
itemProps?: ListItemProps): InputReference => {
+ if (itemProps) {
+ const [_, property] = binding.split("$");
+ return {
+ binding: binding.replace("$", `[${itemProps.indexVariableName}]`),
+ stateName:
`${itemProps.listStateName}?.[${itemProps.indexVariableName}]${property}`,
+ stateSetter: itemProps.listStateSetter,
+ dataType,
+ };
+ }
const stateName = binding.split(".").join(NS_SEPARATOR);
const stateSetter = `${FIELD_SET_PREFFIX}${NS_SEPARATOR}${stateName}`;
return {
@@ -56,6 +66,7 @@ type DefaultInputProps = {
requiredCode?: string[];
wrapper: WrapperProps;
disabled: boolean;
+ itemProps: ListItemProps | undefined;
};
type WrapperProps = {
@@ -72,6 +83,7 @@ export const buildDefaultInputElement = ({
wrapper,
requiredCode,
disabled,
+ itemProps,
}: DefaultInputProps): FormInput => {
const stateCode = getStateCodeFromRef(ref);
@@ -90,9 +102,9 @@ export const buildDefaultInputElement = ({
pfImports,
pfIconImports,
reactImports: ["useState"],
- requiredCode: requiredCode,
+ requiredCode,
jsxCode,
- stateCode,
+ stateCode: itemProps?.isListItem ? "" : stateCode,
isReadonly: disabled,
};
};
diff --git
a/packages/form-code-generator-patternfly-theme/tests/AutoField.test.tsx
b/packages/form-code-generator-patternfly-theme/tests/AutoField.test.tsx
index 63f64a00da9..9acec4d43a5 100644
--- a/packages/form-code-generator-patternfly-theme/tests/AutoField.test.tsx
+++ b/packages/form-code-generator-patternfly-theme/tests/AutoField.test.tsx
@@ -21,13 +21,7 @@ import * as React from "react";
import SimpleSchema from "simpl-schema";
import { renderField } from "./_render";
import { AutoField } from "../src/uniforms";
-import {
- CHECKBOX_GROUP_FUNCTIONS,
- DATE_FUNCTIONS,
- MULTIPLE_SELECT_FUNCTIONS,
- SELECT_FUNCTIONS,
- TIME_FUNCTIONS,
-} from "../src/uniforms/staticCode/staticCodeBlocks";
+import { DATE_FUNCTIONS } from "../src/uniforms/staticCode/staticCodeBlocks";
import { SELECT_IMPORTS } from "../src/uniforms/SelectField";
const schema = {
@@ -97,10 +91,6 @@ describe("<AutoField> tests", () => {
expect(formElement.pfImports).toContain("FormGroup");
expect(formElement.pfImports).toContain("Checkbox");
-
- expect(formElement.requiredCode).not.toBeUndefined();
- expect(formElement.requiredCode).toHaveLength(1);
- expect(formElement.requiredCode).toContain(CHECKBOX_GROUP_FUNCTIONS);
});
it("<DateField> - rendering", () => {
@@ -109,9 +99,8 @@ describe("<AutoField> tests", () => {
expect(formElement.pfImports).toContain("FormGroup");
expect(formElement.pfImports).toContain("DatePicker");
expect(formElement.pfImports).toContain("TimePicker");
- expect(formElement.requiredCode).toHaveLength(2);
+ expect(formElement.requiredCode).toHaveLength(1);
expect(formElement.requiredCode).toContain(DATE_FUNCTIONS);
- expect(formElement.requiredCode).toContain(TIME_FUNCTIONS);
});
it("<ListField> - rendering", () => {
@@ -157,10 +146,6 @@ describe("<AutoField> tests", () => {
expect(formElement.pfImports).toHaveLength(SELECT_IMPORTS.length);
SELECT_IMPORTS.forEach((pfImport) =>
expect(formElement.pfImports).toContain(pfImport));
-
- expect(formElement.requiredCode).not.toBeUndefined();
- expect(formElement.requiredCode).toHaveLength(1);
- expect(formElement.requiredCode).toContain(SELECT_FUNCTIONS);
});
it("<SelectField> - multiple value rendering", () => {
@@ -169,10 +154,6 @@ describe("<AutoField> tests", () => {
expect(formElement.reactImports).toContain("useState");
expect(formElement.pfImports).toHaveLength(SELECT_IMPORTS.length);
SELECT_IMPORTS.forEach((pfImport) =>
expect(formElement.pfImports).toContain(pfImport));
-
- expect(formElement.requiredCode).not.toBeUndefined();
- expect(formElement.requiredCode).toHaveLength(1);
- expect(formElement.requiredCode).toContain(MULTIPLE_SELECT_FUNCTIONS);
});
it("<TextField> - TextInput rendering", () => {
diff --git
a/packages/form-code-generator-patternfly-theme/tests/AutoForm.test.tsx
b/packages/form-code-generator-patternfly-theme/tests/AutoForm.test.tsx
index bfcf22fc40f..33b66a0f567 100644
--- a/packages/form-code-generator-patternfly-theme/tests/AutoForm.test.tsx
+++ b/packages/form-code-generator-patternfly-theme/tests/AutoForm.test.tsx
@@ -72,6 +72,8 @@ const schema = {
"friends.$": Object,
"friends.$.name": { type: String },
"friends.$.age": { type: Number },
+ "friends.$.know": { type: Array },
+ "friends.$.know.$": { type: String },
};
const props: AutoFormProps = {
diff --git
a/packages/form-code-generator-patternfly-theme/tests/BoolField.test.tsx
b/packages/form-code-generator-patternfly-theme/tests/BoolField.test.tsx
index 683759743ba..f08adaa907c 100644
--- a/packages/form-code-generator-patternfly-theme/tests/BoolField.test.tsx
+++ b/packages/form-code-generator-patternfly-theme/tests/BoolField.test.tsx
@@ -35,9 +35,9 @@ describe("<BoolField> tests", () => {
onChange: jest.fn(),
};
- const { container, formElement } = renderField(BoolField, props, schema);
+ const { formElement } = renderField(BoolField, props, schema);
- expect(container).toMatchSnapshot();
+ expect(formElement.jsxCode).toMatchSnapshot();
expect(formElement.reactImports).toContain("useState");
expect(formElement.pfImports).toContain("FormGroup");
@@ -63,9 +63,9 @@ describe("<BoolField> tests", () => {
onChange: jest.fn(),
};
- const { container, formElement } = renderField(BoolField, props, schema);
+ const { formElement } = renderField(BoolField, props, schema);
- expect(container).toMatchSnapshot();
+ expect(formElement.jsxCode).toMatchSnapshot();
expect(formElement.jsxCode).not.toBeNull();
expect(formElement.jsxCode).toContain(`label={'${props.label}'}`);
diff --git
a/packages/form-code-generator-patternfly-theme/tests/CheckBoxGroupField.test.tsx
b/packages/form-code-generator-patternfly-theme/tests/CheckBoxGroupField.test.tsx
index a4376b771a5..6308bc57cd2 100644
---
a/packages/form-code-generator-patternfly-theme/tests/CheckBoxGroupField.test.tsx
+++
b/packages/form-code-generator-patternfly-theme/tests/CheckBoxGroupField.test.tsx
@@ -19,20 +19,8 @@
import * as React from "react";
import { renderField } from "./_render";
-import { CHECKBOX_GROUP_FUNCTIONS } from
"../src/uniforms/staticCode/staticCodeBlocks";
import { CheckBoxGroupField } from "../src/uniforms";
-const schema = {
- roles: {
- type: Array,
- allowedValues: ["Developer", "HR", "UX"],
- uniforms: {
- checkboxes: true,
- },
- },
- "roles.$": String,
-};
-
describe("<CheckBoxGroupField> tests", () => {
it("<CheckBoxGroupField> - rendering", () => {
const props = {
@@ -44,18 +32,24 @@ describe("<CheckBoxGroupField> tests", () => {
onChange: jest.fn(),
};
- const { container, formElement } = renderField(CheckBoxGroupField, props,
schema);
+ const { formElement } = renderField(CheckBoxGroupField, props, {
+ roles: {
+ type: Array,
+ allowedValues: ["Developer", "HR", "UX"],
+ uniforms: {
+ checkboxes: true,
+ },
+ },
+ "roles.$": String,
+ });
- expect(container).toMatchSnapshot();
+ expect(formElement.jsxCode).toMatchSnapshot();
expect(formElement.reactImports).toContain("useState");
expect(formElement.pfImports).toContain("FormGroup");
expect(formElement.pfImports).toContain("Checkbox");
- expect(formElement.requiredCode).not.toBeUndefined();
- expect(formElement.requiredCode).toHaveLength(1);
- expect(formElement.requiredCode).toContain(CHECKBOX_GROUP_FUNCTIONS);
expect(formElement.ref.binding).toBe(props.name);
expect(formElement.ref.stateName).toBe(props.name);
expect(formElement.ref.stateSetter).toBe(`set__${props.name}`);
@@ -74,9 +68,6 @@ describe("<CheckBoxGroupField> tests", () => {
expect(formElement.jsxCode).toContain(checkbox);
expect(formElement.jsxCode).toContain(`label={'${value}'}`);
expect(formElement.jsxCode).toContain(`isChecked={${formElement.ref.stateName}.indexOf('${value}')
!== -1}`);
- expect(formElement.jsxCode).toContain(
- `onChange={() => handleCheckboxGroupChange('${value}',
${formElement.ref.stateName}, ${formElement.ref.stateSetter})}`
- );
expect(formElement.jsxCode).toContain(`value={'${value}'}`);
});
expect(formElement.stateCode).not.toBeNull();
diff --git
a/packages/form-code-generator-patternfly-theme/tests/DateField.test.tsx
b/packages/form-code-generator-patternfly-theme/tests/DateField.test.tsx
index 03f3b97c874..468cb5afe35 100644
--- a/packages/form-code-generator-patternfly-theme/tests/DateField.test.tsx
+++ b/packages/form-code-generator-patternfly-theme/tests/DateField.test.tsx
@@ -19,7 +19,7 @@
import * as React from "react";
import { renderField } from "./_render";
-import { DATE_FUNCTIONS, TIME_FUNCTIONS } from
"../src/uniforms/staticCode/staticCodeBlocks";
+import { DATE_FUNCTIONS } from "../src/uniforms/staticCode/staticCodeBlocks";
import { DateField } from "../src/uniforms";
const schema = {
@@ -36,18 +36,17 @@ describe("<DateField> tests", () => {
onChange: jest.fn(),
};
- const { container, formElement } = renderField(DateField, props, schema);
+ const { formElement } = renderField(DateField, props, schema);
- expect(container).toMatchSnapshot();
+ expect(formElement.jsxCode).toMatchSnapshot();
expect(formElement.reactImports).toContain("useState");
expect(formElement.pfImports).toContain("FormGroup");
expect(formElement.pfImports).toContain("DatePicker");
expect(formElement.pfImports).toContain("TimePicker");
expect(formElement.requiredCode).not.toBeUndefined();
- expect(formElement.requiredCode).toHaveLength(2);
+ expect(formElement.requiredCode).toHaveLength(1);
expect(formElement.requiredCode).toContain(DATE_FUNCTIONS);
- expect(formElement.requiredCode).toContain(TIME_FUNCTIONS);
expect(formElement.ref.binding).toBe(props.name);
expect(formElement.ref.stateName).toBe(props.name);
expect(formElement.ref.stateSetter).toBe(`set__${props.name}`);
@@ -56,14 +55,8 @@ describe("<DateField> tests", () => {
expect(formElement.jsxCode).toContain(`label={'${props.label}'}`);
expect(formElement.jsxCode).toContain(`name={'${props.name}'}`);
expect(formElement.jsxCode).toContain("isDisabled={false}");
- expect(formElement.jsxCode).toContain(
- `onChange={newDate => onDateChange(newDate,
${formElement.ref.stateSetter}, ${formElement.ref.stateName})}`
- );
expect(formElement.jsxCode).toContain(`value={parseDate(${formElement.ref.stateName})}`);
expect(formElement.jsxCode).toContain(`time={parseTime(${formElement.ref.stateName})}`);
- expect(formElement.jsxCode).toContain(
- `onChange={(time, hours?, minutes?) => onTimeChange(time,
${formElement.ref.stateSetter}, ${formElement.ref.stateName}, hours, minutes)}`
- );
expect(formElement.stateCode).not.toBeNull();
});
@@ -76,9 +69,9 @@ describe("<DateField> tests", () => {
onChange: jest.fn(),
};
- const { container, formElement } = renderField(DateField, props, schema);
+ const { formElement } = renderField(DateField, props, schema);
- expect(container).toMatchSnapshot();
+ expect(formElement.jsxCode).toMatchSnapshot();
expect(formElement.jsxCode).not.toBeNull();
expect(formElement.jsxCode).toContain(`label={'${props.label}'}`);
diff --git
a/packages/form-code-generator-patternfly-theme/tests/ListField.test.tsx
b/packages/form-code-generator-patternfly-theme/tests/ListField.test.tsx
index 599378d9045..4ea21c23f37 100644
--- a/packages/form-code-generator-patternfly-theme/tests/ListField.test.tsx
+++ b/packages/form-code-generator-patternfly-theme/tests/ListField.test.tsx
@@ -20,10 +20,11 @@
import { renderField } from "./_render";
import { ListField } from "../src/uniforms";
import { InputsContainer } from "../src/api";
+import { DATE_FUNCTIONS } from "../src/uniforms/staticCode/staticCodeBlocks";
describe("<ListField> tests", () => {
it("<ListField>", () => {
- const { container, formElement } = renderField(
+ const { formElement } = renderField(
ListField,
{
id: "id",
@@ -40,21 +41,33 @@ describe("<ListField> tests", () => {
"friends.$.married": { type: Boolean },
"friends.$.know": {
type: Array,
- allowedValues: ["Java", "Node", "Docker"],
uniforms: {
checkboxes: true,
},
},
"friends.$.know.$": String,
"friends.$.areas": {
- type: String,
+ type: Array,
allowedValues: ["Developer", "HR", "UX"],
},
+ "friends.$.areas.$": String,
"friends.$.birthday": { type: Date },
+ "friends.$.transport": {
+ type: Array,
+ allowedValues: ["Taxi", "Uber"],
+ uniforms: {
+ checkboxes: true,
+ },
+ },
+ "friends.$.transport.$": String,
+ "friends.$.children": {
+ type: String,
+ allowedValues: ["0", "1", "2+"],
+ },
}
);
-
- expect(container).toMatchSnapshot();
+ expect(formElement.jsxCode).toMatchSnapshot();
+ expect(formElement.stateCode).toMatchSnapshot();
const inputContainer = formElement as InputsContainer;
expect(inputContainer.pfImports).toStrictEqual([
@@ -77,5 +90,9 @@ describe("<ListField> tests", () => {
"TimePicker",
]);
expect(inputContainer.pfIconImports).toStrictEqual(["PlusCircleIcon",
"MinusCircleIcon"]);
+
+ expect(inputContainer.requiredCode).not.toBeUndefined();
+ expect(inputContainer.requiredCode).toHaveLength(1);
+ expect(inputContainer.requiredCode).toContain(DATE_FUNCTIONS);
});
});
diff --git
a/packages/form-code-generator-patternfly-theme/tests/NestField.test.tsx
b/packages/form-code-generator-patternfly-theme/tests/NestField.test.tsx
index 458f1f7f3c4..6032482f174 100644
--- a/packages/form-code-generator-patternfly-theme/tests/NestField.test.tsx
+++ b/packages/form-code-generator-patternfly-theme/tests/NestField.test.tsx
@@ -39,7 +39,7 @@ const schema = {
describe("<NestField> tests", () => {
it("<NestField> - rendering", () => {
- const { container, formElement } = renderField(
+ const { formElement } = renderField(
NestField,
{
id: "id",
@@ -50,7 +50,7 @@ describe("<NestField> tests", () => {
schema
);
- expect(container).toMatchSnapshot();
+ expect(formElement.jsxCode).toMatchSnapshot();
const inputContainer = formElement as InputsContainer;
expect(inputContainer.pfImports).toStrictEqual([
diff --git
a/packages/form-code-generator-patternfly-theme/tests/NumField.test.tsx
b/packages/form-code-generator-patternfly-theme/tests/NumField.test.tsx
index 852e43ed163..565addb2804 100644
--- a/packages/form-code-generator-patternfly-theme/tests/NumField.test.tsx
+++ b/packages/form-code-generator-patternfly-theme/tests/NumField.test.tsx
@@ -39,9 +39,9 @@ describe("<NumField> tests", () => {
onChange: jest.fn(),
};
- const { container, formElement } = renderField(NumField, props, schema);
+ const { formElement } = renderField(NumField, props, schema);
- expect(container).toMatchSnapshot();
+ expect(formElement.jsxCode).toMatchSnapshot();
expect(formElement.reactImports).toContain("useState");
expect(formElement.pfImports).toContain("FormGroup");
@@ -99,9 +99,9 @@ describe("<NumField> tests", () => {
onChange: jest.fn(),
};
- const { container, formElement } = renderField(NumField, props, schema);
+ const { formElement } = renderField(NumField, props, schema);
- expect(container).toMatchSnapshot();
+ expect(formElement.jsxCode).toMatchSnapshot();
expect(formElement.reactImports).toContain("useState");
expect(formElement.pfImports).toContain("FormGroup");
@@ -132,9 +132,9 @@ describe("<NumField> tests", () => {
onChange: jest.fn(),
};
- const { container, formElement } = renderField(NumField, props, schema);
+ const { formElement } = renderField(NumField, props, schema);
- expect(container).toMatchSnapshot();
+ expect(formElement.jsxCode).toMatchSnapshot();
expect(formElement.reactImports).toContain("useState");
expect(formElement.pfImports).toContain("FormGroup");
diff --git
a/packages/form-code-generator-patternfly-theme/tests/RadioField.test.tsx
b/packages/form-code-generator-patternfly-theme/tests/RadioField.test.tsx
index f0dfd012d2f..72bb13377a8 100644
--- a/packages/form-code-generator-patternfly-theme/tests/RadioField.test.tsx
+++ b/packages/form-code-generator-patternfly-theme/tests/RadioField.test.tsx
@@ -21,13 +21,6 @@ import * as React from "react";
import { RadioField } from "../src/uniforms";
import { renderField } from "./_render";
-const schema = {
- role: {
- type: String,
- allowedValues: ["Developer", "HR", "UX"],
- },
-};
-
describe("<RadioField> tests", () => {
it("<RadioField> - rendering", () => {
const props = {
@@ -38,9 +31,14 @@ describe("<RadioField> tests", () => {
allowedValues: ["Developer", "HR", "UX"],
onChange: jest.fn(),
};
- const { container, formElement } = renderField(RadioField, props, schema);
+ const { formElement } = renderField(RadioField, props, {
+ role: {
+ type: String,
+ allowedValues: ["Developer", "HR", "UX"],
+ },
+ });
- expect(container).toMatchSnapshot();
+ expect(formElement.jsxCode).toMatchSnapshot();
expect(formElement.reactImports).toContain("useState");
expect(formElement.pfImports).toContain("FormGroup");
@@ -68,9 +66,14 @@ describe("<RadioField> tests", () => {
onChange: jest.fn(),
};
- const { container, formElement } = renderField(RadioField, props, schema);
+ const { formElement } = renderField(RadioField, props, {
+ role: {
+ type: String,
+ allowedValues: ["Developer", "HR", "UX"],
+ },
+ });
- expect(container).toMatchSnapshot();
+ expect(formElement.jsxCode).toMatchSnapshot();
expect(formElement.jsxCode).not.toBeNull();
expect(formElement.jsxCode).toContain(`label={'${props.label}'}`);
diff --git
a/packages/form-code-generator-patternfly-theme/tests/SelectField.test.tsx
b/packages/form-code-generator-patternfly-theme/tests/SelectField.test.tsx
index 8a48bf4eeac..e7ea63407ec 100644
--- a/packages/form-code-generator-patternfly-theme/tests/SelectField.test.tsx
+++ b/packages/form-code-generator-patternfly-theme/tests/SelectField.test.tsx
@@ -19,23 +19,10 @@
import * as React from "react";
import { renderField } from "./_render";
-import { MULTIPLE_SELECT_FUNCTIONS, SELECT_FUNCTIONS } from
"../src/uniforms/staticCode/staticCodeBlocks";
import { SelectField } from "../src/uniforms";
import { NS_SEPARATOR } from "../src/uniforms/utils/Utils";
import { SELECT_IMPORTS } from "../src/uniforms/SelectField";
-const schema = {
- role: {
- type: String,
- allowedValues: ["Developer", "HR", "UX"],
- },
- otherPositions: {
- type: Array,
- allowedValues: ["Developer", "HR", "UX"],
- },
- "otherPositions.$": String,
-};
-
describe("<SelectField> tests", () => {
it("<SelectField> - single value rendering", () => {
const props = {
@@ -47,17 +34,19 @@ describe("<SelectField> tests", () => {
onChange: jest.fn(),
};
- const { container, formElement } = renderField(SelectField, props, schema);
+ const { formElement } = renderField(SelectField, props, {
+ role: {
+ type: String,
+ allowedValues: ["Developer", "HR", "UX"],
+ },
+ });
- expect(container).toMatchSnapshot();
+ expect(formElement.jsxCode).toMatchSnapshot();
expect(formElement.reactImports).toContain("useState");
expect(formElement.pfImports).toHaveLength(SELECT_IMPORTS.length);
SELECT_IMPORTS.forEach((pfImport) =>
expect(formElement.pfImports).toContain(pfImport));
- expect(formElement.requiredCode).not.toBeUndefined();
- expect(formElement.requiredCode).toHaveLength(1);
- expect(formElement.requiredCode).toContain(SELECT_FUNCTIONS);
expect(formElement.ref.binding).toBe(props.name);
expect(formElement.ref.stateName).toBe(props.name);
expect(formElement.ref.stateSetter).toBe(`set__${props.name}`);
@@ -71,9 +60,6 @@ describe("<SelectField> tests", () => {
const expandedStateName =
`${formElement.ref.stateName}${NS_SEPARATOR}expanded`;
const expandedStateNameSetter =
`${formElement.ref.stateSetter}${NS_SEPARATOR}expanded`;
expect(formElement.jsxCode).toContain(`isOpen={${expandedStateName}}`);
- expect(formElement.jsxCode).toContain(
- `handleSelect(value, isPlaceHolder, ${formElement.ref.stateName},
${formElement.ref.stateSetter}, ${expandedStateNameSetter})`
- );
expect(formElement.jsxCode).toContain(`selections={${formElement.ref.stateName}}`);
expect(formElement.jsxCode).toContain(`onToggle={(isOpen) =>
${expandedStateNameSetter}(isOpen)}`);
@@ -96,17 +82,20 @@ describe("<SelectField> tests", () => {
onChange: jest.fn(),
};
- const { container, formElement } = renderField(SelectField, props, schema);
+ const { formElement } = renderField(SelectField, props, {
+ otherPositions: {
+ type: Array,
+ allowedValues: ["Developer", "HR", "UX"],
+ },
+ "otherPositions.$": String,
+ });
- expect(container).toMatchSnapshot();
+ expect(formElement.jsxCode).toMatchSnapshot();
expect(formElement.reactImports).toContain("useState");
expect(formElement.pfImports).toHaveLength(SELECT_IMPORTS.length);
SELECT_IMPORTS.forEach((pfImport) =>
expect(formElement.pfImports).toContain(pfImport));
- expect(formElement.requiredCode).not.toBeUndefined();
- expect(formElement.requiredCode).toHaveLength(1);
- expect(formElement.requiredCode).toContain(MULTIPLE_SELECT_FUNCTIONS);
expect(formElement.ref.binding).toBe(props.name);
expect(formElement.ref.stateName).toBe(props.name);
expect(formElement.ref.stateSetter).toBe(`set__${props.name}`);
@@ -120,9 +109,6 @@ describe("<SelectField> tests", () => {
const expandedStateName =
`${formElement.ref.stateName}${NS_SEPARATOR}expanded`;
const expandedStateNameSetter =
`${formElement.ref.stateSetter}${NS_SEPARATOR}expanded`;
expect(formElement.jsxCode).toContain(`isOpen={${expandedStateName}}`);
- expect(formElement.jsxCode).toContain(
- `handleMultipleSelect(value, isPlaceHolder,
${formElement.ref.stateName}, ${formElement.ref.stateSetter})`
- );
expect(formElement.jsxCode).toContain(`selections={${formElement.ref.stateName}}`);
expect(formElement.jsxCode).toContain(`onToggle={(isOpen) =>
${expandedStateNameSetter}(isOpen)}`);
diff --git
a/packages/form-code-generator-patternfly-theme/tests/TextField.test.tsx
b/packages/form-code-generator-patternfly-theme/tests/TextField.test.tsx
index b76a67aebe3..48b8e4b137c 100644
--- a/packages/form-code-generator-patternfly-theme/tests/TextField.test.tsx
+++ b/packages/form-code-generator-patternfly-theme/tests/TextField.test.tsx
@@ -36,9 +36,9 @@ describe("<TextField> tests", () => {
onChange: jest.fn(),
};
- const { container, formElement } = renderField(TextField, props, schema);
+ const { formElement } = renderField(TextField, props, schema);
- expect(container).toMatchSnapshot();
+ expect(formElement.jsxCode).toMatchSnapshot();
expect(formElement.reactImports).toContain("useState");
expect(formElement.pfImports).toContain("FormGroup");
@@ -64,9 +64,9 @@ describe("<TextField> tests", () => {
onChange: jest.fn(),
};
- const { container, formElement } = renderField(TextField, props, schema);
+ const { formElement } = renderField(TextField, props, schema);
- expect(container).toMatchSnapshot();
+ expect(formElement.jsxCode).toMatchSnapshot();
expect(formElement.jsxCode).not.toBeNull();
expect(formElement.jsxCode).toContain(`label={'${props.label}'}`);
@@ -84,9 +84,9 @@ describe("<TextField> tests", () => {
onChange: jest.fn(),
};
- const { container, formElement } = renderField(TextField, props, schema);
+ const { formElement } = renderField(TextField, props, schema);
- expect(container).toMatchSnapshot();
+ expect(formElement.jsxCode).toMatchSnapshot();
expect(formElement.reactImports).toContain("useState");
expect(formElement.pfImports).toContain("FormGroup");
@@ -119,9 +119,9 @@ describe("<TextField> tests", () => {
onChange: jest.fn(),
};
- const { container, formElement } = renderField(TextField, props, schema);
+ const { formElement } = renderField(TextField, props, schema);
- expect(container).toMatchSnapshot();
+ expect(formElement.jsxCode).toMatchSnapshot();
expect(formElement.jsxCode).not.toBeNull();
expect(formElement.jsxCode).toContain(`label={'${props.label}'}`);
diff --git
a/packages/form-code-generator-patternfly-theme/tests/UnsupportedField.test.tsx
b/packages/form-code-generator-patternfly-theme/tests/UnsupportedField.test.tsx
index bce35fac85d..b3c13051b1a 100644
---
a/packages/form-code-generator-patternfly-theme/tests/UnsupportedField.test.tsx
+++
b/packages/form-code-generator-patternfly-theme/tests/UnsupportedField.test.tsx
@@ -38,9 +38,9 @@ describe("<UnsupportedField> tests", () => {
onChange: jest.fn(),
};
- const { container, formElement } = renderField(UnsupportedField, props,
schema);
+ const { formElement } = renderField(UnsupportedField, props, schema);
- expect(container).toMatchSnapshot();
+ expect(formElement.jsxCode).toMatchSnapshot();
expect(formElement.reactImports).toContain("useState");
expect(formElement.pfImports).toContain("FormGroup");
diff --git
a/packages/form-code-generator-patternfly-theme/tests/__snapshots__/AutoForm.test.tsx.snap
b/packages/form-code-generator-patternfly-theme/tests/__snapshots__/AutoForm.test.tsx.snap
index 65a635d6f79..e24198a44c9 100644
---
a/packages/form-code-generator-patternfly-theme/tests/__snapshots__/AutoForm.test.tsx.snap
+++
b/packages/form-code-generator-patternfly-theme/tests/__snapshots__/AutoForm.test.tsx.snap
@@ -153,16 +153,30 @@ const Form__HRInterview: React.FC<any> = (props:
any) => {
});
setFormApi(api);
}, []);
- {
- }
- {
- }
- {
- }
- {
- }
- {
- }
+ const parseDate = (date?: string): string => {
+ if (!date) {
+ return '';
+ }
+ const dateValue: Date = new Date(Date.parse(date));
+ return dateValue.toISOString().slice(0, -14);
+ };
+ const parseTime = (date?: string): string => {
+ if (!date) {
+ return '';
+ }
+ const dateValue: Date = new Date(Date.parse(date));
+ let isAm = true;
+ let hours = dateValue.getHours();
+ if (hours > 12) {
+ hours %= 12;
+ isAm = false;
+ }
+ let minutes = dateValue.getMinutes().toString();
+ if (minutes.length == 1) {
+ minutes = '0' + minutes;
+ }
+ return \`\${hours}:\${minutes} \${isAm ? 'AM' :
'PM'}\`;
+ };
return (
<div className={'pf-c-form'}>
<Card>
@@ -303,15 +317,18 @@ const Form__HRInterview: React.FC<any> = (props:
any) => {
isOpen={interview__position__expanded}
selections={interview__position}
onToggle={(isOpen)
=> set__interview__position__expanded(isOpen)}
- onSelect={(event,
value, isPlaceHolder) =>
- handleSelect(
- value,
-
isPlaceHolder,
-
interview__position,
-
set__interview__position,
-
set__interview__position__expanded
- )
- }
+ onSelect={(event,
value, isPlaceHolder) => {
+ if
(isPlaceHolder) {
+
set__interview__position('');
+
set__interview__position__expanded(false);
+ } else {
+ const
parsedSelection = value.toString
+
? value.toString()
+
: (value as string);
+
set__interview__position(parsedSelection || '');
+
set__interview__position__expanded(false);
+ }
+ }}
value={interview__position}>
<SelectOption
key={'Developer'} value={'Developer'}>
Developer
@@ -339,14 +356,21 @@ const Form__HRInterview: React.FC<any> = (props:
any) => {
onToggle={(isOpen) =>
set__interview__otherPositions__expanded(isOpen)
}
- onSelect={(event,
value, isPlaceHolder) =>
-
handleMultipleSelect(
- value,
-
isPlaceHolder,
-
interview__otherPositions,
-
set__interview__otherPositions
- )
- }
+ onSelect={(event,
value, isPlaceHolder) => {
+ if
(isPlaceHolder) {
+
set__interview__otherPositions([]);
+ } else {
+
set__interview__otherPositions((prev) => {
+
const selectedValue = value.toString
+
? value.toString()
+
: (value as string);
+
if (prev.indexOf(selectedValue) != -1) {
+
return prev.filter((s) => s !== selectedValue);
+
}
+
return [selectedValue, ...prev];
+ });
+ }
+ }}
value={interview__otherPositions}>
<SelectOption
key={'Developer'} value={'Developer'}>
Developer
@@ -371,13 +395,15 @@ const Form__HRInterview: React.FC<any> = (props:
any) => {
label={'Java'}
isDisabled={false}
isChecked={interview__skills.indexOf('Java') !== -1}
- onChange={() =>
-
handleCheckboxGroupChange(
-
'Java',
-
interview__skills,
-
set__interview__skills
- )
- }
+
onChange={set__interview__skills((prev) => {
+ const newState
= [...prev];
+ if
(newState.indexOf('Java') != -1) {
+
newState.splice(index, 1);
+ } else {
+
newState.push('Java');
+ }
+ return newState;
+ })}
value={'Java'}
/>
<Checkbox
@@ -388,13 +414,15 @@ const Form__HRInterview: React.FC<any> = (props:
any) => {
label={'React'}
isDisabled={false}
isChecked={interview__skills.indexOf('React') !== -1}
- onChange={() =>
-
handleCheckboxGroupChange(
-
'React',
-
interview__skills,
-
set__interview__skills
- )
- }
+
onChange={set__interview__skills((prev) => {
+ const newState
= [...prev];
+ if
(newState.indexOf('React') != -1) {
+
newState.splice(index, 1);
+ } else {
+
newState.push('React');
+ }
+ return newState;
+ })}
value={'React'}
/>
<Checkbox
@@ -405,13 +433,15 @@ const Form__HRInterview: React.FC<any> = (props:
any) => {
label={'TypeScript'}
isDisabled={false}
isChecked={interview__skills.indexOf('TypeScript') !== -1}
- onChange={() =>
-
handleCheckboxGroupChange(
-
'TypeScript',
-
interview__skills,
-
set__interview__skills
- )
- }
+
onChange={set__interview__skills((prev) => {
+ const newState
= [...prev];
+ if
(newState.indexOf('TypeScript') != -1) {
+
newState.splice(index, 1);
+ } else {
+
newState.push('TypeScript');
+ }
+ return newState;
+ })}
value={'TypeScript'}
/>
<Checkbox
@@ -422,13 +452,15 @@ const Form__HRInterview: React.FC<any> = (props:
any) => {
label={'Quarkus'}
isDisabled={false}
isChecked={interview__skills.indexOf('Quarkus') !== -1}
- onChange={() =>
-
handleCheckboxGroupChange(
-
'Quarkus',
-
interview__skills,
-
set__interview__skills
- )
- }
+
onChange={set__interview__skills((prev) => {
+ const newState
= [...prev];
+ if
(newState.indexOf('Quarkus') != -1) {
+
newState.splice(index, 1);
+ } else {
+
newState.push('Quarkus');
+ }
+ return newState;
+ })}
value={'Quarkus'}
/>
</FormGroup>
@@ -543,28 +575,54 @@ const Form__HRInterview: React.FC<any> = (props:
any) => {
id={'date-picker-uniforms-0000-000v'}
isDisabled={false}
name={'interview.hiringDate'}
-
onChange={(newDate) =>
-
onDateChange(
-
newDate,
-
set__interview__hiringDate,
-
interview__hiringDate
-
)
-
}
+
onChange={(newDate) => {
+
set__interview__hiringDate((prev) => {
+
if (newDate) {
+
const newDate = new Date(newDate);
+
const time = parseTime(prev);
+
if (time !== '') {
+
newDate.setHours(
+
parseInt(time &&
time.split(':')[0])
+
);
+
newDate.setMinutes(
+
parseInt(time &&
time.split(':')[1].split(' ')[0])
+
);
+
}
+
return newDate.toISOString();
+
}
+
return prev;
+
});
+
}}
value={parseDate(interview__hiringDate)}
/>
<TimePicker
id={'time-picker-uniforms-0000-000v'}
isDisabled={false}
name={'interview.hiringDate'}
-
onChange={(time, hours?, minutes?) =>
-
onTimeChange(
-
time,
-
set__interview__hiringDate,
-
interview__hiringDate,
-
hours,
-
minutes
-
)
-
}
+
onChange={(time, hours?, minutes?) => {
+
set__interview__hiringDate((prev) => {
+
if (prev) {
+
const newDate = new Date(Date.parse(prev));
+
if (hours && minutes) {
+
newDate.setHours(hours);
+
newDate.setMinutes(minutes);
+
} else if (time !== '') {
+
const localeHours = parseInt(
+
time &&
time.split(':')[0]
+
);
+
const localeMinutes = parseInt(
+
time &&
time.split(':')[1].split(' ')[0]
+
);
+
if (!isNaN(localeHours) &&
!isNaN(localeMinutes)) {
+
newDate.setHours(localeHours);
+
newDate.setMinutes(localeMinutes);
+
}
+
}
+
return newDate.toISOString();
+
}
+
return prev;
+
});
+
}}
style={{ width: '120px' }}
time={parseTime(interview__hiringDate)}
/>
@@ -619,7 +677,7 @@ const Form__HRInterview: React.FC<any> = (props: any)
=> {
isDisabled={false}
placeholder={''}
type={'text'}
-
value={friends[itemIndex].name}
+
value={friends?.[itemIndex].name}
onChange={(newValue) => {
set__friends((s) => {
const newState = [...s];
@@ -640,7 +698,7 @@ const Form__HRInterview: React.FC<any> = (props: any)
=> {
id={'uniforms-0000-0012'}
placeholder={''}
step={0.01}
-
value={friends[itemIndex].age}
+
value={friends?.[itemIndex].age}
onChange={(newValue) => {
set__friends((s) => {
const newState = [...s];
@@ -650,6 +708,106 @@ const Form__HRInterview: React.FC<any> = (props:
any) => {
}}
/>
</FormGroup>
+
<div>
+
<Split hasGutter>
+
<SplitItem>
+
{'Know' && (
+
<label
className={'pf-c-form__label'}>
+
<span
className={'pf-c-form__label-text'}>
+
Know
+
</span>
+
</label>
+
)}
+
</SplitItem>
+
<SplitItem isFilled />
+
<SplitItem>
+
<Button
+
name='$'
+
variant='plain'
+
style={{ paddingLeft: '0',
paddingRight: '0' }}
+
disabled={false}
+
onClick={() => {
+
!false &&
+
set__friends((s) => {
+
const newState = [...s];
+
newState[itemIndex].know = [
+
...(newState[itemIndex].know ?? []),
+
'',
+
];
+
return newState;
+
});
+
}}>
+
<PlusCircleIcon color='#0088ce' />
+
</Button>
+
</SplitItem>
+
</Split>
+
<div>
+
{friends?.[itemIndex].know?.map(
+
(_, nested__itemIndex) => (
+
<div
+
key={nested__itemIndex}
+
style={{
+
marginBottom: '1rem',
+
display: 'flex',
+
justifyContent:
'space-between',
+
}}>
+
<div
+
style={{ width: '100%',
marginRight: '10px' }}>
+
<FormGroup
+
fieldId={'uniforms-0000-0015'}
+
label={'Know'}
+
isRequired={false}>
+
<TextInput
+
name={\`friends?.[itemIndex].know.\${nested__itemIndex}\`}
+
id={'uniforms-0000-0015'}
+
isDisabled={false}
+
placeholder={''}
+
type={'text'}
+
value={
+
friends?.[itemIndex].know?.[
+
nested__itemIndex
+
]
+
}
+
onChange={(newValue) => {
+
set__friends((s) => {
+
const newState = [...s];
+
newState[itemIndex].know[
+
nested__itemIndex
+
] = newValue;
+
return newState;
+
});
+
}}
+
/>
+
</FormGroup>
+
</div>
+
<div>
+
<Button
+
disabled={false}
+
variant='plain'
+
style={{
+
paddingLeft:
'0',
+
paddingRight:
'0',
+
}}
+
onClick={() => {
+
const value = [
+
...friends?.[itemIndex].know,
+
];
+
value.splice(nested__itemIndex, 1);
+
!false
&&
+
set__friends((s) => {
+
const newState = [...s];
+
newState[itemIndex].know = value;
+
return newState;
+
});
+
}}>
+
<MinusCircleIcon
color='#cc0000' />
+
</Button>
+
</div>
+
</div>
+
)
+
)}
+
</div>
+
</div>
</CardBody>
</Card>
</div>
diff --git
a/packages/form-code-generator-patternfly-theme/tests/__snapshots__/BoolField.test.tsx.snap
b/packages/form-code-generator-patternfly-theme/tests/__snapshots__/BoolField.test.tsx.snap
index 9236b1379f7..267220257c3 100644
---
a/packages/form-code-generator-patternfly-theme/tests/__snapshots__/BoolField.test.tsx.snap
+++
b/packages/form-code-generator-patternfly-theme/tests/__snapshots__/BoolField.test.tsx.snap
@@ -1,13 +1,27 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`<BoolField> tests <BoolField> - rendering - disabled 1`] = `
-<div>
-
{"ref":{"binding":"hire","stateName":"hire","stateSetter":"set__hire","dataType":{"name":"boolean","defaultValue":"false"}},"pfImports":["Checkbox","FormGroup"],"reactImports":["useState"],"jsxCode":"<FormGroup
fieldId='id'>\\n <Checkbox\\n isChecked={hire}\\n
isDisabled={true}\\n id={'id'}\\n name={'hire'}\\n
label={'Hire?'}\\n onChange={set__hire}\\n />\\n
</FormGroup>","stateCode":"const [ hire, set__hire ] =
useState<boolean [...]
-</div>
+"<FormGroup fieldId='id'>
+ <Checkbox
+ isChecked={hire}
+ isDisabled={true}
+ id={'id'}
+ name={'hire'}
+ label={'Hire?'}
+ onChange={set__hire}
+ />
+ </FormGroup>"
`;
exports[`<BoolField> tests <BoolField> - rendering 1`] = `
-<div>
-
{"ref":{"binding":"hire","stateName":"hire","stateSetter":"set__hire","dataType":{"name":"boolean","defaultValue":"false"}},"pfImports":["Checkbox","FormGroup"],"reactImports":["useState"],"jsxCode":"<FormGroup
fieldId='id'>\\n <Checkbox\\n isChecked={hire}\\n
isDisabled={false}\\n id={'id'}\\n name={'hire'}\\n
label={'Hire?'}\\n onChange={set__hire}\\n />\\n
</FormGroup>","stateCode":"const [ hire, set__hire ] = useState<boolea
[...]
-</div>
+"<FormGroup fieldId='id'>
+ <Checkbox
+ isChecked={hire}
+ isDisabled={false}
+ id={'id'}
+ name={'hire'}
+ label={'Hire?'}
+ onChange={set__hire}
+ />
+ </FormGroup>"
`;
diff --git
a/packages/form-code-generator-patternfly-theme/tests/__snapshots__/CheckBoxGroupField.test.tsx.snap
b/packages/form-code-generator-patternfly-theme/tests/__snapshots__/CheckBoxGroupField.test.tsx.snap
index dad60b85dff..559dd0f446b 100644
---
a/packages/form-code-generator-patternfly-theme/tests/__snapshots__/CheckBoxGroupField.test.tsx.snap
+++
b/packages/form-code-generator-patternfly-theme/tests/__snapshots__/CheckBoxGroupField.test.tsx.snap
@@ -1,7 +1,67 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`<CheckBoxGroupField> tests <CheckBoxGroupField> - rendering 1`] = `
-<div>
-
{"ref":{"binding":"roles","stateName":"roles","stateSetter":"set__roles","dataType":{"name":"string[]","defaultValue":"[]"}},"pfImports":["Checkbox","FormGroup"],"reactImports":["useState"],"requiredCode":["checkbox_group_functions"],"jsxCode":"<FormGroup\\n
fieldId={'id'}\\n label={'Roles'}\\n isRequired={true}\\n
>\\n <Checkbox\\n key={'id-Developer'}\\n id={'id-Developer'}\\n
name={'roles'}\\n aria-label={'roles'}\\n label={'Developer'} \\n isDi [...]
-</div>
+"<FormGroup
+ fieldId={'id'}
+ label={'Roles'}
+ isRequired={true}
+ >
+ <Checkbox
+ key={'id-Developer'}
+ id={'id-Developer'}
+ name={'roles'}
+ aria-label={'roles'}
+ label={'Developer'}
+ isDisabled={false}
+ isChecked={roles.indexOf('Developer') !== -1}
+ onChange={set__roles(prev => {
+ const newState = [...prev];
+ if(newState.indexOf('Developer') != -1) {
+ newState.splice(index, 1);
+ } else {
+ newState.push('Developer');
+ }
+ return newState;
+ })}
+ value={'Developer'}
+/>
+<Checkbox
+ key={'id-HR'}
+ id={'id-HR'}
+ name={'roles'}
+ aria-label={'roles'}
+ label={'HR'}
+ isDisabled={false}
+ isChecked={roles.indexOf('HR') !== -1}
+ onChange={set__roles(prev => {
+ const newState = [...prev];
+ if(newState.indexOf('HR') != -1) {
+ newState.splice(index, 1);
+ } else {
+ newState.push('HR');
+ }
+ return newState;
+ })}
+ value={'HR'}
+/>
+<Checkbox
+ key={'id-UX'}
+ id={'id-UX'}
+ name={'roles'}
+ aria-label={'roles'}
+ label={'UX'}
+ isDisabled={false}
+ isChecked={roles.indexOf('UX') !== -1}
+ onChange={set__roles(prev => {
+ const newState = [...prev];
+ if(newState.indexOf('UX') != -1) {
+ newState.splice(index, 1);
+ } else {
+ newState.push('UX');
+ }
+ return newState;
+ })}
+ value={'UX'}
+/>
+ </FormGroup>"
`;
diff --git
a/packages/form-code-generator-patternfly-theme/tests/__snapshots__/DateField.test.tsx.snap
b/packages/form-code-generator-patternfly-theme/tests/__snapshots__/DateField.test.tsx.snap
index cd8adc45456..786d51c98df 100644
---
a/packages/form-code-generator-patternfly-theme/tests/__snapshots__/DateField.test.tsx.snap
+++
b/packages/form-code-generator-patternfly-theme/tests/__snapshots__/DateField.test.tsx.snap
@@ -1,13 +1,135 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`<DateField> tests <DateField> - rendering - disabled 1`] = `
-<div>
-
{"ref":{"binding":"birthday","stateName":"birthday","stateSetter":"set__birthday","dataType":{"name":"string"}},"pfImports":["DatePicker","Flex","FlexItem","InputGroup","TimePicker","FormGroup"],"reactImports":["useState"],"requiredCode":["date_functions","time_functions"],"jsxCode":"<FormGroup\\n
fieldId={'id'}\\n label={'Birthday'}\\n isRequired={true}\\n
>\\n <Flex\\n direction={{ default: 'column' }}\\n
id={'id'}\\n >\\n <FlexItem> [...]
-</div>
+"<FormGroup
+ fieldId={'id'}
+ label={'Birthday'}
+ isRequired={true}
+ >
+ <Flex
+ direction={{ default: 'column' }}
+ id={'id'}
+ >
+ <FlexItem>
+ <InputGroup style={{ background: 'transparent' }}>
+ <DatePicker
+ id={'date-picker-id'}
+ isDisabled={true}
+ name={'birthday'}
+ onChange={newDate => {
+ set__birthday(prev => {
+ if (newDate) {
+ const newDate = new Date(newDate);
+ const time = parseTime(prev);
+ if (time !== '') {
+ newDate.setHours(parseInt(time && time.split(':')[0]));
+ newDate.setMinutes(parseInt(time && time.split(':')[1].split('
')[0]));
+ }
+ return newDate.toISOString();
+ }
+ return prev;
+ })
+ }
+ }
+ value={parseDate(birthday)}
+ />
+ <TimePicker
+ id={'time-picker-id'}
+ isDisabled={true}
+ name={'birthday'}
+ onChange={(time, hours?, minutes?) => {
+ set__birthday(prev => {
+ if (prev) {
+ const newDate = new Date(Date.parse(prev));
+ if (hours && minutes) {
+ newDate.setHours(hours);
+ newDate.setMinutes(minutes);
+ } else if (time !== '') {
+ const localeHours = parseInt(time && time.split(':')[0]);
+ const localeMinutes = parseInt(time && time.split(':')[1].split('
')[0]);
+ if (!isNaN(localeHours) && !isNaN(localeMinutes)) {
+ newDate.setHours(localeHours);
+ newDate.setMinutes(localeMinutes);
+ }
+ }
+ return newDate.toISOString();
+ }
+ return prev;
+ })
+ }
+ }
+ style={{ width: '120px' }}
+ time={parseTime(birthday)}
+ />
+ </InputGroup>
+ </FlexItem>
+ </Flex>
+ </FormGroup>"
`;
exports[`<DateField> tests <DateField> - rendering 1`] = `
-<div>
-
{"ref":{"binding":"birthday","stateName":"birthday","stateSetter":"set__birthday","dataType":{"name":"string"}},"pfImports":["DatePicker","Flex","FlexItem","InputGroup","TimePicker","FormGroup"],"reactImports":["useState"],"requiredCode":["date_functions","time_functions"],"jsxCode":"<FormGroup\\n
fieldId={'id'}\\n label={'Birthday'}\\n isRequired={true}\\n
>\\n <Flex\\n direction={{ default: 'column' }}\\n
id={'id'}\\n >\\n <FlexItem> [...]
-</div>
+"<FormGroup
+ fieldId={'id'}
+ label={'Birthday'}
+ isRequired={true}
+ >
+ <Flex
+ direction={{ default: 'column' }}
+ id={'id'}
+ >
+ <FlexItem>
+ <InputGroup style={{ background: 'transparent' }}>
+ <DatePicker
+ id={'date-picker-id'}
+ isDisabled={false}
+ name={'birthday'}
+ onChange={newDate => {
+ set__birthday(prev => {
+ if (newDate) {
+ const newDate = new Date(newDate);
+ const time = parseTime(prev);
+ if (time !== '') {
+ newDate.setHours(parseInt(time && time.split(':')[0]));
+ newDate.setMinutes(parseInt(time && time.split(':')[1].split('
')[0]));
+ }
+ return newDate.toISOString();
+ }
+ return prev;
+ })
+ }
+ }
+ value={parseDate(birthday)}
+ />
+ <TimePicker
+ id={'time-picker-id'}
+ isDisabled={false}
+ name={'birthday'}
+ onChange={(time, hours?, minutes?) => {
+ set__birthday(prev => {
+ if (prev) {
+ const newDate = new Date(Date.parse(prev));
+ if (hours && minutes) {
+ newDate.setHours(hours);
+ newDate.setMinutes(minutes);
+ } else if (time !== '') {
+ const localeHours = parseInt(time && time.split(':')[0]);
+ const localeMinutes = parseInt(time && time.split(':')[1].split('
')[0]);
+ if (!isNaN(localeHours) && !isNaN(localeMinutes)) {
+ newDate.setHours(localeHours);
+ newDate.setMinutes(localeMinutes);
+ }
+ }
+ return newDate.toISOString();
+ }
+ return prev;
+ })
+ }
+ }
+ style={{ width: '120px' }}
+ time={parseTime(birthday)}
+ />
+ </InputGroup>
+ </FlexItem>
+ </Flex>
+ </FormGroup>"
`;
diff --git
a/packages/form-code-generator-patternfly-theme/tests/__snapshots__/ListField.test.tsx.snap
b/packages/form-code-generator-patternfly-theme/tests/__snapshots__/ListField.test.tsx.snap
index d25a6d94536..437f2092f7d 100644
---
a/packages/form-code-generator-patternfly-theme/tests/__snapshots__/ListField.test.tsx.snap
+++
b/packages/form-code-generator-patternfly-theme/tests/__snapshots__/ListField.test.tsx.snap
@@ -1,7 +1,498 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`<ListField> tests <ListField> 1`] = `
+"<div>
+ <Split hasGutter>
+ <SplitItem>
+ {'Friends' && (
+ <label className={"pf-c-form__label"}>
+ <span className={"pf-c-form__label-text"}>
+ Friends
+ </span>
+ </label>
+ )}
+ </SplitItem>
+ <SplitItem isFilled />
+ <SplitItem>
+ <Button
+ name='$'
+ variant='plain'
+ style={{ paddingLeft: '0', paddingRight: '0' }}
+ disabled={false}
+ onClick={() => {
+ !false &&
+ set__friends((friends ?? []).concat([{}]));
+ }}
+ >
+ <PlusCircleIcon color='#0088ce' />
+ </Button>
+ </SplitItem>
+ </Split>
+ <div>
+ {friends?.map((_, itemIndex) =>
+ (<div
+ key={itemIndex}
+ style={{
+ marginBottom: '1rem',
+ display: 'flex',
+ justifyContent: 'space-between',
+ }}
+ >
+ <div style={{ width: '100%', marginRight: '10px' }}><Card>
+ <CardBody className="pf-c-form">
+
+ <FormGroup
+ fieldId={'uniforms-0000-0003'}
+ label={'Name'}
+ isRequired={true}
+ >
+ <TextInput
+ name={\`friends.\${itemIndex}.name\`}
+ id={'uniforms-0000-0003'}
+ isDisabled={false}
+ placeholder={''}
+ type={'text'}
+ value={friends?.[itemIndex].name}
+ onChange={newValue => {
+ set__friends(s => {
+ const newState = [...s];
+ newState[itemIndex].name = newValue;
+ return newState;
+ })
+ }}
+ />
+ </FormGroup>
+<FormGroup
+ fieldId={'uniforms-0000-0005'}
+ label={'Age'}
+ isRequired={true}
+ >
+ <TextInput
+ type={'number'}
+ name={\`friends.\${itemIndex}.age\`}
+ isDisabled={false}
+ id={'uniforms-0000-0005'}
+ placeholder={''}
+ step={0.01}
+ value={friends?.[itemIndex].age}
+ onChange={newValue => {
+ set__friends(s => {
+ const newState = [...s];
+ newState[itemIndex].age = Number(newValue);
+ return newState;
+ })
+ }}
+ />
+ </FormGroup>
+<FormGroup
+ fieldId={'uniforms-0000-0007'}
+ label={'Country'}
+ isRequired={true}
+ ><Select
+ id={'uniforms-0000-0007'}
+ name={\`friends.\${itemIndex}.country\`}
+ variant={SelectVariant.single}
+ isDisabled={false}
+ placeholderText={''}
+ isOpen={friends__expanded?.[itemIndex]?.country ?? false}
+ selections={friends?.[itemIndex].country}
+ onToggle={(isOpen) => set__friends__expanded(prev => {
+ const newState = [...prev];
+ newState[itemIndex] ??= {};
+
+
+
+ newState[itemIndex].country = isOpen
+ return newState;
+ })}
+ onSelect={(event, value, isPlaceHolder) => {
+ if (isPlaceHolder) {
+ set__friends(prev => {
+ const newState = [...prev];
+ newState[itemIndex].country = "";
+ return newState;
+ })
+ set__friends__expanded(prev => {
+ const newState = [...prev];
+ newState[itemIndex] ??= {};
+
+
+
+ newState[itemIndex].country = false;
+ return newState;
+ });
+ } else {
+ const parsedSelection = value.toString ? value.toString() : value
as string;
+ set__friends(prev => {
+ const newState = [...prev];
+ newState[itemIndex].country = parsedSelection || '';
+ return newState;
+ })
+ set__friends__expanded(prev => {
+ const newState = [...prev];
+ newState[itemIndex] ??= {};
+
+
+
+ newState[itemIndex].country = false;
+ return newState;
+ });
+ }
+ }}
+ value={friends?.[itemIndex].country}
+ >
+ <SelectOption key={'US'} value={'US'}>US</SelectOption>
+<SelectOption key={'Brazil'} value={'Brazil'}>Brazil</SelectOption>
+ </Select></FormGroup>
+<FormGroup fieldId='uniforms-0000-0009'>
+ <Checkbox
+ isChecked={friends?.[itemIndex].married}
+ isDisabled={false}
+ id={'uniforms-0000-0009'}
+ name={\`friends.\${itemIndex}.married\`}
+ label={'Married'}
+ onChange={newValue => {
+ set__friends(s => {
+ const newState = [...s];
+ newState[itemIndex].married = newValue;
+ return newState;
+ })
+ }}
+ />
+ </FormGroup>
<div>
-
{"ref":{"binding":"friends","stateName":"friends","stateSetter":"set__friends","dataType":{"name":"object[]","defaultValue":"[]"}},"pfImports":["Split","SplitItem","Button","Card","CardBody","TextInput","FormGroup","SelectOption","SelectOptionObject","Select","SelectVariant","Checkbox","DatePicker","Flex","FlexItem","InputGroup","TimePicker"],"pfIconImports":["PlusCircleIcon","MinusCircleIcon"],"reactImports":["useState"],"jsxCode":"<div>\\n
<Split hasGutter>\\n [...]
-</div>
+ <Split hasGutter>
+ <SplitItem>
+ {'Know' && (
+ <label className={"pf-c-form__label"}>
+ <span className={"pf-c-form__label-text"}>
+ Know
+ </span>
+ </label>
+ )}
+ </SplitItem>
+ <SplitItem isFilled />
+ <SplitItem>
+ <Button
+ name='$'
+ variant='plain'
+ style={{ paddingLeft: '0', paddingRight: '0' }}
+ disabled={false}
+ onClick={() => {
+ !false &&
+ set__friends((s) => {
+ const newState = [...s];
+ (newState[itemIndex].know) = [...(newState[itemIndex].know ?? []), ""];
+ return newState;
+});
+ }}
+ >
+ <PlusCircleIcon color='#0088ce' />
+ </Button>
+ </SplitItem>
+ </Split>
+ <div>
+ {friends?.[itemIndex].know?.map((_, nested__itemIndex) =>
+ (<div
+ key={nested__itemIndex}
+ style={{
+ marginBottom: '1rem',
+ display: 'flex',
+ justifyContent: 'space-between',
+ }}
+ >
+ <div style={{ width: '100%', marginRight: '10px' }}><FormGroup
+ fieldId={'uniforms-0000-000c'}
+ label={'Know'}
+ isRequired={false}
+ >
+ <TextInput
+ name={\`friends?.[itemIndex].know.\${nested__itemIndex}\`}
+ id={'uniforms-0000-000c'}
+ isDisabled={false}
+ placeholder={''}
+ type={'text'}
+ value={friends?.[itemIndex].know?.[nested__itemIndex]}
+ onChange={newValue => {
+ set__friends(s => {
+ const newState = [...s];
+ newState[itemIndex].know[nested__itemIndex] = newValue;
+ return newState;
+ })
+ }}
+ />
+ </FormGroup></div>
+ <div>
+ <Button
+ disabled={false}
+ variant='plain'
+ style={{ paddingLeft: '0', paddingRight: '0' }}
+ onClick={() => {
+ const value = [...friends?.[itemIndex].know]
+ value.splice(nested__itemIndex, 1);
+ !false &&
+ set__friends((s) => {
+ const newState = [...s];
+ (newState[itemIndex].know) = value;
+ return newState;
+});
+ }}
+ >
+ <MinusCircleIcon color='#cc0000' />
+ </Button>
+ </div>
+ </div>)
+ )}
+ </div>
+ </div>
+<FormGroup
+ fieldId={'uniforms-0000-000e'}
+ label={'Areas'}
+ isRequired={true}
+ ><Select
+ id={'uniforms-0000-000e'}
+ name={\`friends.\${itemIndex}.areas\`}
+ variant={SelectVariant.typeaheadMulti}
+ isDisabled={false}
+ placeholderText={''}
+ isOpen={friends__expanded?.[itemIndex]?.areas ?? false}
+ selections={friends?.[itemIndex].areas}
+ onToggle={(isOpen) => set__friends__expanded(prev => {
+ const newState = [...prev];
+ newState[itemIndex] ??= {};
+
+
+
+ newState[itemIndex].areas = isOpen
+ return newState;
+ })}
+ onSelect={(event, value, isPlaceHolder) => {
+ if (isPlaceHolder) {
+ set__friends(prev => {
+ const newState = [...prev];
+ newState[itemIndex].areas = [];
+ return newState;
+ })
+ } else {
+ const selectedValue = newSelection.toString ?
newSelection.toString() : newSelection as string;
+ set__friends(prev => {
+ const newState = [...prev];
+ if (newState[itemIndex].areas.indexOf(selectedValue) != -1) {
+ const filtered = newState[itemIndex].areas.filter((s) => s !==
selectedValue);
+ return newState[itemIndex].areas = filtered;
+ }
+ newState[itemIndex].areas = [selectedValue,
...newState[itemIndex].areas];
+ return newState;
+ })
+ }
+ }}
+ value={friends?.[itemIndex].areas}
+ >
+ <SelectOption key={'Developer'}
value={'Developer'}>Developer</SelectOption>
+<SelectOption key={'HR'} value={'HR'}>HR</SelectOption>
+<SelectOption key={'UX'} value={'UX'}>UX</SelectOption>
+ </Select></FormGroup>
+<FormGroup
+ fieldId={'uniforms-0000-000g'}
+ label={'Birthday'}
+ isRequired={true}
+ >
+ <Flex
+ direction={{ default: 'column' }}
+ id={'uniforms-0000-000g'}
+ >
+ <FlexItem>
+ <InputGroup style={{ background: 'transparent' }}>
+ <DatePicker
+ id={'date-picker-uniforms-0000-000g'}
+ isDisabled={false}
+ name={\`friends.\${itemIndex}.birthday\`}
+ onChange={newDate => {
+ set__friends(prev => {
+ if (newDate) {
+ const newState = [...prev];
+ const currentDate = newState[itemIndex].birthday
+ const newDate = new Date(newDate);
+ const time = parseTime(currentDate);
+ if (time !== '') {
+ newDate.setHours(parseInt(time && time.split(':')[0]));
+ newDate.setMinutes(parseInt(time && time.split(':')[1].split('
')[0]));
+ }
+ newState[itemIndex].birthday = newDate.toISOString();
+ return newState;
+ }
+ return prev;
+ })
+ }}
+ value={parseDate(friends?.[itemIndex].birthday)}
+ />
+ <TimePicker
+ id={'time-picker-uniforms-0000-000g'}
+ isDisabled={false}
+ name={\`friends.\${itemIndex}.birthday\`}
+ onChange={(time, hours?, minutes?) => set__friends(prev => {
+ const newState = [...prev];
+ const currentDate = newState[itemIndex].birthday
+ if (currentDate) {
+ const newDate = new Date(Date.parse(currentDate));
+ if (hours && minutes) {
+ newDate.setHours(hours);
+ newDate.setMinutes(minutes);
+ } else if (time !== '') {
+ const localeHours = parseInt(time && time.split(':')[0]);
+ const localeMinutes = parseInt(time && time.split(':')[1].split('
')[0]);
+ if (!isNaN(localeHours) && !isNaN(localeMinutes)) {
+ newDate.setHours(localeHours);
+ newDate.setMinutes(localeMinutes);
+ }
+ }
+ newState[itemIndex].birthday = newDate.toISOString();
+ return newState;
+ }
+ return prev;
+ })}
+ style={{ width: '120px' }}
+ time={parseTime(friends?.[itemIndex].birthday)}
+ />
+ </InputGroup>
+ </FlexItem>
+ </Flex>
+ </FormGroup>
+<FormGroup
+ fieldId={'uniforms-0000-000i'}
+ label={'Transport'}
+ isRequired={true}
+ >
+ <Checkbox
+ key={'uniforms-0000-000i-Taxi'}
+ id={'uniforms-0000-000i-Taxi'}
+ name={\`friends.\${itemIndex}.transport\`}
+ aria-label={'friends.$.transport'}
+ label={'Taxi'}
+ isDisabled={false}
+ isChecked={friends?.[itemIndex].transport.indexOf('Taxi') !== -1}
+ onChange={
+ set__friends(prev => {
+ const newState = [...prev];
+ const newValue = [...newState[itemIndex].transport]
+ if(newValue.indexOf('Taxi') != -1) {
+ newValue.splice(index, 1);
+ } else {
+ newValue.push('Taxi');
+ }
+ newState[itemIndex].transport = newValue
+ return newState;
+ })}
+ value={friends?.[itemIndex].transport}
+/>
+<Checkbox
+ key={'uniforms-0000-000i-Uber'}
+ id={'uniforms-0000-000i-Uber'}
+ name={\`friends.\${itemIndex}.transport\`}
+ aria-label={'friends.$.transport'}
+ label={'Uber'}
+ isDisabled={false}
+ isChecked={friends?.[itemIndex].transport.indexOf('Uber') !== -1}
+ onChange={
+ set__friends(prev => {
+ const newState = [...prev];
+ const newValue = [...newState[itemIndex].transport]
+ if(newValue.indexOf('Uber') != -1) {
+ newValue.splice(index, 1);
+ } else {
+ newValue.push('Uber');
+ }
+ newState[itemIndex].transport = newValue
+ return newState;
+ })}
+ value={friends?.[itemIndex].transport}
+/>
+ </FormGroup>
+<FormGroup
+ fieldId={'uniforms-0000-000k'}
+ label={'Children'}
+ isRequired={true}
+ ><Select
+ id={'uniforms-0000-000k'}
+ name={\`friends.\${itemIndex}.children\`}
+ variant={SelectVariant.single}
+ isDisabled={false}
+ placeholderText={''}
+ isOpen={friends__expanded?.[itemIndex]?.children ?? false}
+ selections={friends?.[itemIndex].children}
+ onToggle={(isOpen) => set__friends__expanded(prev => {
+ const newState = [...prev];
+ newState[itemIndex] ??= {};
+
+
+
+ newState[itemIndex].children = isOpen
+ return newState;
+ })}
+ onSelect={(event, value, isPlaceHolder) => {
+ if (isPlaceHolder) {
+ set__friends(prev => {
+ const newState = [...prev];
+ newState[itemIndex].children = "";
+ return newState;
+ })
+ set__friends__expanded(prev => {
+ const newState = [...prev];
+ newState[itemIndex] ??= {};
+
+
+
+ newState[itemIndex].children = false;
+ return newState;
+ });
+ } else {
+ const parsedSelection = value.toString ? value.toString() : value
as string;
+ set__friends(prev => {
+ const newState = [...prev];
+ newState[itemIndex].children = parsedSelection || '';
+ return newState;
+ })
+ set__friends__expanded(prev => {
+ const newState = [...prev];
+ newState[itemIndex] ??= {};
+
+
+
+ newState[itemIndex].children = false;
+ return newState;
+ });
+ }
+ }}
+ value={friends?.[itemIndex].children}
+ >
+ <SelectOption key={'0'} value={'0'}>0</SelectOption>
+<SelectOption key={'1'} value={'1'}>1</SelectOption>
+<SelectOption key={'2+'} value={'2+'}>2+</SelectOption>
+ </Select></FormGroup>
+ </CardBody></Card></div>
+ <div>
+ <Button
+ disabled={false}
+ variant='plain'
+ style={{ paddingLeft: '0', paddingRight: '0' }}
+ onClick={() => {
+ const value = [...friends]
+ value.splice(itemIndex, 1);
+ !false &&
+ set__friends(value);
+ }}
+ >
+ <MinusCircleIcon color='#cc0000' />
+ </Button>
+ </div>
+ </div>)
+ )}
+ </div>
+ </div>"
+`;
+
+exports[`<ListField> tests <ListField> 2`] = `
+"const [ friends, set__friends ] = useState<object[]>([]);
+
+const [ friends__expanded, set__friends__expanded ] = useState<object[]>([]);"
`;
diff --git
a/packages/form-code-generator-patternfly-theme/tests/__snapshots__/NestField.test.tsx.snap
b/packages/form-code-generator-patternfly-theme/tests/__snapshots__/NestField.test.tsx.snap
index 28d61072835..90561da3387 100644
---
a/packages/form-code-generator-patternfly-theme/tests/__snapshots__/NestField.test.tsx.snap
+++
b/packages/form-code-generator-patternfly-theme/tests/__snapshots__/NestField.test.tsx.snap
@@ -1,7 +1,149 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`<NestField> tests <NestField> - rendering 1`] = `
+"<Card>
+ <CardBody className="pf-c-form">
+ <label><b>Candidate</b></label>
+ <FormGroup
+ fieldId={'uniforms-0000-0001'}
+ label={'Name'}
+ isRequired={true}
+ >
+ <TextInput
+ name={'candidate.name'}
+ id={'uniforms-0000-0001'}
+ isDisabled={false}
+ placeholder={''}
+ type={'text'}
+ value={candidate__name}
+ onChange={set__candidate__name}
+ />
+ </FormGroup>
+<FormGroup
+ fieldId={'uniforms-0000-0003'}
+ label={'Age'}
+ isRequired={true}
+ >
+ <TextInput
+ type={'number'}
+ name={'candidate.age'}
+ isDisabled={false}
+ id={'uniforms-0000-0003'}
+ placeholder={''}
+ step={1}
+ value={candidate__age}
+ onChange={(newValue) => set__candidate__age(Number(newValue))}
+ />
+ </FormGroup>
+<FormGroup
+ fieldId={'uniforms-0000-0005'}
+ label={'Role'}
+ isRequired={true}
+ ><Select
+ id={'uniforms-0000-0005'}
+ name={'candidate.role'}
+ variant={SelectVariant.single}
+ isDisabled={false}
+ placeholderText={''}
+ isOpen={candidate__role__expanded}
+ selections={candidate__role}
+ onToggle={(isOpen) => set__candidate__role__expanded(isOpen)}
+ onSelect={(event, value, isPlaceHolder) => {
+ if (isPlaceHolder) {
+ set__candidate__role('');
+ set__candidate__role__expanded(false);
+ } else {
+ const parsedSelection = value.toString ? value.toString() : value as
string;
+ set__candidate__role(parsedSelection || '');
+ set__candidate__role__expanded(false);
+ }
+ }}
+ value={candidate__role}
+ >
+ <SelectOption key={'Developer'}
value={'Developer'}>Developer</SelectOption>
+<SelectOption key={'HR'} value={'HR'}>HR</SelectOption>
+<SelectOption key={'UX'} value={'UX'}>UX</SelectOption>
+ </Select></FormGroup>
<div>
-
{"pfImports":["Card","CardBody","TextInput","FormGroup","SelectOption","SelectOptionObject","Select","SelectVariant","Split","SplitItem","Button"],"pfIconImports":["PlusCircleIcon","MinusCircleIcon"],"reactImports":["useState"],"requiredCode":["select_functions"],"stateCode":"const
[ candidate__name, set__candidate__name ] =
useState<string>(\\"\\");\\nconst [ candidate__age, set__candidate__age ]
= useState<number>();\\nconst [ candidate__role, set__candidate__role ] =
use [...]
-</div>
+ <Split hasGutter>
+ <SplitItem>
+ {'Skills' && (
+ <label className={"pf-c-form__label"}>
+ <span className={"pf-c-form__label-text"}>
+ Skills
+ </span>
+ </label>
+ )}
+ </SplitItem>
+ <SplitItem isFilled />
+ <SplitItem>
+ <Button
+ name='$'
+ variant='plain'
+ style={{ paddingLeft: '0', paddingRight: '0' }}
+ disabled={false}
+ onClick={() => {
+ !false &&
+ set__candidate__skills((candidate__skills ?? []).concat([{}]));
+ }}
+ >
+ <PlusCircleIcon color='#0088ce' />
+ </Button>
+ </SplitItem>
+ </Split>
+ <div>
+ {candidate__skills?.map((_, itemIndex) =>
+ (<div
+ key={itemIndex}
+ style={{
+ marginBottom: '1rem',
+ display: 'flex',
+ justifyContent: 'space-between',
+ }}
+ >
+ <div style={{ width: '100%', marginRight: '10px' }}><Card>
+ <CardBody className="pf-c-form">
+
+ <FormGroup
+ fieldId={'uniforms-0000-000a'}
+ label={'Name'}
+ isRequired={true}
+ >
+ <TextInput
+ name={\`candidate__skills.\${itemIndex}.name\`}
+ id={'uniforms-0000-000a'}
+ isDisabled={false}
+ placeholder={''}
+ type={'text'}
+ value={candidate__skills?.[itemIndex].name}
+ onChange={newValue => {
+ set__candidate__skills(s => {
+ const newState = [...s];
+ newState[itemIndex].name = newValue;
+ return newState;
+ })
+ }}
+ />
+ </FormGroup>
+ </CardBody></Card></div>
+ <div>
+ <Button
+ disabled={false}
+ variant='plain'
+ style={{ paddingLeft: '0', paddingRight: '0' }}
+ onClick={() => {
+ const value = [...candidate__skills]
+ value.splice(itemIndex, 1);
+ !false &&
+ set__candidate__skills(value);
+ }}
+ >
+ <MinusCircleIcon color='#cc0000' />
+ </Button>
+ </div>
+ </div>)
+ )}
+ </div>
+ </div>
+ </CardBody></Card>"
`;
diff --git
a/packages/form-code-generator-patternfly-theme/tests/__snapshots__/NumField.test.tsx.snap
b/packages/form-code-generator-patternfly-theme/tests/__snapshots__/NumField.test.tsx.snap
index 4e329486803..c01c9799ca9 100644
---
a/packages/form-code-generator-patternfly-theme/tests/__snapshots__/NumField.test.tsx.snap
+++
b/packages/form-code-generator-patternfly-theme/tests/__snapshots__/NumField.test.tsx.snap
@@ -1,19 +1,58 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`<NumField> tests <NumField> - decimal rendering 1`] = `
-<div>
-
{"ref":{"binding":"salary","stateName":"salary","stateSetter":"set__salary","dataType":{"name":"number"}},"pfImports":["TextInput","FormGroup"],"reactImports":["useState"],"jsxCode":"<FormGroup\\n
fieldId={'id'}\\n label={'salary'}\\n isRequired={true}\\n
>\\n <TextInput\\n type={'number'}\\n name={'salary'}\\n
isDisabled={false}\\n id={'id'}\\n placeholder={''}\\n
step={0.01} max={900.5} min={400.5}\\n value={salary}\\ [...]
-</div>
+"<FormGroup
+ fieldId={'id'}
+ label={'salary'}
+ isRequired={true}
+ >
+ <TextInput
+ type={'number'}
+ name={'salary'}
+ isDisabled={false}
+ id={'id'}
+ placeholder={''}
+ step={0.01} max={900.5} min={400.5}
+ value={salary}
+ onChange={(newValue) => set__salary(Number(newValue))}
+ />
+ </FormGroup>"
`;
exports[`<NumField> tests <NumField> - integer rendering - disabled 1`] = `
-<div>
-
{"ref":{"binding":"age","stateName":"age","stateSetter":"set__age","dataType":{"name":"number"}},"pfImports":["TextInput","FormGroup"],"reactImports":["useState"],"jsxCode":"<FormGroup\\n
fieldId={'id'}\\n label={'age'}\\n isRequired={true}\\n
>\\n <TextInput\\n type={'number'}\\n name={'age'}\\n
isDisabled={true}\\n id={'id'}\\n placeholder={''}\\n step={1}
max={15} min={10}\\n value={age}\\n onChange={(newValue) [...]
-</div>
+"<FormGroup
+ fieldId={'id'}
+ label={'age'}
+ isRequired={true}
+ >
+ <TextInput
+ type={'number'}
+ name={'age'}
+ isDisabled={true}
+ id={'id'}
+ placeholder={''}
+ step={1} max={15} min={10}
+ value={age}
+ onChange={(newValue) => set__age(Number(newValue))}
+ />
+ </FormGroup>"
`;
exports[`<NumField> tests <NumField> - integer rendering 1`] = `
-<div>
-
{"ref":{"binding":"age","stateName":"age","stateSetter":"set__age","dataType":{"name":"number"}},"pfImports":["TextInput","FormGroup"],"reactImports":["useState"],"jsxCode":"<FormGroup\\n
fieldId={'id'}\\n label={'age'}\\n isRequired={true}\\n
>\\n <TextInput\\n type={'number'}\\n name={'age'}\\n
isDisabled={false}\\n id={'id'}\\n placeholder={''}\\n step={1}
max={15} min={10}\\n value={age}\\n onChange={(newValue) [...]
-</div>
+"<FormGroup
+ fieldId={'id'}
+ label={'age'}
+ isRequired={true}
+ >
+ <TextInput
+ type={'number'}
+ name={'age'}
+ isDisabled={false}
+ id={'id'}
+ placeholder={''}
+ step={1} max={15} min={10}
+ value={age}
+ onChange={(newValue) => set__age(Number(newValue))}
+ />
+ </FormGroup>"
`;
diff --git
a/packages/form-code-generator-patternfly-theme/tests/__snapshots__/RadioField.test.tsx.snap
b/packages/form-code-generator-patternfly-theme/tests/__snapshots__/RadioField.test.tsx.snap
index deb019d6b5f..4366aef5a6b 100644
---
a/packages/form-code-generator-patternfly-theme/tests/__snapshots__/RadioField.test.tsx.snap
+++
b/packages/form-code-generator-patternfly-theme/tests/__snapshots__/RadioField.test.tsx.snap
@@ -1,13 +1,79 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`<RadioField> tests <RadioField> - RadioInput rendering - disabled 1`]
= `
-<div>
-
{"ref":{"binding":"role","stateName":"role","stateSetter":"set__role","dataType":{"name":"string","defaultValue":"\\"\\""}},"pfImports":["Radio","FormGroup"],"reactImports":["useState"],"jsxCode":"<FormGroup\\n
fieldId={'id'}\\n label={'Role'}\\n isRequired={true}\\n
>\\n <div><Radio\\n key={'Developer'}\\n
id={'id-Developer'}\\n name={'role'}\\n isChecked={'Developer' ===
role}\\n isDisabled={true}\\n label={'Develope [...]
-</div>
+"<FormGroup
+ fieldId={'id'}
+ label={'Role'}
+ isRequired={true}
+ >
+ <div><Radio
+ key={'Developer'}
+ id={'id-Developer'}
+ name={'role'}
+ isChecked={'Developer' === role}
+ isDisabled={true}
+ label={'Developer'}
+ aria-label={'role'}
+ onChange={() => set__role('Developer')}
+ />
+<Radio
+ key={'HR'}
+ id={'id-HR'}
+ name={'role'}
+ isChecked={'HR' === role}
+ isDisabled={true}
+ label={'HR'}
+ aria-label={'role'}
+ onChange={() => set__role('HR')}
+ />
+<Radio
+ key={'UX'}
+ id={'id-UX'}
+ name={'role'}
+ isChecked={'UX' === role}
+ isDisabled={true}
+ label={'UX'}
+ aria-label={'role'}
+ onChange={() => set__role('UX')}
+ /></div>
+ </FormGroup>"
`;
exports[`<RadioField> tests <RadioField> - rendering 1`] = `
-<div>
-
{"ref":{"binding":"role","stateName":"role","stateSetter":"set__role","dataType":{"name":"string","defaultValue":"\\"\\""}},"pfImports":["Radio","FormGroup"],"reactImports":["useState"],"jsxCode":"<FormGroup\\n
fieldId={'id'}\\n label={'Role'}\\n isRequired={true}\\n
>\\n <div><Radio\\n key={'Developer'}\\n
id={'id-Developer'}\\n name={'role'}\\n isChecked={'Developer' ===
role}\\n isDisabled={false}\\n label={'Develop [...]
-</div>
+"<FormGroup
+ fieldId={'id'}
+ label={'Role'}
+ isRequired={true}
+ >
+ <div><Radio
+ key={'Developer'}
+ id={'id-Developer'}
+ name={'role'}
+ isChecked={'Developer' === role}
+ isDisabled={false}
+ label={'Developer'}
+ aria-label={'role'}
+ onChange={() => set__role('Developer')}
+ />
+<Radio
+ key={'HR'}
+ id={'id-HR'}
+ name={'role'}
+ isChecked={'HR' === role}
+ isDisabled={false}
+ label={'HR'}
+ aria-label={'role'}
+ onChange={() => set__role('HR')}
+ />
+<Radio
+ key={'UX'}
+ id={'id-UX'}
+ name={'role'}
+ isChecked={'UX' === role}
+ isDisabled={false}
+ label={'UX'}
+ aria-label={'role'}
+ onChange={() => set__role('UX')}
+ /></div>
+ </FormGroup>"
`;
diff --git
a/packages/form-code-generator-patternfly-theme/tests/__snapshots__/SelectField.test.tsx.snap
b/packages/form-code-generator-patternfly-theme/tests/__snapshots__/SelectField.test.tsx.snap
index 24b0b19b086..ab41b306dfd 100644
---
a/packages/form-code-generator-patternfly-theme/tests/__snapshots__/SelectField.test.tsx.snap
+++
b/packages/form-code-generator-patternfly-theme/tests/__snapshots__/SelectField.test.tsx.snap
@@ -1,13 +1,68 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`<SelectField> tests <SelectField> - multiple value rendering 1`] = `
-<div>
-
{"ref":{"binding":"otherPositions","stateName":"otherPositions","stateSetter":"set__otherPositions","dataType":{"name":"string[]","defaultValue":"[]"}},"pfImports":["SelectOption","SelectOptionObject","Select","SelectVariant","FormGroup"],"reactImports":["useState"],"requiredCode":["multiple_select_functions"],"jsxCode":"<FormGroup\\n
fieldId={'id'}\\n label={'OtherPositions'}\\n
isRequired={true}\\n ><Select\\n id={'id'}\\n
name={'otherPositions'}\ [...]
-</div>
+"<FormGroup
+ fieldId={'id'}
+ label={'OtherPositions'}
+ isRequired={true}
+ ><Select
+ id={'id'}
+ name={'otherPositions'}
+ variant={SelectVariant.typeaheadMulti}
+ isDisabled={false}
+ placeholderText={''}
+ isOpen={otherPositions__expanded}
+ selections={otherPositions}
+ onToggle={(isOpen) => set__otherPositions__expanded(isOpen)}
+ onSelect={(event, value, isPlaceHolder) => {
+ if (isPlaceHolder) {
+ set__otherPositions([]);
+ } else {
+ set__otherPositions(prev => {
+ const selectedValue = value.toString ? value.toString() : value as
string;
+ if (prev.indexOf(selectedValue) != -1) {
+ return prev.filter((s) => s !== selectedValue);
+ }
+ return [selectedValue, ...prev];
+ });
+ }
+ }}
+ value={otherPositions}
+ >
+ <SelectOption key={'Developer'}
value={'Developer'}>Developer</SelectOption>
+<SelectOption key={'HR'} value={'HR'}>HR</SelectOption>
+<SelectOption key={'UX'} value={'UX'}>UX</SelectOption>
+ </Select></FormGroup>"
`;
exports[`<SelectField> tests <SelectField> - single value rendering 1`] = `
-<div>
-
{"ref":{"binding":"role","stateName":"role","stateSetter":"set__role","dataType":{"name":"string","defaultValue":"\\"\\""}},"pfImports":["SelectOption","SelectOptionObject","Select","SelectVariant","FormGroup"],"reactImports":["useState"],"requiredCode":["select_functions"],"jsxCode":"<FormGroup\\n
fieldId={'id'}\\n label={'Role'}\\n isRequired={true}\\n
><Select\\n id={'id'}\\n name={'role'}\\n
variant={SelectVariant.single}\\n isDisabled [...]
-</div>
+"<FormGroup
+ fieldId={'id'}
+ label={'Role'}
+ isRequired={true}
+ ><Select
+ id={'id'}
+ name={'role'}
+ variant={SelectVariant.single}
+ isDisabled={false}
+ placeholderText={''}
+ isOpen={role__expanded}
+ selections={role}
+ onToggle={(isOpen) => set__role__expanded(isOpen)}
+ onSelect={(event, value, isPlaceHolder) => {
+ if (isPlaceHolder) {
+ set__role('');
+ set__role__expanded(false);
+ } else {
+ const parsedSelection = value.toString ? value.toString() : value as
string;
+ set__role(parsedSelection || '');
+ set__role__expanded(false);
+ }
+ }}
+ value={role}
+ >
+ <SelectOption key={'Developer'}
value={'Developer'}>Developer</SelectOption>
+<SelectOption key={'HR'} value={'HR'}>HR</SelectOption>
+<SelectOption key={'UX'} value={'UX'}>UX</SelectOption>
+ </Select></FormGroup>"
`;
diff --git
a/packages/form-code-generator-patternfly-theme/tests/__snapshots__/TextField.test.tsx.snap
b/packages/form-code-generator-patternfly-theme/tests/__snapshots__/TextField.test.tsx.snap
index ba5823bfb17..6acb3ce9752 100644
---
a/packages/form-code-generator-patternfly-theme/tests/__snapshots__/TextField.test.tsx.snap
+++
b/packages/form-code-generator-patternfly-theme/tests/__snapshots__/TextField.test.tsx.snap
@@ -1,25 +1,69 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`<TextField> tests <TextField> - DatePicker rendering - disabled 1`] =
`
-<div>
-
{"ref":{"binding":"name","stateName":"name","stateSetter":"set__name","dataType":{"name":"string"}},"pfImports":["DatePicker","FormGroup"],"reactImports":["useState"],"requiredCode":["date_functions"],"jsxCode":"<FormGroup\\n
fieldId={'id'}\\n label={'label'}\\n isRequired={true}\\n
>\\n <DatePicker\\n id={'date-picker-id'}\\n
isDisabled={true}\\n name={'name'}\\n onChange={newDate =>
onDateChange(newDate, set__name [...]
-</div>
+"<FormGroup
+ fieldId={'id'}
+ label={'label'}
+ isRequired={true}
+ >
+ <DatePicker
+ id={'date-picker-id'}
+ isDisabled={true}
+ name={'name'}
+ onChange={newDate => onDateChange(newDate, set__name, name)}
+ value={parseDate(name)}
+ />
+ </FormGroup>"
`;
exports[`<TextField> tests <TextField> - DatePicker rendering 1`] = `
-<div>
-
{"ref":{"binding":"name","stateName":"name","stateSetter":"set__name","dataType":{"name":"string"}},"pfImports":["DatePicker","FormGroup"],"reactImports":["useState"],"requiredCode":["date_functions"],"jsxCode":"<FormGroup\\n
fieldId={'id'}\\n label={'label'}\\n isRequired={true}\\n
>\\n <DatePicker\\n id={'date-picker-id'}\\n
isDisabled={false}\\n name={'name'}\\n onChange={newDate
=> onDateChange(newDate, set__nam [...]
-</div>
+"<FormGroup
+ fieldId={'id'}
+ label={'label'}
+ isRequired={true}
+ >
+ <DatePicker
+ id={'date-picker-id'}
+ isDisabled={false}
+ name={'name'}
+ onChange={newDate => onDateChange(newDate, set__name, name)}
+ value={parseDate(name)}
+ />
+ </FormGroup>"
`;
exports[`<TextField> tests <TextField> - TextInput rendering - disabled 1`] = `
-<div>
-
{"ref":{"binding":"name","stateName":"name","stateSetter":"set__name","dataType":{"name":"string","defaultValue":"\\"\\""}},"pfImports":["TextInput","FormGroup"],"reactImports":["useState"],"jsxCode":"<FormGroup\\n
fieldId={'id'}\\n label={'label'}\\n isRequired={true}\\n
>\\n <TextInput\\n name={'name'}\\n id={'id'}\\n
isDisabled={true}\\n placeholder={''}\\n type={'text'}\\n
value={name}\\n onChange={set [...]
-</div>
+"<FormGroup
+ fieldId={'id'}
+ label={'label'}
+ isRequired={true}
+ >
+ <TextInput
+ name={'name'}
+ id={'id'}
+ isDisabled={true}
+ placeholder={''}
+ type={'text'}
+ value={name}
+ onChange={set__name}
+ />
+ </FormGroup>"
`;
exports[`<TextField> tests <TextField> - TextInput rendering 1`] = `
-<div>
-
{"ref":{"binding":"name","stateName":"name","stateSetter":"set__name","dataType":{"name":"string","defaultValue":"\\"\\""}},"pfImports":["TextInput","FormGroup"],"reactImports":["useState"],"jsxCode":"<FormGroup\\n
fieldId={'id'}\\n label={'label'}\\n isRequired={true}\\n
>\\n <TextInput\\n name={'name'}\\n id={'id'}\\n
isDisabled={false}\\n placeholder={''}\\n type={'text'}\\n
value={name}\\n onChange={se [...]
-</div>
+"<FormGroup
+ fieldId={'id'}
+ label={'label'}
+ isRequired={true}
+ >
+ <TextInput
+ name={'name'}
+ id={'id'}
+ isDisabled={false}
+ placeholder={''}
+ type={'text'}
+ value={name}
+ onChange={set__name}
+ />
+ </FormGroup>"
`;
diff --git
a/packages/form-code-generator-patternfly-theme/tests/__snapshots__/UnsupportedField.test.tsx.snap
b/packages/form-code-generator-patternfly-theme/tests/__snapshots__/UnsupportedField.test.tsx.snap
index d66f1fc18e2..d8abf270c12 100644
---
a/packages/form-code-generator-patternfly-theme/tests/__snapshots__/UnsupportedField.test.tsx.snap
+++
b/packages/form-code-generator-patternfly-theme/tests/__snapshots__/UnsupportedField.test.tsx.snap
@@ -1,7 +1,15 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`<UnsupportedField> tests <UnsupportedField> - rendering 1`] = `
-<div>
-
{"ref":{"binding":"friends","stateName":"friends","stateSetter":"set__friends","dataType":{"name":"any[]"}},"pfImports":["Alert","FormGroup"],"reactImports":["useState"],"jsxCode":"<FormGroup\\n
fieldId={'id'}\\n label={'Friends?'}\\n isRequired={true}\\n
>\\n <Alert variant='warning' title='Unsupported field type:
Array'> \\n Cannot find form control for property
<code>friends</code> with type
<code>Array</code>:<br/ [...]
-</div>
+"<FormGroup
+ fieldId={'id'}
+ label={'Friends?'}
+ isRequired={true}
+ >
+ <Alert variant='warning' title='Unsupported field type: Array'>
+ Cannot find form control for property <code>friends</code> with type
<code>Array</code>:<br/>
+ Some complex property types, such as <code>Array<object></code>
aren't yet supported, however, you can still write your
+ own component into the form and use the already existing states
<code>const [ friends, set__friends ]</code>.
+ </Alert>
+ </FormGroup>"
`;
diff --git a/packages/form-code-generator-patternfly-theme/webpack.config.js
b/packages/form-code-generator-patternfly-theme/webpack.config.js
index 08a16c56ab7..b993d997a0c 100644
--- a/packages/form-code-generator-patternfly-theme/webpack.config.js
+++ b/packages/form-code-generator-patternfly-theme/webpack.config.js
@@ -28,7 +28,6 @@ module.exports = (webpackEnv, webpackArgv) => [
index: "./src/index.ts",
theme: "./src/theme.ts",
},
- plugins: [new CopyPlugin({ patterns: [{ from: "./src/resources", to:
"./resources" }] })],
output: {
libraryTarget: "commonjs2",
},
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]