Skip to content

Commit 6cf8038

Browse files
authored
feat: add transform when variables handling (#427)
feat: add content filtration utils
1 parent 75e00e2 commit 6cf8038

File tree

6 files changed

+123
-11
lines changed

6 files changed

+123
-11
lines changed

src/editor/containers/Editor/Editor.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,10 @@ export const Editor = ({children, customSchema, onChange, ...rest}: EditorProps)
2323
onSelect,
2424
injectEditBlockProps,
2525
} = useEditorState(rest);
26+
const formSpecs = useFormSpec(customSchema);
27+
2628
const isEditingMode = viewMode === ViewModeItem.Edititng;
27-
const constructorProps = useMemo(() => {
29+
const outgoingProps = useMemo(() => {
2830
const custom = isEditingMode
2931
? addCustomDecorator(
3032
[
@@ -42,15 +44,13 @@ export const Editor = ({children, customSchema, onChange, ...rest}: EditorProps)
4244
rest.custom,
4345
)
4446
: rest.custom;
45-
return {content, custom};
46-
}, [injectEditBlockProps, content, errorBoundaryState, isEditingMode, rest.custom]);
47+
return {content, custom, viewMode};
48+
}, [injectEditBlockProps, content, errorBoundaryState, isEditingMode, viewMode, rest.custom]);
4749

4850
useEffect(() => {
4951
onChange?.(content);
5052
}, [content, onChange]);
5153

52-
const formSpecs = useFormSpec(customSchema);
53-
5454
return (
5555
<Layout mode={viewMode} onModeChange={onViewModeUpdate}>
5656
{isEditingMode && (
@@ -65,7 +65,7 @@ export const Editor = ({children, customSchema, onChange, ...rest}: EditorProps)
6565
</Layout.Left>
6666
)}
6767
<Layout.Right>
68-
<ErrorBoundary key={errorBoundaryState}>{children(constructorProps)}</ErrorBoundary>
68+
<ErrorBoundary key={errorBoundaryState}>{children(outgoingProps)}</ErrorBoundary>
6969
{isEditingMode && <AddBlock onAdd={onAdd} />}
7070
</Layout.Right>
7171
</Layout>

src/editor/types/index.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,14 @@ import {EditBlockActions} from '../components/EditBlock/EditBlock';
55

66
export type EditorBlockId = number | string;
77

8+
export interface EditorOutgoingProps extends Partial<PageConstructorProps> {
9+
viewMode: ViewModeItem;
10+
}
11+
812
export interface EditorProps
913
extends Required<Pick<PageConstructorProps, 'content'>>,
1014
Partial<Omit<PageConstructorProps, 'content'>> {
11-
children: (props: Partial<PageConstructorProps>) => React.ReactNode;
15+
children: (props: EditorOutgoingProps) => React.ReactNode;
1216
onChange?: (data: PageContent) => void;
1317
customSchema?: SchemaCustomConfig;
1418
}

src/schema/validators/common.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@ export const contentThemes = ['default', 'dark', 'light'];
2323

2424
export const BaseProps = {
2525
type: {},
26-
when: {},
26+
when: {
27+
type: 'string',
28+
},
2729
};
2830

2931
export const containerSizesObject = {

src/text-transform/filter.ts

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/* eslint-disable no-param-reassign */
2+
/* eslint-disable no-not-accumulator-reassign/no-not-accumulator-reassign */
3+
4+
import evalExp from '@doc-tools/transform/lib/liquid/evaluation';
5+
6+
import {ContentVariables} from './transformers';
7+
8+
type WhenValue = string | boolean | undefined;
9+
type OrArray<T> = T | T[];
10+
type OrArrayRecord<T> = Record<string, OrArray<T>>;
11+
interface FilteredContentItem extends OrArrayRecord<FilteredContentItem> {}
12+
type FilteredContent = OrArray<FilteredContentItem>;
13+
type FilterableContentItem = FilteredContentItem & {when: WhenValue};
14+
export type FilterableContent = OrArray<FilterableContentItem>;
15+
16+
function filterItems(
17+
items: FilterableContentItem[],
18+
vars: ContentVariables,
19+
propertyName?: string,
20+
): FilterableContentItem[] | FilteredContentItem[] {
21+
if (!Array.isArray(items)) {
22+
throw new Error(
23+
`Error while filtering: items has invalid key '${propertyName}' equals ${JSON.stringify(
24+
items,
25+
)}`,
26+
);
27+
}
28+
29+
return items.reduce<FilteredContentItem[]>((result, item) => {
30+
const passedFiltration = checkWhenCondition(item.when, vars);
31+
32+
if (passedFiltration) {
33+
const property = propertyName && item[propertyName];
34+
35+
if (property === undefined) {
36+
result.push(item);
37+
} else {
38+
const filteredProperty = filterItems(
39+
property as FilterableContentItem[],
40+
vars,
41+
propertyName,
42+
);
43+
44+
if (filteredProperty.length !== 0) {
45+
result.push({
46+
...item,
47+
[propertyName as string]: filteredProperty,
48+
});
49+
}
50+
}
51+
}
52+
53+
return result;
54+
}, []);
55+
}
56+
57+
function checkWhenCondition(whenValue: WhenValue, vars: ContentVariables) {
58+
return (
59+
whenValue === true ||
60+
whenValue === undefined ||
61+
(typeof whenValue === 'string' && (!whenValue || evalExp(whenValue, vars)))
62+
);
63+
}
64+
65+
function isItemArray(
66+
content: FilterableContent | FilteredContent,
67+
): content is FilterableContentItem[] | FilteredContentItem[] {
68+
return Array.isArray(content);
69+
}
70+
71+
function isItem(
72+
content: FilterableContent | FilteredContent,
73+
): content is FilterableContentItem | FilteredContentItem {
74+
return content && typeof content === 'object' && !Array.isArray(content);
75+
}
76+
77+
/**
78+
* Filters content recoursively by result of it's 'when' property evaluation applied to vars argument.
79+
* e.g. property {..., when: 'someVar == "someValue"' } will be included in result only if vars.someVar === 'someValue'
80+
* @param {FilterableContent} content
81+
* @param {ContentVariables} vars
82+
* @return {FilteredContent}
83+
*/
84+
export function filterContent(
85+
content: FilterableContent | FilteredContent,
86+
vars: ContentVariables,
87+
): FilteredContent {
88+
if (isItemArray(content)) {
89+
return filterItems(content as FilterableContentItem[], vars).map((item) =>
90+
filterContent(item, vars),
91+
) as FilteredContent;
92+
} else if (isItem(content)) {
93+
return Object.entries(content).reduce<FilteredContentItem>((acc, [key, value]) => {
94+
acc[key] = filterContent(value, vars);
95+
return acc;
96+
}, {});
97+
} else {
98+
return content;
99+
}
100+
}

src/text-transform/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ export * from './utils';
22
export * from './config';
33
export * from './common';
44
export * from './transformers';
5+
export * from './filter';

src/text-transform/transformers.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,22 @@
22
/* eslint-disable no-not-accumulator-reassign/no-not-accumulator-reassign */
33
import _ from 'lodash';
44

5-
import {ConstructorBlock} from '../models/constructor';
5+
import {ConstructorBlock, PageContent} from '../models/constructor';
66
import {Lang} from '../utils/configure';
77

88
import {Transformer} from './common';
99
import {BlocksConfig, config} from './config';
10+
import {FilterableContent, filterContent} from './filter';
1011

12+
export type ContentVariables = Record<string, string>;
1113
export type ContentTransformerProps = {
1214
content: {
1315
blocks?: ConstructorBlock[];
1416
};
1517
options: {
1618
lang: Lang;
1719
customConfig?: {};
20+
vars?: ContentVariables;
1821
};
1922
};
2023

@@ -66,8 +69,10 @@ function transformBlock(lang: Lang, blocksConfig: BlocksConfig, block: Construct
6669
}
6770

6871
export const contentTransformer = ({content, options}: ContentTransformerProps) => {
69-
const {lang, customConfig = {}} = options;
70-
const {blocks = []} = content;
72+
const {lang, customConfig = {}, vars} = options;
73+
const {blocks = []} = (
74+
vars ? filterContent(content as FilterableContent, vars) : content
75+
) as PageContent;
7176

7277
const transformedBlocks = transformBlocks(blocks, lang, customConfig);
7378

0 commit comments

Comments
 (0)