Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: resolves the design token values [ALT-171] #195

Merged
merged 1 commit into from
Dec 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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