Skip to content

Commit

Permalink
feat: add TextField primitive (#211)
Browse files Browse the repository at this point in the history
  • Loading branch information
dpilch authored Nov 10, 2021
1 parent 1ccd6f3 commit bc7de0f
Show file tree
Hide file tree
Showing 11 changed files with 135 additions and 42 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2322,6 +2322,41 @@ export default function SimplePropertyBindingDefaultValue(
}
`;
exports[`amplify render tests primitives TextField 1`] = `
"/* eslint-disable */
import React from \\"react\\";
import {
EscapeHatchProps,
TextField,
TextFieldProps,
getOverrideProps,
} from \\"@aws-amplify/ui-react\\";
export type TextFieldPrimitiveProps<Multiline extends boolean> =
React.PropsWithChildren<
Partial<TextFieldProps<Multiline>> & {
overrides?: EscapeHatchProps | undefined | null;
}
>;
export default function TextFieldPrimitive<Multiline extends boolean>(
props: TextFieldPrimitiveProps<Multiline>
): React.ReactElement {
const { overrides: overridesProp, ...rest } = props;
const overrides = { ...overridesProp };
return (
/* @ts-ignore: TS2322 */
<TextField
label=\\"Name\\"
placeholder=\\"Holden\\"
descriptiveText=\\"Please enter valid name\\"
{...rest}
{...getOverrideProps(overrides, \\"TextField\\")}
></TextField>
);
}
"
`;
exports[`amplify render tests sample code snippet tests should generate a sample code snippet for components 1`] = `
"/* eslint-disable */
import React from \\"react\\";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ exports[`Primitives StepperField 1`] = `"<StepperField {...rest} {...getOverride

exports[`Primitives SwitchField 1`] = `"<SwitchField {...rest} {...getOverrideProps(overrides, \\"SwitchField\\")}></SwitchField>"`;

exports[`Primitives TabItem 1`] = `"<TabItem {...rest} {...getOverrideProps(overrides, \\"TabItem\\")}></TabItem>"`;

exports[`Primitives Tabs 1`] = `"<Tabs {...rest} {...getOverrideProps(overrides, \\"Tabs\\")}></Tabs>"`;

exports[`Primitives Text 1`] = `"<Text {...rest} {...getOverrideProps(overrides, \\"Text\\")}></Text>"`;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -387,4 +387,10 @@ describe('amplify render tests', () => {
});
});
});

describe('primitives', () => {
test('TextField', () => {
expect(generateWithAmplifyRenderer('primitives/TextFieldPrimitive').componentText).toMatchSnapshot();
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"id": "1234-5678-9010",
"componentType": "TextField",
"name": "TextFieldPrimitive",
"properties": {
"label": {
"value": "Name"
},
"placeholder": {
"value": "Holden"
},
"descriptiveText": {
"value": "Please enter valid name"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ import {
SwitchFieldProps,
TabItemProps,
TabsProps,
TextFieldProps,
ToggleButtonProps,
ToggleButtonGroupProps,
ViewProps,
Expand Down Expand Up @@ -276,9 +277,7 @@ export class AmplifyRenderer extends ReactStudioTemplateRenderer {
return new TextRenderer(component, this.importCollection, parent).renderElement();

case Primitive.TextField:
// unofficial support to retain functionality
// TODO: add official support
return new ReactComponentWithChildrenRenderer<ViewProps>(
return new ReactComponentWithChildrenRenderer<TextFieldProps<boolean>>(
component,
this.importCollection,
parent,
Expand Down
21 changes: 21 additions & 0 deletions packages/studio-ui-codegen-react/lib/primitive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
See the License for the specific language governing permissions and
limitations under the License.
*/
import { factory, SyntaxKind, TypeParameterDeclaration, TypeNode } from 'typescript';

enum Primitive {
Alert = 'Alert',
Badge = 'Badge',
Expand Down Expand Up @@ -73,3 +75,22 @@ export const PrimitiveChildrenPropMapping: Partial<Record<Primitive, string>> =
[Primitive.Text]: 'label',
[Primitive.ToggleButton]: 'label',
};

export const PrimitiveTypeParameter: Partial<
Record<Primitive, { declaration: () => TypeParameterDeclaration[] | undefined; reference: () => TypeNode[] }>
> = {
[Primitive.TextField]: {
declaration: () => [
factory.createTypeParameterDeclaration(
factory.createIdentifier('Multiline'),
factory.createKeywordTypeNode(SyntaxKind.BooleanKeyword),
undefined,
),
],
reference: () => [factory.createTypeReferenceNode(factory.createIdentifier('Multiline'), undefined)],
},
[Primitive.Collection]: {
declaration: () => undefined,
reference: () => [factory.createKeywordTypeNode(SyntaxKind.AnyKeyword)],
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ import {
jsonToLiteral,
bindingPropertyUsesHook,
} from './react-studio-template-renderer-helper';
import { isPrimitive } from './primitive';
import Primitive, { isPrimitive, PrimitiveTypeParameter } from './primitive';

export abstract class ReactStudioTemplateRenderer extends StudioTemplateRenderer<
string,
Expand Down Expand Up @@ -220,20 +220,23 @@ export abstract class ReactStudioTemplateRenderer extends StudioTemplateRenderer
const modifiers: Modifier[] = renderExport
? [factory.createModifier(SyntaxKind.ExportKeyword), factory.createModifier(SyntaxKind.DefaultKeyword)]
: [];
const typeParameter = PrimitiveTypeParameter[Primitive[this.component.componentType as Primitive]];
// only use type parameter reference if one was declared
const typeParameterReference = typeParameter && typeParameter.declaration() ? typeParameter.reference() : undefined;
return factory.createFunctionDeclaration(
undefined,
modifiers,
undefined,
factory.createIdentifier(componentName),
undefined,
typeParameter ? typeParameter.declaration() : undefined,
[
factory.createParameterDeclaration(
undefined,
undefined,
undefined,
'props',
undefined,
factory.createTypeReferenceNode(componentPropType, undefined),
factory.createTypeReferenceNode(componentPropType, typeParameterReference),
undefined,
),
],
Expand Down Expand Up @@ -290,16 +293,19 @@ export abstract class ReactStudioTemplateRenderer extends StudioTemplateRenderer
),
]);
const componentPropType = getComponentPropName(component.name);
const propsTypeParameter = PrimitiveTypeParameter[Primitive[component.componentType as Primitive]];

this.importCollection.addImport('@aws-amplify/ui-react', 'EscapeHatchProps');

return factory.createTypeAliasDeclaration(
undefined,
[factory.createModifier(ts.SyntaxKind.ExportKeyword)],
factory.createIdentifier(componentPropType),
undefined,
propsTypeParameter ? propsTypeParameter.declaration() : undefined,
factory.createTypeReferenceNode(factory.createIdentifier('React.PropsWithChildren'), [
factory.createIntersectionTypeNode(
this.dropMissingListElements([
this.buildPrimitivePropNode(component),
this.buildBasePropNode(component),
this.buildComponentPropNode(component),
this.buildVariantPropNode(component),
escapeHatchTypeNode,
Expand All @@ -309,39 +315,26 @@ export abstract class ReactStudioTemplateRenderer extends StudioTemplateRenderer
);
}

private hasPrimitivePropType(component: StudioComponent): boolean {
return component.componentType !== 'String';
}

private getParameterizedPrimitivePropType(component: StudioComponent): TypeNode | undefined {
switch (component.componentType) {
case 'Collection':
return factory.createTypeReferenceNode(factory.createIdentifier('CollectionProps'), [
factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword),
]);
default:
return undefined;
}
}

private buildPrimitivePropNode(component: StudioComponent): TypeNode | undefined {
if (!this.hasPrimitivePropType(component)) {
return undefined;
}

private buildBasePropNode(component: StudioComponent): TypeNode | undefined {
const propsType = this.getPropsTypeName(component);

if (isPrimitive(component.componentType)) {
const componentIsPrimitive = isPrimitive(component.componentType);
if (componentIsPrimitive) {
this.importCollection.addImport('@aws-amplify/ui-react', propsType);
} else {
this.importCollection.addImport(`./${component.componentType}`, `${component.componentType}Props`);
}

const parameterizedPrimitivePropType = this.getParameterizedPrimitivePropType(component);
const primitivePropType =
parameterizedPrimitivePropType || factory.createTypeReferenceNode(factory.createIdentifier(propsType), undefined);
const propsTypeParameter = componentIsPrimitive
? PrimitiveTypeParameter[Primitive[component.componentType as Primitive]]
: undefined;

return factory.createTypeReferenceNode(factory.createIdentifier('Partial'), [primitivePropType]);
const basePropType = factory.createTypeReferenceNode(
factory.createIdentifier(propsType),
propsTypeParameter ? propsTypeParameter.reference() : undefined,
);

return factory.createTypeReferenceNode(factory.createIdentifier('Partial'), [basePropType]);
}

/**
Expand Down Expand Up @@ -1050,11 +1043,6 @@ export abstract class ReactStudioTemplateRenderer extends StudioTemplateRenderer
}

private getPropsTypeName(component: StudioComponent): string {
// special case for FieldGroup. All other primitives follow same pattern
if (component.componentType === 'FieldGroup') {
return 'FieldGroupOptions';
}

return `${component.componentType}Props`;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,19 @@ describe('Primitives', () => {
});
});

describe('TextField', () => {
it('Basic', () => {
cy.visit('http://localhost:3000/primitives-tests');
cy.get('#text-field')
.find('.amplify-textfield')
.within(() => {
cy.get('.amplify-label').contains('Name');
cy.get('.amplify-text').contains('Please enter valid name');
cy.get('.amplify-input').should('have.attr', 'placeholder', 'Holden');
});
});
});

describe('ToggleButton', () => {
it('Basic', () => {
cy.visit('http://localhost:3000/primitives-tests');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import StepperFieldPrimitive from './ui-components/StepperFieldPrimitive';
import SwitchFieldPrimitive from './ui-components/SwitchFieldPrimitive';
import TabsPrimitive from './ui-components/TabsPrimitive';
import TextPrimitive from './ui-components/TextPrimitive';
// import TextFieldPrimitive from './ui-components/TextFieldPrimitive';
import TextFieldPrimitive from './ui-components/TextFieldPrimitive';
import ToggleButtonPrimitive from './ui-components/ToggleButtonPrimitive';
import ToggleButtonGroupPrimitive from './ui-components/ToggleButtonGroupPrimitive';
import ViewPrimitive from './ui-components/ViewPrimitive';
Expand Down Expand Up @@ -160,7 +160,10 @@ export default function PrimitivesTests() {
<Heading>Text</Heading>
<TextPrimitive />
</View>
{/* <TextFieldPrimitive /> */}
<View id="text-field">
<Heading>Text Field</Heading>
<TextFieldPrimitive />
</View>
<View id="toggle-button">
<Heading>Toggle Button</Heading>
<ToggleButtonPrimitive />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,15 @@
"id": "1234-5678-9010",
"componentType": "TextField",
"name": "TextFieldPrimitive",
"properties": {}
"properties": {
"label": {
"value": "Name"
},
"placeholder": {
"value": "Holden"
},
"descriptiveText": {
"value": "Please enter valid name"
}
}
}
2 changes: 1 addition & 1 deletion packages/test-generator/lib/components/primitives/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export { default as StepperFieldPrimitive } from './StepperFieldPrimitive.json';
export { default as SwitchFieldPrimitive } from './SwitchFieldPrimitive.json';
export { default as TabsPrimitive } from './TabsPrimitive.json';
export { default as TextPrimitive } from './TextPrimitive.json';
// export { default as TextFieldPrimitive } from './TextFieldPrimitive.json';
export { default as TextFieldPrimitive } from './TextFieldPrimitive.json';
export { default as ToggleButtonPrimitive } from './ToggleButtonPrimitive.json';
export { default as ToggleButtonGroupPrimitive } from './ToggleButtonGroupPrimitive.json';
export { default as ViewPrimitive } from './ViewPrimitive.json';
Expand Down

0 comments on commit bc7de0f

Please sign in to comment.