Spectrum Textfield Issues in Photoshop & XD MAX 2021 Releases

Note: This post by @kerrishotts is a mirror of an Adobe Tech Blog post to facilitate discussion.

In UXP 5.5.1 (the version shipping at MAX’21 in both Adobe XD version 45 and Photoshop version 23), there are some issues that may impact the usability of your plugin. You may wish to apply some of these workarounds in order to avoid any issues after MAX.

Issues you may encounter

  • sp-textfield doesn’t fire change
  • Setting a numeric sp-textfield’s value attribute can fail
  • Typing a decimal point in a numeric sp-textfield is difficult

sp-textfield doesn’t fire change

Applies to PS 23 and XD 45

sp-textfield won’t send change events. If your code relies upon change events for functionality this may cause issues in your plugin. For example, if you rely on change to handle validation checks, then those validation checks won’t run when you expect. If you rely on change to update other UI widgets, those will also fail to be updated.

Manual workarounds

Other events on sp-textfield continue to work, including input, focus, and blur. As such, you may elect to use these events instead of change.

You can also switch from sp-textfield to sp-textarea if your design will accommodate it. Alternatively, you can switch to the native input type="text" widget temporarily.

Automatic workaround (“buggyfill”)

Alternatively, you can drop in the following code snippet in your index.html or your main JavaScript file. This code will attempt to synthesize the change event whenever a change occurs in sp-textfield widgets. This is done by hooking into the focus and blur events. It works with both Vanilla and React. It’s not been tested with other frameworks.


(function () {
  if(require("uxp").versions.uxp.startsWith("uxp-5.5.1")) {
    let curField, curValue;
    document.addEventListener("focus", evt => {
      curField = evt.target;
      curValue = curField.value;
    }, true);
    document.addEventListener("blur", evt => {
      if (evt.target.tagName === "SP-TEXTFIELD") {
        if (curField === evt.target) {
          if (curValue !== evt.target.value) {
            const changeEvent = new Event("change", {
              bubbles: true
            });
            evt.target.dispatchEvent(changeEvent);
          }
        }
      }
    }, true);
  }
})();

Setting a numeric sp-textfield’s value attribute can fail

Applies to PS 23 and XD 45

If you set the value attribute of a numeric sp-textfield, you should do so using strings, as the implicit type conversion will fail.

const numberField = document.querySelector("sp-textfield[type=number]");
numberField.value = 10; /* won't work; field will be blank */
numberField.value = "10"; /* will work. */

Manual workaround

There’s no drop-in code to address this issue. Instead, you should be certain that you’re assigning a string representation of a number instead of the actual number itself.

const numberField = document.querySelector("sp-textfield[type=number]");
numberField.value = `${numberOfSteps}`; /* template literals will work */
numberField.value = "" + numberOfSteps; /* or you can do this. */

Typing a decimal point in a numeric sp-textfield is difficult

Applies to PS 23

Typing a decimal point as part of a non-integer number can be difficult. Users may need to repeat the keystroke several times before the decimal point stays.

Manual workarounds

You can temporarily switch from <sp-textfield type="number"> to <sp-textfield type="text">. This does mean that users could enter non-numeric data, but you can add validators to handle this.

You could also switch to input type="text" temporarily.

Automatic workaround

Alternatively, you can use the following drop-in workaround. It detects any new numeric sp-textfield widgets and automatically converts them to a regular text field. It then adds a handler that forces the contents of the field to be numeric. Note that the type of the value attribute remains a string, so you’ll need to convert the value to a number if you intend on using it as such (e.g., Number(field.value))

(function () {
  function fixNumericSpTextField() {
    if(require("uxp").versions.uxp.startsWith("uxp-5.5.1")) {
      const candidates = document.querySelectorAll("sp-textfield[type=number]");
      candidates.forEach(el => {
        el.setAttribute("type", "text");
        el.setAttribute("data-uxp-type", "number");
      });
    }
    document.addEventListener("input", evt => {
      const { target } = evt;
      if (target.tagName === "SP-TEXTFIELD") {
        if (target.getAttribute("data-uxp-type") === "number") {
          if (Number.isNaN(Number(target.value)) || target.value.indexOf(" ") > -1) {
            target.value = target.getAttribute("data-uxp-last-good-value") || "0";
          } else {
            target.setAttribute("data-uxp-last-good-value", target.value.trim());
          }
        }
      }
    });
    const timer = setInterval(() => {
    /* IMPORTANT: next lines use private APIs to make  sure we keep 
       fixing up text fields as they are added to the DOM.
       DO NOT USE _domClient OR frameAck FOR ANY OTHER PURPOSE. */
      if (document._domClient) {
        document._domClient.addEventListener("frameAck", fixNumericSpTextField);
        clearInterval(timer);
        fixNumericSpTextField();
      }
    }, 16);
  }
})();

