Skip to content

Commit

Permalink
fix: render component values in preview and resolve links properly [] (
Browse files Browse the repository at this point in the history
…#194)

* fix: replace component values and links properly

* fix: remove left over console log

* fix: remove old comment that doesnt fit

* test: adjust tests to consider new resolve logic
  • Loading branch information
Chaoste authored Dec 15, 2023
1 parent dc8831f commit 7183fbd
Show file tree
Hide file tree
Showing 4 changed files with 113 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import type { Entry } from 'contentful';
import { compositionEntry } from '../../../test/__fixtures__/composition';
import {
createDesignComponentEntry,
defaultDesignComponentId,
designComponentGeneratedVariableName,
} from '../../../test/__fixtures__/designComponent';
import { EntityStore } from '../../core/preview/EntityStore';
Expand Down Expand Up @@ -117,26 +118,35 @@ describe('CompositionBlock', () => {
});

it('renders design component node', () => {
const unboundValueKey = 'some-unbound-value-key';
const designComponentEntry = createDesignComponentEntry({
id: 'design-component-id',
id: defaultDesignComponentId,
schemaVersion: '2023-09-28',
});
const experienceEntry = {
...compositionEntry,
fields: {
...compositionEntry.fields,
usedComponents: [designComponentEntry],
unboundValues: {
[unboundValueKey]: {
value: 'New year eve',
},
},
},
} as ExperienceEntry;

const entityStore = new EntityStore({
experienceEntry: {
...compositionEntry,
fields: {
...compositionEntry.fields,
usedComponents: [designComponentEntry],
},
} as unknown as Entry,
experienceEntry: experienceEntry as unknown as Entry,
entities: [...entries, ...assets],
locale: 'en-US',
});

const designComponentNode: CompositionNode = {
definitionId: 'design-component-id',
variables: {},
definitionId: defaultDesignComponentId,
variables: {
[designComponentGeneratedVariableName]: { type: 'UnboundValue', key: unboundValueKey },
},
children: [],
};

Expand All @@ -148,11 +158,7 @@ describe('CompositionBlock', () => {
breakpoints={[]}
entityStore={entityStore}
usedComponents={[designComponentEntry] as ExperienceEntry[]}
unboundValues={{
[designComponentGeneratedVariableName]: {
value: 'New year eve',
},
}}
unboundValues={experienceEntry.fields.unboundValues}
resolveDesignValue={jest.fn()}
/>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,11 +114,6 @@ export const CompositionBlock = ({
acc[variableName] = (entityStore?.unboundValues || unboundValues)[uuid]?.value;
break;
}
case 'ComponentValue': {
const uuid = variable.key;
acc[variableName] = unboundValues[uuid]?.value;
break;
}
default:
break;
}
Expand Down
47 changes: 37 additions & 10 deletions packages/experience-builder-sdk/src/core/preview/EntityStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,24 +56,51 @@ export class EntityStore extends VisualSdkEntityStore {
}

public getValue(
entityLink: UnresolvedLink<'Entry' | 'Asset'>,
entityLinkOrEntity: UnresolvedLink<'Entry' | 'Asset'> | Entry | Asset,
path: string[]
): string | undefined {
const entity =
entityLink.sys.linkType === 'Entry'
? this.entryMap.get(entityLink.sys.id)
: this.assetMap.get(entityLink.sys.id);

if (!entity || entity.sys.type !== entityLink.sys.linkType) {
console.warn(`Experience references unresolved entity: ${JSON.stringify(entityLink)}`);
return;
const isLink = (
entity: typeof entityLinkOrEntity
): entity is UnresolvedLink<'Entry' | 'Asset'> => entityLinkOrEntity.sys.type === 'Link';

let entity: Entry | Asset;
if (isLink(entityLinkOrEntity)) {
const resolvedEntity =
entityLinkOrEntity.sys.linkType === 'Entry'
? this.entryMap.get(entityLinkOrEntity.sys.id)
: this.assetMap.get(entityLinkOrEntity.sys.id);

if (!resolvedEntity || resolvedEntity.sys.type !== entityLinkOrEntity.sys.linkType) {
console.warn(
`Experience references unresolved entity: ${JSON.stringify(entityLinkOrEntity)}`
);
return;
}
entity = resolvedEntity;
} else {
// We already have the complete entity in preview & delivery (resolved by the CMA client)
entity = entityLinkOrEntity;
}

const fieldValue = super.getValue(entityLink, path);
const fieldValue = get<string>(entity, path);

// walk around to render asset files
return fieldValue && typeof fieldValue == 'object' && (fieldValue as AssetFile).url
? (fieldValue as AssetFile).url
: fieldValue;
}
}

// Taken from visual-sdk. We need this when we already have the full entity instead of the link (preview & delivery)
function get<T>(obj: Record<string, any>, path: string[]): T | undefined {
if (!path.length) {
return obj as T;
}

try {
const [currentPath, ...nextPath] = path;
return get(obj[currentPath], nextPath);
} catch (err) {
return undefined;
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,52 @@
import { CompositionNode } from '../../types';
import { CompositionComponentPropValue, CompositionNode } from '../../types';
import { checkIfDesignComponent } from '../../utils/utils';
import { EntityStore } from './EntityStore';

export const deserializeDesignComponentNode = ({
node,
componentInstanceVariables,
}: {
node: CompositionNode;
componentInstanceVariables: CompositionNode['variables'];
}): CompositionNode => {
const variables: Record<string, CompositionComponentPropValue> = {};

for (const [variableName, variable] of Object.entries(node.variables)) {
variables[variableName] = variable;
if (variable.type === 'ComponentValue') {
const componentValueKey = variable.key;
const instanceProperty = componentInstanceVariables[componentValueKey];

// For design component, we look up the variable in the design component instance and
// replace the componentValue with that one.
if (instanceProperty?.type === 'UnboundValue') {
variables[variableName] = {
type: 'UnboundValue',
key: instanceProperty.key,
};
} else if (instanceProperty?.type === 'BoundValue') {
variables[variableName] = {
type: 'BoundValue',
path: instanceProperty.path,
};
}
}
}

const children: CompositionNode[] = node.children.map((child) =>
deserializeDesignComponentNode({
node: child,
componentInstanceVariables,
})
);

return {
definitionId: node.definitionId,
variables,
children,
};
};

export const resolveDesignComponent = ({
node,
entityStore,
Expand All @@ -23,19 +68,20 @@ export const resolveDesignComponent = ({
(component) => component.sys.id === componentId
);

if (!designComponent) {
if (!designComponent || !('fields' in designComponent)) {
return node;
}

if (!('fields' in designComponent)) {
return node;
}
const componentFields = designComponent.fields;

const deserializedNode = {
...node,
children: componentFields.componentTree.children,
};
const deserializedNode = deserializeDesignComponentNode({
node: {
definitionId: node.definitionId,
variables: {},
children: componentFields.componentTree.children,
},
componentInstanceVariables: node.variables,
});

entityStore?.updateUnboundValues(componentFields.unboundValues);

Expand Down

0 comments on commit 7183fbd

Please sign in to comment.