Skip to content

Commit

Permalink
fix: add override for stepper field controlled change event (#389)
Browse files Browse the repository at this point in the history
  • Loading branch information
alharris-at committed Feb 25, 2022
1 parent 562a155 commit ebd2542
Show file tree
Hide file tree
Showing 8 changed files with 234 additions and 79 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5362,6 +5362,57 @@ export default function TwoWayBindings(
}
`;

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

export type StepperControlledElementProps = React.PropsWithChildren<
Partial<FlexProps> & {
overrides?: EscapeHatchProps | undefined | null;
}
>;
export default function StepperControlledElement(
props: StepperControlledElementProps
): React.ReactElement {
const { overrides, ...rest } = props;
const [inputValue, setInputValue] = useStateMutationAction(undefined);
return (
/* @ts-ignore: TS2322 */
<Flex
{...rest}
{...getOverrideProps(overrides, \\"StepperControlledElement\\")}
>
<StepperField
label=\\"Stepper\\"
defaultValue={0}
min={0}
max={10}
step={1}
labelHidden={true}
value={inputValue}
onStepChange={(value: number) => setInputValue(value)}
{...getOverrideProps(overrides, \\"Input\\")}
></StepperField>
<Text
children={inputValue}
{...getOverrideProps(overrides, \\"StepperFieldValue\\")}
></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 @@ -5923,9 +5974,7 @@ export default function TwoWayBindings(
step={1}
labelHidden={true}
value={sliderFieldInputValue}
onChange={(event: SyntheticEvent) => {
setSliderFieldInputValue(event.target.value);
}}
onChange={(value: number) => setSliderFieldInputValue(value)}
{...getOverrideProps(overrides, \\"SliderFieldInput\\")}
></SliderField>
<Text
Expand Down Expand Up @@ -5957,9 +6006,7 @@ export default function TwoWayBindings(
step={1}
labelHidden={true}
value={stepperFieldInputValue}
onChange={(event: SyntheticEvent) => {
setStepperFieldInputValue(event.target.value);
}}
onStepChange={(value: number) => setStepperFieldInputValue(value)}
{...getOverrideProps(overrides, \\"StepperFieldInput\\")}
></StepperField>
<Text
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,10 @@ describe('amplify render tests', () => {
expect(generateWithAmplifyRenderer('workflow/twoWayBindings')).toMatchSnapshot();
});

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

it('modifies text in component on input change', () => {
expect(generateWithAmplifyRenderer('workflow/inputToTextChange')).toMatchSnapshot();
});
Expand Down
4 changes: 4 additions & 0 deletions packages/codegen-ui-react/lib/primitive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@
import { factory, SyntaxKind, TypeParameterDeclaration, TypeNode } from 'typescript';
import iconset from './iconset';

export type PrimitiveLevelPropConfiguration<ConfigType> = {
[componentType: string]: { [eventType: string]: ConfigType };
};

enum Primitive {
Alert = 'Alert',
Badge = 'Badge',
Expand Down
3 changes: 2 additions & 1 deletion packages/codegen-ui-react/lib/react-component-renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,13 +87,14 @@ export class ReactComponentRenderer<TPropIn> extends ComponentRendererBase<
});

const eventAttributes = Object.entries(this.component.events || {}).map(([key, value]) =>
buildOpeningElementEvents(value, key, this.component.name),
buildOpeningElementEvents(this.component.componentType, value, key, this.component.name),
);

const controlEventAttributes = Object.entries(localStateReferences)
.filter(([, references]) => references.some(({ addControlEvent }) => addControlEvent))
.map(([key]) =>
buildOpeningElementControlEvents(
this.component.componentType,
getSetStateName({ componentName: this.component.name || '', property: key }),
'change',
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,14 +85,15 @@ export class ReactComponentWithChildrenRenderer<TPropIn> extends ComponentWithCh
});

const eventAttributes = Object.entries(this.component.events || {}).map(([key, value]) =>
buildOpeningElementEvents(value, key, this.component.name),
buildOpeningElementEvents(this.component.componentType, value, key, this.component.name),
);

// TODO: Should we always control form elements?
const controlEventAttributes = Object.entries(localStateReferences)
.filter(([, references]) => references.some(({ addControlEvent }) => addControlEvent))
.map(([key]) =>
buildOpeningElementControlEvents(
this.component.componentType,
getSetStateName({ componentName: this.component.name || '', property: key }),
'change',
),
Expand Down
83 changes: 46 additions & 37 deletions packages/codegen-ui-react/lib/workflow/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,48 +13,75 @@
See the License for the specific language governing permissions and
limitations under the License.
*/
import {
StudioGenericEvent,
StudioComponentEvent,
BoundStudioComponentEvent,
ActionStudioComponentEvent,
} from '@aws-amplify/codegen-ui';

import { StudioGenericEvent, StudioComponentEvent, BoundStudioComponentEvent } from '@aws-amplify/codegen-ui';
import { factory, JsxAttribute, SyntaxKind } from 'typescript';

import { getActionIdentifier } from './action';
import { isBoundEvent, isActionEvent } from '../react-component-render-helper';
import Primitive, { PrimitiveLevelPropConfiguration } from '../primitive';

/*
* Temporary hardcoded mapping of generic to react events, long-term this will be exported by amplify-ui.
*/
const genericEventToReactEventMapping = {
[StudioGenericEvent.click]: 'onClick',
[StudioGenericEvent.doubleclick]: 'onDoubleClick',
[StudioGenericEvent.mousedown]: 'onMouseDown',
[StudioGenericEvent.mouseenter]: 'onMouseEnter',
[StudioGenericEvent.mouseleave]: 'onMouseLeave',
[StudioGenericEvent.mousemove]: 'onMouseMove',
[StudioGenericEvent.mouseout]: 'onMouseOut',
[StudioGenericEvent.mouseover]: 'onMouseOver',
[StudioGenericEvent.mouseup]: 'onMouseUp',
[StudioGenericEvent.change]: 'onChange',
[StudioGenericEvent.input]: 'onInput',
[StudioGenericEvent.focus]: 'onFocus',
[StudioGenericEvent.blur]: 'onBlur',
[StudioGenericEvent.keydown]: 'onKeyDown',
[StudioGenericEvent.keypress]: 'onKeyPress',
[StudioGenericEvent.keyup]: 'onKeyUp',
};

const genericEventToReactEventMappingOverrides: PrimitiveLevelPropConfiguration<string> = {
[Primitive.StepperField]: {
[StudioGenericEvent.change]: 'onStepChange',
},
};

export function buildOpeningElementEvents(
componentType: string,
event: StudioComponentEvent,
name: string,
componentName: string | undefined,
): JsxAttribute {
if (isBoundEvent(event)) {
return buildBindingEvent(event, name);
return buildBindingEvent(componentType, event, name);
}
if (isActionEvent(event)) {
return buildActionEvent(event, name, componentName);
return buildActionEvent(componentType, name, componentName);
}

return factory.createJsxAttribute(factory.createIdentifier(name), undefined);
}

export function buildBindingEvent(event: BoundStudioComponentEvent, eventName: string): JsxAttribute {
export function buildBindingEvent(
componentType: string,
event: BoundStudioComponentEvent,
eventName: string,
): JsxAttribute {
const expr = factory.createIdentifier(event.bindingEvent);
return factory.createJsxAttribute(
factory.createIdentifier(mapGenericEventToReact(eventName as StudioGenericEvent)),
factory.createIdentifier(mapGenericEventToReact(componentType as Primitive, eventName as StudioGenericEvent)),
factory.createJsxExpression(undefined, expr),
);
}

export function buildActionEvent(
event: ActionStudioComponentEvent,
componentType: string,
eventName: string,
componentName: string | undefined,
): JsxAttribute {
return factory.createJsxAttribute(
factory.createIdentifier(mapGenericEventToReact(eventName as StudioGenericEvent)),
factory.createIdentifier(mapGenericEventToReact(componentType as Primitive, eventName as StudioGenericEvent)),
factory.createJsxExpression(
undefined,
factory.createArrowFunction(
Expand All @@ -80,29 +107,11 @@ export function buildActionEvent(
);
}

/*
* Temporary hardcoded mapping of generic to react events, long-term this will be exported by amplify-ui.
*/
const genericEventToReactEventMapping = {
[StudioGenericEvent.click]: 'onClick',
[StudioGenericEvent.doubleclick]: 'onDoubleClick',
[StudioGenericEvent.mousedown]: 'onMouseDown',
[StudioGenericEvent.mouseenter]: 'onMouseEnter',
[StudioGenericEvent.mouseleave]: 'onMouseLeave',
[StudioGenericEvent.mousemove]: 'onMouseMove',
[StudioGenericEvent.mouseout]: 'onMouseOut',
[StudioGenericEvent.mouseover]: 'onMouseOver',
[StudioGenericEvent.mouseup]: 'onMouseUp',
[StudioGenericEvent.change]: 'onChange',
[StudioGenericEvent.input]: 'onInput',
[StudioGenericEvent.focus]: 'onFocus',
[StudioGenericEvent.blur]: 'onBlur',
[StudioGenericEvent.keydown]: 'onKeyDown',
[StudioGenericEvent.keypress]: 'onKeyPress',
[StudioGenericEvent.keyup]: 'onKeyUp',
};

export function mapGenericEventToReact(genericEventBinding: StudioGenericEvent): string {
export function mapGenericEventToReact(componentType: Primitive, genericEventBinding: StudioGenericEvent): string {
const componentOverrides = genericEventToReactEventMappingOverrides[componentType];
if (componentOverrides && componentOverrides[genericEventBinding]) {
return componentOverrides[genericEventBinding];
}
const reactEventMapping = genericEventToReactEventMapping[genericEventBinding];
if (!reactEventMapping) {
throw new Error(`${genericEventBinding} is not a possible event.`);
Expand Down

0 comments on commit ebd2542

Please sign in to comment.