Skip to content

Commit

Permalink
feat: DisabledUpdateMode for ShortcutRegistration (#20874)
Browse files Browse the repository at this point in the history
* feat: DisabledUpdateMode for ShortcutRegistration
  • Loading branch information
tepi authored Jan 17, 2025
1 parent 248bbd3 commit 32c4914
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -95,6 +96,8 @@ public class ShortcutRegistration implements Registration, Serializable {

private List<Registration> registrations = new ArrayList<>();

private DisabledUpdateMode mode = DisabledUpdateMode.ONLY_WHEN_ENABLED;

// beforeClientResponse callback
// needs to be an anonymous class to prevent deserialization issues
// see #17201
Expand Down Expand Up @@ -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.
*
Expand Down Expand Up @@ -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);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -506,6 +507,24 @@ public void constructedRegistration_lifecycleOnwerIsDisabled_shorcutEventIsNotFi
Assert.assertNull(event.get());
}

@Test
public void constructedRegistration_lifecycleOwnerIsDisabledWithDisabledUpdateModeAlways_shortcutEventIsFired() {
AtomicReference<ShortcutEvent> 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<ShortcutEvent> event = new AtomicReference<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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);
}

Expand Down

0 comments on commit 32c4914

Please sign in to comment.