vaadin / flow-components

Java counterpart of Vaadin Web Components
101 stars 66 forks source link

If a Click on a Button with disableOnClick is interrupted by an opening Dialog, the Button is permanently disabled. #5268

Open ZImmerse opened 1 year ago

ZImmerse commented 1 year ago

Description

I have the following situation: There is a TextField that can open a Dialog depending on its Input. If the user enters a value and (without leaving the TextField) presses a Button that has disableOnClick set to true, then the Dialog opens (with some slight delay) but the Button Click is not proccessed. The Button is therefore not reenabled by its own ClickListener. This persists after the dialog was closed. Furthermore the Button can not be enabled by another button.setEnabled(true) call (by say another Button). Only a page reload will reenable the Button.

Expected outcome

Either the button click is not called and the Button is not disabeled. Or the Button gets Disabled, the click is called and the Button can be reenabled later.

Minimal reproducible example

        TextField textField = new TextField();
        add(textField);
        textField.addValueChangeListener(event -> {
            try {
                Thread.sleep(100); //This only work is there is a delay here
            } catch (InterruptedException e1) {
                e1.printStackTrace();
            }
            Button dB = new Button(VaadinIcon.CLOSE.create());
            Dialog dialog = new Dialog(dB);
            dialog.open();
            dB.addClickListener(event2 -> {
                dialog.close();
            });
        });

        Button disableOnClickButton = new Button(VaadinIcon.CURSOR.create());
        disableOnClickButton.setDisableOnClick(true);
        disableOnClickButton.addClickListener(event -> event.getSource().setEnabled(true));
        add(disableOnClickButton);

        Button enableButton = new Button(VaadinIcon.CHECK.create());
        enableButton.addClickListener(event -> {
            disableOnClickButton.setEnabled(true);
        });
        add(enableButton);

Steps to reproduce

  1. Add the snippet above to a view
  2. Enter a value in the text field.( Without leaving it!)
  3. Press the button with the cursor on it.
  4. A dialog will open
  5. Close The Dialog with the X Button
  6. The button with the cursor is now permanently disabled, even the check button can not reenable it.

Environment

Vaadin version(s): 23.3.13

Browsers

Chrome, Firefox, Edge

knoobie commented 1 year ago

That is the normal behaviour because the dialog blocks all underlying (behind the dialog) server interaction.

Dialog::setModal(false) resolves this.

ZImmerse commented 1 year ago

That is the normal behaviour because the dialog blocks all underlying (behind the dialog) server interaction.

Dialog::setModal(false) resolves this.

Thank you for your reply, that might help in my case. I still think there is a bug here though. It may not have been clear from my original desctiption, but the Button is still visibly disabled after the dialog is closed. This behaviour seems faulty to me as it does not happend if I open the dialog without pressing the button "at the same time". grafik

sissbruecker commented 1 year ago

The click event here happens after the dialog has opened. Due to the dialog being modal, Flow ignores all sub-sequent events for components that are not part of the dialog, such as the button. This causes a desync of the button's disabled state - on the client-side the button is immediately disabled due to some JS running on click, but the button is never disabled on the server because the click event is never processed there.

As for your workaround, it doesn't work because the button is actually still enabled on the server, so setEnabled(true) doesn't do anything. To fix the client-side state the only thing you can do is run some JS to enable it again:

button.getElement().executeJs("this.disabled = false");