Skip to content

Commit

Permalink
chore: add control overrides for checkbox and switch (#390)
Browse files Browse the repository at this point in the history
  • Loading branch information
alharris-at authored Feb 9, 2022
1 parent c30ccc7 commit c254c5a
Show file tree
Hide file tree
Showing 9 changed files with 220 additions and 50 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5406,6 +5406,53 @@ export default function TwoWayBindings(
}
`;

exports[`amplify render tests mutations supports a controlled checkbox primitive 1`] = `
Object {
"componentText": "/* eslint-disable */
import React from \\"react\\";
import {
EscapeHatchProps,
getOverrideProps,
useStateMutationAction,
} from \\"@aws-amplify/ui-react/internal\\";
import { CheckboxField, Flex, FlexProps, Text } from \\"@aws-amplify/ui-react\\";

export type CheckboxControlledElementProps = React.PropsWithChildren<
Partial<FlexProps> & {
overrides?: EscapeHatchProps | undefined | null;
}
>;
export default function CheckboxControlledElement(
props: CheckboxControlledElementProps
): React.ReactElement {
const { overrides, ...rest } = props;
const [inputChecked, setInputChecked] = useStateMutationAction(undefined);
return (
/* @ts-ignore: TS2322 */
<Flex
{...rest}
{...getOverrideProps(overrides, \\"CheckboxControlledElement\\")}
>
<CheckboxField
checked={inputChecked}
onChange={(event: SyntheticEvent) => {
setInputChecked(event.target.checked);
}}
{...getOverrideProps(overrides, \\"Input\\")}
></CheckboxField>
<Text
children={inputChecked}
{...getOverrideProps(overrides, \\"CheckboxFieldValue\\")}
></Text>
</Flex>
);
}
",
"declaration": undefined,
"renderComponentToFilesystem": [Function],
}
`;

exports[`amplify render tests mutations supports a controlled stepper primitive 1`] = `
Object {
"componentText": "/* eslint-disable */
Expand Down Expand Up @@ -5457,6 +5504,48 @@ export default function StepperControlledElement(
}
`;

exports[`amplify render tests mutations supports a controlled switch primitive 1`] = `
Object {
"componentText": "/* eslint-disable */
import React from \\"react\\";
import {
EscapeHatchProps,
getOverrideProps,
useStateMutationAction,
} from \\"@aws-amplify/ui-react/internal\\";
import { Flex, FlexProps, SwitchField, Text } from \\"@aws-amplify/ui-react\\";

export type SwitchControlledElementProps = React.PropsWithChildren<
Partial<FlexProps> & {
overrides?: EscapeHatchProps | undefined | null;
}
>;
export default function SwitchControlledElement(
props: SwitchControlledElementProps
): React.ReactElement {
const { overrides, ...rest } = props;
const [inputIsChecked, setInputIsChecked] = useStateMutationAction(undefined);
return (
/* @ts-ignore: TS2322 */
<Flex {...rest} {...getOverrideProps(overrides, \\"SwitchControlledElement\\")}>
<SwitchField
isChecked={inputIsChecked}
onChange={() => setInputIsChecked(!inputIsChecked)}
{...getOverrideProps(overrides, \\"Input\\")}
></SwitchField>
<Text
children={inputIsChecked}
{...getOverrideProps(overrides, \\"SwitchFieldValue\\")}
></Text>
</Flex>
);
}
",
"declaration": undefined,
"renderComponentToFilesystem": [Function],
}
`;

exports[`amplify render tests mutations supports multiple actions pointing to the same value 1`] = `
Object {
"componentText": "/* eslint-disable */
Expand Down Expand Up @@ -5741,7 +5830,7 @@ export default function TwoWayBindings(
useStateMutationAction(undefined);
const [stepperFieldInputValue, setStepperFieldInputValue] =
useStateMutationAction(undefined);
const [switchFieldInputValue, setSwitchFieldInputValue] =
const [switchFieldInputIsChecked, setSwitchFieldInputIsChecked] =
useStateMutationAction(undefined);
const [textFieldInputValue, setTextFieldInputValue] =
useStateMutationAction(undefined);
Expand Down Expand Up @@ -5770,7 +5859,7 @@ export default function TwoWayBindings(
setStepperFieldInputValue(9);
};
const setSwitchFieldValueClick = () => {
setSwitchFieldInputValue(true);
setSwitchFieldInputIsChecked(true);
};
const setTextFieldValueClick = () => {
setTextFieldInputValue(\\"Hardcoded Value\\");
Expand Down Expand Up @@ -5802,7 +5891,7 @@ export default function TwoWayBindings(
value=\\"yes\\"
checked={checkboxFieldInputChecked}
onChange={(event: SyntheticEvent) => {
setCheckboxFieldInputChecked(event.target.value);
setCheckboxFieldInputChecked(event.target.checked);
}}
{...getOverrideProps(overrides, \\"CheckboxFieldInput\\")}
></CheckboxField>
Expand Down Expand Up @@ -6079,14 +6168,14 @@ export default function TwoWayBindings(
labelPosition=\\"start\\"
isDisabled={false}
isLabelHidden={false}
value={switchFieldInputValue}
onChange={(event: SyntheticEvent) => {
setSwitchFieldInputValue(event.target.value);
}}
isChecked={switchFieldInputIsChecked}
onChange={() =>
setSwitchFieldInputIsChecked(!switchFieldInputIsChecked)
}
{...getOverrideProps(overrides, \\"SwitchFieldInput\\")}
></SwitchField>
<Text
children={switchFieldInputValue}
children={switchFieldInputIsChecked}
{...getOverrideProps(overrides, \\"SwitchFieldValue\\")}
></Text>
<Button
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,14 @@ describe('amplify render tests', () => {
expect(generateWithAmplifyRenderer('workflow/stepperControlledElement')).toMatchSnapshot();
});

it('supports a controlled checkbox primitive', () => {
expect(generateWithAmplifyRenderer('workflow/checkboxControlledElement')).toMatchSnapshot();
});

it('supports a controlled switch primitive', () => {
expect(generateWithAmplifyRenderer('workflow/switchControlledElement')).toMatchSnapshot();
});

it('modifies text in component on input change', () => {
expect(generateWithAmplifyRenderer('workflow/inputToTextChange')).toMatchSnapshot();
});
Expand Down
1 change: 1 addition & 0 deletions packages/codegen-ui-react/lib/react-component-renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ export class ReactComponentRenderer<TPropIn> extends ComponentRendererBase<
buildOpeningElementControlEvents(
this.component.componentType,
getSetStateName({ componentName: this.component.name || '', property: key }),
getStateName({ componentName: this.component.name || '', property: key }),
'change',
),
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ export class ReactComponentWithChildrenRenderer<TPropIn> extends ComponentWithCh
buildOpeningElementControlEvents(
this.component.componentType,
getSetStateName({ componentName: this.component.name || '', property: key }),
getStateName({ componentName: this.component.name || '', property: key }),
'change',
),
);
Expand Down
99 changes: 61 additions & 38 deletions packages/codegen-ui-react/lib/workflow/mutation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,17 @@ import {
getSetStateName,
} from '../react-component-render-helper';
import { ImportCollection, ImportValue } from '../imports';
import Primitive, { PrimitivesWithChangeEvent, PrimitiveLevelPropConfiguration } from '../primitive';
import { mapGenericEventToReact } from './events';
import { getChildPropMappingForComponentName } from './utils';
import Primitive, { PrimitivesWithChangeEvent, PrimitiveLevelPropConfiguration } from '../primitive';

type EventHandlerBuilder = (stateName: string) => JsxExpression;
type EventHandlerBuilder = (setStateName: string, stateName: string) => JsxExpression;

const genericEventToReactEventImplementationOverrides: PrimitiveLevelPropConfiguration<EventHandlerBuilder> = {
[Primitive.StepperField]: { [StudioGenericEvent.change]: numericValueCallbackGenerator },
[Primitive.SliderField]: { [StudioGenericEvent.change]: numericValueCallbackGenerator },
[Primitive.StepperField]: { [StudioGenericEvent.change]: numericValueCallback },
[Primitive.SliderField]: { [StudioGenericEvent.change]: numericValueCallback },
[Primitive.CheckboxField]: { [StudioGenericEvent.change]: buildSyntheticEventTargetCallback('checked') },
[Primitive.SwitchField]: { [StudioGenericEvent.change]: toggleBooleanStateCallback },
};

export function getComponentStateReferences(component: StudioComponent) {
Expand Down Expand Up @@ -107,7 +109,48 @@ export function getActionStateParameters(action: ActionStudioComponentEvent): St
return [];
}

function syntheticEventTargetValueCallbackGenerator(stateName: string): JsxExpression {
function buildSyntheticEventTargetCallback(targetFieldName: string): (setStateName: string) => JsxExpression {
return (setStateName: string) => {
return factory.createJsxExpression(
undefined,
factory.createArrowFunction(
undefined,
undefined,
[
factory.createParameterDeclaration(
undefined,
undefined,
undefined,
factory.createIdentifier('event'),
undefined,
factory.createTypeReferenceNode(factory.createIdentifier('SyntheticEvent'), undefined),
undefined,
),
],
undefined,
factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken),
factory.createBlock(
[
factory.createExpressionStatement(
factory.createCallExpression(factory.createIdentifier(setStateName), undefined, [
factory.createPropertyAccessExpression(
factory.createPropertyAccessExpression(
factory.createIdentifier('event'),
factory.createIdentifier('target'),
),
factory.createIdentifier(targetFieldName),
),
]),
),
],
false,
),
),
);
};
}

function numericValueCallback(setStateName: string): JsxExpression {
return factory.createJsxExpression(
undefined,
factory.createArrowFunction(
Expand All @@ -118,72 +161,52 @@ function syntheticEventTargetValueCallbackGenerator(stateName: string): JsxExpre
undefined,
undefined,
undefined,
factory.createIdentifier('event'),
factory.createIdentifier('value'),
undefined,
factory.createTypeReferenceNode(factory.createIdentifier('SyntheticEvent'), undefined),
factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword),
undefined,
),
],
undefined,
factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken),
factory.createBlock(
[
factory.createExpressionStatement(
factory.createCallExpression(factory.createIdentifier(stateName), undefined, [
factory.createPropertyAccessExpression(
factory.createPropertyAccessExpression(
factory.createIdentifier('event'),
factory.createIdentifier('target'),
),
factory.createIdentifier('value'),
),
]),
),
],
false,
),
factory.createCallExpression(factory.createIdentifier(setStateName), undefined, [
factory.createIdentifier('value'),
]),
),
);
}

function numericValueCallbackGenerator(stateName: string): JsxExpression {
function toggleBooleanStateCallback(setStateName: string, stateName: string): JsxExpression {
return factory.createJsxExpression(
undefined,
factory.createArrowFunction(
undefined,
undefined,
[
factory.createParameterDeclaration(
undefined,
undefined,
undefined,
factory.createIdentifier('value'),
undefined,
factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword),
undefined,
),
],
[],
undefined,
factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken),
factory.createCallExpression(factory.createIdentifier(stateName), undefined, [factory.createIdentifier('value')]),
factory.createCallExpression(factory.createIdentifier(setStateName), undefined, [
factory.createPrefixUnaryExpression(ts.SyntaxKind.ExclamationToken, factory.createIdentifier(stateName)),
]),
),
);
}

// TODO: Update in here so we can support customer `change` events for form elements.
export function buildOpeningElementControlEvents(
componentType: string,
setStateName: string,
stateName: string,
event: string,
): JsxAttribute {
const implementationOverrides = genericEventToReactEventImplementationOverrides[componentType];
const controlEventBuilder =
implementationOverrides && implementationOverrides[event]
? implementationOverrides[event]
: syntheticEventTargetValueCallbackGenerator;
: buildSyntheticEventTargetCallback('value');
return factory.createJsxAttribute(
factory.createIdentifier(mapGenericEventToReact(componentType as Primitive, event as StudioGenericEvent)),
controlEventBuilder(stateName),
controlEventBuilder(setStateName, stateName),
);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"id": "1234-5678-9010",
"componentType": "Flex",
"name": "CheckboxControlledElement",
"properties": {},
"children": [
{
"componentType": "CheckboxField",
"name": "Input",
"properties": {}
},
{
"componentType": "Text",
"name": "CheckboxFieldValue",
"properties": {
"label": {
"componentName": "Input",
"property": "checked"
}
}
}
]
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"id": "1234-5678-9010",
"componentType": "Flex",
"name": "SwitchControlledElement",
"properties": {},
"children": [
{
"componentType": "SwitchField",
"name": "Input",
"properties": {}
},
{
"componentType": "Text",
"name": "SwitchFieldValue",
"properties": {
"label": {
"componentName": "Input",
"property": "isChecked"
}
}
}
]
}

Loading

0 comments on commit c254c5a

Please sign in to comment.