Browse Source

Merge branch 'dev' into carlos/505-improve-grapesjs-absolute-mode

carlos/505-improve-grapesjs-absolute-mode
Carlos Rufo 11 months ago
committed by GitHub
parent
commit
81bef19067
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 11
      docs/.vuepress/theme/layouts/StudioSdkBannerSidebar.vue
  2. 8
      docs/.vuepress/theme/layouts/utils.js
  3. 1
      docs/api/canvas.md
  4. 14
      docs/api/commands.md
  5. 3
      docs/api/component.md
  6. 64
      docs/api/editor.md
  7. 30
      docs/api/pages.md
  8. 38
      docs/api/parser.md
  9. 2
      docs/package.json
  10. 5
      packages/cli/src/build.ts
  11. 2
      packages/core/package.json
  12. 40
      packages/core/src/data_sources/model/ComponentDataVariable.ts
  13. 19
      packages/core/src/data_sources/model/DataResolverListener.ts
  14. 2
      packages/core/src/data_sources/model/DataVariable.ts
  15. 124
      packages/core/src/data_sources/model/conditional_variables/ComponentDataCondition.ts
  16. 19
      packages/core/src/data_sources/model/conditional_variables/ConditionalOutputBase.ts
  17. 196
      packages/core/src/data_sources/model/conditional_variables/DataCondition.ts
  18. 15
      packages/core/src/data_sources/model/conditional_variables/DataConditionEvaluator.ts
  19. 2
      packages/core/src/data_sources/model/conditional_variables/constants.ts
  20. 9
      packages/core/src/data_sources/model/data_collection/ComponentDataCollection.ts
  21. 67
      packages/core/src/data_sources/model/utils.ts
  22. 91
      packages/core/src/data_sources/utils.ts
  23. 44
      packages/core/src/data_sources/view/ComponentDataConditionView.ts
  24. 17
      packages/core/src/dom_components/index.ts
  25. 3
      packages/core/src/dom_components/model/Component.ts
  26. 8
      packages/core/src/dom_components/model/ComponentResolverWatcher.ts
  27. 2
      packages/core/src/domain_abstract/model/StyleableModel.ts
  28. 3
      packages/core/src/editor/types.ts
  29. 3
      packages/core/src/parser/types.ts
  30. 60
      packages/core/test/common.ts
  31. 6
      packages/core/test/specs/data_sources/__snapshots__/serialization.ts.snap
  32. 12
      packages/core/test/specs/data_sources/__snapshots__/storage.ts.snap
  33. 9
      packages/core/test/specs/data_sources/jsonplaceholder.ts
  34. 15
      packages/core/test/specs/data_sources/model/ComponentDataVariable.getters-setters.ts
  35. 28
      packages/core/test/specs/data_sources/model/ComponentDataVariable.ts
  36. 147
      packages/core/test/specs/data_sources/model/conditional_variables/ComponentDataCondition.getters-setters.ts
  37. 318
      packages/core/test/specs/data_sources/model/conditional_variables/ComponentDataCondition.ts
  38. 12
      packages/core/test/specs/data_sources/serialization.ts
  39. 8
      packages/core/test/specs/data_sources/storage.ts
  40. 6
      packages/core/test/specs/data_sources/transformers.ts

11
docs/.vuepress/theme/layouts/StudioSdkBannerSidebar.vue

@ -1,22 +1,23 @@
<template>
<a class="banner-sdk-sidebar" :href="getSdkDocsLink()">
<h2 class="banner-sdk-sidebar__title">Supercharge Your GrapesJS Development 🚀</h2>
<img class="banner-sdk-sidebar__image" :src="$withBase('/studio-banner.jpg')" alt="Studio SDK">
<img class="banner-sdk-sidebar__image" :src="$withBase('/studio-banner.jpg')" alt="Studio SDK" />
<p class="banner-sdk-sidebar__content">
Studio SDK is the next-level visual builder with advanced features, custom plugins, and seamless integration. Save time and build faster!
Studio SDK is the next-level visual builder with advanced features, custom plugins, and seamless integration. Save
time and build faster!
</p>
</a>
</template>
<script>
import { getSdkDocsLink } from './utils';
import { getSdkDocsLink } from './utils';
export default {
methods: {
getSdkDocsLink() {
return getSdkDocsLink('sidebar');
}
}
},
},
};
</script>

8
docs/.vuepress/theme/layouts/utils.js

@ -1,7 +1,7 @@
export const getSdkUtmParams = (medium = '') => {
return `utm_source=grapesjs-docs&utm_medium=${medium}`;
}
return `utm_source=grapesjs-docs&utm_medium=${medium}`;
};
export const getSdkDocsLink = (medium = '') => {
return `https://app.grapesjs.com/docs-sdk/overview/getting-started?${getSdkUtmParams(medium)}`;
}
return `https://app.grapesjs.com/docs-sdk/overview/getting-started?${getSdkUtmParams(medium)}`;
};

1
docs/api/canvas.md

@ -257,6 +257,7 @@ Set canvas zoom value
### Parameters
* `value` **[Number][9]** The zoom value, from 0 to 100
* `opts` **SetZoomOptions** (optional, default `{}`)
### Examples

14
docs/api/commands.md

@ -77,6 +77,20 @@ editor.on('command:run:my-command', ({ result, options }) => { ... });
editor.on('command:stop:before:my-command', ({ options }) => { ... });
```
* `command:call` Triggered on run or stop of a command.
```javascript
editor.on('command:call', ({ id, result, options, type }) => {
console.log('Command id', id, 'command result', result, 'call type', type);
});
```
* `command:call:COMMAND-ID` Triggered on run or stop of a specific command.
```javascript
editor.on('command:call:my-command', ({ result, options, type }) => { ... });
```
## Methods
* [add][2]

3
docs/api/component.md

@ -137,6 +137,7 @@ By setting override to specific properties, changes of those properties will be
### Parameters
* `value` **([Boolean][3] | [String][1] | [Array][5]<[String][1]>)**&#x20;
* `options` **DynamicWatchersOptions** (optional, default `{}`)
### Examples
@ -280,7 +281,7 @@ Update attributes of the component
### Parameters
* `attrs` **[Object][2]** Key value attributes
* `opts` **SetAttrOptions** (optional, default `{}`)
* `opts` **SetAttrOptions** (optional, default `{skipWatcherUpdates:false,fromDataSource:false}`)
* `options` **[Object][2]** Options for the model update
### Examples

64
docs/api/editor.md

@ -12,19 +12,65 @@ const editor = grapesjs.init({
```
## Available Events
* `update` Event triggered on any change of the project (eg. component added/removed, style changes, etc.)
You can make use of available events in this way
```javascript
editor.on('update', () => { ... });
```
```js
editor.on('EVENT-NAME', (some, argument) => {
// do something
})
* `undo` Undo executed.
```javascript
editor.on('undo', () => { ... });
```
* `redo` Redo executed.
```javascript
editor.on('redo', () => { ... });
```
* `load` Editor is loaded. At this stage, the project is loaded in the editor and elements in the canvas are rendered.
```javascript
editor.on('load', () => { ... });
```
* `project:load` Project JSON loaded in the editor. The event is triggered on the initial load and on the `editor.loadProjectData` method.
```javascript
editor.on('project:load', ({ project, initial }) => { ... });
```
* `project:get` Event triggered on request of the project data. This can be used to extend the project with custom data.
```javascript
editor.on('project:get', ({ project }) => { project.myCustomKey = 'value' });
```
* `log` Log message triggered.
```javascript
editor.on('log', (msg, opts) => { ... });
```
* `telemetry:init` Initial telemetry data are sent.
```javascript
editor.on('telemetry:init', () => { ... });
```
* `update` - The structure of the template is updated (its HTML/CSS)
* `undo` - Undo executed
* `redo` - Redo executed
* `load` - Editor is loaded
* `destroy` Editor started destroy (on `editor.destroy()`).
```javascript
editor.on('destroy', () => { ... });
```
* `destroyed` Editor destroyed.
```javascript
editor.on('destroyed', () => { ... });
```
### Components

30
docs/api/pages.md

@ -113,6 +113,32 @@ pageManager.remove(somePage);
Returns **[Page]** Removed Page
## move
Move a page to a specific index in the pages collection.
If the index is out of bounds, the page will not be moved.
### Parameters
* `page` **([string][3] | [Page])** Page or page id to move.
* `opts` **[Object][2]?** Move options (optional, default `{}`)
* `opts.at` **[number][4]?** The target index where the page should be moved.
### Examples
```javascript
// Move a page to index 2
const movedPage = pageManager.move('page-id', { at: 2 });
if (movedPage) {
console.log('Page moved successfully:', movedPage);
} else {
console.log('Page could not be moved.');
}
```
Returns **(Page | [undefined][5])** The moved page, or `undefined` if the page does not exist or the index is out of bounds.
## get
Get page by id
@ -192,3 +218,7 @@ Returns **[Page]**&#x20;
[2]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object
[3]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String
[4]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number
[5]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined

38
docs/api/parser.md

@ -19,9 +19,43 @@ const { Parser } = editor;
```
## Available Events
* `parse:html` On HTML parse, an object containing the input and the output of the parser is passed as an argument.
* `parse:html` - On HTML parse, an object containing the input and the output of the parser is passed as an argument
* `parse:css` - On CSS parse, an object containing the input and the output of the parser is passed as an argument
```javascript
editor.on('parse:html', ({ input, output }) => { ... });
```
* `parse:html:before` Event triggered before the HTML parsing starts. An object containing the input is passed as an argument.
```javascript
editor.on('parse:html:before', (options) => {
console.log('Parser input', options.input);
// You can also process the input and update `options.input`
options.input += '<div>Extra content</div>';
});
```
* `parse:css` On CSS parse, an object containing the input and the output of the parser is passed as an argument.
```javascript
editor.on('parse:css', ({ input, output }) => { ... });
```
* `parse:css:before` Event triggered before the CSS parsing starts. An object containing the input is passed as an argument.
```javascript
editor.on('parse:css:before', (options) => {
console.log('Parser input', options.input);
// You can also process the input and update `options.input`
options.input += '.my-class { color: red; }';
});
```
* `parse` Catch-all event for all the events mentioned above. An object containing all the available data about the triggered event is passed as an argument to the callback.
```javascript
editor.on('parse', ({ event, ... }) => { ... });
```
## Methods

