Skip to content

Commit

Permalink
fix: add default value support for controlled textfield components (#884
Browse files Browse the repository at this point in the history
)

Co-authored-by: Scott Young <scoyou@amazon.com>
  • Loading branch information
scottyoung and Scott Young authored Jan 17, 2023
1 parent 728e61a commit b90e191
Show file tree
Hide file tree
Showing 7 changed files with 63 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -936,12 +936,12 @@ export default function CustomDataForm(props) {
const { onSubmit, onCancel, onValidate, onChange, overrides, ...rest } =
props;
const initialValues = {
name: \\"\\",
email: \\"\\",
name: \\"John Doe\\",
email: \\"johndoe@amplify.com\\",
city: undefined,
category: undefined,
pages: 0,
phone: \\"\\",
phone: \\"+1-401-152-6995\\",
};
const [name, setName] = React.useState(initialValues.name);
const [email, setEmail] = React.useState(initialValues.email);
Expand Down Expand Up @@ -1026,7 +1026,6 @@ export default function CustomDataForm(props) {
<TextField
label=\\"name\\"
isRequired={true}
defaultValue=\\"John Doe\\"
value={name}
onChange={(e) => {
let { value } = e.target;
Expand Down Expand Up @@ -1055,7 +1054,6 @@ export default function CustomDataForm(props) {
<TextField
label=\\"E-mail\\"
isRequired={true}
defaultValue=\\"johndoe@amplify.com\\"
value={email}
onChange={(e) => {
let { value } = e.target;
Expand Down Expand Up @@ -1200,7 +1198,6 @@ export default function CustomDataForm(props) {
<TextField
label=\\"Phone Number\\"
isRequired={true}
defaultValue=\\"+1-401-152-6995\\"
type=\\"tel\\"
value={phone}
onChange={(e) => {
Expand Down Expand Up @@ -1335,12 +1332,12 @@ export default function CustomDataForm(props) {
props;
const { tokens } = useTheme();
const initialValues = {
name: \\"\\",
email: \\"\\",
name: \\"John Doe\\",
email: \\"johndoe@amplify.com\\",
city: undefined,
category: undefined,
pages: 0,
phone: \\"\\",
phone: \\"+1-401-152-6995\\",
};
const [name, setName] = React.useState(initialValues.name);
const [email, setEmail] = React.useState(initialValues.email);
Expand Down Expand Up @@ -1425,7 +1422,6 @@ export default function CustomDataForm(props) {
<TextField
label=\\"name\\"
isRequired={true}
defaultValue=\\"John Doe\\"
value={name}
onChange={(e) => {
let { value } = e.target;
Expand Down Expand Up @@ -1454,7 +1450,6 @@ export default function CustomDataForm(props) {
<TextField
label=\\"E-mail\\"
isRequired={true}
defaultValue=\\"johndoe@amplify.com\\"
value={email}
onChange={(e) => {
let { value } = e.target;
Expand Down Expand Up @@ -1599,7 +1594,6 @@ export default function CustomDataForm(props) {
<TextField
label=\\"Phone Number\\"
isRequired={true}
defaultValue=\\"+1-401-152-6995\\"
type=\\"tel\\"
value={phone}
onChange={(e) => {
Expand Down Expand Up @@ -1878,9 +1872,9 @@ export default function CustomDataForm(props) {
const { onSubmit, onCancel, onValidate, onChange, overrides, ...rest } =
props;
const initialValues = {
name: \\"\\",
email: [],
phone: [],
name: \\"John Doe\\",
email: [\\"johndoe@amplify.com\\"],
phone: [\\"+1-401-152-6995\\"],
};
const [name, setName] = React.useState(initialValues.name);
const [email, setEmail] = React.useState(initialValues.email);
Expand Down Expand Up @@ -1959,7 +1953,6 @@ export default function CustomDataForm(props) {
<TextField
label=\\"name\\"
isRequired={true}
defaultValue=\\"John Doe\\"
value={name}
onChange={(e) => {
let { value } = e.target;
Expand Down Expand Up @@ -2008,7 +2001,6 @@ export default function CustomDataForm(props) {
<TextField
label=\\"E-mail\\"
isRequired={true}
defaultValue=\\"johndoe@amplify.com\\"
value={currentEmailValue}
onChange={(e) => {
let { value } = e.target;
Expand Down Expand Up @@ -2051,7 +2043,6 @@ export default function CustomDataForm(props) {
<TextField
label=\\"phone\\"
isRequired={true}
defaultValue=\\"+1-401-152-6995\\"
type=\\"tel\\"
value={currentPhoneValue}
onChange={(e) => {
Expand Down Expand Up @@ -2174,8 +2165,8 @@ export default function CustomDataForm(props) {
...rest
} = props;
const initialValues = {
name: \\"\\",
email: \\"\\",
name: \\"John Doe\\",
email: \\"johndoe@amplify.com\\",
\\"metadata-field\\": \\"\\",
city: undefined,
category: undefined,
Expand Down Expand Up @@ -2280,7 +2271,6 @@ export default function CustomDataForm(props) {
<TextField
label=\\"name\\"
isRequired={true}
defaultValue=\\"John Doe\\"
value={name}
onChange={(e) => {
let { value } = e.target;
Expand Down Expand Up @@ -2309,7 +2299,6 @@ export default function CustomDataForm(props) {
<TextField
label=\\"E-mail\\"
isRequired={true}
defaultValue=\\"johndoe@amplify.com\\"
value={email}
onChange={(e) => {
let { value } = e.target;
Expand Down
16 changes: 15 additions & 1 deletion packages/codegen-ui-react/lib/__tests__/forms/form-state.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ describe('set field state', () => {
});

describe('get default values', () => {
it('should generate the proper default value for a TextField', () => {
it('should generate the proper default value for an empty TextField', () => {
const expression = getDefaultValueExpression('name', 'TextField');
expect(expression).toMatchObject({ text: '' });
});
Expand All @@ -72,4 +72,18 @@ describe('get default values', () => {
const expression = getDefaultValueExpression('name', 'SliderField');
expect(expression).toMatchObject({ text: '0' });
});

it('should generate the proper default value for non-empty TextField', () => {
const expression = getDefaultValueExpression('name', 'TextField', undefined, false, false, 'Don Corleone');
expect(expression).toMatchObject({ text: 'Don Corleone' });
});

it('should generate the proper default value for non-empty TextField array', () => {
const expression = getDefaultValueExpression('name', 'TextField', undefined, true, false, 'mobster');
expect(expression).toEqual(
expect.objectContaining({
elements: expect.arrayContaining([expect.objectContaining({ text: 'mobster' })]),
}),
);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
DataFieldDataType,
isValidVariableName,
isNonModelDataType,
StudioForm,
} from '@aws-amplify/codegen-ui';
import {
factory,
Expand All @@ -39,7 +40,13 @@ import {
ConditionalExpression,
CallChain,
} from 'typescript';
import { capitalizeFirstLetter, lowerCaseFirst, getSetNameIdentifier, buildUseStateExpression } from '../../helpers';
import {
capitalizeFirstLetter,
lowerCaseFirst,
getSetNameIdentifier,
buildUseStateExpression,
getControlledComponentDefaultValue,
} from '../../helpers';
import { getElementAccessExpression } from './invalid-variable-helpers';
import { isModelDataType, shouldWrapInArrayField } from './render-checkers';

Expand Down Expand Up @@ -134,6 +141,7 @@ export const getDefaultValueExpression = (
dataType?: DataFieldDataType,
isArray?: boolean,
isDisplayValue?: boolean,
defaultValue?: string | null,
): Expression => {
const componentTypeToDefaultValueMap: { [key: string]: Expression } = {
Autocomplete: isDisplayValue ? factory.createStringLiteral('') : factory.createIdentifier('undefined'),
Expand All @@ -146,6 +154,12 @@ export const getDefaultValueExpression = (
TextAreaField: factory.createStringLiteral(''),
};

if (defaultValue) {
return isArray
? factory.createArrayLiteralExpression([factory.createStringLiteral(defaultValue)], false)
: factory.createStringLiteral(defaultValue);
}

if (isArray) {
return factory.createArrayLiteralExpression([], false);
}
Expand All @@ -160,14 +174,18 @@ export const getDefaultValueExpression = (
isChecked: false
}
*/
export const getInitialValues = (fieldConfigs: Record<string, FieldConfigMetadata>): Statement => {
export const getInitialValues = (
fieldConfigs: Record<string, FieldConfigMetadata>,
component: StudioForm,
): Statement => {
const stateNames = new Set<string>();
const propertyAssignments = Object.entries(fieldConfigs).reduce<PropertyAssignment[]>(
(acc, [name, { dataType, componentType, isArray }]) => {
const isNested = name.includes('.');
// we are only setting top-level keys
const stateName = name.split('.')[0];
let initialValue = getDefaultValueExpression(name, componentType, dataType, isArray);
const defaultValue = getControlledComponentDefaultValue(component.fields, componentType, name);
let initialValue = getDefaultValueExpression(name, componentType, dataType, isArray, false, defaultValue);
if (isNested) {
// if nested, just set up an empty object for the top-level key
initialValue = factory.createObjectLiteralExpression();
Expand Down
2 changes: 1 addition & 1 deletion packages/codegen-ui-react/lib/forms/react-form-renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -452,7 +452,7 @@ export abstract class ReactFormTemplateRenderer extends StudioTemplateRenderer<
);
}

statements.push(getInitialValues(formMetadata.fieldConfigs));
statements.push(getInitialValues(formMetadata.fieldConfigs, this.component));

statements.push(...getUseStateHooks(formMetadata.fieldConfigs));

Expand Down
16 changes: 15 additions & 1 deletion packages/codegen-ui-react/lib/helpers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
See the License for the specific language governing permissions and
limitations under the License.
*/
import { GenericDataField, GenericDataSchema } from '@aws-amplify/codegen-ui/lib/types';
import { GenericDataField, GenericDataSchema, StudioFormFields } from '@aws-amplify/codegen-ui/lib/types';
import { factory, Statement, Expression, NodeFlags, Identifier } from 'typescript';
import { isPrimitive } from '../primitive';

Expand Down Expand Up @@ -113,3 +113,17 @@ export function isAliased(componentType: string): boolean {
export function removeAlias(aliasString: string): string {
return aliasString.replace(/(Custom$)/g, '');
}

export function getControlledComponentDefaultValue(
fields: StudioFormFields,
componentType: string,
name: string,
): string | null {
let defaultValue = null;
Object.entries(fields).forEach(([key, value]) => {
if (key === name && 'inputType' in value && value.inputType?.defaultValue && componentType === 'TextField') {
defaultValue = value.inputType.defaultValue;
}
});
return defaultValue;
}
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,6 @@ export function getFormDefinitionInputElement(
isRequired: isRequiredValue,
isReadOnly: getFirstDefinedValue([config.inputType?.readOnly, baseConfig?.inputType?.readOnly]),
placeholder: config.inputType?.placeholder || baseConfig?.inputType?.placeholder,
defaultValue: defaultStringValue,
type: getTextFieldType(componentType),
},
studioFormComponentType: componentType,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ export type FormDefinitionTextFieldElement = {
isRequired?: boolean;
isReadOnly?: boolean;
placeholder?: string;
defaultValue?: string;
type?: string;
};
studioFormComponentType:
Expand Down

0 comments on commit b90e191

Please sign in to comment.