Skip to content

Commit

Permalink
feat: Introduced a new optional prop in apl renderComponent to cust…
Browse files Browse the repository at this point in the history
…omize the APL component returned by a control

 refactor: Changed the renderAPLComponent API signature.
 feat: Created new classes on control APL built-ins namespace for few default implementations of APL component mode.
 refactor: Removed the renderStyle concept in favour of above props and built-ins provided.
  • Loading branch information
shreraju-amzn authored and Shreyas-vgr committed Mar 15, 2021
1 parent 2fd688a commit 222d8e5
Show file tree
Hide file tree
Showing 8 changed files with 516 additions and 249 deletions.
26 changes: 7 additions & 19 deletions demo/ComponentModeAPL/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { SkillBuilders } from 'ask-sdk-core';
import { ControlInput, ControlResponseBuilder, ControlResult, NumberControl } from '../../../src';
import { MultiValueListControl } from '../../../src/commonControls/multiValueListControl/MultiValueListControl';
import { MultiValueListControlComponentAPLBuiltIns } from '../../../src/commonControls/multiValueListControl/MultiValueListControlAPL';
import { ComponentModeControlManager } from '../../../src/controls/ComponentModeControlManager';
import { Control } from '../../../src/controls/Control';
import { ControlHandler } from '../../../src/runtime/ControlHandler';
Expand Down Expand Up @@ -60,6 +61,9 @@ export namespace ComponentModeDemo {
interactionModel: {
targets: ['builtin_choice', 'builtin_it', 'theme'],
},
apl: {
renderComponent: MultiValueListControlComponentAPLBuiltIns.CheckBoxRenderer.default,
},
})),
);

