From 60fbe00d207b6159af6bbd82a73fcdc231cb3d37 Mon Sep 17 00:00:00 2001 From: Jan-CoreBunch Date: Wed, 2 Sep 2026 16:43:23 +0200 Subject: [PATCH] fix(figma): synchronize builder data after desktop saves --- packages/figma/src/utils/frameMessaging.ts | 2 +- .../wp/Tests/BricksSynchronizationTest.php | 56 +++++++++++++++++++ packages/wp/wp/App/Rest/AllPoints.php | 18 +++++- packages/www/src/hooks/usePushFigma.ts | 2 +- packages/www/src/hooks/usePushFigmaSync.ts | 28 ++++------ 5 files changed, 86 insertions(+), 20 deletions(-) diff --git a/packages/figma/src/utils/frameMessaging.ts b/packages/figma/src/utils/frameMessaging.ts index 1daf614..104ab3b 100644 --- a/packages/figma/src/utils/frameMessaging.ts +++ b/packages/figma/src/utils/frameMessaging.ts @@ -1,6 +1,6 @@ export const postMessageToIframe = (type: string, payload: Record) => { const iframe = document.getElementById("web-app") as HTMLIFrameElement | null; - iframe?.contentWindow?.postMessage({ type, ...payload }, "*"); + iframe?.contentWindow?.postMessage({ ...payload, type }, "*"); }; export const isMessageFromEditor = (event: MessageEvent) => { diff --git a/packages/wp/Tests/BricksSynchronizationTest.php b/packages/wp/Tests/BricksSynchronizationTest.php index b86ed82..8c31885 100644 --- a/packages/wp/Tests/BricksSynchronizationTest.php +++ b/packages/wp/Tests/BricksSynchronizationTest.php @@ -22,6 +22,20 @@ public function get_data() { } } +if ( ! class_exists( 'WP_REST_Request' ) ) { + class WP_REST_Request { + private $body; + + public function __construct( $body = '' ) { + $this->body = $body; + } + + public function get_body() { + return $this->body; + } + } +} + if ( ! function_exists( 'get_option' ) ) { function get_option( $option, $default = false ) { return array_key_exists( $option, $GLOBALS['cf_test_options'] ) @@ -219,6 +233,10 @@ public function get_param( $key ) { }; } + private function createFigmaRequest( array $body ): WP_REST_Request { + return new WP_REST_Request( json_encode( $body ) ); + } + public function testEmptySelectorPayloadStillRefreshesBricksVariables(): void { $request = $this->createRequest( array( @@ -263,6 +281,44 @@ public function testSelectorPayloadIsTrimmedFilteredAndDeduplicated(): void { $this->assertSame( array( array( 'padding', 'margin' ) ), $GLOBALS['cf_test_bricks_builder']->selectors ); } + public function testFigmaEmptySelectorPayloadDoesNotCreateBlankOxygenSelector(): void { + $GLOBALS['cf_test_options']['core_framework_main'] = array( + 'bricks' => false, + 'oxygen' => true, + ); + $GLOBALS['cf_test_bricks_builder'] = new CoreFrameworkTestBuilder( false ); + $GLOBALS['cf_test_oxygen_builder'] = new CoreFrameworkTestBuilder( true ); + + $response = $this->createRestController()->figma_update_classes( + $this->createFigmaRequest( array( 'classes' => '' ) ) + ); + + $this->assertSame( array( array() ), $GLOBALS['cf_test_oxygen_builder']->selectors ); + $this->assertSame( + array( + 'success' => true, + 'active_builders' => array( 'oxygen' ), + ), + $response->get_data() + ); + } + + public function testFigmaEmptySelectorPayloadStillRefreshesBricksVariables(): void { + $response = $this->createRestController()->figma_update_classes( + $this->createFigmaRequest( array( 'classes' => '' ) ) + ); + + $this->assertSame( array( array() ), $GLOBALS['cf_test_bricks_builder']->selectors ); + $this->assertSame( 1, $GLOBALS['cf_test_bricks_builder']->variable_refreshes ); + $this->assertSame( + array( + 'success' => true, + 'active_builders' => array( 'bricks' ), + ), + $response->get_data() + ); + } + public function testClassSynchronizationRemovesFinalCoreClassAndOnlyItsReferences(): void { $GLOBALS['cf_test_options'][ BricksFunctions::BRICKS_CLASSES_OPTION ] = array( array( diff --git a/packages/wp/wp/App/Rest/AllPoints.php b/packages/wp/wp/App/Rest/AllPoints.php index 1321445..bafe5c3 100644 --- a/packages/wp/wp/App/Rest/AllPoints.php +++ b/packages/wp/wp/App/Rest/AllPoints.php @@ -1738,7 +1738,23 @@ public function figma_update_classes( \WP_REST_Request $request ) { ); } - $new_selectors_array = explode( ',', $classes ) ?? array(); + if ( is_string( $classes ) ) { + $classes = explode( ',', $classes ); + } + + $new_selectors_array = is_array( $classes ) + ? array_values( + array_unique( + array_filter( + array_map( + fn( $class ): string => is_string( $class ) ? trim( $class ) : '', + $classes + ), + fn( $class ): bool => '' !== $class + ) + ) + ) + : array(); $builder_array = array( 'oxygen' => array( 'is_active' => CoreFrameworkOxygen()->is_oxygen(), diff --git a/packages/www/src/hooks/usePushFigma.ts b/packages/www/src/hooks/usePushFigma.ts index 94ec105..d848db6 100644 --- a/packages/www/src/hooks/usePushFigma.ts +++ b/packages/www/src/hooks/usePushFigma.ts @@ -44,10 +44,10 @@ export function usePushFigma() { const [cssResponse, presetResponse] = await Promise.all([ syncCSSWithFigma({ cssString: props.cssString, ...wpApiProxyProps }), updatePresetWithFigma({ newPresetData: props.newPresetData, ...wpApiProxyProps }), - handleFigmaPushSync({ ...wpApiProxyProps }), ]); if (cssResponse && presetResponse) { + await handleFigmaPushSync({ preset: props.newPresetData, ...wpApiProxyProps }); toast.success("Synced successfully"); } diff --git a/packages/www/src/hooks/usePushFigmaSync.ts b/packages/www/src/hooks/usePushFigmaSync.ts index 915c90f..bbc3c05 100644 --- a/packages/www/src/hooks/usePushFigmaSync.ts +++ b/packages/www/src/hooks/usePushFigmaSync.ts @@ -20,14 +20,12 @@ import { getFirstSelector } from "components/modules/components/Components.edito import { cssGenerator } from "cssGenerator"; import { colorSystemFormDataAtom, - getPresetFromCurrentPresetAtom, joinedStylesAtom, presetPreferencesSelector, } from "state"; export function usePushFigmaSync() { const getJoinedStyles = useAtomCallback(useCallback((get) => get(joinedStylesAtom), [])); - const getNewPreset = useAtomCallback(useCallback((get) => get(getPresetFromCurrentPresetAtom), [])); const getPresetPreferences = useAtomCallback(useCallback((get) => get(presetPreferencesSelector), [])); const getColorSystemFormDataAtom = useAtomCallback(useCallback((get) => get(colorSystemFormDataAtom), [])); @@ -85,10 +83,8 @@ export function usePushFigmaSync() { classAccumulator.push(themeInvertedClass); } - if (!classAccumulator.length) { - return; - } - + // The classes endpoint also refreshes builder variables, so it must run for + // projects whose stylesheet contains variables but no class selectors. await Promise.allSettled([ updateGroupedClasses({ groupedClassNames: getClassNamesGroupedByGroups(preset), @@ -390,28 +386,26 @@ export function usePushFigmaSync() { } } - interface IHandleFigmaPushSync extends WpApiProxyProps {} + interface IHandleFigmaPushSync extends WpApiProxyProps { + readonly preset: Preset; + } const handleFigmaPushSync = useCallback(async (props: IHandleFigmaPushSync) => { - const preset = getNewPreset(); - - if (!preset) { - return; - } + const { preset, ...wpApiProxyProps } = props; const cssObjects = getJoinedStyles(); const preferences = getPresetPreferences(); await Promise.allSettled([ - handleClassesRefresh({ cssObjects, preset, ...props }), - handleColorsRefresh({ preset, ...props }), + handleClassesRefresh({ cssObjects, preset, ...wpApiProxyProps }), + handleColorsRefresh({ preset, ...wpApiProxyProps }), handleCssGeneratorPrefixed({ cssObjects, classPrefix: preset.classPrefix, variablePrefix: preset.variablePrefix, minScreenWidth: preferences.min_screen_width, maxScreenWidth: preferences.max_screen_width, - ...props, + ...wpApiProxyProps, }), handleOxygenHelperStyleSheetGenerator({ preset, @@ -419,10 +413,10 @@ export function usePushFigmaSync() { variablePrefix: preset.variablePrefix, minScreenWidth: preferences.min_screen_width, maxScreenWidth: preferences.max_screen_width, - ...props, + ...wpApiProxyProps, }), ]); - }, [getJoinedStyles, getNewPreset, getPresetPreferences, handleClassesRefresh, handleColorsRefresh, handleCssGeneratorPrefixed, handleOxygenHelperStyleSheetGenerator]); + }, [getJoinedStyles, getPresetPreferences, handleClassesRefresh, handleColorsRefresh, handleCssGeneratorPrefixed, handleOxygenHelperStyleSheetGenerator]); return { handleFigmaPushSync,