Code Monkey home page Code Monkey logo

amplify-codegen-ui's Introduction

AWS Amplify


Amplify Codegen UI

GitHub Discord Build CircleCI Language grade: JavaScript codecov Open Bugs Feature Requests

Generate React components for use in an AWS Amplify project.

Usage

Amplify Codegen UI supports component generation in Node or a browser environment.

Generate in Node

Components

import {
  AmplifyRenderer,
  StudioTemplateRendererFactory,
  StudioTemplateRendererManager,
} from '@aws-amplify/codegen-ui-react';

const renderConfig = {};
const outputConfig = {
  outputPathDir: './src/ui-components';
};

const componentRendererFactory = new StudioTemplateRendererFactory(
  (component) => new AmplifyRenderer(component, renderConfig),
);

const rendererManager = new StudioTemplateRendererManager(componentRendererFactory, outputConfig);

const component = {
  id: '1234-5678-9010',
  componentType: 'Text',
  name: 'TextPrimitive',
  properties: {
    label: {
      value: 'Hello world',
    },
  },
};

rendererManager.renderSchemaToTemplate(component);

Themes

import {
  ReactThemeStudioTemplateRenderer,
  StudioTemplateRendererFactory,
  StudioTemplateRendererManager,
} from '@aws-amplify/codegen-ui-react';

const renderConfig = {};
const outputConfig = {
  outputPathDir: './src/ui-components';
};

const themeRendererFactory = new StudioTemplateRendererFactory(
  (theme) => new ReactThemeStudioTemplateRenderer(theme, renderConfig),
);

const themeRendererManager = new StudioTemplateRendererManager(themeRendererFactory, outputConfig);

const theme = {
  id: '1234-5678-9010',
  name: 'MyTheme',
  values: [
    {
      key: 'tokens',
      value: {
        children: [
          {
            key: 'colors',
            value: {
              children: [
                {
                  key: 'font',
                  value: {
                    children: [
                      {
                        key: 'primary',
                        value: {
                          children: [
                            {
                              key: 'value',
                              value: {
                                value: '#008080',
                              },
                            },
                          ],
                        },
                      },
                    ],
                  },
                },
              ],
            },
          },
        ],
      },
    },
  ],
};

themeRendererManager.renderSchemaToTemplate(theme);

index.js File

import {
  ReactIndexStudioTemplateRenderer,
  StudioTemplateRendererFactory,
  StudioTemplateRendererManager,
} from '@aws-amplify/codegen-ui-react';

const renderConfig = {};
const outputConfig = {
  outputPathDir: './src/ui-components',
};

const indexRendererFactory = new StudioTemplateRendererFactory(
  (components) => new ReactIndexStudioTemplateRenderer(components, renderConfig),
);

const indexRendererManager = new StudioTemplateRendererManager(indexRendererFactory, outputConfig);

const components = [
  {
    id: '1234-5678-9010',
    componentType: 'Text',
    name: 'MyHelloWorld',
    properties: {
      label: {
        value: 'Hello world!',
      },
    },
  },
  {
    id: '1234-5678-9012',
    componentType: 'Text',
    name: 'MyCodegen',
    properties: {
      label: {
        value: 'Codegen!',
      },
    },
  },
];

indexRendererManager.renderSchemaToTemplate(components);

Generate in Browser

When generating components in the browser, components will not be written to the file system.

Components

import { AmplifyRenderer } from '@aws-amplify/codegen-ui-react';

const renderConfig = {};

const component = {
  id: '1234-5678-9010',
  componentType: 'Text',
  name: 'TextPrimitive',
  properties: {
    label: {
      value: 'Hello world',
    },
  },
};

const { importsText, compText } = new AmplifyRenderer(component, renderConfig).renderComponentOnly();

Themes

import { ReactThemeStudioTemplateRenderer } from '@aws-amplify/codegen-ui-react';

const renderConfig = {};

const theme = {
  id: '1234-5678-9010',
  name: 'MyTheme',
  values: [
    {
      key: 'tokens',
      value: {
        children: [
          {
            key: 'colors',
            value: {
              children: [
                {
                  key: 'font',
                  value: {
                    children: [
                      {
                        key: 'primary',
                        value: {
                          children: [
                            {
                              key: 'value',
                              value: {
                                value: '#008080',
                              },
                            },
                          ],
                        },
                      },
                    ],
                  },
                },
              ],
            },
          },
        ],
      },
    },
  ],
};

const { componentText } = new ReactThemeStudioTemplateRenderer(theme, renderConfig).renderComponent();

index.js File

import { ReactIndexStudioTemplateRenderer } from '@aws-amplify/codegen-ui-react';

const renderConfig = {};
const components = [
  {
    id: '1234-5678-9010',
    componentType: 'Text',
    name: 'MyHelloWorld',
    properties: {
      label: {
        value: 'Hello world!',
      },
    },
  },
  {
    id: '1234-5678-9012',
    componentType: 'Text',
    name: 'MyCodegen',
    properties: {
      label: {
        value: 'CodeGen',
      },
    },
  },
];

const { componentText } = new ReactIndexStudioTemplateRenderer(components, renderConfig);

Config

Output Config

outputPathDir (Required)

The directory generated components are written to.

const outputConfig = {
  outputPathDir: './src/ui-components',
};

Render Config

script

The script kind (JSX, TSX, etc.) of generated components.

Default: TSX Allowed: TSX, JSX, JS

import { ScriptKind } from '@aws-amplify/codegen-ui-react';

const renderConfig = {
  script: ScriptKind.JSX,
};
target

