Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sync changes up to 57537b9 #69

Merged
merged 1 commit into from
Aug 19, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 14 additions & 18 deletions core/flow/src/flow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export class FlowInstance {

/** A hook to intercept and block a transition */
skipTransition: new SyncBailHook<
[NamedState | undefined, string],
[NamedState | undefined],
boolean | undefined
>(),

Expand Down Expand Up @@ -124,6 +124,19 @@ export class FlowInstance {
throw new Error("Cannot transition when there's no current state");
}

if (options?.force) {
this.log?.debug(`Forced transition. Skipping validation checks`);
} else {
const skipTransition = this.hooks.skipTransition.call(this.currentState);

if (skipTransition) {
this.log?.debug(
`Skipping transition from ${this.currentState} b/c hook told us to`
);
return;
}
}

const state = this.hooks.beforeTransition.call(
this.currentState.value,
transitionValue
Expand Down Expand Up @@ -170,23 +183,6 @@ export class FlowInstance {

const prevState = this.currentState;

if (options?.force) {
this.log?.debug(`Forced transition. Skipping validation checks`);
} else {
const skipTransition = this.hooks.skipTransition.call(
prevState,
stateName
);

if (skipTransition) {
this.log?.debug(
`Skipping transition to ${stateName} b/c hook told us to`
);

return;
}
}

nextState = this.hooks.resolveTransitionNode.call(nextState);

const newCurrentState = {
Expand Down
50 changes: 50 additions & 0 deletions core/player/src/__tests__/view.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,56 @@ describe('state node expression tests', () => {
});
});

test('evaluates onEnd before transition', () => {
player.start({
...minimal,
data: {
...minimal.data,
viewRef: 'initial-view',
},
navigation: {
BEGIN: 'FLOW_1',
FLOW_1: {
startState: 'ACTION_1',
ACTION_1: {
state_type: 'ACTION',
exp: "{{viewRef}} = 'view-exp'",
onStart: "{{viewRef}} = 'view-onStart'",
onEnd: "{{viewRef}} = 'VIEW_1'",
transitions: {
Next: '{{viewRef}}',
},
},
VIEW_1: {
state_type: 'VIEW',
ref: 'view-1',
transitions: {},
},
},
},
});
jest.runOnlyPendingTimers();
flowController?.transition('Next');

/**
* Expected eval order:
* 1. onStart
* 2. exp
* 3. onEnd
*/
expect(getView()).toStrictEqual({
id: 'view-1',
type: 'view',
label: {
asset: {
id: 'action-label',
type: 'text',
value: 'Clicked 0 times',
},
},
});
});

test('triggers onStart before resolving view IDs', () => {
player.start({
id: 'resolve-view-flow',
Expand Down
16 changes: 16 additions & 0 deletions core/player/src/player.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,11 @@ export class Player {
});
}

/** Returns currently registered plugins */
public getPlugins(): PlayerPlugin[] {
return this.config.plugins ?? [];
}

/** Find instance of [Plugin] that has been registered to Player */
public findPlugin<Plugin extends PlayerPlugin>(
symbol: symbol
Expand Down Expand Up @@ -278,6 +283,17 @@ export class Player {

flowController.hooks.flow.tap('player', (flow: FlowInstance) => {
flow.hooks.beforeTransition.tap('player', (state, transitionVal) => {
if (
state.onEnd &&
(state.transitions[transitionVal] || state.transitions['*'])
) {
if (typeof state.onEnd === 'object' && 'exp' in state.onEnd) {
expressionEvaluator?.evaluate(state.onEnd.exp);
} else {
expressionEvaluator?.evaluate(state.onEnd);
}
}

if (!('transitions' in state) || !state.transitions[transitionVal]) {
return state;
}
Expand Down
11 changes: 0 additions & 11 deletions core/player/src/plugins/flow-exp-plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,17 +47,6 @@ export class FlowExpPlugin implements PlayerPlugin {
// Eval state nodes
flow.hooks.resolveTransitionNode.intercept({
call: (nextState: NavigationFlowState) => {
/** Get the current state of Player */
const currentState = () => player.getState() as InProgressState;

/** Get the current flow state */
const currentFlowState =
currentState().controllers.flow.current?.currentState;

if (currentFlowState?.value.onEnd) {
handleEval(currentFlowState.value.onEnd);
}

if (nextState?.onStart) {
handleEval(nextState.onStart);
}
Expand Down
2 changes: 1 addition & 1 deletion core/player/src/validation/controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -553,7 +553,7 @@ export class ValidationController implements BindingTracker {
);
}

private getValidator(type: string) {
public getValidator(type: string) {
if (this.validatorRegistry) {
return this.validatorRegistry.get(type);
}
Expand Down
2 changes: 1 addition & 1 deletion core/schema/src/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ export class SchemaController implements ValidationProvider {
new Map();

private types: Map<string, SchemaType.DataType<any>> = new Map();
private schema: Map<string, SchemaType.DataType> = new Map();
public readonly schema: Map<string, SchemaType.DataType> = new Map();

private bindingSchemaNormalizedCache: Map<BindingInstance, string> =
new Map();
Expand Down
73 changes: 48 additions & 25 deletions core/view/src/parser/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { SyncWaterfallHook } from 'tapable-ts';
import type { Template, AssetSwitch } from '@player-ui/types';
import type { Node, AnyAssetType } from './types';
import { NodeType } from './types';
import { hasSwitch } from './utils';
import { hasSwitch, hasApplicability } from './utils';

export * from './types';

Expand Down Expand Up @@ -56,6 +56,36 @@ export class Parser {
return viewNode as Node.View;
}

private parseApplicability(
obj: object,
type: Node.ChildrenTypes,
options: ParseObjectOptions
): Node.Node | null {
const parsedApplicability = this.parseObject(
omit(obj, 'applicability'),
type,
options
);
if (parsedApplicability !== null) {
const applicabilityNode = this.createASTNode(
{
type: NodeType.Applicability,
expression: (obj as any).applicability,
value: parsedApplicability,
},
obj
);

if (applicabilityNode?.type === NodeType.Applicability) {
applicabilityNode.value.parent = applicabilityNode;
}

return applicabilityNode;
}

return null;
}

private parseSwitch(
obj: AssetSwitch,
options: ParseObjectOptions
Expand Down Expand Up @@ -116,29 +146,8 @@ export class Parser {
type: Node.ChildrenTypes = NodeType.Value,
options: ParseObjectOptions = { templateDepth: 0 }
): Node.Node | null {
if (Object.prototype.hasOwnProperty.call(obj, 'applicability')) {
const parsedApplicability = this.parseObject(
omit(obj, 'applicability'),
type,
options
);

if (parsedApplicability !== null) {
const applicabilityNode = this.createASTNode(
{
type: NodeType.Applicability,
expression: (obj as any).applicability,
value: parsedApplicability,
},
obj
);

if (applicabilityNode?.type === NodeType.Applicability) {
applicabilityNode.value.parent = applicabilityNode;
}

return applicabilityNode;
}
if (hasApplicability(obj)) {
return this.parseApplicability(obj, type, options);
}

if (hasSwitch(obj)) {
Expand Down Expand Up @@ -249,7 +258,21 @@ export class Parser {
}
}
} else if (localValue && typeof localValue === 'object') {
parseLocalObject(localValue, [...path, localKey]);
if (hasApplicability(localValue)) {
const applicabilityNode = this.parseApplicability(
localValue,
type,
options
);
if (applicabilityNode) {
children.push({
path: [...path, localKey],
value: applicabilityNode,
});
}
} else {
parseLocalObject(localValue, [...path, localKey]);
}
} else {
value = setIn(value, [...path, localKey], localValue);
}
Expand Down
5 changes: 5 additions & 0 deletions core/view/src/parser/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,8 @@ export function hasSwitch(obj: object): obj is AssetSwitch<Asset> {
Object.prototype.hasOwnProperty.call(obj, 'staticSwitch')
);
}

/** Check to see if the object contains applicability */
export function hasApplicability(obj: object): boolean {
return Object.prototype.hasOwnProperty.call(obj, 'applicability');
}
27 changes: 27 additions & 0 deletions core/view/src/plugins/__tests__/applicability.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,33 @@ describe('applicability', () => {
});
});

it('removes asset wrappers', () => {
const root = parser.parseObject({
asset: {
title: {
applicability: '{{foo}}',
asset: {
value: 'foo',
},
},
value: 'Hello World',
},
});
model.set([['foo', true]]);
const resolver = new Resolver(root!, resolverOptions);

new ApplicabilityPlugin().applyResolver(resolver);
new StringResolverPlugin().applyResolver(resolver);

expect(resolver.update()).toStrictEqual({
asset: { title: { asset: { value: 'foo' } }, value: 'Hello World' },
});
model.set([['foo', false]]);
expect(resolver.update()).toStrictEqual({
asset: { value: 'Hello World' },
});
});

it('handles empty models', () => {
const root = parser.parseObject({
asset: {
Expand Down
2 changes: 1 addition & 1 deletion core/view/src/view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ export class ViewInstance implements ValidationProvider {

private resolver?: Resolver;
public readonly initialView: ViewType;
private readonly resolverOptions: Resolve.ResolverOptions;
public readonly resolverOptions: Resolve.ResolverOptions;
private rootNode?: Node.Node;

private validationProvider?: CrossfieldProvider;
Expand Down
1 change: 1 addition & 0 deletions language/dsl/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ javascript_pipeline(
"@npm//react-flatten-children",
"@npm//react-json-reconciler",
"@npm//react-merge-refs",
"@npm//source-map-js",
"@npm//signale",
"@npm//ts-node",
"@npm//typescript",
Expand Down
Loading