From bef693c56740904661d60c5296e8843e8be28eee Mon Sep 17 00:00:00 2001 From: b4rtaz Date: Mon, 12 Jun 2023 21:04:00 +0200 Subject: [PATCH] 0.3.1. --- CHANGELOG.md | 4 ++ demos/webpack-app/package.json | 6 +-- .../src/playground/default-state.ts | 4 +- .../machine/activities/if-activity.ts | 13 +++++-- .../machine/activities/log-activity.ts | 2 +- .../machine/services/dynamics-service.ts | 13 +++++-- .../src/playground/model/if-step-model.ts | 26 +++++++------ demos/webpack-app/src/playground/storage.ts | 2 +- editor/package.json | 6 +-- .../boolean/boolean-value-editor.ts | 38 +++++++++++++++++++ .../value-editor-factory-resolver.ts | 2 + model/package.json | 2 +- model/src/test-tools/definition-model-stub.ts | 26 +++++++++++++ model/src/test-tools/model-activator-stub.ts | 7 ++++ .../boolean-value-model-configuration.ts | 3 ++ .../boolean/boolean-value-model.spec.ts | 28 ++++++++++++++ .../boolean/boolean-value-model.ts | 24 ++++++++++++ model/src/value-models/boolean/index.ts | 2 + .../dynamic/dynamic-value-model.ts | 3 +- model/src/value-models/index.ts | 1 + 20 files changed, 180 insertions(+), 32 deletions(-) create mode 100644 editor/src/value-editors/boolean/boolean-value-editor.ts create mode 100644 model/src/test-tools/definition-model-stub.ts create mode 100644 model/src/test-tools/model-activator-stub.ts create mode 100644 model/src/value-models/boolean/boolean-value-model-configuration.ts create mode 100644 model/src/value-models/boolean/boolean-value-model.spec.ts create mode 100644 model/src/value-models/boolean/boolean-value-model.ts create mode 100644 model/src/value-models/boolean/index.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 95e3592..4f71216 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.3.1 + +Added new value model: boolean (`booleanValueModel({ ... })`). + ## 0.3.0 * Added new value model: nullable any variable (`nullableAnyVariableValueModel({ ... })`). This value model allows you to select any variable. Additionally, you can specify a variable type that can be selected by a user. diff --git a/demos/webpack-app/package.json b/demos/webpack-app/package.json index 9db9a5e..2063805 100644 --- a/demos/webpack-app/package.json +++ b/demos/webpack-app/package.json @@ -18,8 +18,8 @@ "sequential-workflow-model": "^0.1.3", "sequential-workflow-designer": "^0.13.2", "sequential-workflow-machine": "^0.2.0", - "sequential-workflow-editor-model": "^0.3.0", - "sequential-workflow-editor": "^0.3.0" + "sequential-workflow-editor-model": "^0.3.1", + "sequential-workflow-editor": "^0.3.1" }, "devDependencies": { "ts-loader": "^9.4.2", @@ -33,4 +33,4 @@ "@typescript-eslint/parser": "^5.47.0", "eslint": "^8.30.0" } -} +} \ No newline at end of file diff --git a/demos/webpack-app/src/playground/default-state.ts b/demos/webpack-app/src/playground/default-state.ts index dfbd13d..14ea7b2 100644 --- a/demos/webpack-app/src/playground/default-state.ts +++ b/demos/webpack-app/src/playground/default-state.ts @@ -41,8 +41,8 @@ const definition: MyDefinition = { type: 'if', componentType: 'switch', properties: { - a: { modelId: 'nullableVariable', value: { name: 'remainder' } }, - operator: '=', + a: { modelId: 'nullableAnyVariable', value: { name: 'remainder', type: 'number' } }, + operator: '==', b: { modelId: 'number', value: 0 } }, branches: { diff --git a/demos/webpack-app/src/playground/machine/activities/if-activity.ts b/demos/webpack-app/src/playground/machine/activities/if-activity.ts index db6134b..e5d485e 100644 --- a/demos/webpack-app/src/playground/machine/activities/if-activity.ts +++ b/demos/webpack-app/src/playground/machine/activities/if-activity.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ import { branchName, createForkActivity } from 'sequential-workflow-machine'; import { IfStep } from '../../model/if-step-model'; import { GlobalState } from '../global-state'; @@ -6,19 +7,23 @@ export const ifActivity = createForkActivity({ stepType: 'if', init: () => ({}), handler: async (step: IfStep, { $dynamics }: GlobalState) => { - const a = $dynamics.readNumber(step.properties.a); - const b = $dynamics.readNumber(step.properties.b); + const a = $dynamics.readAny(step.properties.a); + const b = $dynamics.readAny(step.properties.b); const result = compare(a, b, step.properties.operator); return branchName(result ? 'true' : 'false'); } }); -function compare(a: number, b: number, operator: string): boolean { +function compare(a: any, b: any, operator: string): boolean { switch (operator) { - case '=': + case '==': + return a == b; + case '===': return a === b; case '!=': + return a != b; + case '!==': return a !== b; case '>': return a > b; diff --git a/demos/webpack-app/src/playground/machine/activities/log-activity.ts b/demos/webpack-app/src/playground/machine/activities/log-activity.ts index 34f36c0..4040d94 100644 --- a/demos/webpack-app/src/playground/machine/activities/log-activity.ts +++ b/demos/webpack-app/src/playground/machine/activities/log-activity.ts @@ -7,7 +7,7 @@ export const logActivity = createAtomActivity({ init: () => ({}), stepType: 'log', handler: async (step: LogStep, { $variables, $dynamics, $logger }: GlobalState) => { - let message = $dynamics.readAny(step.properties.message); + let message = $dynamics.readString(step.properties.message); for (const variable of step.properties.variables.variables) { const value = $variables.isSet(variable.name) ? $variables.read(variable.name) || '' : ''; diff --git a/demos/webpack-app/src/playground/machine/services/dynamics-service.ts b/demos/webpack-app/src/playground/machine/services/dynamics-service.ts index a1b7ce4..6fc98f4 100644 --- a/demos/webpack-app/src/playground/machine/services/dynamics-service.ts +++ b/demos/webpack-app/src/playground/machine/services/dynamics-service.ts @@ -1,6 +1,9 @@ import { Dynamic, + NullableAnyVariable, NullableVariable, + booleanValueModelId, + nullableAnyVariableValueModelId, nullableVariableValueModelId, numberValueModelId, stringValueModelId @@ -10,20 +13,22 @@ import { VariablesService } from './variables-service'; export class DynamicsService { public constructor(private readonly $variables: VariablesService) {} - public readAny(dynamic: Dynamic): TValue { + public readAny(dynamic: Dynamic): TValue { switch (dynamic.modelId) { case stringValueModelId: case numberValueModelId: + case booleanValueModelId: return dynamic.value as TValue; - case nullableVariableValueModelId: { - const variable = dynamic.value as NullableVariable; + case nullableVariableValueModelId: + case nullableAnyVariableValueModelId: { + const variable = dynamic.value as NullableVariable | NullableAnyVariable; if (!variable || !variable.name) { throw new Error('Variable is not set'); } return this.$variables.read(variable.name); } } - throw new Error(`Model is not supported: ${dynamic.modelId}`); + throw new Error(`Dynamic model is not supported: ${dynamic.modelId}`); } public readString(dynamic: Dynamic): string { diff --git a/demos/webpack-app/src/playground/model/if-step-model.ts b/demos/webpack-app/src/playground/model/if-step-model.ts index 48e1059..cf13592 100644 --- a/demos/webpack-app/src/playground/model/if-step-model.ts +++ b/demos/webpack-app/src/playground/model/if-step-model.ts @@ -1,13 +1,14 @@ import { Dynamic, NullableVariable, - ValueKnownType, + booleanValueModel, branchesValueModel, choiceValueModel, createBranchedStepModel, dynamicValueModel, - nullableVariableValueModel, - numberValueModel + nullableAnyVariableValueModel, + numberValueModel, + stringValueModel } from 'sequential-workflow-editor-model'; import { BranchedStep } from 'sequential-workflow-model'; @@ -15,34 +16,35 @@ export interface IfStep extends BranchedStep { type: 'if'; componentType: 'switch'; properties: { - a: Dynamic; + a: Dynamic; operator: string; - b: Dynamic; + b: Dynamic; }; } export const ifStepModel = createBranchedStepModel('if', 'switch', step => { - const val = dynamicValueModel({ + const ab = dynamicValueModel({ models: [ numberValueModel({}), - nullableVariableValueModel({ - isRequired: true, - valueType: ValueKnownType.number + stringValueModel({}), + booleanValueModel({}), + nullableAnyVariableValueModel({ + isRequired: true }) ] }); - step.property('a').value(val).label('A'); + step.property('a').value(ab).label('A'); step.property('operator') .label('Operator') .value( choiceValueModel({ - choices: ['=', '!=', '>', '>=', '<', '<='] + choices: ['==', '===', '!=', '!==', '>', '>=', '<', '<='] }) ); - step.property('b').value(val).label('B'); + step.property('b').value(ab).label('B'); step.branches().value( branchesValueModel({ diff --git a/demos/webpack-app/src/playground/storage.ts b/demos/webpack-app/src/playground/storage.ts index 80b6fdc..0d455c7 100644 --- a/demos/webpack-app/src/playground/storage.ts +++ b/demos/webpack-app/src/playground/storage.ts @@ -1,7 +1,7 @@ import { MyDefinition } from './model/definition-model'; import { RawInputData } from './playground'; -const version = 1; +const version = 2; const definitionKey = `definition_${version}`; const inputDataKey = `inputData_${version}`; diff --git a/editor/package.json b/editor/package.json index 434d9ff..a76df69 100644 --- a/editor/package.json +++ b/editor/package.json @@ -1,6 +1,6 @@ { "name": "sequential-workflow-editor", - "version": "0.3.0", + "version": "0.3.1", "type": "module", "main": "./lib/esm/index.js", "types": "./lib/index.d.ts", @@ -46,11 +46,11 @@ "prettier:fix": "prettier --write ./src ./css" }, "dependencies": { - "sequential-workflow-editor-model": "^0.3.0", + "sequential-workflow-editor-model": "^0.3.1", "sequential-workflow-model": "^0.1.3" }, "peerDependencies": { - "sequential-workflow-editor-model": "^0.3.0", + "sequential-workflow-editor-model": "^0.3.1", "sequential-workflow-model": "^0.1.3" }, "devDependencies": { diff --git a/editor/src/value-editors/boolean/boolean-value-editor.ts b/editor/src/value-editors/boolean/boolean-value-editor.ts new file mode 100644 index 0000000..fe7c4bd --- /dev/null +++ b/editor/src/value-editors/boolean/boolean-value-editor.ts @@ -0,0 +1,38 @@ +import { BooleanValueModel, ValueModelContext } from 'sequential-workflow-editor-model'; +import { ValueEditor } from '../value-editor'; +import { validationErrorComponent } from '../../components/validation-error-component'; +import { valueEditorContainerComponent } from '../../components/value-editor-container-component'; +import { rowComponent } from '../../components/row-component'; +import { selectComponent } from '../../components/select-component'; + +export const booleanValueEditorId = 'boolean'; + +export function booleanValueEditor(context: ValueModelContext): ValueEditor { + function validate() { + validation.setDefaultError(context.validate()); + } + + function onSelected(index: number) { + context.setValue(index === 1); + validate(); + } + + const select = selectComponent({ + stretched: true + }); + select.setValues(['False', 'True']); + select.selectIndex(context.getValue() ? 1 : 0); + select.onSelected.subscribe(onSelected); + + const row = rowComponent([select.view]); + + const validation = validationErrorComponent(); + const container = valueEditorContainerComponent([row.view, validation.view]); + + validate(); + + return { + view: container.view, + validate + }; +} diff --git a/editor/src/value-editors/value-editor-factory-resolver.ts b/editor/src/value-editors/value-editor-factory-resolver.ts index 67f3b6c..c2f6160 100644 --- a/editor/src/value-editors/value-editor-factory-resolver.ts +++ b/editor/src/value-editors/value-editor-factory-resolver.ts @@ -12,9 +12,11 @@ import { dynamicValueEditor, dynamicValueEditorId } from './dynamic/dynamic-valu import { choiceValueEditor, choiceValueEditorId } from './choice/choice-value-editor'; import { branchesValueModelId, sequenceValueModelId } from 'sequential-workflow-editor-model'; import { nullableAnyVariableValueEditor, nullableAnyVariableValueEditorId } from './nullable-any-variable/nullable-any-variable-editor'; +import { booleanValueEditor, booleanValueEditorId } from './boolean/boolean-value-editor'; const editors: { id: string; factory: ValueEditorFactory | null }[] = [ { id: anyVariablesValueEditorId, factory: anyVariablesValueEditor as ValueEditorFactory }, + { id: booleanValueEditorId, factory: booleanValueEditor as ValueEditorFactory }, { id: choiceValueEditorId, factory: choiceValueEditor as ValueEditorFactory }, { id: nullableAnyVariableValueEditorId, factory: nullableAnyVariableValueEditor as ValueEditorFactory }, { id: dynamicValueEditorId, factory: dynamicValueEditor as ValueEditorFactory }, diff --git a/model/package.json b/model/package.json index 0801c50..f7c54e8 100644 --- a/model/package.json +++ b/model/package.json @@ -1,6 +1,6 @@ { "name": "sequential-workflow-editor-model", - "version": "0.3.0", + "version": "0.3.1", "homepage": "https://nocode-js.com/", "author": { "name": "NoCode JS", diff --git a/model/src/test-tools/definition-model-stub.ts b/model/src/test-tools/definition-model-stub.ts new file mode 100644 index 0000000..d41874d --- /dev/null +++ b/model/src/test-tools/definition-model-stub.ts @@ -0,0 +1,26 @@ +import { Path } from '../core'; +import { DefinitionModel } from '../model'; + +export function createDefinitionModelStub(): DefinitionModel { + return { + root: { + properties: [], + sequence: { + dependencies: [], + name: 'stub', + label: 'Stub sequence', + value: { + id: 'stub', + label: 'Stub', + path: Path.create(['sequence']), + configuration: {}, + getDefaultValue: () => [], + getVariableDefinitions: () => null, + validate: () => null + } + } + }, + steps: {}, + valueTypes: [] + }; +} diff --git a/model/src/test-tools/model-activator-stub.ts b/model/src/test-tools/model-activator-stub.ts new file mode 100644 index 0000000..6dd1049 --- /dev/null +++ b/model/src/test-tools/model-activator-stub.ts @@ -0,0 +1,7 @@ +import { ModelActivator } from '../activator'; +import { createDefinitionModelStub } from './definition-model-stub'; + +export function createModelActivatorStub(): ModelActivator { + let index = 0; + return ModelActivator.create(createDefinitionModelStub(), () => `0x${index++}`); +} diff --git a/model/src/value-models/boolean/boolean-value-model-configuration.ts b/model/src/value-models/boolean/boolean-value-model-configuration.ts new file mode 100644 index 0000000..d32e9bd --- /dev/null +++ b/model/src/value-models/boolean/boolean-value-model-configuration.ts @@ -0,0 +1,3 @@ +export interface BooleanValueModelConfiguration { + defaultValue?: boolean; +} diff --git a/model/src/value-models/boolean/boolean-value-model.spec.ts b/model/src/value-models/boolean/boolean-value-model.spec.ts new file mode 100644 index 0000000..0ba864b --- /dev/null +++ b/model/src/value-models/boolean/boolean-value-model.spec.ts @@ -0,0 +1,28 @@ +import { Path } from '../../core'; +import { createModelActivatorStub } from '../../test-tools/model-activator-stub'; +import { booleanValueModel } from './boolean-value-model'; +import { BooleanValueModelConfiguration } from './boolean-value-model-configuration'; + +describe('booleanValueModel', () => { + const modelActivator = createModelActivatorStub(); + + function getModel(configuration: BooleanValueModelConfiguration) { + return booleanValueModel(configuration)(Path.create('test')); + } + + describe('getDefaultValue()', () => { + it('returns false as default', () => { + const value = getModel({}).getDefaultValue(modelActivator); + + expect(value).toBe(false); + }); + + it('returns the configured default value', () => { + const valueTrue = getModel({ defaultValue: true }).getDefaultValue(modelActivator); + expect(valueTrue).toBe(true); + + const valueFalse = getModel({ defaultValue: false }).getDefaultValue(modelActivator); + expect(valueFalse).toBe(false); + }); + }); +}); diff --git a/model/src/value-models/boolean/boolean-value-model.ts b/model/src/value-models/boolean/boolean-value-model.ts new file mode 100644 index 0000000..da905ee --- /dev/null +++ b/model/src/value-models/boolean/boolean-value-model.ts @@ -0,0 +1,24 @@ +import { ValueModel, ValueModelFactory } from '../../model'; +import { Path } from '../../core/path'; +import { BooleanValueModelConfiguration } from './boolean-value-model-configuration'; + +export type BooleanValueModel = ValueModel; + +export const booleanValueModelId = 'boolean'; + +export function booleanValueModel(configuration: BooleanValueModelConfiguration): ValueModelFactory { + return (path: Path) => ({ + id: booleanValueModelId, + label: 'Boolean', + path, + configuration, + getDefaultValue() { + if (configuration.defaultValue !== undefined) { + return configuration.defaultValue; + } + return false; + }, + getVariableDefinitions: () => null, + validate: () => null + }); +} diff --git a/model/src/value-models/boolean/index.ts b/model/src/value-models/boolean/index.ts new file mode 100644 index 0000000..fd9fc10 --- /dev/null +++ b/model/src/value-models/boolean/index.ts @@ -0,0 +1,2 @@ +export * from './boolean-value-model-configuration'; +export * from './boolean-value-model'; diff --git a/model/src/value-models/dynamic/dynamic-value-model.ts b/model/src/value-models/dynamic/dynamic-value-model.ts index 60473fa..8fbbf59 100644 --- a/model/src/value-models/dynamic/dynamic-value-model.ts +++ b/model/src/value-models/dynamic/dynamic-value-model.ts @@ -49,7 +49,8 @@ export function dynamicValueModel m.id === value.modelId); if (!model) { - throw new Error(`Cannot find model id: ${value.modelId}`); + const availableModels = subModels.map(m => m.id).join(', '); + throw new Error(`Cannot find sub model id: ${value.modelId} (available: ${availableModels})`); } const childContext = context.createChildContext(model); return model.validate(childContext); diff --git a/model/src/value-models/index.ts b/model/src/value-models/index.ts index d480df0..160c407 100644 --- a/model/src/value-models/index.ts +++ b/model/src/value-models/index.ts @@ -1,4 +1,5 @@ export * from './any-variables'; +export * from './boolean'; export * from './branches'; export * from './choice'; export * from './dynamic';