2
docs/package.json

@ -2,7 +2,7 @@
"name": "@grapesjs/docs",
"private": true,
"description": "Free and Open Source Web Builder Framework",
"version": "0.22.4",
"version": "0.22.5",
"license": "BSD-3-Clause",
"homepage": "http://grapesjs.com",
"files": [

5
packages/cli/src/build.ts

@ -52,10 +52,15 @@ export const buildLocale = async (opts: BuildOptions = {}) => {
const babelOpts = { ...babelConfig(buildWebpackArgs(opts) as any) };
fs.readdirSync(localDst).forEach((file) => {
const filePath = `${localDst}/${file}`;
const esModuleFileName = filePath.replace(/\.[^.]+$/, '.mjs');
fs.copyFileSync(filePath, esModuleFileName);
const compiled = transformFileSync(filePath, babelOpts).code;
fs.writeFileSync(filePath, compiled);
});
// Remove the index.mjs as it is useless
fs.unlinkSync(`${localDst}/index.mjs`);
printRow('Locale files building completed successfully!');
};

2
packages/core/package.json

@ -1,7 +1,7 @@
{
"name": "grapesjs",
"description": "Free and Open Source Web Builder Framework",
"version": "0.22.6-rc.0",
"version": "0.22.6",
"author": "Artur Arseniev",
"license": "BSD-3-Clause",
"homepage": "http://grapesjs.com",

40
packages/core/src/data_sources/model/ComponentDataVariable.ts

@ -1,8 +1,14 @@
import { ObjectAny } from '../../common';
import Component from '../../dom_components/model/Component';
import { ComponentOptions } from '../../dom_components/model/types';
import { ComponentDefinition, ComponentOptions, ComponentProperties } from '../../dom_components/model/types';
import { toLowerCase } from '../../utils/mixins';
import DataVariable, { DataVariableProps, DataVariableType } from './DataVariable';
export interface ComponentDataVariableProps extends ComponentProperties {
type: typeof DataVariableType;
dataResolver: DataVariableProps;
}
export default class ComponentDataVariable extends Component {
dataResolver: DataVariable;
@ -10,17 +16,20 @@ export default class ComponentDataVariable extends Component {
return {
// @ts-ignore
...super.defaults,
type: DataVariableType,
path: '',
defaultValue: '',
droppable: false,
type: DataVariableType,
dataResolver: {
path: '',
defaultValue: '',
},
};
}
constructor(props: DataVariableProps, opt: ComponentOptions) {
constructor(props: ComponentDataVariableProps, opt: ComponentOptions) {
super(props, opt);
const { type, path, defaultValue } = props;
this.dataResolver = new DataVariable({ type, path, defaultValue }, opt);
this.dataResolver = new DataVariable(props.dataResolver, opt);
this.listenToPropsChange();
}
getPath() {
@ -47,6 +56,23 @@ export default class ComponentDataVariable extends Component {
this.dataResolver.set('defaultValue', newValue);
}
private listenToPropsChange() {
this.on('change:dataResolver', () => {
this.dataResolver.set(this.get('dataResolver'));
});
}
toJSON(opts?: ObjectAny): ComponentDefinition {
const json = super.toJSON(opts);
const dataResolver = this.dataResolver.toJSON();
delete dataResolver.type;
return {
...json,
dataResolver,
};
}
static isComponent(el: HTMLElement) {
return toLowerCase(el.tagName) === DataVariableType;
}

19
packages/core/src/data_sources/model/DataResolverListener.ts

@ -4,7 +4,11 @@ import { Model } from '../../common';
import EditorModel from '../../editor/model/Editor';
import DataVariable, { DataVariableType } from './DataVariable';
import { DataResolver } from '../types';
import { DataCondition, DataConditionType } from './conditional_variables/DataCondition';
import {
DataCondition,
DataConditionOutputChangedEvent,
DataConditionType,
} from './conditional_variables/DataCondition';
import { DataCollectionVariableType } from './data_collection/constants';
import DataCollectionVariable from './data_collection/DataCollectionVariable';
@ -64,12 +68,13 @@ export default class DataResolverListener {
}
private listenToConditionalVariable(dataVariable: DataCondition): ListenerWithCallback[] {
const { em } = this;
const dataListeners = dataVariable.getDependentDataVariables().flatMap((dataVariable) => {
return this.listenToDataVariable(new DataVariable(dataVariable, { em }));
});
return dataListeners;
return [
{
obj: dataVariable,
event: DataConditionOutputChangedEvent,
callback: this.onChange,
},
];
}
private listenToDataVariable(dataVariable: DataVariable): ListenerWithCallback[] {

2
packages/core/src/data_sources/model/DataVariable.ts

@ -4,7 +4,7 @@ import EditorModel from '../../editor/model/Editor';
export const DataVariableType = 'data-variable' as const;
export interface DataVariableProps {
type: typeof DataVariableType;
type?: typeof DataVariableType;
path: string;
defaultValue?: string;
}

124
packages/core/src/data_sources/model/conditional_variables/ComponentDataCondition.ts

@ -1,61 +1,123 @@
import Component from '../../../dom_components/model/Component';
import { ComponentDefinition, ComponentOptions } from '../../../dom_components/model/types';
import {
ComponentDefinition as ComponentProperties,
ComponentDefinitionDefined,
ComponentOptions,
ToHTMLOptions,
ComponentAddType,
} from '../../../dom_components/model/types';
import { toLowerCase } from '../../../utils/mixins';
import { DataCondition, DataConditionProps, DataConditionType } from './DataCondition';
import { DataCondition, DataConditionOutputChangedEvent, DataConditionProps, DataConditionType } from './DataCondition';
import { ConditionProps } from './DataConditionEvaluator';
import { StringOperation } from './operators/StringOperator';
import { ObjectAny } from '../../../common';
import { DataConditionIfTrueType, DataConditionIfFalseType } from './constants';
export type DataConditionDisplayType = typeof DataConditionIfTrueType | typeof DataConditionIfFalseType;
export interface ComponentDataConditionProps extends ComponentProperties {
type: typeof DataConditionType;
dataResolver: DataConditionProps;
}
export default class ComponentDataCondition extends Component {
dataResolver: DataCondition;
constructor(props: DataConditionProps, opt: ComponentOptions) {
const dataConditionInstance = new DataCondition(props, { em: opt.em });
super(
{
...props,
type: DataConditionType,
components: dataConditionInstance.getDataValue(),
droppable: false,
get defaults(): ComponentDefinitionDefined {
return {
// @ts-ignore
...super.defaults,
droppable: false,
type: DataConditionType,
dataResolver: {
condition: {
left: '',
operator: StringOperation.equalsIgnoreCase,
right: '',
},
},
opt,
);
this.dataResolver = dataConditionInstance;
this.dataResolver.onValueChange = this.handleConditionChange.bind(this);
components: [
{
type: DataConditionIfTrueType,
},
{
type: DataConditionIfFalseType,
},
],
};
}
getCondition() {
return this.dataResolver.getCondition();
constructor(props: ComponentDataConditionProps, opt: ComponentOptions) {
// @ts-ignore
super(props, opt);
const { condition } = props.dataResolver;
this.dataResolver = new DataCondition({ condition }, { em: opt.em });
this.listenToPropsChange();
}
getIfTrue() {
return this.dataResolver.getIfTrue();
isTrue() {
return this.dataResolver.isTrue();
}
getIfFalse() {
return this.dataResolver.getIfFalse();
getCondition() {
return this.dataResolver.getCondition();
}
private handleConditionChange() {
this.components(this.dataResolver.getDataValue());
getIfTrueContent(): Component | undefined {
return this.components().at(0);
}
static isComponent(el: HTMLElement) {
return toLowerCase(el.tagName) === DataConditionType;
getIfFalseContent(): Component | undefined {
return this.components().at(1);
}
getOutputContent(): Component | undefined {
return this.isTrue() ? this.getIfTrueContent() : this.getIfFalseContent();
}
setCondition(newCondition: ConditionProps) {
this.dataResolver.setCondition(newCondition);
}
setIfTrue(newIfTrue: any) {
this.dataResolver.setIfTrue(newIfTrue);
setIfTrueComponents(content: ComponentAddType) {
this.setComponentsAtIndex(0, content);
}
setIfFalseComponents(content: ComponentAddType) {
this.setComponentsAtIndex(1, content);
}
getInnerHTML(opts?: ToHTMLOptions): string {
return this.getOutputContent()?.getInnerHTML(opts) ?? '';
}
private setComponentsAtIndex(index: number, newContent: ComponentAddType) {
const component = this.components().at(index);
component?.components(newContent);
}
private listenToPropsChange() {
this.on('change:dataResolver', () => {
this.dataResolver.set(this.get('dataResolver'));
});
}
setIfFalse(newIfFalse: any) {
this.dataResolver.setIfFalse(newIfFalse);
toJSON(opts?: ObjectAny): ComponentProperties {
const json = super.toJSON(opts);
const dataResolver = this.dataResolver.toJSON();
delete dataResolver.type;
delete dataResolver.ifTrue;
delete dataResolver.ifFalse;
return {
...json,
dataResolver,
};
}
toJSON(): ComponentDefinition {
return this.dataResolver.toJSON();
static isComponent(el: HTMLElement) {
return toLowerCase(el.tagName) === DataConditionType;
}
}

19
packages/core/src/data_sources/model/conditional_variables/ConditionalOutputBase.ts

@ -0,0 +1,19 @@
import Component from '../../../dom_components/model/Component';
import { ComponentDefinitionDefined, ToHTMLOptions } from '../../../dom_components/model/types';
import { toLowerCase } from '../../../utils/mixins';
import { isDataConditionDisplayType } from '../../utils';
export default class ConditionalOutputBase extends Component {
get defaults(): ComponentDefinitionDefined {
return {
// @ts-ignore
...super.defaults,
removable: false,
draggable: false,
};
}
static isComponent(el: HTMLElement) {
return isDataConditionDisplayType(toLowerCase(el.tagName));
}
}

196
packages/core/src/data_sources/model/conditional_variables/DataCondition.ts

@ -2,7 +2,7 @@ import { Model } from '../../../common';
import EditorModel from '../../../editor/model/Editor';
import DataVariable, { DataVariableProps } from '../DataVariable';
import DataResolverListener from '../DataResolverListener';
import { evaluateVariable, isDataVariable } from '../utils';
import { resolveDynamicValue, isDataVariable } from '../../utils';
import { DataConditionEvaluator, ConditionProps } from './DataConditionEvaluator';
import { AnyTypeOperation } from './operators/AnyTypeOperator';
import { BooleanOperation } from './operators/BooleanOperator';
@ -10,12 +10,14 @@ import { NumberOperation } from './operators/NumberOperator';
import { StringOperation } from './operators/StringOperator';
import { isUndefined } from 'underscore';
export const DataConditionType = 'data-condition';
export const DataConditionType = 'data-condition' as const;
export const DataConditionEvaluationChangedEvent = 'data-condition-evaluation-changed';
export const DataConditionOutputChangedEvent = 'data-condition-output-changed';
export interface ExpressionProps {
left: any;
operator: AnyTypeOperation | StringOperation | NumberOperation;
right: any;
left?: any;
operator?: AnyTypeOperation | StringOperation | NumberOperation;
right?: any;
}
export interface LogicGroupProps {
@ -24,129 +26,159 @@ export interface LogicGroupProps {
}
export interface DataConditionProps {
type: typeof DataConditionType;
type?: typeof DataConditionType;
condition: ConditionProps;
ifTrue: any;
ifFalse: any;
ifTrue?: any;
ifFalse?: any;
}
interface DataConditionPropsDefined extends Omit<DataConditionProps, 'condition'> {
condition: DataConditionEvaluator;
}
export class DataCondition extends Model<DataConditionPropsDefined> {
export class DataCondition extends Model<DataConditionProps> {
private em: EditorModel;
private resolverListeners: DataResolverListener[] = [];
private _onValueChange?: () => void;
constructor(
props: {
condition: ConditionProps;
ifTrue: any;
ifFalse: any;
},
opts: { em: EditorModel; onValueChange?: () => void },
) {
private _previousEvaluationResult: boolean | null = null;
private _conditionEvaluator: DataConditionEvaluator;
defaults() {
return {
type: DataConditionType,
condition: {
left: '',
operator: StringOperation.equalsIgnoreCase,
right: '',
},
ifTrue: {},
ifFalse: {},
};
}
constructor(props: DataConditionProps, opts: { em: EditorModel; onValueChange?: () => void }) {
if (isUndefined(props.condition)) {
opts.em.logError('No condition was provided to a conditional component.');
}
const conditionInstance = new DataConditionEvaluator({ condition: props.condition }, { em: opts.em });
super({
type: DataConditionType,
...props,
condition: conditionInstance,
});
// @ts-ignore
super(props, opts);
this.em = opts.em;
this.listenToDataVariables();
this._onValueChange = opts.onValueChange;
this.on('change:condition change:ifTrue change:ifFalse', () => {
this.listenToDataVariables();
this._onValueChange?.();
});
}
private get conditionEvaluator() {
return this.get('condition')!;
const { condition = {} } = props;
const instance = new DataConditionEvaluator({ condition }, { em: this.em });
this._conditionEvaluator = instance;
this.listenToDataVariables();
this.listenToPropsChange();
}
getCondition(): ConditionProps {
return this.get('condition')?.get('condition')!;
return this._conditionEvaluator.get('condition')!;
}
getIfTrue() {
return this.get('ifTrue')!;
return this.get('ifTrue');
}
getIfFalse() {
return this.get('ifFalse')!;
return this.get('ifFalse');
}
setCondition(condition: ConditionProps) {
this._conditionEvaluator.set('condition', condition);
this.trigger(DataConditionOutputChangedEvent, this.getDataValue());
}
setIfTrue(newIfTrue: any) {
this.set('ifTrue', newIfTrue);
}
setIfFalse(newIfFalse: any) {
this.set('ifFalse', newIfFalse);
}
isTrue(): boolean {
return this.conditionEvaluator.evaluate();
return this._conditionEvaluator.evaluate();
}
getDataValue(skipDynamicValueResolution: boolean = false): any {
const ifTrue = this.get('ifTrue');
const ifFalse = this.get('ifFalse');
const ifTrue = this.getIfTrue();
const ifFalse = this.getIfFalse();
const isConditionTrue = this.isTrue();
if (skipDynamicValueResolution) {
return isConditionTrue ? ifTrue : ifFalse;
}
return isConditionTrue ? evaluateVariable(ifTrue, this.em) : evaluateVariable(ifFalse, this.em);
return isConditionTrue ? resolveDynamicValue(ifTrue, this.em) : resolveDynamicValue(ifFalse, this.em);
}
set onValueChange(newFunction: () => void) {
this._onValueChange = newFunction;
private listenToPropsChange() {
this.on('change:condition', this.handleConditionChange.bind(this));
this.on('change:condition change:ifTrue change:ifFalse', () => {
this.listenToDataVariables();
});
}
setCondition(newCondition: ConditionProps) {
const newConditionInstance = new DataConditionEvaluator({ condition: newCondition }, { em: this.em });
this.set('condition', newConditionInstance);
private handleConditionChange() {
this.setCondition(this.get('condition')!);
}
setIfTrue(newIfTrue: any) {
this.set('ifTrue', newIfTrue);
}
private listenToDataVariables() {
// Clear previous listeners to avoid memory leaks
this.cleanupListeners();
setIfFalse(newIfFalse: any) {
this.set('ifFalse', newIfFalse);
this.setupConditionDataVariableListeners();
this.setupOutputDataVariableListeners();
}
private listenToDataVariables() {
const { em } = this;
if (!em) return;
private setupConditionDataVariableListeners() {
this._conditionEvaluator.getDependentDataVariables().forEach((variable) => {
this.addListener(variable, () => {
this.emitConditionEvaluationChange();
});
});
}
// Clear previous listeners to avoid memory leaks
this.cleanupListeners();
private setupOutputDataVariableListeners() {
const isConditionTrue = this.isTrue();
const dataVariables = this.getDependentDataVariables();
this.setupOutputVariableListener(this.getIfTrue(), isConditionTrue);
this.setupOutputVariableListener(this.getIfFalse(), !isConditionTrue);
}
dataVariables.forEach((variable) => {
const listener = new DataResolverListener({
em,
resolver: new DataVariable(variable, { em: this.em }),
onUpdate: (() => {
this._onValueChange?.();
}).bind(this),
/**
* Sets up a listener for an output variable (ifTrue or ifFalse).
* @param outputVariable - The output variable to listen to.
* @param isConditionTrue - Whether the condition is currently true.
*/
private setupOutputVariableListener(outputVariable: any, isConditionTrue: boolean) {
if (isDataVariable(outputVariable)) {
this.addListener(outputVariable, () => {
if (isConditionTrue) {
this.trigger(DataConditionOutputChangedEvent, outputVariable);
}
});
}
}
this.resolverListeners.push(listener);
private addListener(variable: DataVariableProps, onUpdate: () => void) {
const listener = new DataResolverListener({
em: this.em,
resolver: new DataVariable(variable, { em: this.em }),
onUpdate,
});
this.resolverListeners.push(listener);
}
getDependentDataVariables() {
const dataVariables: DataVariableProps[] = this.conditionEvaluator.getDependentDataVariables();
const ifTrue = this.get('ifTrue');
const ifFalse = this.get('ifFalse');
if (isDataVariable(ifTrue)) dataVariables.push(ifTrue);
if (isDataVariable(ifFalse)) dataVariables.push(ifFalse);
private emitConditionEvaluationChange() {
const currentEvaluationResult = this.isTrue();
if (this._previousEvaluationResult !== currentEvaluationResult) {
this._previousEvaluationResult = currentEvaluationResult;
this.trigger(DataConditionEvaluationChangedEvent, currentEvaluationResult);
this.emitOutputValueChange();
}
}
return dataVariables;
private emitOutputValueChange() {
const currentOutputValue = this.getDataValue();
this.trigger(DataConditionOutputChangedEvent, currentOutputValue);
}
private cleanupListeners() {
@ -154,13 +186,13 @@ export class DataCondition extends Model<DataConditionPropsDefined> {
this.resolverListeners = [];
}
toJSON() {
const ifTrue = this.get('ifTrue');
const ifFalse = this.get('ifFalse');
toJSON(): DataConditionProps {
const ifTrue = this.getIfTrue();
const ifFalse = this.getIfFalse();
return {
type: DataConditionType,
condition: this.conditionEvaluator,
condition: this._conditionEvaluator.toJSON(),
ifTrue,
ifFalse,
};

15
packages/core/src/data_sources/model/conditional_variables/DataConditionEvaluator.ts

@ -1,6 +1,6 @@
import { DataVariableProps } from '../DataVariable';
import EditorModel from '../../../editor/model/Editor';
import { evaluateVariable, isDataVariable } from '../utils';
import { resolveDynamicValue, isDataVariable } from '../../utils';
import { ExpressionProps, LogicGroupProps } from './DataCondition';
import { LogicalGroupEvaluator } from './LogicalGroupEvaluator';
import { Operator } from './operators/BaseOperator';
@ -39,9 +39,10 @@ export class DataConditionEvaluator extends Model<DataConditionEvaluatorProps> {
if (this.isExpression(condition)) {
const { left, operator, right } = condition;
const evaluateLeft = evaluateVariable(left, this.em);
const evaluateRight = evaluateVariable(right, this.em);
const evaluateLeft = resolveDynamicValue(left, this.em);
const evaluateRight = resolveDynamicValue(right, this.em);
const op = this.getOperator(evaluateLeft, operator);
if (!op) return false;
const evaluated = op.evaluate(evaluateLeft, evaluateRight);
return evaluated;
@ -54,7 +55,7 @@ export class DataConditionEvaluator extends Model<DataConditionEvaluatorProps> {
/**
* Factory method for creating operators based on the data type.
*/
private getOperator(left: any, operator: string): Operator<DataConditionOperation> {
private getOperator(left: any, operator: string | undefined): Operator<DataConditionOperation> | undefined {
const em = this.em;
if (this.isOperatorInEnum(operator, AnyTypeOperation)) {
@ -64,7 +65,9 @@ export class DataConditionEvaluator extends Model<DataConditionEvaluatorProps> {
} else if (typeof left === 'string') {
return new StringOperator(operator as StringOperation, { em });
}
throw new Error(`Unsupported data type: ${typeof left}`);
this.em?.logError(`Unsupported data type: ${typeof left}`);
return;
}
getDependentDataVariables(): DataVariableProps[] {
@ -95,7 +98,7 @@ export class DataConditionEvaluator extends Model<DataConditionEvaluatorProps> {
return condition && typeof condition.left !== 'undefined' && typeof condition.operator === 'string';
}
private isOperatorInEnum(operator: string, enumObject: any): boolean {
private isOperatorInEnum(operator: string | undefined, enumObject: any): boolean {
return Object.values(enumObject).includes(operator);
}

2
packages/core/src/data_sources/model/conditional_variables/constants.ts

@ -0,0 +1,2 @@
export const DataConditionIfTrueType = 'data-condition-true-content';
export const DataConditionIfFalseType = 'data-condition-false-content';

9
packages/core/src/data_sources/model/data_collection/ComponentDataCollection.ts

@ -7,7 +7,7 @@ import { isObject, serialize, toLowerCase } from '../../../utils/mixins';
import DataResolverListener from '../DataResolverListener';
import DataSource from '../DataSource';
import DataVariable, { DataVariableProps, DataVariableType } from '../DataVariable';
import { isDataVariable } from '../utils';
import { ensureComponentInstance, isDataVariable } from '../../utils';
import { DataCollectionType, keyCollectionDefinition, keyCollectionsStateMap, keyIsCollectionItem } from './constants';
import {
ComponentDataCollectionProps,
@ -200,12 +200,9 @@ export default class ComponentDataCollection extends Component {
};
if (isFirstItem) {
const componentType = (componentDef?.type as string) || 'default';
let type = this.em.Components.getType(componentType) || this.em.Components.getType('default');
const Model = type.model;
symbolMain = new Model(
symbolMain = ensureComponentInstance(
{
...serialize(componentDef),
...componentDef,
draggable: false,
removable: false,
},

67
packages/core/src/data_sources/model/utils.ts

@ -1,67 +0,0 @@
import EditorModel from '../../editor/model/Editor';
import { DataResolver, DataResolverProps } from '../types';
import { DataCollectionStateMap } from './data_collection/types';
import DataCollectionVariable from './data_collection/DataCollectionVariable';
import { DataCollectionVariableType } from './data_collection/constants';
import { DataConditionType, DataCondition } from './conditional_variables/DataCondition';
import DataVariable, { DataVariableProps, DataVariableType } from './DataVariable';
export function isDataResolverProps(value: any): value is DataResolverProps {
return (
typeof value === 'object' && [DataVariableType, DataConditionType, DataCollectionVariableType].includes(value?.type)
);
}
export function isDataResolver(value: any): value is DataResolver {
return value instanceof DataVariable || value instanceof DataCondition;
}
export function isDataVariable(variable: any): variable is DataVariableProps {
return variable?.type === DataVariableType;
}
export function isDataCondition(variable: any) {
return variable?.type === DataConditionType;
}
export function evaluateVariable(variable: any, em: EditorModel) {
return isDataVariable(variable) ? new DataVariable(variable, { em }).getDataValue() : variable;
}
export function getDataResolverInstance(
resolverProps: DataResolverProps,
options: { em: EditorModel; collectionsStateMap?: DataCollectionStateMap },
): DataResolver {
const { type } = resolverProps;
let resolver: DataResolver;
switch (type) {
case DataVariableType:
resolver = new DataVariable(resolverProps, options);
break;
case DataConditionType: {
resolver = new DataCondition(resolverProps, options);
break;
}
case DataCollectionVariableType: {
resolver = new DataCollectionVariable(resolverProps, options);
break;
}
default:
throw new Error(`Unsupported dynamic type: ${type}`);
}
return resolver;
}
export function getDataResolverInstanceValue(
resolverProps: DataResolverProps,
options: {
em: EditorModel;
collectionsStateMap?: DataCollectionStateMap;
},
) {
const resolver = getDataResolverInstance(resolverProps, options);
return resolver.getDataValue();
}

91
packages/core/src/data_sources/utils.ts

@ -0,0 +1,91 @@
import EditorModel from '../editor/model/Editor';
import { DataResolver, DataResolverProps } from './types';
import { DataConditionDisplayType } from './model/conditional_variables/ComponentDataCondition';
import { DataCollectionStateMap } from './model/data_collection/types';
import DataCollectionVariable from './model/data_collection/DataCollectionVariable';
import { DataCollectionVariableType } from './model/data_collection/constants';
import { DataConditionType, DataCondition } from './model/conditional_variables/DataCondition';
import DataVariable, { DataVariableProps, DataVariableType } from './model/DataVariable';
import Component from '../dom_components/model/Component';
import { ComponentDefinition, ComponentOptions } from '../dom_components/model/types';
import { serialize } from '../utils/mixins';
import { DataConditionIfFalseType, DataConditionIfTrueType } from './model/conditional_variables/constants';
export function isDataResolverProps(value: any): value is DataResolverProps {
return (
typeof value === 'object' && [DataVariableType, DataConditionType, DataCollectionVariableType].includes(value?.type)
);
}
export function isDataResolver(value: any): value is DataResolver {
return value instanceof DataVariable || value instanceof DataCondition;
}
export function isDataVariable(variable: any): variable is DataVariableProps {
return variable?.type === DataVariableType;
}
export function isDataCondition(variable: any) {
return variable?.type === DataConditionType;
}
export function resolveDynamicValue(variable: any, em: EditorModel) {
return isDataResolverProps(variable) ? getDataResolverInstanceValue(variable, { em }) : variable;
}
export function getDataResolverInstance(
resolverProps: DataResolverProps,
options: { em: EditorModel; collectionsStateMap?: DataCollectionStateMap },
) {
const { type } = resolverProps;
let resolver: DataResolver;
switch (type) {
case DataVariableType:
resolver = new DataVariable(resolverProps, options);
break;
case DataConditionType: {
resolver = new DataCondition(resolverProps, options);
break;
}
case DataCollectionVariableType: {
resolver = new DataCollectionVariable(resolverProps, options);
break;
}
default:
options.em?.logError(`Unsupported dynamic type: ${type}`);
return;
}
return resolver;
}
export function getDataResolverInstanceValue(
resolverProps: DataResolverProps,
options: {
em: EditorModel;
collectionsStateMap?: DataCollectionStateMap;
},
) {
const resolver = getDataResolverInstance(resolverProps, options);
return resolver?.getDataValue();
}
export const ensureComponentInstance = (
cmp: Component | ComponentDefinition | undefined,
opt: ComponentOptions,
): Component => {
if (cmp instanceof Component) return cmp;
const componentType = (cmp?.type as string) ?? 'default';
const defaultModel = opt.em.Components.getType('default');
const type = opt.em.Components.getType(componentType) ?? defaultModel;
const Model = type.model;
return new Model(serialize(cmp ?? {}), opt);
};
export const isDataConditionDisplayType = (type: string | undefined): type is DataConditionDisplayType => {
return !!type && [DataConditionIfTrueType, DataConditionIfFalseType].includes(type);
};

44
packages/core/src/data_sources/view/ComponentDataConditionView.ts

@ -1,4 +1,46 @@
import ComponentView from '../../dom_components/view/ComponentView';
import ComponentDataCondition from '../model/conditional_variables/ComponentDataCondition';
import DataResolverListener from '../model/DataResolverListener';
export default class ComponentDataConditionView extends ComponentView<ComponentDataCondition> {}
export default class ComponentDataConditionView extends ComponentView<ComponentDataCondition> {
dataResolverListener!: DataResolverListener;
initialize(opt = {}) {
super.initialize(opt);
this.postRender = this.postRender.bind(this);
this.listenTo(this.model.components(), 'reset', this.postRender);
this.dataResolverListener = new DataResolverListener({
em: this.em,
resolver: this.model.dataResolver,
onUpdate: this.postRender,
});
}
renderDataResolver() {
const componentTrue = this.model.getIfTrueContent();
const componentFalse = this.model.getIfFalseContent();
const elTrue = componentTrue?.getEl();
const elFalse = componentFalse?.getEl();
const isTrue = this.model.isTrue();
if (elTrue) {
elTrue.style.display = isTrue ? '' : 'none';
}
if (elFalse) {
elFalse.style.display = isTrue ? 'none' : '';
}
}
postRender() {
this.renderDataResolver();
super.postRender();
}
remove() {
this.stopListening(this.model.components(), 'reset', this.postRender);
this.dataResolverListener.destroy();
return super.remove();
}
}

17
packages/core/src/dom_components/index.ts

@ -126,13 +126,18 @@ import ComponentDataVariable from '../data_sources/model/ComponentDataVariable';
import ComponentDataVariableView from '../data_sources/view/ComponentDataVariableView';
import { DataVariableType } from '../data_sources/model/DataVariable';
import { DataConditionType } from '../data_sources/model/conditional_variables/DataCondition';
import ComponentDataCondition from '../data_sources/model/conditional_variables/ComponentDataCondition';
import ComponentDataConditionView from '../data_sources/view/ComponentDataConditionView';
import ComponentDataCollection from '../data_sources/model/data_collection/ComponentDataCollection';
import { DataCollectionType, DataCollectionVariableType } from '../data_sources/model/data_collection/constants';
import ComponentDataCollectionVariable from '../data_sources/model/data_collection/ComponentDataCollectionVariable';
import ComponentDataCollectionVariableView from '../data_sources/view/ComponentDataCollectionVariableView';
import ComponentDataCollectionView from '../data_sources/view/ComponentDataCollectionView';
import ComponentDataCondition from '../data_sources/model/conditional_variables/ComponentDataCondition';
import {
DataConditionIfFalseType,
DataConditionIfTrueType,
} from '../data_sources/model/conditional_variables/constants';
import ConditionalOutputBase from '../data_sources/model/conditional_variables/ConditionalOutputBase';
export type ComponentEvent =
| 'component:create'
@ -198,6 +203,16 @@ export interface CanMoveResult {
export default class ComponentManager extends ItemManagerModule<DomComponentsConfig, any> {
componentTypes: ComponentStackItem[] = [
{
id: DataConditionIfTrueType,
model: ConditionalOutputBase,
view: ComponentView,
},
{
id: DataConditionIfFalseType,
model: ConditionalOutputBase,
view: ComponentView,
},
{
id: DataCollectionVariableType,
model: ComponentDataCollectionVariable,

3
packages/core/src/dom_components/model/Component.ts

@ -1378,8 +1378,9 @@ export default class Component extends StyleableModel<ComponentProperties> {
cloned.set(keySymbol, 0);
cloned.set(keySymbols, 0);
} else if (symbol) {
const mainSymbolInstances = getSymbolInstances(symbol) ?? [];
// Contains already a reference to a symbol
symbol.set(keySymbols, [...getSymbolInstances(symbol)!, cloned]);
symbol.set(keySymbols, [...mainSymbolInstances!, cloned]);
initSymbol(cloned);
} else if (opt.symbol) {
// Request to create a symbol

8
packages/core/src/dom_components/model/ComponentResolverWatcher.ts

@ -2,11 +2,7 @@ import { ObjectAny } from '../../common';
import { DataCollectionVariableType } from '../../data_sources/model/data_collection/constants';
import { DataCollectionStateMap } from '../../data_sources/model/data_collection/types';
import DataResolverListener from '../../data_sources/model/DataResolverListener';
import {
getDataResolverInstance,
getDataResolverInstanceValue,
isDataResolverProps,
} from '../../data_sources/model/utils';
import { getDataResolverInstance, getDataResolverInstanceValue, isDataResolverProps } from '../../data_sources/utils';
import EditorModel from '../../editor/model/Editor';
import { DataResolverProps } from '../../data_sources/types';
import Component from './Component';
@ -94,7 +90,7 @@ export class ComponentResolverWatcher {
continue;
}
const resolver = getDataResolverInstance(resolverProps, { em, collectionsStateMap });
const resolver = getDataResolverInstance(resolverProps, { em, collectionsStateMap })!;
this.resolverListeners[key] = new DataResolverListener({
em,
resolver,

2
packages/core/src/domain_abstract/model/StyleableModel.ts

@ -15,7 +15,7 @@ import {
getDataResolverInstanceValue,
isDataResolver,
isDataResolverProps,
} from '../../data_sources/model/utils';
} from '../../data_sources/utils';
import { DataResolver } from '../../data_sources/types';
export type StyleProps = Record<string, string | string[] | DataVariableProps | DataConditionProps>;

3
packages/core/src/editor/types.ts

@ -71,3 +71,6 @@ export enum EditorEvents {
destroyed = 'destroyed',
}
/**{END_EVENTS}*/
// need this to avoid the TS documentation generator to break
export default EditorEvents;

3
packages/core/src/parser/types.ts

@ -45,3 +45,6 @@ export enum ParserEvents {
all = 'parse',
}
/**{END_EVENTS}*/
// need this to avoid the TS documentation generator to break
export default ParserEvents;

60
packages/core/test/common.ts

@ -1,4 +1,11 @@
import { DataSourceManager } from '../src';
import CanvasEvents from '../src/canvas/types';
import { ObjectAny } from '../src/common';
import {
DataConditionIfFalseType,
DataConditionIfTrueType,
} from '../src/data_sources/model/conditional_variables/constants';
import { NumberOperation } from '../src/data_sources/model/conditional_variables/operators/NumberOperator';
import Editor from '../src/editor';
import { EditorConfig } from '../src/editor/config/config';
import EditorModel from '../src/editor/model/Editor';
@ -97,3 +104,56 @@ export function filterObjectForSnapshot(obj: any, parentKey: string = ''): any {
return result;
}
const baseComponent = {
type: 'text',
tagName: 'h1',
};
const createContent = (content: string) => ({
...baseComponent,
content,
});
/**
* Creates a component definition for a conditional component (ifTrue or ifFalse).
* @param type - The component type (e.g., DataConditionIfTrueType).
* @param content - The text content.
* @returns The component definition.
*/
const createConditionalComponentDef = (type: string, content: string) => ({
type,
components: [createContent(content)],
});
export const ifTrueText = 'true text';
export const newIfTrueText = 'new true text';
export const ifFalseText = 'false text';
export const newIfFalseText = 'new false text';
export const ifTrueComponentDef = createConditionalComponentDef(DataConditionIfTrueType, ifTrueText);
export const newIfTrueComponentDef = createConditionalComponentDef(DataConditionIfTrueType, newIfTrueText);
export const ifFalseComponentDef = createConditionalComponentDef(DataConditionIfFalseType, ifFalseText);
export const newIfFalseComponentDef = createConditionalComponentDef(DataConditionIfFalseType, newIfFalseText);
export function isObjectContained(received: ObjectAny, expected: ObjectAny): boolean {
return Object.keys(expected).every((key) => {
if (typeof expected[key] === 'object' && expected[key] !== null) {
return isObjectContained(received[key], expected[key]);
}
return received?.[key] === expected?.[key];
});
}
export const TRUE_CONDITION = {
left: 1,
operator: NumberOperation.greaterThan,
right: 0,
};
export const FALSE_CONDITION = {
left: 0,
operator: NumberOperation.lessThan,
right: -1,
};

6
packages/core/test/specs/data_sources/__snapshots__/serialization.ts.snap

@ -13,8 +13,10 @@ exports[`DataSource Serialization .getProjectData ComponentDataVariable 1`] = `
{
"components": [
{
"defaultValue": "default",
"path": "component-serialization.id1.content",
"dataResolver": {
"defaultValue": "default",
"path": "component-serialization.id1.content",
},
"type": "data-variable",
},
],

12
packages/core/test/specs/data_sources/__snapshots__/storage.ts.snap

@ -23,8 +23,10 @@ exports[`DataSource Storage .getProjectData ComponentDataVariable 1`] = `
{
"components": [
{
"defaultValue": "default",
"path": "component-storage.id1.content",
"dataResolver": {
"defaultValue": "default",
"path": "component-storage.id1.content",
},
"type": "data-variable",
},
],
@ -84,8 +86,10 @@ exports[`DataSource Storage .loadProjectData ComponentDataVariable 1`] = `
{
"components": [
{
"defaultValue": "default",
"path": "component-storage.id1.content",
"dataResolver": {
"defaultValue": "default",
"path": "component-storage.id1.content",
},
"type": "data-variable",
},
],

9
packages/core/test/specs/data_sources/jsonplaceholder.ts

@ -89,8 +89,7 @@ describe('JsonPlaceholder Usage', () => {
components: [
{
type: DataVariableType,
defaultValue: 'default',
path: `comments.${record?.id}.name`,
dataResolver: { defaultValue: 'default', path: `comments.${record?.id}.name` },
},
],
},
@ -99,8 +98,7 @@ describe('JsonPlaceholder Usage', () => {
components: [
{
type: DataVariableType,
defaultValue: 'default',
path: `comments.${record?.id}.id`,
dataResolver: { defaultValue: 'default', path: `comments.${record?.id}.id` },
},
],
},
@ -109,8 +107,7 @@ describe('JsonPlaceholder Usage', () => {
components: [
{
type: DataVariableType,
defaultValue: 'default',
path: `comments.${record?.id}.body`,
dataResolver: { defaultValue: 'default', path: `comments.${record?.id}.body` },
},
],
},

15
packages/core/test/specs/data_sources/model/ComponentDataVariable.getters-setters.ts

@ -30,8 +30,10 @@ describe('ComponentDataVariable - setPath and setDefaultValue', () => {
test('component updates when path is changed using setPath', () => {
const cmp = cmpRoot.append({
type: DataVariableType,
defaultValue: 'default',
path: 'ds_id.id1.name',
dataResolver: {
defaultValue: 'default',
path: 'ds_id.id1.name',
},
})[0] as ComponentDataVariable;
expect(cmp.getEl()?.innerHTML).toContain('Name1');
@ -45,8 +47,7 @@ describe('ComponentDataVariable - setPath and setDefaultValue', () => {
test('component updates when default value is changed using setDefaultValue', () => {
const cmp = cmpRoot.append({
type: DataVariableType,
defaultValue: 'default',
path: 'unknown.id1.name',
dataResolver: { defaultValue: 'default', path: 'unknown.id1.name' },
})[0] as ComponentDataVariable;
expect(cmp.getEl()?.innerHTML).toContain('default');
@ -60,8 +61,7 @@ describe('ComponentDataVariable - setPath and setDefaultValue', () => {
test('component updates correctly after path and default value are changed', () => {
const cmp = cmpRoot.append({
type: DataVariableType,
defaultValue: 'default',
path: 'ds_id.id1.name',
dataResolver: { defaultValue: 'default', path: 'ds_id.id1.name' },
})[0] as ComponentDataVariable;
expect(cmp.getEl()?.innerHTML).toContain('Name1');
@ -78,8 +78,7 @@ describe('ComponentDataVariable - setPath and setDefaultValue', () => {
test('component updates correctly after path is changed and data is updated', () => {
const cmp = cmpRoot.append({
type: DataVariableType,
defaultValue: 'default',
path: 'ds_id.id1.name',
dataResolver: { defaultValue: 'default', path: 'ds_id.id1.name' },
})[0] as ComponentDataVariable;
expect(cmp.getEl()?.innerHTML).toContain('Name1');

28
packages/core/test/specs/data_sources/model/ComponentDataVariable.ts

@ -1,7 +1,6 @@
import DataSourceManager from '../../../../src/data_sources';
import ComponentWrapper from '../../../../src/dom_components/model/ComponentWrapper';
import { DataVariableType } from '../../../../src/data_sources/model/DataVariable';
import { DataSourceProps } from '../../../../src/data_sources/types';
import { setupTestEditor } from '../../../common';
import EditorModel from '../../../../src/editor/model/Editor';
@ -31,8 +30,7 @@ describe('ComponentDataVariable', () => {
components: [
{
type: DataVariableType,
defaultValue: 'default',
path: 'ds1.id1.name',
dataResolver: { defaultValue: 'default', path: 'ds1.id1.name' },
},
],
})[0];
@ -54,8 +52,7 @@ describe('ComponentDataVariable', () => {
components: [
{
type: DataVariableType,
defaultValue: 'default',
path: 'ds2.id1.name',
dataResolver: { defaultValue: 'default', path: 'ds2.id1.name' },
},
],
})[0];
@ -77,8 +74,7 @@ describe('ComponentDataVariable', () => {
components: [
{
type: DataVariableType,
defaultValue: 'default',
path: 'unknown.id1.name',
dataResolver: { defaultValue: 'default', path: 'unknown.id1.name' },
},
],
})[0];
@ -99,8 +95,7 @@ describe('ComponentDataVariable', () => {
components: [
{
type: DataVariableType,
defaultValue: 'default',
path: 'ds3.id1.name',
dataResolver: { defaultValue: 'default', path: 'ds3.id1.name' },
},
],
})[0];
@ -126,8 +121,7 @@ describe('ComponentDataVariable', () => {
components: [
{
type: DataVariableType,
defaultValue: 'default',
path: `${dataSource.id}.id1.name`,
dataResolver: { defaultValue: 'default', path: `${dataSource.id}.id1.name` },
},
],
})[0];
@ -155,8 +149,7 @@ describe('ComponentDataVariable', () => {
components: [
{
type: DataVariableType,
defaultValue: 'default',
path: 'ds4.id1.name',
dataResolver: { defaultValue: 'default', path: 'ds4.id1.name' },
},
],
})[0];
@ -191,8 +184,7 @@ describe('ComponentDataVariable', () => {
components: [
{
type: DataVariableType,
defaultValue: 'default',
path: 'dsNestedObject.id1.nestedObject.name',
dataResolver: { defaultValue: 'default', path: 'dsNestedObject.id1.nestedObject.name' },
},
],
})[0];
@ -232,8 +224,7 @@ describe('ComponentDataVariable', () => {
components: [
{
type: DataVariableType,
defaultValue: 'default',
path: 'dsNestedArray.id1.items.0.nestedObject.name',
dataResolver: { defaultValue: 'default', path: 'dsNestedArray.id1.items.0.nestedObject.name' },
},
],
})[0];
@ -268,8 +259,7 @@ describe('ComponentDataVariable', () => {
components: [
{
type: DataVariableType,
defaultValue: 'default',
path: `${dataSource.id}.id1.content`,
dataResolver: { defaultValue: 'default', path: `${dataSource.id}.id1.content` },
},
],
style: {

147
packages/core/test/specs/data_sources/model/conditional_variables/ComponentDataCondition.getters-setters.ts

@ -1,22 +1,32 @@
import { Component, DataSourceManager, Editor } from '../../../../../src';
import { DataSourceManager } from '../../../../../src';
import { DataVariableType } from '../../../../../src/data_sources/model/DataVariable';
import ComponentDataCondition from '../../../../../src/data_sources/model/conditional_variables/ComponentDataCondition';
import { DataConditionType } from '../../../../../src/data_sources/model/conditional_variables/DataCondition';
import { AnyTypeOperation } from '../../../../../src/data_sources/model/conditional_variables/operators/AnyTypeOperator';
import { NumberOperation } from '../../../../../src/data_sources/model/conditional_variables/operators/NumberOperator';
import ComponentDataConditionView from '../../../../../src/data_sources/view/ComponentDataConditionView';
import ComponentWrapper from '../../../../../src/dom_components/model/ComponentWrapper';
import EditorModel from '../../../../../src/editor/model/Editor';
import { setupTestEditor } from '../../../../common';
import {
ifFalseText,
setupTestEditor,
ifTrueComponentDef,
ifFalseComponentDef,
newIfTrueText,
ifTrueText,
FALSE_CONDITION,
TRUE_CONDITION,
newIfFalseText,
newIfTrueComponentDef,
newIfFalseComponentDef,
} from '../../../../common';
describe('ComponentDataCondition Setters', () => {
let editor: Editor;
let em: EditorModel;
let dsm: DataSourceManager;
let cmpRoot: ComponentWrapper;
beforeEach(() => {
({ editor, em, dsm, cmpRoot } = setupTestEditor());
({ em, dsm, cmpRoot } = setupTestEditor());
});
afterEach(() => {
@ -26,66 +36,42 @@ describe('ComponentDataCondition Setters', () => {
it('should update the condition using setCondition', () => {
const component = cmpRoot.append({
type: DataConditionType,
condition: {
left: 0,
operator: NumberOperation.greaterThan,
right: -1,
},
ifTrue: '<h1>some text</h1>',
ifFalse: '<h1>false text</h1>',
dataResolver: { condition: TRUE_CONDITION },
components: [ifTrueComponentDef, ifFalseComponentDef],
})[0] as ComponentDataCondition;
const newCondition = {
left: 1,
operator: NumberOperation.lessThan,
right: 0,
};
component.setCondition(newCondition);
expect(component.getCondition()).toEqual(newCondition);
expect(component.getInnerHTML()).toBe('<h1>false text</h1>');
component.setCondition(FALSE_CONDITION);
expect(component.getCondition()).toEqual(FALSE_CONDITION);
expect(component.getInnerHTML()).toContain(ifFalseText);
expect(component.getEl()?.innerHTML).toContain(ifFalseText);
});
it('should update the ifTrue value using setIfTrue', () => {
it('should update the ifTrue value using setIfTrueComponents', () => {
const component = cmpRoot.append({
type: DataConditionType,
condition: {
left: 0,
operator: NumberOperation.greaterThan,
right: -1,
},
ifTrue: '<h1>some text</h1>',
ifFalse: '<h1>false text</h1>',
dataResolver: { condition: TRUE_CONDITION },
components: [ifTrueComponentDef, ifFalseComponentDef],
})[0] as ComponentDataCondition;
const newIfTrue = '<h1>new true text</h1>';
component.setIfTrue(newIfTrue);
expect(component.getIfTrue()).toEqual(newIfTrue);
expect(component.getInnerHTML()).toBe(newIfTrue);
component.setIfTrueComponents(newIfTrueComponentDef.components);
expect(JSON.parse(JSON.stringify(component.getIfTrueContent()))).toEqual(newIfTrueComponentDef);
expect(component.getInnerHTML()).toContain(newIfTrueText);
expect(component.getEl()?.innerHTML).toContain(newIfTrueText);
});
it('should update the ifFalse value using setIfFalse', () => {
it('should update the ifFalse value using setIfFalseComponents', () => {
const component = cmpRoot.append({
type: DataConditionType,
condition: {
left: 0,
operator: NumberOperation.greaterThan,
right: -1,
},
ifTrue: '<h1>some text</h1>',
ifFalse: '<h1>false text</h1>',
dataResolver: { condition: TRUE_CONDITION },
components: [ifTrueComponentDef, ifFalseComponentDef],
})[0] as ComponentDataCondition;
const newIfFalse = '<h1>new false text</h1>';
component.setIfFalse(newIfFalse);
expect(component.getIfFalse()).toEqual(newIfFalse);
component.setIfFalseComponents(newIfFalseComponentDef.components);
expect(JSON.parse(JSON.stringify(component.getIfFalseContent()))).toEqual(newIfFalseComponentDef);
component.setCondition({
left: 0,
operator: NumberOperation.lessThan,
right: -1,
});
expect(component.getInnerHTML()).toBe(newIfFalse);
component.setCondition(FALSE_CONDITION);
expect(component.getInnerHTML()).toContain(newIfFalseText);
expect(component.getEl()?.innerHTML).toContain(newIfFalseText);
});
it('should update the data sources and re-evaluate the condition', () => {
@ -100,57 +86,54 @@ describe('ComponentDataCondition Setters', () => {
const component = cmpRoot.append({
type: DataConditionType,
condition: {
left: {
type: DataVariableType,
path: 'ds1.left_id.left',
},
operator: AnyTypeOperation.equals,
right: {
type: DataVariableType,
path: 'ds1.right_id.right',
dataResolver: {
condition: {
left: {
type: DataVariableType,
path: 'ds1.left_id.left',
},
operator: AnyTypeOperation.equals,
right: {
type: DataVariableType,
path: 'ds1.right_id.right',
},
},
},
ifTrue: '<h1>True value</h1>',
ifFalse: '<h1>False value</h1>',
components: [ifTrueComponentDef, ifFalseComponentDef],
})[0] as ComponentDataCondition;
expect(component.getInnerHTML()).toBe('<h1>True value</h1>');
expect(component.getInnerHTML()).toContain(ifTrueText);
changeDataSourceValue(dsm, 'Different value');
expect(component.getInnerHTML()).toBe('<h1>False value</h1>');
expect(component.getInnerHTML()).toContain(ifFalseText);
expect(component.getEl()?.innerHTML).toContain(ifFalseText);
changeDataSourceValue(dsm, 'Name1');
expect(component.getInnerHTML()).toBe('<h1>True value</h1>');
expect(component.getInnerHTML()).toContain(ifTrueText);
expect(component.getEl()?.innerHTML).toContain(ifTrueText);
});
it('should re-render the component when condition, ifTrue, or ifFalse changes', () => {
const component = cmpRoot.append({
type: DataConditionType,
condition: {
left: 0,
operator: NumberOperation.greaterThan,
right: -1,
},
ifTrue: '<h1>some text</h1>',
ifFalse: '<h1>false text</h1>',
dataResolver: { condition: TRUE_CONDITION },
components: [ifTrueComponentDef, ifFalseComponentDef],
})[0] as ComponentDataCondition;
const componentView = component.getView() as ComponentDataConditionView;
component.setIfTrue('<h1>new true text</h1>');
expect(componentView.el.innerHTML).toContain('new true text');
component.setIfTrueComponents(newIfTrueComponentDef);
expect(component.getInnerHTML()).toContain(newIfTrueText);
expect(componentView.el.innerHTML).toContain(newIfTrueText);
component.setIfFalse('<h1>new false text</h1>');
component.setCondition({
left: 0,
operator: NumberOperation.lessThan,
right: -1,
});
expect(componentView.el.innerHTML).toContain('new false text');
component.setIfFalseComponents(newIfFalseComponentDef);
component.setCondition(FALSE_CONDITION);
expect(component.getInnerHTML()).toContain(newIfFalseText);
expect(componentView.el.innerHTML).toContain(newIfFalseText);
});
});
function changeDataSourceValue(dsm: DataSourceManager, newValue: string) {
export const changeDataSourceValue = (dsm: DataSourceManager, newValue: string) => {
dsm.get('ds1').getRecord('left_id')?.set('left', newValue);
}
};

318
packages/core/test/specs/data_sources/model/conditional_variables/ComponentDataCondition.ts

@ -1,14 +1,23 @@
import { Component, DataSourceManager, Editor } from '../../../../../src';
import { Component, Components, ComponentView, DataSourceManager, Editor } from '../../../../../src';
import { DataConditionIfTrueType } from '../../../../../src/data_sources/model/conditional_variables/constants';
import { DataVariableType } from '../../../../../src/data_sources/model/DataVariable';
import { DataConditionType } from '../../../../../src/data_sources/model/conditional_variables/DataCondition';
import { AnyTypeOperation } from '../../../../../src/data_sources/model/conditional_variables/operators/AnyTypeOperator';
import { NumberOperation } from '../../../../../src/data_sources/model/conditional_variables/operators/NumberOperator';
import ComponentDataConditionView from '../../../../../src/data_sources/view/ComponentDataConditionView';
import ComponentWrapper from '../../../../../src/dom_components/model/ComponentWrapper';
import ComponentTableView from '../../../../../src/dom_components/view/ComponentTableView';
import ComponentTextView from '../../../../../src/dom_components/view/ComponentTextView';
import EditorModel from '../../../../../src/editor/model/Editor';
import { setupTestEditor } from '../../../../common';
import {
FALSE_CONDITION,
ifFalseComponentDef,
ifFalseText,
ifTrueComponentDef,
ifTrueText,
isObjectContained,
setupTestEditor,
TRUE_CONDITION,
} from '../../../../common';
import ComponentDataCondition from '../../../../../src/data_sources/model/conditional_variables/ComponentDataCondition';
describe('ComponentDataCondition', () => {
let editor: Editor;
@ -24,60 +33,34 @@ describe('ComponentDataCondition', () => {
em.destroy();
});
it('should add a component with a condition that evaluates a component definition', () => {
it('should add a component with a condition', () => {
const component = cmpRoot.append({
type: DataConditionType,
condition: {
left: 0,
operator: NumberOperation.greaterThan,
right: -1,
},
ifTrue: {
tagName: 'h1',
type: 'text',
content: 'some text',
},
})[0];
dataResolver: { condition: TRUE_CONDITION },
components: [ifTrueComponentDef],
})[0] as ComponentDataCondition;
expect(component).toBeDefined();
expect(component.get('type')).toBe(DataConditionType);
expect(component.getInnerHTML()).toBe('<h1>some text</h1>');
const componentView = component.getView();
expect(componentView).toBeInstanceOf(ComponentDataConditionView);
expect(componentView?.el.textContent).toBe('some text');
const childComponent = getFirstChild(component);
const childView = getFirstChildView(component);
expect(childComponent).toBeDefined();
expect(childComponent.get('type')).toBe('text');
expect(childComponent.getInnerHTML()).toBe('some text');
expect(childView).toBeInstanceOf(ComponentTextView);
expect(childView?.el.innerHTML).toBe('some text');
expect(component.getInnerHTML()).toContain(ifTrueText);
expect(component.getEl()?.innerHTML).toContain(ifTrueText);
const ifTrueContent = component.getIfTrueContent()!;
expect(ifTrueContent.getInnerHTML()).toContain(ifTrueText);
expect(ifTrueContent.getEl()?.textContent).toBe(ifTrueText);
expect(ifTrueContent.getEl()?.style.display).toBe('');
});
it('should add a component with a condition that evaluates a string', () => {
it('ComponentDataCondition getIfTrueContent and getIfFalseContent', () => {
const component = cmpRoot.append({
type: DataConditionType,
condition: {
left: 0,
operator: NumberOperation.greaterThan,
right: -1,
},
ifTrue: '<h1>some text</h1>',
})[0];
expect(component).toBeDefined();
expect(component.get('type')).toBe(DataConditionType);
expect(component.getInnerHTML()).toBe('<h1>some text</h1>');
const componentView = component.getView();
expect(componentView).toBeInstanceOf(ComponentDataConditionView);
expect(componentView?.el.textContent).toBe('some text');
const childComponent = getFirstChild(component);
const childView = getFirstChildView(component);
expect(childComponent).toBeDefined();
expect(childComponent.get('type')).toBe('text');
expect(childComponent.getInnerHTML()).toBe('some text');
expect(childView).toBeInstanceOf(ComponentTextView);
expect(childView?.el.innerHTML).toBe('some text');
dataResolver: { condition: TRUE_CONDITION },
components: [ifTrueComponentDef, ifFalseComponentDef],
})[0] as ComponentDataCondition;
expect(JSON.parse(JSON.stringify(component.getIfTrueContent()!))).toEqual(ifTrueComponentDef);
expect(JSON.parse(JSON.stringify(component.getIfFalseContent()!))).toEqual(ifFalseComponentDef);
});
it('should test component variable with data-source', () => {
@ -92,41 +75,49 @@ describe('ComponentDataCondition', () => {
const component = cmpRoot.append({
type: DataConditionType,
condition: {
left: {
type: DataVariableType,
path: 'ds1.left_id.left',
},
operator: AnyTypeOperation.equals,
right: {
type: DataVariableType,
path: 'ds1.right_id.right',
dataResolver: {
condition: {
left: {
type: DataVariableType,
path: 'ds1.left_id.left',
},
operator: AnyTypeOperation.equals,
right: {
type: DataVariableType,
path: 'ds1.right_id.right',
},
},
},
ifTrue: {
tagName: 'h1',
type: 'text',
content: 'Some value',
},
ifFalse: {
tagName: 'h1',
type: 'text',
content: 'False value',
},
})[0];
components: [ifTrueComponentDef, ifFalseComponentDef],
})[0] as ComponentDataCondition;
expect(component.getInnerHTML()).toContain(ifTrueText);
expect(component.getEl()?.innerHTML).toContain(ifTrueText);
const ifTrueContent = component.getIfTrueContent()!;
expect(ifTrueContent.getInnerHTML()).toContain(ifTrueText);
expect(ifTrueContent.getEl()?.textContent).toBe(ifTrueText);
expect(ifTrueContent.getEl()?.style.display).toBe('');
const childComponent = getFirstChild(component);
expect(childComponent).toBeDefined();
expect(childComponent.get('type')).toBe('text');
expect(childComponent.getInnerHTML()).toBe('Some value');
expect(component.getInnerHTML()).not.toContain(ifFalseText);
expect(component.getEl()?.innerHTML).toContain(ifFalseText);
const ifFalseContent = component.getIfFalseContent()!;
expect(ifFalseContent.getInnerHTML()).toContain(ifFalseText);
expect(ifFalseContent.getEl()?.textContent).toBe(ifFalseText);
expect(ifFalseContent.getEl()?.style.display).toBe('none');
/* Test changing datasources */
changeDataSourceValue(dsm, 'Diffirent value');
expect(getFirstChild(component).getInnerHTML()).toBe('False value');
expect(getFirstChildView(component)?.el.innerHTML).toBe('False value');
changeDataSourceValue(dsm, 'Name1');
expect(getFirstChild(component).getInnerHTML()).toBe('Some value');
expect(getFirstChildView(component)?.el.innerHTML).toBe('Some value');
const WrongValue = 'Diffirent value';
changeDataSourceValue(dsm, WrongValue);
expect(component.getEl()?.innerHTML).toContain(ifTrueText);
expect(component.getEl()?.innerHTML).toContain(ifFalseText);
expect(ifTrueContent.getEl()?.style.display).toBe('none');
expect(ifFalseContent.getEl()?.style.display).toBe('');
const CorrectValue = 'Name1';
changeDataSourceValue(dsm, CorrectValue);
expect(component.getEl()?.innerHTML).toContain(ifTrueText);
expect(component.getEl()?.innerHTML).toContain(ifFalseText);
expect(ifTrueContent.getEl()?.style.display).toBe('');
expect(ifFalseContent.getEl()?.style.display).toBe('none');
});
it('should test a conditional component with a child that is also a conditional component', () => {
@ -141,65 +132,54 @@ describe('ComponentDataCondition', () => {
const component = cmpRoot.append({
type: DataConditionType,
condition: {
left: {
type: DataVariableType,
path: 'ds1.left_id.left',
},
operator: AnyTypeOperation.equals,
right: {
type: DataVariableType,
path: 'ds1.right_id.right',
dataResolver: {
condition: {
left: {
type: DataVariableType,
path: 'ds1.left_id.left',
},
operator: AnyTypeOperation.equals,
right: {
type: DataVariableType,
path: 'ds1.right_id.right',
},
},
},
ifTrue: {
tagName: 'div',
components: [
{
components: [
{
type: DataConditionIfTrueType,
components: {
type: DataConditionType,
condition: {
left: {
type: DataVariableType,
path: 'ds1.left_id.left',
},
operator: AnyTypeOperation.equals,
right: {
type: DataVariableType,
path: 'ds1.right_id.right',
dataResolver: {
condition: {
left: {
type: DataVariableType,
path: 'ds1.left_id.left',
},
operator: AnyTypeOperation.equals,
right: {
type: DataVariableType,
path: 'ds1.right_id.right',
},
},
},
ifTrue: {
tagName: 'table',
type: 'table',
},
components: ifTrueComponentDef,
},
],
},
})[0];
const innerComponent = getFirstChild(getFirstChild(component));
const innerComponentView = getFirstChildView(innerComponent);
const innerHTML = '<table><tbody><tr class="row"><td class="cell"></td></tr></tbody></table>';
expect(innerComponent.getInnerHTML()).toBe(innerHTML);
expect(innerComponentView).toBeInstanceOf(ComponentTableView);
expect(innerComponentView?.el.tagName).toBe('TABLE');
},
ifFalseComponentDef,
],
})[0] as ComponentDataCondition;
const ifTrueContent = component.getIfTrueContent()!;
expect(ifTrueContent.getInnerHTML()).toContain(ifTrueText);
expect(ifTrueContent.getEl()?.textContent).toBe(ifTrueText);
expect(ifTrueContent.getEl()?.style.display).toBe('');
});
it('should store conditional components', () => {
const conditionalCmptDef = {
type: DataConditionType,
condition: {
left: 0,
operator: NumberOperation.greaterThan,
right: -1,
},
ifTrue: [
{
tagName: 'h1',
type: 'text',
content: 'some text',
},
],
dataResolver: { condition: FALSE_CONDITION },
components: [ifTrueComponentDef, ifFalseComponentDef],
};
cmpRoot.append(conditionalCmptDef)[0];
@ -208,18 +188,84 @@ describe('ComponentDataCondition', () => {
const page = projectData.pages[0];
const frame = page.frames[0];
const storageCmptDef = frame.component.components[0];
expect(storageCmptDef).toEqual(conditionalCmptDef);
expect(isObjectContained(storageCmptDef, conditionalCmptDef)).toBe(true);
});
});
function changeDataSourceValue(dsm: DataSourceManager, newValue: string) {
dsm.get('ds1').getRecord('left_id')?.set('left', newValue);
}
it('should dynamically display ifTrue, ifFalse components in the correct order', () => {
const component = cmpRoot.append({
type: DataConditionType,
dataResolver: { condition: TRUE_CONDITION },
components: [ifTrueComponentDef, ifFalseComponentDef],
})[0] as ComponentDataCondition;
const el = component.getEl()!;
const ifTrueEl = el.childNodes[0] as any;
const ifFalseEl = el.childNodes[1] as any;
expect(ifTrueEl.textContent).toContain(ifTrueText);
expect(ifTrueEl.style.display).toBe('');
expect(ifFalseEl.textContent).toContain(ifFalseText);
expect(ifFalseEl.style.display).toBe('none');
function getFirstChildView(component: Component) {
return getFirstChild(component).getView();
}
component.setCondition(FALSE_CONDITION);
expect(ifTrueEl.style.display).toBe('none');
expect(ifTrueEl.textContent).toContain(ifTrueText);
expect(ifFalseEl.style.display).toBe('');
expect(ifFalseEl.textContent).toContain(ifFalseText);
});
it('should dynamically update display components when data source changes', () => {
const dataSource = {
id: 'ds1',
records: [{ id: 'left_id', left: 1 }],
};
dsm.add(dataSource);
const component = cmpRoot.append({
type: DataConditionType,
dataResolver: {
condition: {
left: {
type: DataVariableType,
path: 'ds1.left_id.left',
},
operator: NumberOperation.greaterThan,
right: 0,
},
},
components: [ifTrueComponentDef, ifFalseComponentDef],
})[0] as ComponentDataCondition;
const el = component.view!.el!;
const falseValue = -1;
changeDataSourceValue(dsm, falseValue);
expect(el.innerHTML).toContain(ifTrueText);
expect(el.innerHTML).toContain(ifFalseText);
const ifTrueEl = el.childNodes[0] as any;
const ifFalseEl = el.childNodes[1] as any;
expect(ifTrueEl!.style.display).toBe('none');
expect(ifTrueEl.textContent).toContain(ifTrueText);
expect(ifFalseEl.style.display).toBe('');
expect(ifFalseEl.textContent).toContain(ifFalseText);
});
function getFirstChild(component: Component) {
return component.components().at(0);
it('should update content of ifTrue, ifFalse components when condition changes', () => {
const component = cmpRoot.append({
type: DataConditionType,
dataResolver: { condition: TRUE_CONDITION },
components: [ifTrueComponentDef, ifFalseComponentDef],
})[0] as ComponentDataCondition;
const el = component.view!.el;
component.setCondition(FALSE_CONDITION);
const ifTrueEl = el.childNodes[0] as any;
const ifFalseEl = el.childNodes[1] as any;
expect(ifTrueEl!.style.display).toBe('none');
expect(ifTrueEl.textContent).toContain(ifTrueText);
expect(ifFalseEl.style.display).toBe('');
expect(ifFalseEl.textContent).toContain(ifFalseText);
});
});
function changeDataSourceValue(dsm: DataSourceManager, newValue: string | number) {
dsm.get('ds1').getRecord('left_id')?.set('left', newValue);
}

12
packages/core/test/specs/data_sources/serialization.ts

@ -53,8 +53,7 @@ describe('DataSource Serialization', () => {
components: [
{
type: DataVariableType,
defaultValue: 'default',
path: `${componentDataSource.id}.id1.content`,
dataResolver: { defaultValue: 'default', path: `${componentDataSource.id}.id1.content` },
},
],
})[0];
@ -118,8 +117,7 @@ describe('DataSource Serialization', () => {
test('ComponentDataVariable', () => {
const dataVariable = {
type: DataVariableType,
defaultValue: 'default',
path: `${componentDataSource.id}.id1.content`,
dataResolver: { defaultValue: 'default', path: `${componentDataSource.id}.id1.content` },
};
cmpRoot.append({
@ -309,9 +307,9 @@ describe('DataSource Serialization', () => {
{
components: [
{
path: 'component-serialization.id1.content',
type: 'data-variable',
value: 'default',
type: DataVariableType,
dataResolver: { path: 'component-serialization.id1.content' },
},
],
tagName: 'h1',
@ -403,7 +401,7 @@ describe('DataSource Serialization', () => {
style: {
color: {
path: 'colors-data.id1.color',
type: 'data-variable',
type: DataVariableType,
defaultValue: 'black',
},
},

8
packages/core/test/specs/data_sources/storage.ts

@ -39,8 +39,7 @@ describe('DataSource Storage', () => {
test('ComponentDataVariable', () => {
const dataVariable = {
type: DataVariableType,
defaultValue: 'default',
path: `${storedDataSource.id}.id1.content`,
dataResolver: { defaultValue: 'default', path: `${storedDataSource.id}.id1.content` },
};
cmpRoot.append({
@ -87,9 +86,8 @@ describe('DataSource Storage', () => {
{
components: [
{
defaultValue: 'default',
path: `${storedDataSource.id}.id1.content`,
type: 'data-variable',
type: DataVariableType,
dataResolver: { defaultValue: 'default', path: `${storedDataSource.id}.id1.content` },
},
],
tagName: 'h1',

6
packages/core/test/specs/data_sources/transformers.ts

@ -41,8 +41,7 @@ describe('DataSource Transformers', () => {
components: [
{
type: DataVariableType,
defaultValue: 'default',
path: 'test-data-source.id1.content',
dataResolver: { defaultValue: 'default', path: 'test-data-source.id1.content' },
},
],
})[0];
@ -85,8 +84,7 @@ describe('DataSource Transformers', () => {
components: [
{
type: DataVariableType,
defaultValue: 'default',
path: 'test-data-source.id1.content',
dataResolver: { defaultValue: 'default', path: 'test-data-source.id1.content' },
},
],
})[0];

Loading…
Cancel
Save