The EcmaScript version (ES2016, ESNext, etc.) of generated components.

Default: ES2015 Allowed: ES3, ES5, ES6/ES2015, ES2016, ES2017, ES2018, ES2019, ES2020, ES2021, ESNext

import { ScriptTarget } from '@aws-amplify/codegen-ui-react';

const renderConfig = {
  target: ScriptTaget.ESNext,
};
module

The JavaScript module system of generated components.

Default: CommonJS Allowed: CommonJS, ESNext

import { ScriptTarget } from '@aws-amplify/codegen-ui-react';

const renderConfig = {
  module: ModuleKind.ESNext,
};
renderTypeDeclarations

Generate the type declaration files (.d.ts) for components.

Default: false Allowed: false, true

Rendering type declarations will negatively affect performance. Only generate type declarations if necessary.

Not supported in browser environments.

const renderConfig = {
  renderTypeDeclarations: true,
};

Contributing

CONTRIBUTING.md

amplify-codegen-ui's People

Contributors

aaronzylee avatar aherschel avatar alharris-at avatar awinberg-aws avatar bombguy avatar cwoolum avatar dependabot[bot] avatar dhandsar-aws avatar dpilch avatar felipechiave avatar frimfram avatar goldbez avatar hein-j avatar hvergara avatar jacoblogan avatar joebuono avatar jshhhh avatar kpranoto-aws avatar letsbelopez avatar lyonsbp avatar milan-shah avatar rtpascual avatar scottyoung avatar sdstolworthy avatar swaysway avatar yeung-wah avatar zchenwei avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

amplify-codegen-ui's Issues

Need additional functionality

The method renderComponentOnly does not currently return enough information to be rendered. It need to be modified so that it returns both the imports required for the component to be renderered as well as the compiled JSX.

I would expect the return type to be similar to

{
  componentText,
  componentImports
}

`renderComponentOnly` method broken

After getting the latest codegen packages, renderComponentOnly now seems to return export default before the function. This causes the preview renderer to break. Please revert this functionality.

`renderThemeJson` outputting the wrong code.

For the following StudioTheme:

{
    "name": "studioTheme",
    "id": "001",
    "values": [
        {
            "key": "tokens",
            "value": {
                "children": [
                    {
                        "key": "shadows",
                        "value": {
                            "children": [
                                {
                                    "key": "small",
                                    "value": {
                                        "children": [
                                            {
                                                "key": "offsetX",
                                                "value": {
                                                    "children": [
                                                        {
                                                            "key": "value",
                                                            "value": {
                                                                "value": "20px"
                                                            }
                                                        }
                                                    ]
                                                }
                                            },
                                            {
                                                "key": "offsetY",
                                                "value": {
                                                    "children": [
                                                        {
                                                            "key": "value",
                                                            "value": {
                                                                "value": "40px"
                                                            }
                                                        }
                                                    ]
                                                }
                                            },
                                            {
                                                "key": "blurRadius",
                                                "value": {
                                                    "children": [
                                                        {
                                                            "key": "value",
                                                            "value": {
                                                                "value": "60px"
                                                            }
                                                        }
                                                    ]
                                                }
                                            },
                                            {
                                                "key": "color",
                                                "value": {
                                                    "children": [
                                                        {
                                                            "key": "value",
                                                            "value": {
                                                                "value": "{colors.shadow.tertiary.value}"
                                                            }
                                                        }
                                                    ]
                                                }
                                            }
                                        ]
                                    }
                                }
                            ]
                        }
                    }
                ]
            }
        }
    ]
}

it is outputting

{
  name: "studioTheme",
  tokens: {
    shadows: {
      small: {
        offsetX: { value: "20px" },
        offsetY: { value: "40px" },
        blurRadius: { value: "60px" },
        color: { value: "{colors.shadow.tertiary.value}" },
      },
    },
  },
}

It should be outputting

{
  name: "studioTheme",
  tokens: {
    shadows: {
      small: {
        value: {
          offsetX:  "20px" ,
          offsetY: "40px" ,
          blurRadius: "60px" ,
          color: "{colors.shadow.tertiary.value}",
        }
      },
    },
  },
}

`Not pulling components because this project is not configured with the "react" framework` prints before and after push

How did you install the Amplify CLI?

pnpm

If applicable, what version of Node.js are you using?

18

Amplify CLI Version

11.0.0-beta.7

What operating system are you using?

mac

Did you make any manual changes to the cloud resources managed by Amplify? Please describe the changes made.

n/a

Describe the bug

The message in the title and noted below appears both before and after push, which is a small Amplify project without Studio enabled

Not pulling components because this project is not configured with the "react" framework.
full logs
➜  ay push --allow-destructive-graphql-schema-updates --yes --debug
Not pulling components because this project is not configured with the "react" framework.
⠏ Fetching updates to backend environment: dev from the cloud.Overrides functionality is not implemented for this category
⠴ Fetching updates to backend environment: dev from the cloud.
⚠️  WARNING: Some types do not have authorization rules configured. That means all create, read, update, and delete operations are denied on these types:
	 - Todo2
Learn more about "@auth" authorization rules here: https://docs.amplify.aws/cli/graphql/authorization-rules
⠦ Fetching updates to backend environment: dev from the cloud.Be careful when using @auth directives on a field in a root type. @auth directives on field definitions use the source object to perform authorization logic and the source will be an empty object for fields on root types. Static group authorization should perform as expected.
⠧ Fetching updates to backend environment: dev from the cloud.✅ GraphQL schema compiled successfully.

