Shopify / shopify-app-bridge

https://shopify.dev/docs/api/app-bridge
82 stars 9 forks source link

Cannot programatically adjust the `loading` and `disabled` state of buttons in the `ui-save-bar` #334

Closed darrynten closed 2 months ago

darrynten commented 2 months ago

Describe the bug

I'm trying to disable the "save" and "discard" buttons and put them into a loading state.

The documentation currently says to add the loading and the disabled attributes to achieve this:

image image

I've tried to do this both with Turbo Stimulus, and without, using the following markups when migrating from v3 to v4 contextual save bar:

Without Stimulus

  <ui-save-bar id="my-save-bar" discardConfirmation>
    <button variant="primary" id="my-save-button"></button>
    <button id="my-discard-button"></button>
  </ui-save-bar>

  <form
    id="my-form"
    action="/api/save"
    method="POST"
    autocomplete="off"
    data-save-bar
    >
    <!-- rest of form -->
  </form>

Things that have not worked

document.getElementById('my-save-button').setAttribute('disabled', '')
document.getElementById('my-discard-button').setAttribute('disabled', '')
document.getElementById('my-save-bar').children[0].setAttribute('disabled', '')

Although upon logging document.getElementById('my-save-button') you can see the attributes have been set in line with the documentation:

<button variant="primary" id="my-save-button" disabled class></button>

However the button in the header remains clickable

image

With Stimulus

  <ui-save-bar id="my-save-bar" discardConfirmation data-app-target="saveBar">
    <button variant="primary" id="my-save-button" data-app-target="save">SAVE</button>
    <button id="my-discard-button" data-app-target="discard"></button>
  </ui-save-bar>

  <form
    id="my-form"
    action="/api/save"
    method="POST"
    autocomplete="off"
    data-app-target="form"
    data-save-bar
    data-action="submit->app#save reset->app#discard"
    >
    <!-- rest of form -->
  </form>

Things that have not worked

export default class extends Controller {
  static targets = [
    "form",
    "saveBar",
    "save",
    "discard"
  ]

  save() {
    console.log('this triggers when save button is clicked')

    // these do nothing to the visible save bar
    this.saveTarget.setAttribute('disabled', '')
    this.discardTarget.setAttribute('disabled', '')

    // however the attributes are present when logging:
    console.log(this.saveTarget)
  }

  discard() {
    console.log('this triggers when discard button is clicked')
  }
}

Console log results in:

<button variant="primary" id="my-save-button" data-app-target="save" disabled="" class=""></button>

To Reproduce

See above with and without Stimulus examples

Expected behaviour

We should easily be able to enable and disable the save/discard buttons and set their loading state like we could in App Bridge 3.x

Contextual information

Packages and versions

List the relevant packages you’re using, and their versions. For example:

Platform

Additional context

It's interesting to note that even when the initial markup contains the disabled attribute the buttons are still clickable

henrytao-me commented 2 months ago

@darrynten In this case, both <ui-save-bar> and <form data-save-bar> at the same time is causing the issue. <form data-save-bar> is the one controlling the SaveBar UI right now. That's why manipulating ui-save-bar doesn't work.

I suggest to remove data-save-bar from the form in this case.

darrynten commented 2 months ago

The documentation makes it seem like they should live together in the DOM

Does that mean we have to manage our own dirty/clean state, discard actions etc using only the ui-save-bar?

MitchLillie commented 2 months ago

Hi @darrynten - Could you point to the docs that suggest they should live together? We should definitely update to clarify that.

As Henry mentioned, they should be somewhat mutually exclusive as described in the ui-save-bar docs:

The Contextual Save Bar API is used to indicate that a form on the current page has unsaved information. It can be used in 2 ways:

  1. It is automatically configured when you provide the data-save-bar attribute to a form element and will display the contextual save bar when there are unsaved changes. For more information, refer to the contextual save bar API.
  2. It can be controlled declaratively through the ui-save-bar element.
darrynten commented 2 months ago

Hi @MitchLillie

The way that is written reads like the Contextual Save Bar API is configured through the data-save-bar attribute on the form, and if you want finer control over things such as loading state you need to do so declaratively with the ui-save-bar element, making it seem like you need both the form and the ui-save-bar in an app in order to use the Contextual Save Bar API.

This is not the case.

MitchLillie commented 2 months ago

@darrynten - Totally see your point. We will update the doc to clarify that the two ways are mutually-exclusive 👍