Skip to content

Commit

Permalink
feat: change default component for AWSTimestamp to NumberField
Browse files Browse the repository at this point in the history
  • Loading branch information
Hein Jeong authored and hein-j committed Feb 3, 2023
1 parent fa131f7 commit 820094c
Show file tree
Hide file tree
Showing 10 changed files with 113 additions and 96 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18112,12 +18112,6 @@ export default function BlogCreateForm(props) {
setErrors((errors) => ({ ...errors, [fieldName]: validationResponse }));
return validationResponse;
};
const convertTimeStampToDate = (ts) => {
if (Math.abs(Date.now() - ts) < Math.abs(Date.now() - ts * 1000)) {
return new Date(ts);
}
return new Date(ts * 1000);
};
const convertToLocal = (date) => {
const df = new Intl.DateTimeFormat(\\"default\\", {
year: \\"numeric\\",
Expand Down Expand Up @@ -18338,14 +18332,13 @@ export default function BlogCreateForm(props) {
label=\\"Edited at\\"
isRequired={false}
isReadOnly={false}
type=\\"datetime-local\\"
value={
currentEditedAtValue &&
convertToLocal(convertTimeStampToDate(currentEditedAtValue))
}
type=\\"number\\"
step=\\"any\\"
value={currentEditedAtValue}
onChange={(e) => {
let value =
e.target.value === \\"\\" ? \\"\\" : Number(new Date(e.target.value));
let value = isNaN(parseInt(e.target.value))
? e.target.value
: parseInt(e.target.value);
if (errors.editedAt?.hasError) {
runValidationTasks(\\"editedAt\\", value);
}
Expand Down Expand Up @@ -19628,29 +19621,6 @@ export default function InputGalleryUpdateForm(props) {
setErrors((errors) => ({ ...errors, [fieldName]: validationResponse }));
return validationResponse;
};
const convertTimeStampToDate = (ts) => {
if (Math.abs(Date.now() - ts) < Math.abs(Date.now() - ts * 1000)) {
return new Date(ts);
}
return new Date(ts * 1000);
};
const convertToLocal = (date) => {
const df = new Intl.DateTimeFormat(\\"default\\", {
year: \\"numeric\\",
month: \\"2-digit\\",
day: \\"2-digit\\",
hour: \\"2-digit\\",
minute: \\"2-digit\\",
calendar: \\"iso8601\\",
numberingSystem: \\"latn\\",
hourCycle: \\"h23\\",
});
const parts = df.formatToParts(date).reduce((acc, part) => {
acc[part.type] = part.value;
return acc;
}, {});
return \`\${parts.year}-\${parts.month}-\${parts.day}T\${parts.hour}:\${parts.minute}\`;
};
return (
<Grid
as=\\"form\\"
Expand Down Expand Up @@ -20053,11 +20023,13 @@ export default function InputGalleryUpdateForm(props) {
label=\\"Timestamp\\"
isRequired={false}
isReadOnly={false}
type=\\"datetime-local\\"
value={timestamp && convertToLocal(convertTimeStampToDate(timestamp))}
type=\\"number\\"
step=\\"any\\"
value={timestamp}
onChange={(e) => {
let value =
e.target.value === \\"\\" ? \\"\\" : Number(new Date(e.target.value));
let value = isNaN(parseInt(e.target.value))
? e.target.value
: parseInt(e.target.value);
if (onChange) {
const modelFields = {
num,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,7 @@ describe('amplify form renderer tests', () => {
{ isNonModelSupported: true, isRelationshipSupported: true },
);
expect(componentText).toContain('DataStore.save');
expect(componentText).toContain('const convertToLocal');
expect(componentText).not.toContain('const convertToLocal');
expect(componentText).toMatchSnapshot();
expect(declaration).toMatchSnapshot();
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -270,11 +270,13 @@ export const buildOnChangeStatement = (
) => {
const { name: fieldName, componentType: fieldType } = component;
const fieldConfig = fieldConfigs[fieldName];
const { dataType, sanitizedFieldName } = fieldConfig;
const { dataType, sanitizedFieldName, studioFormComponentType } = fieldConfig;
const renderedFieldName = sanitizedFieldName || fieldName;

// build statements that handle new value
const handleChangeStatements: Statement[] = [...buildTargetVariable(fieldType, renderedFieldName, dataType)];
const handleChangeStatements: Statement[] = [
...buildTargetVariable(studioFormComponentType || fieldType, renderedFieldName, dataType),
];

if (!shouldWrapInArrayField(fieldConfig)) {
handleChangeStatements.push(buildOverrideOnChangeStatement(fieldName, fieldConfigs));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,24 +85,38 @@ export const buildTargetVariable = (fieldType: string, fieldName: string, dataTy
fieldTypeToExpressionMap[fieldType]?.identifier ?? expressionMap.destructuredValue;
switch (dataType) {
case 'AWSTimestamp':
// value = e.target.value === '' ? '' : Number(new Date(e.target.value))
defaultIdentifier = expressionMap.value;
expression = factory.createConditionalExpression(
factory.createBinaryExpression(
if (fieldType === 'NumberField') {
// value = isNaN(parseInt(e.target.value)) ? e.target.value : parseInt(e.target.value);
defaultIdentifier = expressionMap.value;
expression = factory.createConditionalExpression(
factory.createCallExpression(factory.createIdentifier('isNaN'), undefined, [
factory.createCallExpression(factory.createIdentifier('parseInt'), undefined, [expressionMap.eTargetValue]),
]),
factory.createToken(SyntaxKind.QuestionToken),
expressionMap.eTargetValue,
factory.createToken(SyntaxKind.EqualsEqualsEqualsToken),
factory.createToken(SyntaxKind.ColonToken),
factory.createCallExpression(factory.createIdentifier('parseInt'), undefined, [expressionMap.eTargetValue]),
);
} else if (fieldType === 'DateTimeField') {
// value = e.target.value === '' ? '' : Number(new Date(e.target.value))
defaultIdentifier = expressionMap.value;
expression = factory.createConditionalExpression(
factory.createBinaryExpression(
expressionMap.eTargetValue,
factory.createToken(SyntaxKind.EqualsEqualsEqualsToken),
factory.createStringLiteral(''),
),
factory.createToken(SyntaxKind.QuestionToken),
factory.createStringLiteral(''),
),
factory.createToken(SyntaxKind.QuestionToken),
factory.createStringLiteral(''),
factory.createToken(SyntaxKind.ColonToken),
factory.createCallExpression(factory.createIdentifier('Number'), undefined, [
factory.createNewExpression(factory.createIdentifier('Date'), undefined, [expressionMap.eTargetValue]),
]),
);
factory.createToken(SyntaxKind.ColonToken),
factory.createCallExpression(factory.createIdentifier('Number'), undefined, [
factory.createNewExpression(factory.createIdentifier('Date'), undefined, [expressionMap.eTargetValue]),
]),
);
}
return [setVariableStatement(defaultIdentifier, expression)];
case 'Float':
if (fieldType === 'TextField') {
if (fieldType === 'NumberField') {
// value = isNaN(parseFloat(e.target.value)) ? e.target.value : parseFloat(e.target.value);
defaultIdentifier = expressionMap.value;
expression = factory.createConditionalExpression(
Expand All @@ -119,7 +133,7 @@ export const buildTargetVariable = (fieldType: string, fieldName: string, dataTy
}
return [setVariableStatement(defaultIdentifier, expression)];
case 'Int':
if (fieldType === 'TextField') {
if (fieldType === 'NumberField') {
// value = isNaN(parseInt(e.target.value)) ? e.target.value : parseInt(e.target.value);
defaultIdentifier = expressionMap.value;
expression = factory.createConditionalExpression(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
*/

import { FieldConfigMetadata, StudioDataSourceType, StudioFormActionType } from '@aws-amplify/codegen-ui';
import { BinaryExpression, factory, Identifier, JsxAttribute, SyntaxKind, ElementAccessExpression } from 'typescript';
import { factory, Identifier, JsxAttribute, SyntaxKind, ElementAccessExpression } from 'typescript';
import { getCurrentDisplayValueName, getCurrentValueName, resetValuesName } from './form-state';
import { isModelDataType, shouldWrapInArrayField } from './render-checkers';
import { FIELD_TYPE_TO_TYPESCRIPT_MAP } from './typescript-type-map';
Expand All @@ -37,23 +37,36 @@ export const ControlledComponents = [
*/
export const isControlledComponent = (componentType: string): boolean => ControlledComponents.includes(componentType);

export const convertedValueAttributeMap: Record<string, (valueIdentifier: Identifier) => BinaryExpression> = {
AWSTimestamp: (valueIdentifier: Identifier) =>
factory.createBinaryExpression(
valueIdentifier,
factory.createToken(SyntaxKind.AmpersandAmpersandToken),
factory.createCallExpression(factory.createIdentifier('convertToLocal'), undefined, [
factory.createCallExpression(factory.createIdentifier('convertTimeStampToDate'), undefined, [valueIdentifier]),
]),
),
AWSDateTime: (valueIdentifier: Identifier) =>
factory.createBinaryExpression(
valueIdentifier,
factory.createToken(SyntaxKind.AmpersandAmpersandToken),
factory.createCallExpression(factory.createIdentifier('convertToLocal'), undefined, [
factory.createNewExpression(factory.createIdentifier('Date'), undefined, [valueIdentifier]),
]),
),
export const getConvertedValueAttribute = (dataType: string, studioFormComponentType: string) => {
switch (dataType) {
case 'AWSDateTime':
return (valueIdentifier: Identifier) =>
factory.createBinaryExpression(
valueIdentifier,
factory.createToken(SyntaxKind.AmpersandAmpersandToken),
factory.createCallExpression(factory.createIdentifier('convertToLocal'), undefined, [
factory.createNewExpression(factory.createIdentifier('Date'), undefined, [valueIdentifier]),
]),
);

case 'AWSTimestamp':
if (studioFormComponentType === 'DateTimeField') {
return (valueIdentifier: Identifier) =>
factory.createBinaryExpression(
valueIdentifier,
factory.createToken(SyntaxKind.AmpersandAmpersandToken),
factory.createCallExpression(factory.createIdentifier('convertToLocal'), undefined, [
factory.createCallExpression(factory.createIdentifier('convertTimeStampToDate'), undefined, [
valueIdentifier,
]),
]),
);
}
break;
default:
return undefined;
}
return undefined;
};

/**
Expand All @@ -64,14 +77,17 @@ export const convertedValueAttributeMap: Record<string, (valueIdentifier: Identi
*/
export const renderDefaultValueAttribute = (
stateName: string,
{ dataType }: FieldConfigMetadata,
{ dataType, studioFormComponentType }: FieldConfigMetadata,
componentType: string,
) => {
const identifier = factory.createIdentifier(stateName);
let expression = factory.createJsxExpression(undefined, identifier);

if (dataType && typeof dataType !== 'object' && convertedValueAttributeMap[dataType]) {
expression = factory.createJsxExpression(undefined, convertedValueAttributeMap[dataType](identifier));
if (dataType && typeof dataType !== 'object') {
const convertedValueAttribute = getConvertedValueAttribute(dataType, studioFormComponentType || componentType);
if (convertedValueAttribute) {
expression = factory.createJsxExpression(undefined, convertedValueAttribute(identifier));
}
}

return factory.createJsxAttribute(
Expand All @@ -93,7 +109,7 @@ export const renderValueAttribute = ({
}): JsxAttribute | undefined => {
const componentType = fieldConfig.studioFormComponentType ?? fieldConfig.componentType;
const shouldGetForUncontrolled = shouldWrapInArrayField(fieldConfig);
const { dataType } = fieldConfig;
const { dataType, studioFormComponentType } = fieldConfig;

let valueIdentifier = currentValueIdentifier || getValueIdentifier(componentName, componentType);

Expand All @@ -120,11 +136,11 @@ export const renderValueAttribute = ({

let controlledExpression = factory.createJsxExpression(undefined, fieldNameIdentifier);

if (dataType && typeof dataType !== 'object' && convertedValueAttributeMap[dataType]) {
controlledExpression = factory.createJsxExpression(
undefined,
convertedValueAttributeMap[dataType](valueIdentifier),
);
if (dataType && typeof dataType !== 'object') {
const convertedValueAttribute = getConvertedValueAttribute(dataType, studioFormComponentType || componentType);
if (convertedValueAttribute) {
controlledExpression = factory.createJsxExpression(undefined, convertedValueAttribute(valueIdentifier));
}
}

const controlledComponentToAttributesMap: { [key: string]: JsxAttribute } = {
Expand Down Expand Up @@ -171,8 +187,11 @@ export const renderValueAttribute = ({
if (shouldGetForUncontrolled) {
let expression = factory.createJsxExpression(undefined, valueIdentifier);

if (dataType && typeof dataType !== 'object' && convertedValueAttributeMap[dataType]) {
expression = factory.createJsxExpression(undefined, convertedValueAttributeMap[dataType](valueIdentifier));
if (dataType && typeof dataType !== 'object') {
const convertedValueAttribute = getConvertedValueAttribute(dataType, studioFormComponentType || componentType);
if (convertedValueAttribute) {
expression = factory.createJsxExpression(undefined, convertedValueAttribute(valueIdentifier));
}
}

return factory.createJsxAttribute(factory.createIdentifier('value'), expression);
Expand Down
11 changes: 10 additions & 1 deletion packages/codegen-ui-react/lib/forms/react-form-renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -614,7 +614,16 @@ export abstract class ReactFormTemplateRenderer extends StudioTemplateRenderer<

// timestamp type takes precedence over datetime as it includes formatter for datetime
// we include both the timestamp conversion and local date formatter
if (dataTypesMap.AWSTimestamp) {
if (
dataTypesMap.AWSTimestamp &&
dataTypesMap.AWSTimestamp.some((fieldName) => {
const field = formMetadata.fieldConfigs[fieldName];
if (field && field.studioFormComponentType === 'DateTimeField') {
return true;
}
return false;
})
) {
statements.push(convertTimeStampToDateAST, convertToLocalAST);
}
// if we only have date time then we only need the local conversion
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,11 @@
"inputType": {
"type": "CheckboxField"
}
}
},
"timestamp": {
"inputType": {
"type": "DateTimeField"
} }
},
"formActionType": "create",
"name": "InputGalleryCreateForm",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ export const FIELD_TYPE_MAP: {
supportedComponents: new Set(['DateTimeField']),
},
AWSTimestamp: {
defaultComponent: 'DateTimeField',
supportedComponents: new Set(['DateTimeField']),
defaultComponent: 'NumberField',
supportedComponents: new Set(['DateTimeField', 'NumberField']),
},
AWSEmail: {
defaultComponent: 'EmailField',
Expand Down
2 changes: 1 addition & 1 deletion packages/codegen-ui/lib/types/form/form-metadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export type StudioFormDataType = {
export type FieldConfigMetadata = {
// ex. name field has a string validation type where the rule is char length > 5
validationRules: FieldValidationConfiguration[];
// component field is of type AWSTimestamp will need to map this to date then get time from date
// needed for mapping conversions e.g. for Int
dataType?: DataFieldDataType;
relationship?: GenericDataRelationshipType;
// for JSON type with invalid variable field name ie. { "1first-Name": "John" } => "firstName"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,10 +123,7 @@ describe('CreateForms', () => {
getInputByLabel('Aws date').type('2022-10-12');
getInputByLabel('Aws time').type('10:12');
getInputByLabel('Aws date time').type('2017-06-01T08:30');
getInputByLabel('Aws timestamp').type('2022-12-01T00:30');

// handle chrome bug - https://support.google.com/chrome/thread/29828561?hl=en
getInputByLabel('Aws timestamp').should('have.value', '2022-12-01T00:30');
getInputByLabel('Aws timestamp').type('1669854600000');

getInputByLabel('Aws email').type('myemail@yahoo.com');
getInputByLabel('Aws url').type('https://amazon.com');
Expand Down

0 comments on commit 820094c

Please sign in to comment.