Edit your schema at /Users/josef/Documents/projects/aws-amplify/reproductions/a1268/amplify/backend/api/a1268/schema.graphql or place .graphql files in a directory at /Users/josef/Documents/projects/aws-amplify/reproductions/a1268/amplify/backend/api/a1268/schema
Overrides functionality is not implemented for this category
✔ Successfully pulled backend environment dev from the cloud.
⠹ Building resource api/a1268
⚠️  WARNING: Some types do not have authorization rules configured. That means all create, read, update, and delete operations are denied on these types:
	 - Todo2
Learn more about "@auth" authorization rules here: https://docs.amplify.aws/cli/graphql/authorization-rules
Be careful when using @auth directives on a field in a root type. @auth directives on field definitions use the source object to perform authorization logic and the source will be an empty object for fields on root types. Static group authorization should perform as expected.
⠸ Building resource api/a1268✅ GraphQL schema compiled successfully.

Edit your schema at /Users/josef/Documents/projects/aws-amplify/reproductions/a1268/amplify/backend/api/a1268/schema.graphql or place .graphql files in a directory at /Users/josef/Documents/projects/aws-amplify/reproductions/a1268/amplify/backend/api/a1268/schema

    Current Environment: dev
    
┌──────────┬────────────────┬───────────┬───────────────────┐
│ Category │ Resource name  │ Operation │ Provider plugin   │
├──────────┼────────────────┼───────────┼───────────────────┤
│ Auth     │ a12682544f6a9  │ Create    │ awscloudformation │
├──────────┼────────────────┼───────────┼───────────────────┤
│ Api      │ a1268          │ Update    │ awscloudformation │
├──────────┼────────────────┼───────────┼───────────────────┤
│ Hosting  │ amplifyhosting │ No Change │                   │
└──────────┴────────────────┴───────────┴───────────────────┘

⚠️  WARNING: Some types do not have authorization rules configured. That means all create, read, update, and delete operations are denied on these types:
	 - Todo2
Learn more about "@auth" authorization rules here: https://docs.amplify.aws/cli/graphql/authorization-rules
Be careful when using @auth directives on a field in a root type. @auth directives on field definitions use the source object to perform authorization logic and the source will be an empty object for fields on root types. Static group authorization should perform as expected.
✅ GraphQL schema compiled successfully.

Edit your schema at /Users/josef/Documents/projects/aws-amplify/reproductions/a1268/amplify/backend/api/a1268/schema.graphql or place .graphql files in a directory at /Users/josef/Documents/projects/aws-amplify/reproductions/a1268/amplify/backend/api/a1268/schema
⠼ Building resource api/a1268
⚠️  WARNING: Some types do not have authorization rules configured. That means all create, read, update, and delete operations are denied on these types:
	 - Todo2
Learn more about "@auth" authorization rules here: https://docs.amplify.aws/cli/graphql/authorization-rules
⠴ Building resource api/a1268Be careful when using @auth directives on a field in a root type. @auth directives on field definitions use the source object to perform authorization logic and the source will be an empty object for fields on root types. Static group authorization should perform as expected.
⠦ Building resource api/a1268✅ GraphQL schema compiled successfully.

Edit your schema at /Users/josef/Documents/projects/aws-amplify/reproductions/a1268/amplify/backend/api/a1268/schema.graphql or place .graphql files in a directory at /Users/josef/Documents/projects/aws-amplify/reproductions/a1268/amplify/backend/api/a1268/schema

Deployment completed.
Deploying root stack a1268 [ ====================-------------------- ] 2/4
	amplify-a1268-dev-155611       AWS::CloudFormation::Stack     UPDATE_COMPLETE_C
	autha12682544f6a9              AWS::CloudFormation::Stack     CREATE_COMPLETE  
	apia1268                       AWS::CloudFormation::Stack     UPDATE_COMPLETE  
Deployed api a1268 [ ======================================== ] 9/9
	GraphQLAPI                     AWS::AppSync::GraphQLApi       UPDATE_COMPLETE  
	GraphQLAPITransformerSchema3C… AWS::AppSync::GraphQLSchema    UPDATE_COMPLETE  
	GraphQLAPIDefaultApiKey215A6D… AWS::AppSync::ApiKey           UPDATE_COMPLETE  
	QueryHelloDataResolverFnQuery… AWS::AppSync::FunctionConfigu… CREATE_COMPLETE  
	Todo                           AWS::CloudFormation::Stack     UPDATE_COMPLETE  
	QueryhelloResolver             AWS::AppSync::Resolver         CREATE_COMPLETE  
	Todo2                          AWS::CloudFormation::Stack     UPDATE_IN_PROGRES
Deployed auth a12682544f6a9 [ ======================================== ] 10/10
	UserPool                       AWS::Cognito::UserPool         CREATE_COMPLETE  
	UserPoolClient                 AWS::Cognito::UserPoolClient   CREATE_COMPLETE  
	UserPoolClientWeb              AWS::Cognito::UserPoolClient   CREATE_COMPLETE  
	UserPoolClientRole             AWS::IAM::Role                 CREATE_COMPLETE  
	UserPoolClientLambda           AWS::Lambda::Function          CREATE_COMPLETE  
	UserPoolClientLambdaPolicy     AWS::IAM::Policy               CREATE_COMPLETE  
	UserPoolClientLogPolicy        AWS::IAM::Policy               CREATE_COMPLETE  
	UserPoolClientInputs           Custom::LambdaCallout          CREATE_COMPLETE  
	IdentityPool                   AWS::Cognito::IdentityPool     CREATE_COMPLETE  
	IdentityPoolRoleMap            AWS::Cognito::IdentityPoolRol… CREATE_COMPLETE  

