Skip to content

Commit

Permalink
feat: resolves the design token values
Browse files Browse the repository at this point in the history
  • Loading branch information
ryunsong-contentful committed Dec 14, 2023
1 parent bcc2878 commit 2b2936a
Show file tree
Hide file tree
Showing 6 changed files with 520 additions and 471 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -102,9 +102,11 @@ export const VisualEditorBlock = ({
[variableName]: variableDefinition.defaultValue,
};
}

if (variableMapping.type === 'DesignValue') {
const valueByBreakpoint = resolveDesignValue(variableMapping.valuesByBreakpoint);
const valueByBreakpoint = resolveDesignValue(
variableMapping.valuesByBreakpoint,
variableName
);
const designValue =
variableName === 'cfHeight'
? calculateNodeDefaultHeight({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ export const CompositionBlock = ({
return Object.entries(node.variables).reduce((acc, [variableName, variable]) => {
switch (variable.type) {
case 'DesignValue':
acc[variableName] = resolveDesignValue(variable.valuesByBreakpoint);
acc[variableName] = resolveDesignValue(variable.valuesByBreakpoint, variableName);
break;
case 'BoundValue': {
const [, uuid, ...path] = variable.path.split('/');
Expand Down
30 changes: 30 additions & 0 deletions packages/experience-builder-sdk/src/core/designTokenRegistry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,43 @@ import { OUTGOING_EVENTS } from '../constants';
import { sendMessage } from '../communication/sendMessage';
import { DesignTokensDefinition } from '../types';

const designTokensRegistry = {} as DesignTokensDefinition;
const templateStringRegex = /\$\{([^}]+)\}/g;

/**
* Register design tokens styling
* @param designTokenDefinition - {[key:string]: Record<string, string>}
* @returns void
*/
export const defineDesignTokens = (designTokenDefinition: DesignTokensDefinition) => {
Object.assign(designTokensRegistry, designTokenDefinition);
sendMessage(OUTGOING_EVENTS.DesignTokens, {
designTokens: designTokenDefinition,
});
};

export const getDesignTokenRegistrationForSpacing = (breakpointValue: string) => {
let resolvedValue = '';
const values = breakpointValue.split(' ');
values.forEach((value) => {
let tokenValue = value;
if (isTemplateStringFormat(value)) tokenValue = resolveSpacingDesignToken(value);
resolvedValue += `${tokenValue} `;
});

return resolvedValue;
};

// Using this because export const StringTemplateRegex = /\${(.*?)\}/g doesn't work
const isTemplateStringFormat = (str: string) => {
return templateStringRegex.test(str);
};

const resolveSpacingDesignToken = (templateString: string) => {
if (!templateStringRegex.test(templateString)) return templateString;
const nonTemplateValue = templateString.replace(templateStringRegex, '$1');
const designKeys = nonTemplateValue.split('.');
const spacingValues = designTokensRegistry[designKeys[0]] as DesignTokensDefinition;
const resolvedValue = spacingValues[designKeys[1]] as string;
return resolvedValue || '0px';
};
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ describe('useBreakpoints', () => {
[breakpoints[1].id]: 'blue',
[breakpoints[2].id]: 'green',
};
const value = getValueForBreakpoint(valuesByBreakpoint, breakpoints, 1);
const value = getValueForBreakpoint(valuesByBreakpoint, breakpoints, 1, 'cfColor');
expect(value).toBe('blue');
});

Expand All @@ -40,7 +40,7 @@ describe('useBreakpoints', () => {
[breakpoints[1].id]: 'blue',
[breakpoints[2].id]: 'green',
};
const value = getValueForBreakpoint(valuesByBreakpoint, breakpoints, -1);
const value = getValueForBreakpoint(valuesByBreakpoint, breakpoints, -1, 'cfColor');
expect(value).toBe('red');
});

Expand All @@ -51,7 +51,7 @@ describe('useBreakpoints', () => {
};
// We ask for the mobile value but it's not defined.
// Thus, we expect to get the tablet value.
const value = getValueForBreakpoint(valuesByBreakpoint, breakpoints, 2);
const value = getValueForBreakpoint(valuesByBreakpoint, breakpoints, 2, 'cfColor');
expect(value).toBe('blue');
});
});
Expand Down
25 changes: 21 additions & 4 deletions packages/experience-builder-sdk/src/hooks/useBreakpoints.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useCallback, useEffect, useMemo, useState } from 'react';
import { Breakpoint, CompositionVariableValueType } from '../types';
import { getDesignTokenRegistrationForSpacing } from '../core/designTokenRegistry';

export const MEDIA_QUERY_REGEXP = /(<|>)(\d{1,})(px|cm|mm|in|pt|pc)$/;

Expand All @@ -8,7 +9,8 @@ export type ValuesByBreakpoint =
| CompositionVariableValueType;

export type ResolveDesignValueType = (
valuesByBreakpoint: ValuesByBreakpoint
valuesByBreakpoint: ValuesByBreakpoint,
variableName: string
) => CompositionVariableValueType;

const toCSSMediaQuery = ({ query }: Breakpoint): string | undefined => {
Expand All @@ -26,6 +28,8 @@ const toCSSMediaQuery = ({ query }: Breakpoint): string | undefined => {
return undefined;
};

const availableDesignTokenVariables = new Set(['cfPadding', 'cfMargin']);

// Remove this helper when upgrading to TypeScript 5.0 - https://github.com/microsoft/TypeScript/issues/48829
const findLast = <T>(
array: Array<T>,
Expand All @@ -46,14 +50,19 @@ const getFallbackBreakpointIndex = (breakpoints: Breakpoint[]) => {
export const getValueForBreakpoint = (
valuesByBreakpoint: ValuesByBreakpoint,
breakpoints: Breakpoint[],
activeBreakpointIndex: number
activeBreakpointIndex: number,
variableName: string
) => {
const fallbackBreakpointIndex = getFallbackBreakpointIndex(breakpoints);
const fallbackBreakpointId = breakpoints[fallbackBreakpointIndex].id;
if (valuesByBreakpoint instanceof Object) {
// Assume that the values are sorted by media query to apply the cascading CSS logic
for (let index = activeBreakpointIndex; index >= 0; index--) {
const breakpointId = breakpoints[index].id;
if (availableDesignTokenVariables.has(variableName)) {
if (variableName === 'cfMargin' || variableName === 'cfPadding')
return getDesignTokenRegistrationForSpacing(valuesByBreakpoint[breakpointId]);
}
if (valuesByBreakpoint[breakpointId]) {
// If the value is defined, we use it and stop the breakpoints cascade
return valuesByBreakpoint[breakpointId];
Expand Down Expand Up @@ -132,8 +141,16 @@ export const useBreakpoints = (breakpoints: Breakpoint[]) => {
}, [breakpoints, fallbackBreakpointIndex, mediaQueryMatches]);

const resolveDesignValue: ResolveDesignValueType = useCallback(
(valuesByBreakpoint: ValuesByBreakpoint): CompositionVariableValueType => {
return getValueForBreakpoint(valuesByBreakpoint, breakpoints, activeBreakpointIndex);
(
valuesByBreakpoint: ValuesByBreakpoint,
variableName: string
): CompositionVariableValueType => {
return getValueForBreakpoint(
valuesByBreakpoint,
breakpoints,
activeBreakpointIndex,
variableName
);
},
[activeBreakpointIndex, breakpoints]
);
Expand Down
Loading

0 comments on commit 2b2936a

Please sign in to comment.