Hero component: renamed buttons reset in preview after tab navigation

Created on 17 April 2025, 25 days ago

Overview

When editing the Hero component in the XB, if the button labels are renamed (for example, "View" to "Accept" and "Click" to "Decline"), the changes initially reflect correctly in both the preview and the editing slide.

However, after selecting a renamed button (like "Accept") and pressing the Tab key twice, the button labels in the preview revert back to their original values ("View" and "Click"), while the editing slide still displays the renamed labels ("Accept" and "Decline").

This creates a mismatch between the preview and the editor panel.

Steps to Reproduce:

  1. Drag and drop a Hero component onto the canvas.
  2. Rename the button labels:
    • Change "View" to "Accept"
    • Change "Click" to "Decline"
  3. Click into the "Accept" button field in the editing slide.
  4. Press the Tab key twice.
  5. Observe the issue:
    • In the preview, the button labels revert to "View" and "Click".
    • In the editing slide, the labels still display "Accept" and "Decline".

Proposed resolution

User interface changes

๐Ÿ› Bug report
Status

Active

Version

0.0

Component

Page builder

Created by

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Merge Requests

Comments & Activities

  • Issue created by @mayur-sose
  • ๐Ÿ‡ง๐Ÿ‡ชBelgium wim leers Ghent ๐Ÿ‡ง๐Ÿ‡ช๐Ÿ‡ช๐Ÿ‡บ

    Interesting find! Not sure yet if this is a client-side or server-side bug. But since the server side renders the preview for the component tree it receives, I suspect it's the client side. If it's the server side, then I suspect it's in the auto-save functionality.

  • ๐Ÿ‡ฎ๐Ÿ‡ณIndia BhumikaVarshney Delhi

    Hi
    I tried to reporduce the issue but not able to reporcude the same.
    I edited the button and followed the steps added above.
    But in preview i can see the same text which we added.
    please refer the screenshots

  • ๐Ÿ‡ง๐Ÿ‡ชBelgium wim leers Ghent ๐Ÿ‡ง๐Ÿ‡ช๐Ÿ‡ช๐Ÿ‡บ

    Thanks, @bhumikavarshney!

  • ๐Ÿ‡ง๐Ÿ‡ชBelgium wim leers Ghent ๐Ÿ‡ง๐Ÿ‡ช๐Ÿ‡ช๐Ÿ‡บ

    Definitely a bug! ๐Ÿ‘ Thanks! ๐Ÿ™

    This is either a bug on the back-end (internal HTTP API or Auto-save), in the client-side handling of Auto-save, or in the Redux-integrated field widgets. Can you please attach the request body that was sent to generate that form when it was broken? ๐Ÿ™

  • Below is the request payload (when it was broken) :

    {
        "componentInstanceUuid": "ebfd85b0-1622-4a67-aa18-4847301ce5a5",
        "componentType": "sdc.experience_builder.my-hero",
        "model": {
            "source": {
                "heading": {
                    "expression": "โ„น๏ธŽstringโŸvalue",
                    "sourceType": "static:field_item:string",
                    "value": "There goes my hero"
                },
                "subheading": {
                    "expression": "โ„น๏ธŽstringโŸvalue",
                    "sourceType": "static:field_item:string",
                    "value": "Watch him as he goes!"
                },
                "cta1": {
                    "expression": "โ„น๏ธŽstringโŸvalue",
                    "sourceType": "static:field_item:string",
                    "value": "View"
                },
                "cta1href": {
                    "expression": "โ„น๏ธŽlinkโŸuri",
                    "sourceType": "static:field_item:link",
                    "value": "https://example.com",
                    "sourceTypeSettings": {
                        "instance": {
                            "title": 0
                        }
                    }
                },
                "cta2": {
                    "expression": "โ„น๏ธŽstringโŸvalue",
                    "sourceType": "static:field_item:string",
                    "value": "Click"
                }
            },
            "resolved": {
                "heading": "There goes my hero",
                "subheading": "Watch him as he goes!",
                "cta1": "View",
                "cta1href": "https://example.com",
                "cta2": "Click"
            }
        }
    }
    
  • ๐Ÿ‡ซ๐Ÿ‡ฎFinland lauriii Finland

    I was able to reproduce this! This is critical because of data loss.

  • ๐Ÿ‡ฌ๐Ÿ‡งUnited Kingdom jessebaker

    When editing the Hero component that is bundled with XB

    1. Update the values of some of the fields
    2. Observe that the preview is updated with those changes
    3. Focus into and then focus out of the "CTA 1 link" (autocomplete) field
    4. Observe that, while the form values remain updated, the preview reverts back to before the changes made in step 1.

    If you view the network request that occurs on blur you can see that the data sent as PATCH to the server contains the starting values of the form, not the updated ones.

  • First commit to issue fork.
  • ๐Ÿ‡ฎ๐Ÿ‡ณIndia omkar-pd

    Adding attributes.onChange in dependency array fixes the issue.

    While this causes the effect to run multiple times as onChange reference update, but I think it's necessary to ensure event handlers always have access to the current state.

  • Pipeline finished with Failed
    5 days ago
    Total: 671s
    #491515
  • ๐Ÿ‡ฎ๐Ÿ‡ณIndia omkar-pd

    Or we could use useRef to fix the issue

    diff --git a/ui/src/components/form/components/TextFieldAutocomplete.tsx b/ui/src/components/form/components/TextFieldAutocomplete.tsx
    index 762e9b47..6ff8adee 100644
    --- a/ui/src/components/form/components/TextFieldAutocomplete.tsx
    +++ b/ui/src/components/form/components/TextFieldAutocomplete.tsx
    @@ -1,6 +1,6 @@
     import clsx from 'clsx';
     import { TextField as RadixThemesTextField } from '@radix-ui/themes';
    -import { forwardRef, useEffect, useImperativeHandle } from 'react';
    +import { forwardRef, useEffect, useImperativeHandle, useRef } from 'react';
     import type { MutableRefObject } from 'react';
     import type { Attributes } from '@/types/DrupalAttribute';
     import styles from './TextField.module.css';
    @@ -40,6 +40,12 @@ const TextFieldAutocomplete = forwardRef(
         attributes['data-xb-no-update'] = '';
     
         const inputRef = ref as MutableRefObject<HTMLInputElement>;
    +    const onChangeRef = useRef(attributes.onChange);
    +
    +    // Update ref when onChange changes
    +    useEffect(() => {
    +      onChangeRef.current = attributes.onChange;
    +    }, [attributes.onChange]);
     
         // Create a version of the ref that will appease Typescript.
         useImperativeHandle(ref, () => inputRef.current as HTMLInputElement);
    @@ -55,7 +61,7 @@ const TextFieldAutocomplete = forwardRef(
           inputRef.current.removeAttribute('data-xb-no-update');
           setTimeout(() => {
             // Call the onChange listener so the Redux store is updated.
    -        if (attributes?.onChange) {
    +        if (onChangeRef.current) {
               const event = new Event('change');
     
               inputRef.current.value = e.detail.target.value;
    @@ -63,8 +69,8 @@ const TextFieldAutocomplete = forwardRef(
                 writable: false,
                 value: inputRef.current,
               });
    -          if (typeof attributes?.onChange === 'function') {
    -            attributes.onChange(event);
    +          if (typeof onChangeRef.current === 'function') {
    +            onChangeRef.current(event);
               }
             }
           });
    @@ -100,14 +106,14 @@ const TextFieldAutocomplete = forwardRef(
               // If the input is blurred, remove the attribute that prevents real
               // time preview updates.
               inputRef.current.removeAttribute('data-xb-no-update');
    -          if (attributes?.onChange) {
    +          if (onChangeRef.current) {
                 const event = new Event('change');
                 Object.defineProperty(event, 'target', {
                   writable: false,
                   value: inputRef.current,
                 });
    -            if (typeof attributes?.onChange === 'function') {
    -              attributes.onChange(event);
    +            if (typeof onChangeRef.current === 'function') {
    +              onChangeRef.current(event);
                 }
               }
             };
    
  • ๐Ÿ‡ฎ๐Ÿ‡ณIndia omkar-pd
  • ๐Ÿ‡บ๐Ÿ‡ธUnited States bnjmnm Ann Arbor, MI

    Or we could use useRef to fix the issue

    Good idea. Lets do that to minimize useEffect, even if it means a bit of extra logic.

  • ๐Ÿ‡บ๐Ÿ‡ธUnited States bnjmnm Ann Arbor, MI

    bnjmnm โ†’ changed the visibility of the branch 3519734-blur-on-the to hidden.

  • ๐Ÿ‡บ๐Ÿ‡ธUnited States bnjmnm Ann Arbor, MI

    Added 3519734-ref-approach, first commit is an intentionally failing test and the second commit is a version of the ref approach suggested by @omkar-pd in #14

  • Pipeline finished with Skipped
    3 days ago
    #493368
    • bnjmnm โ†’ committed f98dd275 on 0.x
      Issue #3519734 by bnjmnm, omkar-pd, mayur-sose, jessebaker: Blur on the...
  • ๐Ÿ‡ฆ๐Ÿ‡บAustralia larowlan ๐Ÿ‡ฆ๐Ÿ‡บ๐Ÿ.au GMT+10
  • ๐Ÿ‡บ๐Ÿ‡ธUnited States bnjmnm Ann Arbor, MI

    With @larowlan approval this can be merged.

Production build 0.71.5 2024