Deployment state saved successfully.

GraphQL endpoint: https://mifc66khtzg7tcrmlio422kz2u.appsync-api.us-east-1.amazonaws.com/graphql
GraphQL API KEY: da2-54qsjoh6zbge3mfad4gypm34ta

GraphQL transformer version: 2

Not pulling components because this project is not configured with the "react" framework.

Expected behavior

this message does not print unless this project has Studio enabled or I am expecting to pull components (/attempt to).

Reproduction steps

  1. create an Amplify project with amplify init -y
  2. create a GraphQL API amplify add api > GraphQL
  3. push with amplify push -y
  4. push to git and add Hosting with amplify add hosting
  5. let hosting build, run amplify pull
  6. try to make a change in your schema and push

Project Identifier

6eb2e5ca4610f067a938060bf5ef796a

Log output

# Put your logs below this line


Additional information

No response

Before submitting, please confirm:

  • I have done my best to include a minimal, self-contained set of instructions for consistently reproducing the issue.
  • I have removed any sensitive information from my code snippets and submission.

Top level props need to be available to render function

export type ListingCardProps = {
  title: String
} & {
  overrides?: EscapeHatchProps | undefined | null
};
export default function ListingCard(props: ListingCardProps): JSX.Element {
  return (
    <Flex

Should be

export type ListingCardProps = {
  title?: String
} & {
  overrides?: EscapeHatchProps | undefined | null
};
export default function ListingCard(props: ListingCardProps): JSX.Element {
  const { tiitle } = props;

  return (
    <Flex

[Feature Request] Expose `buildThemeObject` when using `ReactThemeStudioTemplateRenderer`

I'd like the ability to codegen the shape of the object so it's easy to pass into createTheme in memory.
The following function returns the shape already though it's private

private buildThemeObject(): ObjectLiteralExpression {
return factory.createObjectLiteralExpression(
[
factory.createPropertyAssignment(
factory.createIdentifier('name'),
factory.createStringLiteral(this.component.name),
),
]
.concat(this.buildThemeValues(this.component.values))
.concat(this.buildThemeOverrides(this.component.overrides)),
true,
);
}

Text value props should always be rendered as content

<Text
            border="5px solid #0073bb"
            fontFamily="Inter"
            color="rgb(15.539060980081558,29.73937589675188,47.8125)"
            textAlign="left"
            fontSize="24px"
            lineHeight="24px"
            value={title}
            fontWeight="500"
            {...props}
            {...getOverrideProps(props.overrides, "Text")}
          ></Text>

Should be

<Text
            border="5px solid #0073bb"
            fontFamily="Inter"
            color="rgb(15.539060980081558,29.73937589675188,47.8125)"
            textAlign="left"
            fontSize="24px"
            lineHeight="24px"
            fontWeight="500"
            {...props}
            {...getOverrideProps(props.overrides, "Text")}
          >{title}</Text>

Add a note to the generated files that asks customers not to edit

From the CLI, calls to amplify pull will overwrite existing components with freshly codegen'd ones.

This means that any manual edits developers make to these files will be overwritten upon the next pull.

To properly set expectations, there should be a comment at the top of each generated file instructing developers not to edit the code within the file.

CommonJS generated components are not usable

I have a component with the following definition:

{
  "name": "Frame",
  "children": [
    {
      "children": [],
      "name": "Frame2",
      "componentType": "Box",
      "properties": {
        "width": {
          "value": "133px"
        },
        "height": {
          "value": "55px"
        },
        "padding": {
          "value": "0px 0px 0px 0px"
        },
        "backgroundColor": {
          "value": "rgb(255,255,255)"
        }
      },
      "overrides": {},
      "variants": []
    }
  ],
  "id": "7:6",
  "bindingProperties": {},
  "componentType": "Box",
  "properties": {
    "width": {
      "value": "291px"
    },
    "height": {
      "value": "158px"
    },
    "padding": {
      "value": "0px 0px 0px 0px"
    },
    "backgroundColor": {
      "value": "rgb(255,255,0)"
    }
  },
  "overrides": {},
  "variants": []
}

I run this through codegen with the following config:

new AmplifyRenderer(component, {
    module: ModuleKind.CommonJS,
    target: ScriptTarget.ES2015,
    script: ScriptKind.JS,
  })

The resulting code looks like this:

/***************************************************************************
 * The contents of this file were generated with Amplify Studio.           *
 * Please refrain from making any modifications to this file.              *
 * Any changes to this file will be overwritten when running amplify pull. *
 **************************************************************************/

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
/* eslint-disable */
const react_1 = require("react");
const ui_react_1 = require("@aws-amplify/ui-react");
function Frame1(props) {
  const {} = props;
  return react_1.default.createElement(
    ui_react_1.View,
    Object.assign(
      {
        width: "291px",
        padding: "0px 0px 0px 0px",
        backgroundColor: "rgb(255,255,0)",
        height: "158px",
      },
      props,
      ui_react_1.getOverrideProps(props.overrides, "Box")
    ),
    react_1.default.createElement(
      ui_react_1.View,
      Object.assign(
        {
          width: "133px",
          padding: "0px 0px 0px 0px",
          backgroundColor: "rgb(255,255,255)",
          height: "55px",
        },
        ui_react_1.getOverrideProps(props.overrides, "Box.Box")
      )
    )
  );
}
exports.default = Frame1;

When imported into my react app, I get the following error:

TypeError: Cannot read properties of undefined (reading 'createElement')

  11 | const ui_react_1 = require("@aws-amplify/ui-react");
  12 | function Frame1(props) {
  13 |   const {} = props;
> 14 |   return react_1.default.createElement(
  15 |     ui_react_1.View,
  16 |     Object.assign(
  17 |       {

Implement logger

Currently this library logs to the console and all of the logs appear in browser debugger window. A lot of this information is unnecessary and ends up making it hard to find the logs you actually need.

By adding a logging library with varying log levels and allowing the log level to be configured by the end consumer, we can clean a lot of this up.

image

Null reference error when trying to call renderer

Here is the relevant error message.

TypeError: Cannot read property 'createJsxElement' of undefined
    at BoxRenderer.renderElement (box.ts:21)
    at AmplifyRenderer.renderJsx (amplify-renderer.ts:24)
    at AmplifyRenderer.renderComponentOnly (react-studio-template-renderer.ts:39)
    at studio-preview.tsx:17

image

I think this might be related to how factory is being imported but I'm not sure.

Nested components shouldn't receive {...props} and should accurately create overrideProps string

Example:

/* eslint-disable */
import React from "react";
import {
  EscapeHatchProps,
  Flex,
  Text,
  getOverrideProps
} from "@aws-amplify/ui-react";

export type SectionHeadingProps = {} & {
  overrides?: EscapeHatchProps | undefined | null
};
export default function SectionHeading(
  props: SectionHeadingProps
): JSX.Element {
  return (
    <Flex
      padding="0px 0px 10px 0px"
      backgroundColor="rgb(255,255,255)"
      direction="column"
      {...props}
      {...getOverrideProps(props.overrides, "Flex")}
    >
      <Flex
        padding="0px 0px 0px 0px"
        backgroundColor="rgb(255,255,255)"
        flexGrow="0"
        alignItems="flex-start"
        gap="10px"
        direction="row"
        {...props}
        {...getOverrideProps(props.overrides, "Flex")}
      >
        <Text
          fontFamily="Inter"
          color="rgb(15.539060980081558,29.73937589675188,47.8125)"
          textAlign="left"
          fontSize="48px"
          lineHeight="48px"
          value="Heading 2"
          fontWeight="300"
          {...props}
          {...getOverrideProps(props.overrides, "Text")}
        >
          Heading 2
        </Text>
      </Flex>
      <Text
        fontFamily="Inter"
        color="rgb(0,0,0)"
        textAlign="left"
        fontSize="32px"
        lineHeight="48px"
        value="subtitle"
        fontWeight="400"
        {...props}
        {...getOverrideProps(props.overrides, "Text")}
      >
        subtitle
      </Text>
    </Flex>
  );
}

Should be:

Example:

/* eslint-disable */
import React from "react";
import {
  EscapeHatchProps,
  Flex,
  Text,
  getOverrideProps
} from "@aws-amplify/ui-react";

export type SectionHeadingProps = {} & {
  overrides?: EscapeHatchProps | undefined | null
};
export default function SectionHeading(
  props: SectionHeadingProps
): JSX.Element {
  return (
    <Flex
      padding="0px 0px 10px 0px"
      backgroundColor="rgb(255,255,255)"
      direction="column"
      {...props}
      {...getOverrideProps(props.overrides, "Flex")}
    >
      <Flex
        padding="0px 0px 0px 0px"
        backgroundColor="rgb(255,255,255)"
        flexGrow="0"
        alignItems="flex-start"
        gap="10px"
        direction="row"
        {...props}
        {...getOverrideProps(props.overrides, "Flex.Flex")}
      >
        <Text
          fontFamily="Inter"
          color="rgb(15.539060980081558,29.73937589675188,47.8125)"
          textAlign="left"
          fontSize="48px"
          lineHeight="48px"
          value="Heading 2"
          fontWeight="300"
          {...getOverrideProps(props.overrides, "Flex.Flex.Text")}
        >
          Heading 2
        </Text>
      </Flex>
      <Text
        fontFamily="Inter"
        color="rgb(0,0,0)"
        textAlign="left"
        fontSize="32px"
        lineHeight="48px"
        value="subtitle"
        fontWeight="400"
        {...getOverrideProps(props.overrides, "Flex.Text")}
      >
        subtitle
      </Text>
    </Flex>
  );
}

Schema validation should happen earlier in the execution

Today, schema validation only happens when we get to rendering the jsx portion of the components, rather than immediately at the API layer. We should refactor our logic to move this validation earlier. This causes some behavior in actions to effectively execute before we've validated for the existence of basic shapes like properties, etc.

`convertToLocal` incorrectly parses dates between midnight and 1am

Given the following code

 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",
      hour12: false,
    });
    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}`;
  };

If I pass in a date like 2023-03-06T08:58:00.000Z, It incorrectly treats the hour as 24 instead of 0. This leads to an invalid date of 2023-03-06T24:58

Codegen should ensure all property values are converted to string

In the JSON schema, values can be string or number. Because of the way the compiler handles props, it assumes all values are only strings which causes codegen to blow up with the following error.

Anywhere we set a prop value, we should be checking if it's not a string and do a toString if possible.

Error Message

Couldn't transform code TypeError: Cannot read property 'length' of undefined
    at Object.escapeLeadingUnderscores (typescript.js:12203)
    at createBaseIdentifier (typescript.js:21673)
    at Object.createIdentifier (typescript.js:21685)
    at AmplifyRenderer.renderFunctionWrapper (react-studio-template-renderer.ts:116)
    at AmplifyRenderer.renderComponentOnly (react-studio-template-renderer.ts:54)
    at EditorProvider.transpile (editor-provider.tsx:63)
    at EditorProvider.componentDidUpdate (editor-provider.tsx:38)
    at commitLifeCycles (react-dom.development.js:19835)
    at commitLayoutEffects (react-dom.development.js:22803)
    at HTMLUnknownElement.callCallback (react-dom.development.js:188)
    at Object.invokeGuardedCallbackDev (react-dom.development.js:237)
    at invokeGuardedCallback (react-dom.development.js:292)
    at commitRootImpl (react-dom.development.js:22541)
    at unstable_runWithPriority (scheduler.development.js:653)
    at runWithPriority$1 (react-dom.development.js:11039)
    at commitRoot (react-dom.development.js:22381)
    at finishSyncRender (react-dom.development.js:21807)
    at performSyncWorkOnRoot (react-dom.development.js:21793)
    at react-dom.development.js:11089
    at unstable_runWithPriority (scheduler.development.js:653)
    at runWithPriority$1 (react-dom.development.js:11039)
    at flushSyncCallbackQueueImpl (react-dom.development.js:11084)
    at flushSyncCallbackQueue (react-dom.development.js:11072)
    at scheduleUpdateOnFiber (react-dom.development.js:21199)
    at dispatchAction (react-dom.development.js:15660)
    at ui-builder.tsx:36
    at Generator.next (<anonymous>)
    at fulfilled (tslib.es6.js:71) 
    at EditorProvider (http://localhost:3000/static/js/main.chunk.js:2560:5)
    at div
    at div
    at StudioPreview (http://localhost:3000/static/js/main.chunk.js:2733:3)
    at div
    at div
    at UiBuilderContent (http://localhost:3000/static/js/main.chunk.js:2811:3)
    at div
    at div
    at InternalContainer (http://localhost:3000/static/js/452.chunk.js:10158:19)
    at Container (http://localhost:3000/static/js/452.chunk.js:10122:110)
    at UiBuilder (http://localhost:3000/static/js/main.chunk.js:2843:75)
    at Route (http://localhost:3000/static/js/452.chunk.js:660591:29)
    at AuthenticatedRoute (http://localhost:3000/static/js/main.chunk.js:12368:1)
    at Switch (http://localhost:3000/static/js/452.chunk.js:660793:29)
    at awsui-app-layout
    at AwsUiComponent (http://localhost:3000/static/js/452.chunk.js:204244:45)
    at Router (http://localhost:3000/static/js/452.chunk.js:660226:30)
    at BrowserRouter (http://localhost:3000/static/js/452.chunk.js:659846:35)
    at div
    at AppShell (http://localhost:3000/static/js/main.chunk.js:11324:5)
    at ConnectFunction (http://localhost:3000/static/js/452.chunk.js:658405:75)
    at Provider (http://localhost:3000/static/js/452.chunk.js:658118:20)

{
   "name":"HeaderWithAction3",
   "componentType":"Flex",
   "componentId":"O8phmtdR-chILsHCWXQBo",
   "children":[
      {
         "componentType":"Flex",
         "componentId":"243:20",
         "children":[
            {
               "componentType":"Text",
               "componentId":"243:22",
               "children":[
                  
               ],
               "properties":{
                  "fontSize":{
                     "value":"32px"
                  },
                  "fontFamily":{
                     "value":"Arial"
                  },
                  "color":{
                     "value":"rgb(0,0,0)"
                  },
                  "value":{
                     "value":"Header"
                  },
                  "fontWeight":{
                     "value":700
                  }
               }
            }
         ],
         "properties":{
            "padding":{
               "value":"0px 55.5px 0px 12px"
            },
            "margin":{
               "value":"0 10px"
            },
            "alignSelf":{
               "value":"stretch"
            },
            "flexGrow":{
               "value":"1"
            },
            "alignItems":{
               "value":"center"
            },
            "display":{
               "value":"flex"
            },
            "gap":{
               "value":"10px"
            },
            "width":{
               "value":"100%"
            },
            "direction":{
               "value":"row"
            },
            "height":{
               "value":"100%"
            }
         }
      },
      {
         "componentType":"Flex",
         "componentId":"243:21",
         "children":[
            {
               "componentType":"Flex",
               "componentId":"230:1929",
               "children":[
                  {
                     "componentType":"Text",
                     "componentId":"230:1930",
                     "children":[
                        
                     ],
                     "properties":{
                        "fontSize":{
                           "value":"16px"
                        },
                        "fontFamily":{
                           "value":"Arial"
                        },
                        "color":{
                           "value":"rgb(255,255,255)"
                        },
                        "value":{
                           "value":"Action"
                        },
                        "fontWeight":{
                           "value":"400"
                        }
                     }
                  }
               ],
               "properties":{
                  "padding":{
                     "value":"10px 0px 10px 0px"
                  },
                  "margin":{
                     "value":"0 10px"
                  },
                  "backgroundColor":{
                     "value":"rgb(42.13476486504078,41.23828276991844,86.06250151991844)"
                  },
                  "flexGrow":{
                     "value":"0"
                  },
                  "alignItems":{
                     "value":"center"
                  },
                  "display":{
                     "value":"flex"
                  },
                  "justifyContent":{
                     "value":"center"
                  },
                  "borderTopLeftRadius":{
                     "value":"8px"
                  },
                  "borderRadius":{
                     "value":"8px"
                  },
                  "borderBottomLeftRadius":{
                     "value":"8px"
                  },
                  "width":{
                     "value":"166px"
                  },
                  "borderBottomRightRadius":{
                     "value":"8px"
                  },
                  "borderTopRightRadius":{
                     "value":"8px"
                  },
                  "direction":{
                     "value":"column"
                  },
                  "height":{
                     "value":"44px"
                  }
               }
            }
         ],
         "properties":{
            "padding":{
               "value":"42px 42px 42px 42px"
            },
            "margin":{
               "value":"0 10px"
            },
            "alignSelf":{
               "value":"stretch"
            },
            "flexGrow":{
               "value":"0"
            },
            "alignItems":{
               "value":"center"
            },
            "display":{
               "value":"flex"
            },
            "gap":{
               "value":"10px"
            },
            "justifyContent":{
               "value":"flex-end"
            },
            "direction":{
               "value":"row"
            },
            "height":{
               "value":"64px"
            }
         }
      }
   ],
   "properties":{
      "padding":{
         "value":"12px 12px 12px 12px"
      },
      "backgroundColor":{
         "value":"rgb(255,255,255)"
      },
      "alignItems":{
         "value":"flex-start"
      },
      "display":{
         "value":"flex"
      },
      "gap":{
         "value":"10px"
      },
      "width":{
         "value":"1133px"
      },
      "direction":{
         "value":"row"
      },
      "height":{
         "value":"88px"
      }
   },
   "figmaMetadata":{
      "nodeId":"243:25",
      "documentUrl":"https://www.figma.com/file/hAY0FyLQ4H7Cjh7ylpWYta/Playground_v1-Copy?node-id=122%3A1763"
   }
}

Rendered JSX code including helper functions

The output of the rendered code for renderComponentOnly seems to be creating helpers for Object.assign which can't be parsed by the renderer in the browser. I think the fix for now is to transpile to ES6 which already has Object.assign and we can transpile in the browser to ES5.

var __assign = (this && this.__assign) || function () {
    __assign = Object.assign || function(t) {
        for (var s, i = 1, n = arguments.length; i < n; i++) {
            s = arguments[i];
            for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
                t[p] = s[p];
        }
        return t;
    };
    return __assign.apply(this, arguments);
};
function MyCoolComponent(props) {
    var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s;
    return (React.createElement(View, __assign({ paddingBottom: (_a = props.paddingBottom) !== null && _a !== void 0 ? _a : '58px', flexDirection: (_b = props.flexDirection) !== null && _b !== void 0 ? _b : 'column', display: (_c = props.display) !== null && _c !== void 0 ? _c : 'flex', paddingRight: (_d = props.paddingRight) !== null && _d !== void 0 ? _d : '64px', width: (_e = props.width) !== null && _e !== void 0 ? _e : '415px', paddingTop: (_f = props.paddingTop) !== null && _f !== void 0 ? _f : '10px', paddingLeft: (_g = props.paddingLeft) !== null && _g !== void 0 ? _g : '10px', height: (_h = props.height) !== null && _h !== void 0 ? _h : '328px' }, props, getOverrideProps(props.overrides, "View")),
        React.createElement(View, __assign({}, props, getOverrideProps(props.overrides, "View"))),
        React.createElement(View, __assign({ border: (_j = props.border) !== null && _j !== void 0 ? _j : 'border: 1px SOLID rgb(0,0,0);', margin: (_k = props.margin) !== null && _k !== void 0 ? _k : '10px 0' }, props, getOverrideProps(props.overrides, "View"))),
        React.createElement(View, __assign({ margin: (_l = props.margin) !== null && _l !== void 0 ? _l : '10px 0', alignItems: (_m = props.alignItems) !== null && _m !== void 0 ? _m : 'center', flexDirection: (_o = props.flexDirection) !== null && _o !== void 0 ? _o : 'row', display: (_p = props.display) !== null && _p !== void 0 ? _p : 'flex', paddingRight: (_q = props.paddingRight) !== null && _q !== void 0 ? _q : '21px', paddingLeft: (_r = props.paddingLeft) !== null && _r !== void 0 ? _r : '21px', justifyContent: (_s = props.justifyContent) !== null && _s !== void 0 ? _s : 'center' }, props, getOverrideProps(props.overrides, "View")),
            React.createElement(React.Fragment, null, "I\u2019m Text that\u2019s intended to be overridden"))));
}

Ideally the output should only be the component function itself.

function MyCoolComponent(props) {
    var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s;
    return (React.createElement(View, __assign({ paddingBottom: (_a = props.paddingBottom) !== null && _a !== void 0 ? _a : '58px', flexDirection: (_b = props.flexDirection) !== null && _b !== void 0 ? _b : 'column', display: (_c = props.display) !== null && _c !== void 0 ? _c : 'flex', paddingRight: (_d = props.paddingRight) !== null && _d !== void 0 ? _d : '64px', width: (_e = props.width) !== null && _e !== void 0 ? _e : '415px', paddingTop: (_f = props.paddingTop) !== null && _f !== void 0 ? _f : '10px', paddingLeft: (_g = props.paddingLeft) !== null && _g !== void 0 ? _g : '10px', height: (_h = props.height) !== null && _h !== void 0 ? _h : '328px' }, props, getOverrideProps(props.overrides, "View")),
        React.createElement(View, __assign({}, props, getOverrideProps(props.overrides, "View"))),
        React.createElement(View, __assign({ border: (_j = props.border) !== null && _j !== void 0 ? _j : 'border: 1px SOLID rgb(0,0,0);', margin: (_k = props.margin) !== null && _k !== void 0 ? _k : '10px 0' }, props, getOverrideProps(props.overrides, "View"))),
        React.createElement(View, __assign({ margin: (_l = props.margin) !== null && _l !== void 0 ? _l : '10px 0', alignItems: (_m = props.alignItems) !== null && _m !== void 0 ? _m : 'center', flexDirection: (_o = props.flexDirection) !== null && _o !== void 0 ? _o : 'row', display: (_p = props.display) !== null && _p !== void 0 ? _p : 'flex', paddingRight: (_q = props.paddingRight) !== null && _q !== void 0 ? _q : '21px', paddingLeft: (_r = props.paddingLeft) !== null && _r !== void 0 ? _r : '21px', justifyContent: (_s = props.justifyContent) !== null && _s !== void 0 ? _s : 'center' }, props, getOverrideProps(props.overrides, "View")),
            React.createElement(React.Fragment, null, "I\u2019m Text that\u2019s intended to be overridden"))));
}

Collections rendering invalid `items` variable

When rendering a collection, for some reason, it's trying to bind an items vairable without actually mapping it from props.

function ListingCardCollection(props) {
  var _a = props;
  return (
    <Collection
      type="list"
      isPaginated="true"
      items={items}
      {...props}
      {...getOverrideProps(props.overrides, "Collection")}
    >
      {function (item, index) {
        return (
          <ListingCard
            {...findChildOverrides(props.overrides, "ListingCard")}
          ></ListingCard>
        );
      }}
    </Collection>
  );
}
{"name":"ListingCardCollection","children":[{"children":[],"name":"ListingCard","componentType":"ListingCard","properties":{},"overrides":{},"variants":[]}],"id":"c-7m9oAaC8SO0M40Pnvu","bindingProperties":{},"componentType":"Collection","properties":{"type":{"value":"list"},"isPaginated":{"value":"true"}},"overrides":{},"variants":[]}

Component with custom child does not match golden file

A mismatch with golden file exists for the component with custom child. Take the following json file as the example,

{
  "componentId": "1234-5678-9010",
  "componentType": "Box",
  "name": "BoxWithCustomButton",
  "properties": {},
  "children": [
    {
      "componentId": "0987-6543-3211",
      "componentType": "CustomButton",
      "properties": {
        "color": {
          "value": "#ff0000"
        },
        "width": {
          "value": "20px"
        }
      }
    }
  ]
}

Current codegen output:

  /* eslint-disable */
  import React from "react";
  import { CustomButton, View } from "@aws-amplify/ui-react";
  
  export default function BoxWithCustomButton(props: BoxWithCustomButtonProps): JSX.Element {
      return (<View {...props} {...getOverrideProps(props.overrides, "View")}>
        <CustomButton color={'#ff0000'} width={'20px'} {...findChildOverrides(props.overrides, "CustomButton")}>
        </CustomButton>
      </View>);

The differences are:

  • The parent View is generated instead of Box
  • Additional curly braces are generated around string literal of prop
  • In findChildOverrides method, the CustomButton is generated instead of Box.CustomButton
  • No type definition for component props

Original golden file:

import React from 'react';

import { getOverrideProps, findChildOverrides } from './helpers/escape-hatch-helpers';
import { CommonProps } from './props/studio-props';

import { Box } from '@aws-amplify/amplify-ui';
import { CustomButton } from ',/CustomButton';

export type BoxWithButtonProps = {} & CommonProps;
export function BoxWithButton(props: BoxWithButtonProps) {
  const overrides = props.overrides;
  return (
    <Box {...props} {...getOverrideProps(overrides, 'Box')}>
      <CustomButton {...findChildOverrides(overrides, 'Box.CustomButton')} color="#ff0000" width="20px"></CustomButton>
    </Box>
  );
}

Generated studio components are broken by default

I ran this script:

const rendererFactory = new StudioTemplateRendererFactory(
    (component) => new AmplifyRenderer(component)
  );

  const outputPathDir = path.resolve(path.join('.', 'src', 'ui-components'));
  const outputConfig = {
    outputPathDir,
    module: JSModuleEnum.CommonJS,
    compileTarget: CompileTargetEnum.ES6,
    outputFormat: JSOutputFormatEnum.tsx
  };

  const rendererManager = new StudioTemplateRendererManager(rendererFactory, outputConfig);
  rendererManager.renderSchemaToTemplate(schema);

The schema passed in was:

{
  appId: 'd37nrm8rzt3oek',
  bindingProperties: {},
  componentType: 'Box',
  environmentName: 'staging',
  id: 's-s4mU579Ycf6JGHwhqT',
  name: 'aawwdd',
  overrides: {},
  properties: {},
  variants: []
}

Which generated this component:

/* eslint-disable */
import React from "react";
import { View } from "@aws-amplify/ui-react";

export type aawwddProps = {} & CommonProps;
export default function aawwdd(props: aawwddProps): JSX.Element {
    return (<View {...props} {...getOverrideProps(props.overrides, "View")}></View>);
}

As you can see, the component code itself depends on calls to getOverrideProps, JSX.Element and CommonProps which are not imported. This means the component is not usable.

Top level bindingProperties should be optional

export type CustomButtonProps = {
  buttonUser: User;
  width: Number;
} & CommonProps;

Should be

export type CustomButtonProps = {
  buttonUser?: User;
  width?: Number;
} & CommonProps;

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.