From 32c4914abf21e6c5653d49154ed73f67fd556577 Mon Sep 17 00:00:00 2001 From: Teppo Kurki Date: Fri, 17 Jan 2025 18:26:08 +0200 Subject: [PATCH] feat: DisabledUpdateMode for ShortcutRegistration (#20874) * feat: DisabledUpdateMode for ShortcutRegistration --- .../flow/component/ShortcutRegistration.java | 41 ++++++++++++++++++- .../component/ShortcutRegistrationTest.java | 19 +++++++++ .../vaadin/flow/uitest/ui/ShortcutsView.java | 15 +++++++ .../vaadin/flow/uitest/ui/ShortcutsIT.java | 14 ++++++- 4 files changed, 85 insertions(+), 4 deletions(-) diff --git a/flow-server/src/main/java/com/vaadin/flow/component/ShortcutRegistration.java b/flow-server/src/main/java/com/vaadin/flow/component/ShortcutRegistration.java index ce65298fda2..8c232e51cc0 100644 --- a/flow-server/src/main/java/com/vaadin/flow/component/ShortcutRegistration.java +++ b/flow-server/src/main/java/com/vaadin/flow/component/ShortcutRegistration.java @@ -30,6 +30,7 @@ import java.util.stream.Collectors; import com.vaadin.flow.component.internal.UIInternals; +import com.vaadin.flow.dom.DisabledUpdateMode; import com.vaadin.flow.dom.DomListenerRegistration; import com.vaadin.flow.function.SerializableConsumer; import com.vaadin.flow.function.SerializableSupplier; @@ -95,6 +96,8 @@ public class ShortcutRegistration implements Registration, Serializable { private List registrations = new ArrayList<>(); + private DisabledUpdateMode mode = DisabledUpdateMode.ONLY_WHEN_ENABLED; + // beforeClientResponse callback // needs to be an anonymous class to prevent deserialization issues // see #17201 @@ -517,6 +520,39 @@ public Component getLifecycleOwner() { return lifecycleOwner; } + /** + * Configure whether this listener will be called even in cases when the + * component is disabled. Defaults to + * {@link DisabledUpdateMode#ONLY_WHEN_ENABLED}. + * + * @param disabledUpdateMode + * {@link DisabledUpdateMode#ONLY_WHEN_ENABLED} to only fire + * events when the component is enabled, + * {@link DisabledUpdateMode#ALWAYS} to fire events also when the + * component is disabled. + * + * @return this registration, for chaining + */ + public ShortcutRegistration setDisabledUpdateMode( + DisabledUpdateMode disabledUpdateMode) { + if (disabledUpdateMode == null) { + throw new IllegalArgumentException( + "RPC communication control mode for disabled element must not be null"); + } + mode = disabledUpdateMode; + return this; + } + + /** + * Returns whether this listener will be called even in cases when the + * component is disabled. + * + * @return current disabledUpdateMode for this listener + */ + public DisabledUpdateMode getDisabledUpdateMode() { + return mode; + } + /** * Used for testing purposes. * @@ -637,8 +673,9 @@ private Component getComponentEventSource(int listenOnIndex) { } private void fireShortcutEvent(Component component) { - if (ancestorsOrSelfAreVisible(lifecycleOwner) - && lifecycleOwner.getElement().isEnabled()) { + if (ancestorsOrSelfAreVisible(lifecycleOwner) && (lifecycleOwner + .getElement().isEnabled() + || DisabledUpdateMode.ALWAYS.equals(getDisabledUpdateMode()))) { invokeShortcutEventListener(component); } } diff --git a/flow-server/src/test/java/com/vaadin/flow/component/ShortcutRegistrationTest.java b/flow-server/src/test/java/com/vaadin/flow/component/ShortcutRegistrationTest.java index 4e27fa0f440..5ca99cb1f4c 100644 --- a/flow-server/src/test/java/com/vaadin/flow/component/ShortcutRegistrationTest.java +++ b/flow-server/src/test/java/com/vaadin/flow/component/ShortcutRegistrationTest.java @@ -33,6 +33,7 @@ import com.vaadin.flow.component.internal.PendingJavaScriptInvocation; import com.vaadin.flow.component.internal.UIInternals; +import com.vaadin.flow.dom.DisabledUpdateMode; import com.vaadin.flow.dom.Element; import com.vaadin.flow.dom.ElementFactory; import com.vaadin.flow.function.SerializableConsumer; @@ -506,6 +507,24 @@ public void constructedRegistration_lifecycleOnwerIsDisabled_shorcutEventIsNotFi Assert.assertNull(event.get()); } + @Test + public void constructedRegistration_lifecycleOwnerIsDisabledWithDisabledUpdateModeAlways_shortcutEventIsFired() { + AtomicReference event = new AtomicReference<>(); + + new ShortcutRegistration(lifecycleOwner, () -> listenOn, event::set, + Key.KEY_A).setDisabledUpdateMode(DisabledUpdateMode.ALWAYS); + + Element element = mockLifecycle(true); + element.setEnabled(false); + + clientResponse(); + + listenOn[0].getEventBus() + .fireEvent(new KeyDownEvent(listenOn[0], Key.KEY_A.toString())); + + Assert.assertNotNull(event.get()); + } + @Test public void constructedRegistration_lifecycleOnwerIsInvisible_shorcutEventIsNotFired() { AtomicReference event = new AtomicReference<>(); diff --git a/flow-tests/test-root-context/src/main/java/com/vaadin/flow/uitest/ui/ShortcutsView.java b/flow-tests/test-root-context/src/main/java/com/vaadin/flow/uitest/ui/ShortcutsView.java index ef634efa933..d8e9213a926 100644 --- a/flow-tests/test-root-context/src/main/java/com/vaadin/flow/uitest/ui/ShortcutsView.java +++ b/flow-tests/test-root-context/src/main/java/com/vaadin/flow/uitest/ui/ShortcutsView.java @@ -29,6 +29,7 @@ import com.vaadin.flow.component.html.NativeButton; import com.vaadin.flow.component.html.Paragraph; import com.vaadin.flow.data.value.ValueChangeMode; +import com.vaadin.flow.dom.DisabledUpdateMode; import com.vaadin.flow.router.Route; import com.vaadin.flow.uitest.servlet.ViewTestLayout; @@ -86,6 +87,20 @@ public ShortcutsView() { add(disabledButton); + // DisabledUpdateMode.ALWAYS makes shortcut work when component is + // disabled + NativeButton disabledButtonWithAlwaysMode = new NativeButton(); + disabledButtonWithAlwaysMode.setEnabled(false); + disabledButtonWithAlwaysMode.addClickListener(event -> { + actual.setValue("DISABLED CLICKED"); + }); + disabledButtonWithAlwaysMode + .addClickShortcut(Key.KEY_P, KeyModifier.SHIFT, + KeyModifier.CONTROL) + .setDisabledUpdateMode(DisabledUpdateMode.ALWAYS); + + add(disabledButtonWithAlwaysMode); + // listenOnScopesTheShortcut Div subview = new Div(); subview.setId("subview"); diff --git a/flow-tests/test-root-context/src/test/java/com/vaadin/flow/uitest/ui/ShortcutsIT.java b/flow-tests/test-root-context/src/test/java/com/vaadin/flow/uitest/ui/ShortcutsIT.java index 2617b1c1d9a..d452d263224 100644 --- a/flow-tests/test-root-context/src/test/java/com/vaadin/flow/uitest/ui/ShortcutsIT.java +++ b/flow-tests/test-root-context/src/test/java/com/vaadin/flow/uitest/ui/ShortcutsIT.java @@ -80,7 +80,7 @@ public void shortcutsOnlyWorkWhenComponentIsVisible() { @Test public void shortcutOnlyWorksWhenComponentIsEnabled() { - sendKeys(Keys.CONTROL, "U"); // ctrl+shift+u + sendKeys(Keys.CONTROL, Keys.SHIFT, "u"); // clicking the button disables it, and clicking again should not have // and effect @@ -89,7 +89,17 @@ public void shortcutOnlyWorksWhenComponentIsEnabled() { resetActual(); assertActualEquals(DEFAULT_VALUE); - sendKeys(Keys.CONTROL, "U"); // ctrl+shift+u + sendKeys(Keys.CONTROL, Keys.SHIFT, "u"); + assertActualEquals(DEFAULT_VALUE); + } + + @Test + public void shortcutWithDisabledUpdateModeAlwaysWorksWhenComponentIsDisabled() { + sendKeys(Keys.CONTROL, Keys.SHIFT, "p"); + + assertActualEquals("DISABLED CLICKED"); + + resetActual(); assertActualEquals(DEFAULT_VALUE); }