Next Steps

Now that you know the issues, what should you do next?

  1. You should immediately test your plugins in the Prerelease build of Photoshop and/or XD. (To join the Photoshop or XD prerelease groups, if you’re not already a member, reach out to finnegan@adobe.com.)This will help you determine if your plugins are impacted by the issues.

    While the issues themselves are present in all builds, how your plugin is impacted will be based upon your code. If you use input instead of change, for example, you’re unlikely to be impacted by the fact that sp-textfield doesn’t want to fire the change event.

  2. Apply the appropriate remediations to your plugin and re-test.

    You may elect to use the automated workarounds above to help address the issue more quickly, but you should still test your plugin to ensure that these workarounds provide an acceptable experience.

  3. If you’ve made changes, resubmit your plugin to the marketplace.

    Please be sure to let us know if you submit a change so that we can ensure your fix is approved before MAX. Email xdplugindevs1@adobe.com, or respond here in the forums.

If you have questions or run into issues, please don’t hesitate to reach out.

The latest Photoshop (beta) also has a cursor issue on both Mac and Windows platforms. For plugins that are flexible and allow the user to grab the sides and drag them larger or smaller, i.e. “maximumSize” is greater than “minimumSize” in the plugin’s manifest, the cursor changes to the “grabber” when it passes slowly over the edge of the plugin and does NOT revert to the default pointer cursor when it’s over the contents of the plugin. It continues to be the grabber. This is most easily replicated passing the cursor over the sides of the plugin and not passing over the top and the bottom. I see this happening with both the UI Kitchen Sink and Alchemist plugins.

The fix is to explicitly define the “cursor” with CSS: body {cursor: default;}

If you’re plugin has flexible sides and you resubmit to resolve the above issues, you might consider including this also.

I’m not seeing this with plugins that have fixed dimensions, i.e. “maximumSize” equals “minimumSize” in the plugin’s manifest.

Thanks for the work-arounds. I just tested the change workaround and it works perfectly.

Regarding the truncated text issue, is that going to be fixed in the Max release? The latest beta release still has the issue where document.getElementById("yourSPTextfieldID").value is returning the truncated text with “…” instead of the full text string.

Isn’t the article about how these issues are in the MAX release? Maybe I’m misunderstanding… @kerrishotts ?

The truncation issues are fixed as part of the MAX release builds.

Thanks for confirming. Glad to know it will be fixed. I just wanted to make sure on this one in particular because it affects the license key inputs for all of my plugins.

Also, I tested and confirmed the other issue with the placeholder text was fixed in the latest beta release.

I just submitted updates to 4 plugins and sent an email as you requested. Thanks :slight_smile:

No matter how I tried to make onChange work with buggyfill script solution, I couldn’t. Tried putting it just before entrypoints.setup(). Also right after React.render(). No luck

Switching to <input /> worked, but it looks out of place. Is there maybe some CSS ready for input to look like <sp-textfield/>?

Just tried to style myself, but none of these work as I’d expect:

  • padding - text is still at the top left no matter what padding I set
  • line-height does not change anything

Managed to make it work (kinda) by using a wrapper

const SpTextfield = wrapWc('sp-textfield')

// <...>

    return (
        <form>
            <sp-body class="form">
                <SpTextfield onChange={(e) => setTitle(e.target.value)} value={title}>
                    <sp-label slot="label">Label</sp-label>
                </SpTextfield>
            </sp-body>
            <sp-footer>
                <sp-button type="submit" onClick={handleForm}>Submit</sp-button>
            </sp-footer>
        </form>
    )

But apparently there’s another issue. There’s no way form submission would work on [Enter]. Also if I try to move onSubmit from <sp-button/> to <form onSubmit={handleForm}/>, then it fails even when I click the button.

Although, if I leave form and button as they are, but change SpTextfield with HTML:

                <sp-label>Label</sp-label>
                <div>
                    <input onChange={(e) => setTitle(e.target.value)} value={title}></input>
                </div>

Then form submission works both on [Enter] and on button click

I am experiencing a few issues with the sp-textfield.

1: I have 4 sp-textfields to input parameters and a clear button to reset them.

When you input text or hover the label changes from grey to white.

But when I clear them like this:
document.querySelector(’.sptextfield0’).value = ‘’

They stay white until I hover again. Is this a known issue or am I doing something wrong?

Also I think that sometimes my plugin is not properly giving up focus after using it and then pressing command space to zoom in my document. It does not always happen but often i hear the MacOS beep like when something is blocked but the zooming still works.