Expand Down Expand Up @@ -136,13 +140,7 @@ export namespace ComponentModeDemo {
left: '50px',
width: '200px',
height: '100px',
items: [
this.ageControl.renderAPLComponent(
{ renderStyle: 'modalKeypad' },
input,
controlResponseBuilder,
),
],
items: [this.ageControl.renderAPLComponent(input, controlResponseBuilder)],
},
{
id: 'label2',
Expand All @@ -164,13 +162,7 @@ export namespace ComponentModeDemo {
left: '50px',
width: '200px',
height: '100px',
items: [
this.guestsControl.renderAPLComponent(
{ renderStyle: 'modalKeypad' },
input,
controlResponseBuilder,
),
],
items: [this.guestsControl.renderAPLComponent(input, controlResponseBuilder)],
},
{
id: 'label3',
Expand All @@ -193,11 +185,7 @@ export namespace ComponentModeDemo {
width: '700px',
height: '360px',
items: [
this.partyThemeControl.renderAPLComponent(
{ renderStyle: 'aggregateDuplicates' },
input,
controlResponseBuilder,
),
this.partyThemeControl.renderAPLComponent(input, controlResponseBuilder),
],
},

Expand Down
52 changes: 32 additions & 20 deletions src/commonControls/listControl/ListControl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import _ from 'lodash';
import { ModelData } from '../..';
import { Strings as $ } from '../../constants/Strings';
import {
APLComponentProps,
Control,
ControlInputHandler,
ControlInputHandlingProps,
Expand Down Expand Up @@ -62,7 +61,7 @@ import { DeepRequired } from '../../utils/DeepRequired';
import { InputUtil } from '../../utils/InputUtil';
import { defaultIntentToValueMapper } from '../../utils/IntentUtils';
import { falseIfGuardFailed, okIf, StateConsistencyError } from '../../utils/Predicates';
import { ListControlAPLPropsBuiltIns, ListControlComponentAPLBuiltIns, ListStyles } from './ListControlAPL';
import { ListControlAPLPropsBuiltIns, ListControlComponentAPLBuiltIns } from './ListControlAPL';

// TODO: feature: support "what are my choices"
// TODO: feature: voice pagination of choices.
Expand Down Expand Up @@ -369,7 +368,12 @@ export class ListControlPromptProps {
export type AplContent = { document: { [key: string]: any }; dataSource: { [key: string]: any } };
export type AplContentFunc = (control: ListControl, input: ControlInput) => AplContent;
export type AplDocumentPropNewStyle = AplContent | AplContentFunc;

export type AplRenderComponentFunc = (
control: ListControl,
props: ListAPLComponentProps,
input: ControlInput,
resultBuilder: ControlResponseBuilder,
) => { [key: string]: any };
/**
* Props associated with the APL produced by ListControl.
*/
Expand All @@ -386,6 +390,23 @@ export class ListControlAPLProps {
*/
requestValue?: AplDocumentPropNewStyle;
requestChangedValue?: AplDocumentPropNewStyle;

/**
* Determines the APL Component rendering mode.
*
* Usage:
*
* 1) Use pre-defined built-ins under ListControlComponentAPLBuiltIns.* namespace which provides both default
* implementations and customization of props(ListAPLComponentProps) to render an APL component.
*
* e.g renderComponent: ListControlComponentAPLBuiltIns.ImageListRenderer.default --- Default Implementation
* renderComponent: ListControlComponentAPLBuiltIns.ImageListRenderer.of(props: ListAPLComponentProps) --- Override few properties
*
* 2) Provide a custom function which returns an APL component.
*
* Default: ListControlComponentAPLBuiltIns.TextListRenderer.default
*/
renderComponent?: AplRenderComponentFunc;
}

/**
Expand All @@ -401,12 +422,7 @@ interface LastInitiativeState {
/**
* Props to customize ListControl APLComponent rendering.
*/
export interface ListAPLComponentProps extends APLComponentProps {
/**
* Defines the render style of APL component produced by the control.
*/
renderStyle: ListStyles;

export interface ListAPLComponentProps {
/**
* Boolean to determine to highlight user selected choice from the
* list of items.
Expand Down Expand Up @@ -633,6 +649,7 @@ export class ListControl extends Control implements InteractionModelContributor
requestChangedValue: ListControlAPLPropsBuiltIns.defaultSelectValueAPLContent({
valueRenderer: props.valueRenderer ?? ListControl.defaultValueRenderer(),
}),
renderComponent: ListControlComponentAPLBuiltIns.TextListRenderer.default,
},
};

Expand Down Expand Up @@ -1303,17 +1320,12 @@ export class ListControl extends Control implements InteractionModelContributor
}
}

renderAPLComponent(
props: ListAPLComponentProps,
input: ControlInput,
resultBuilder: ControlResponseBuilder,
): { [key: string]: any } {
return ListControlComponentAPLBuiltIns.renderComponent(
this,
{ ...props, valueRenderer: this.props.valueRenderer },
input,
resultBuilder,
);
renderAPLComponent(input: ControlInput, resultBuilder: ControlResponseBuilder): { [key: string]: any } {
const aplRenderFunc = this.props.apl.renderComponent;
const defaultProps: ListAPLComponentProps = {
valueRenderer: this.props.valueRenderer,
};
return aplRenderFunc.call(this, this, defaultProps, input, resultBuilder);
}

private evaluateAPLPropNewStyle(prop: AplDocumentPropNewStyle, input: ControlInput): AplContent {
Expand Down
120 changes: 103 additions & 17 deletions src/commonControls/listControl/ListControlAPL.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import i18next from 'i18next';
import _ from 'lodash';
import { ControlResponseBuilder } from '../..';
import { ControlInput } from '../../controls/ControlInput';
import { AplContent, ListAPLComponentProps, ListControl, ListControlRenderedItem } from './ListControl';
Expand Down Expand Up @@ -117,24 +118,18 @@ export namespace ListControlAPLPropsBuiltIns {
}
}

export type ListStyles = 'textList' | 'imageList';

/**
* Namespace which define built-in renderers for APL Component Mode.
*/
export namespace ListControlComponentAPLBuiltIns {
export function renderComponent(
control: ListControl,
props: ListAPLComponentProps,
input: ControlInput,
resultBuilder: ControlResponseBuilder,
) {
if (props.renderStyle === 'textList') {
return renderTextList(control, props, input, resultBuilder);
} else if (props.renderStyle === 'imageList') {
return renderImageList(control, props, input, resultBuilder);
} else {
throw new Error('Invalid render style');
}
}

/**
* Function which returns an APL component using ImageListLayout.
*
* @param control - ListControl
* @param props - Control context props e.g valueRenderer
* @param input - Input
* @param resultBuilder - Result builder
*/
export function renderImageList(
control: ListControl,
props: ListAPLComponentProps,
Expand Down Expand Up @@ -288,6 +283,14 @@ export namespace ListControlComponentAPLBuiltIns {
};
}

/**
* Function which returns an APL component using TextListLayout.
*
* @param control - ListControl
* @param props - Control context props e.g valueRenderer
* @param input - Input
* @param resultBuilder - Result builder
*/
export function renderTextList(
control: ListControl,
props: ListAPLComponentProps,
Expand Down Expand Up @@ -365,4 +368,87 @@ export namespace ListControlComponentAPLBuiltIns {
listItems,
};
}

/**
* Defines TextListRenderer for APLComponentMode.
*/
export class TextListRenderer {
/**
* Provides a default implementation of textList with default props.
*
* @param control - ListControl
* @param defaultProps - props
* @param input - Input
* @param resultBuilder - Result builder
*/
static default = (
control: ListControl,
defaultProps: ListAPLComponentProps,
input: ControlInput,
resultBuilder: ControlResponseBuilder,
) => renderTextList(control, defaultProps, input, resultBuilder);

/**
* Provides customization over `renderTextList()` arguments where the input
* props overrides the defaults.
*
* @param props - props
*/
static of(props: ListAPLComponentProps) {
return (
control: ListControl,
defaultProps: ListAPLComponentProps,
input: ControlInput,
resultBuilder: ControlResponseBuilder,
) => {
// Merges the user-provided props with the default props.
// Any property defined by the user-provided data overrides the defaults.
const mergedProps = _.merge(defaultProps, props);
return renderTextList(control, mergedProps, input, resultBuilder);
};
}
}

/**
* Defines ImageListRenderer for APLComponentMode.
*/
export class ImageListRenderer {
/**
* Provides a default implementation of imageList with default props.
*
* @param control - ListControl
* @param defaultProps - props
* @param input - Input
* @param resultBuilder - Result builder
*/
static default = (
control: ListControl,
defaultProps: ListAPLComponentProps,
input: ControlInput,
resultBuilder: ControlResponseBuilder,
) => renderImageList(control, { ...defaultProps, highlightSelected: true }, input, resultBuilder);

/**
* Provides customization over `renderImageList()` arguments where the input
* props overrides the defaults.
*
* @param props - props
*/
static of(props: ListAPLComponentProps) {
return (
control: ListControl,
defaultProps: ListAPLComponentProps,
input: ControlInput,
resultBuilder: ControlResponseBuilder,
) => {
// Assign defaults to props.
defaultProps.highlightSelected = true;

// Merges the user-provided props with the default props.
// Any property defined by the user-provided data overrides the defaults.
const mergedProps = _.merge(defaultProps, props);
return renderImageList(control, mergedProps, input, resultBuilder);
};
}
}
}
Loading

0 comments on commit 222d8e5

Please sign in to comment.