Skip to content

Commit

Permalink
enable focus listeners in disabled state, add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
vursen committed Jan 17, 2025
1 parent fc5a988 commit d897a39
Show file tree
Hide file tree
Showing 4 changed files with 221 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
* Copyright 2000-2025 Vaadin Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.vaadin.flow.component.button.tests;

import java.util.concurrent.atomic.AtomicInteger;

import com.vaadin.experimental.FeatureFlags;
import com.vaadin.flow.component.Key;
import com.vaadin.flow.component.KeyModifier;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.html.Div;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.server.VaadinService;

import elemental.json.Json;
import elemental.json.JsonObject;

@Route("vaadin-button/accessible-disabled-button")
public class AccessibleDisabledButtonPage extends Div {
private AtomicInteger blurListenerCounter = new AtomicInteger(0);
private AtomicInteger focusListenerCounter = new AtomicInteger(0);
private AtomicInteger clickListenerCounter = new AtomicInteger(0);
private AtomicInteger doubleClickListenerCounter = new AtomicInteger(0);

private Div listenerCounters;

public AccessibleDisabledButtonPage() {
FeatureFlags.get(VaadinService.getCurrent().getContext())
.setEnabled("exampleFeatureFlag", true);

Button button = new Button("Disabled button");
button.setEnabled(false);

button.addFocusShortcut(Key.KEY_A, KeyModifier.ALT);
button.addBlurListener(
event -> incrementListenerCounter(blurListenerCounter));
button.addFocusListener(
event -> incrementListenerCounter(focusListenerCounter));
button.addClickListener(
event -> incrementListenerCounter(clickListenerCounter));
button.addDoubleClickListener(
event -> incrementListenerCounter(doubleClickListenerCounter));

listenerCounters = new Div();
listenerCounters.setId("listener-counters");

add(button, listenerCounters);
}

private void incrementListenerCounter(AtomicInteger listenerCounter) {
listenerCounter.incrementAndGet();
updateListenerCounters();
}

private void updateListenerCounters() {
JsonObject json = Json.createObject();
json.put("blur", blurListenerCounter.get());
json.put("focus", focusListenerCounter.get());
json.put("click", clickListenerCounter.get());
json.put("doubleClick", doubleClickListenerCounter.get());
listenerCounters.setText(json.toJson());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Example feature. Will be removed once the first real feature flag is added
com.vaadin.experimental.exampleFeatureFlag=true
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
* Copyright 2000-2025 Vaadin Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.vaadin.flow.component.button.tests;

import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.openqa.selenium.Keys;
import org.openqa.selenium.interactions.Actions;

import com.vaadin.flow.component.button.testbench.ButtonElement;
import com.vaadin.flow.testutil.TestPath;
import com.vaadin.testbench.TestBenchElement;
import com.vaadin.tests.AbstractComponentIT;

import elemental.json.Json;
import elemental.json.JsonObject;

@TestPath("vaadin-button/accessible-disabled-button")
public class AccessibleDisabledButtonIT extends AbstractComponentIT {
private ButtonElement button;
private TestBenchElement listenerCounters;

@Before
public void init() {
open();
button = $(ButtonElement.class).first();
listenerCounters = $("div").id("listener-counters");
}

@Test
public void click_noClickEvent() {
button.click();
assertListenerCounter("click", 0);
}

@Test
public void doubleClick_noDoubleClickEvent() {
button.doubleClick();
assertListenerCounter("doubleClick", 0);
}

@Test
public void focus_focusEventIsFired() {
button.focus();
assertListenerCounter("focus", 1);
}

@Test
public void focusWithShortcut_focusEventIsFired() {
new Actions(getDriver()).keyDown(Keys.ALT).sendKeys("A").keyUp(Keys.ALT)
.build().perform();
assertListenerCounter("focus", 1);
}

@Test
public void blur_blurEventIsFired() {
button.sendKeys(Keys.TAB);
assertListenerCounter("blur", 1);
}

private void assertListenerCounter(String name, int expectedCount) {
JsonObject json = Json.parse(listenerCounters.getText());
Assert.assertEquals("Unexpected " + name + " value", expectedCount,
(int) json.getNumber(name));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
import java.util.stream.Collectors;
import java.util.stream.Stream;

import com.vaadin.experimental.Feature;
import com.vaadin.experimental.FeatureFlags;
import com.vaadin.flow.component.ClickEvent;
import com.vaadin.flow.component.ClickNotifier;
import com.vaadin.flow.component.Component;
Expand All @@ -30,7 +32,11 @@
import com.vaadin.flow.component.HasSize;
import com.vaadin.flow.component.HasStyle;
import com.vaadin.flow.component.HasText;
import com.vaadin.flow.component.Key;
import com.vaadin.flow.component.KeyModifier;
import com.vaadin.flow.component.ShortcutRegistration;
import com.vaadin.flow.component.Tag;
import com.vaadin.flow.component.UI;
import com.vaadin.flow.component.dependency.JsModule;
import com.vaadin.flow.component.dependency.NpmPackage;
import com.vaadin.flow.component.html.Image;
Expand All @@ -39,7 +45,9 @@
import com.vaadin.flow.component.shared.HasThemeVariant;
import com.vaadin.flow.component.shared.HasTooltip;
import com.vaadin.flow.component.shared.internal.DisableOnClickController;
import com.vaadin.flow.dom.DisabledUpdateMode;
import com.vaadin.flow.dom.Element;
import com.vaadin.flow.shared.Registration;

/**
* The Button component allows users to perform actions. It comes in several
Expand Down Expand Up @@ -160,6 +168,43 @@ public Button(String text, Component icon,
addClickListener(clickListener);
}

@Override
public ShortcutRegistration addFocusShortcut(Key key,
KeyModifier... keyModifiers) {
ShortcutRegistration registration = Focusable.super.addFocusShortcut(
key, keyModifiers);
if (isFeatureFlagEnabled(FeatureFlags.EXAMPLE)) {
registration.setDisabledUpdateMode(DisabledUpdateMode.ALWAYS);
}
return registration;
}

@SuppressWarnings({ "unchecked", "rawtypes" })
@Override
public Registration addFocusListener(
ComponentEventListener<FocusEvent<Button>> listener) {
return getEventBus().addListener(FocusEvent.class,
(ComponentEventListener) listener, registration -> {
if (isFeatureFlagEnabled(FeatureFlags.EXAMPLE)) {
registration.setDisabledUpdateMode(
DisabledUpdateMode.ALWAYS);
}
});
}

@SuppressWarnings({ "unchecked", "rawtypes" })
@Override
public Registration addBlurListener(
ComponentEventListener<BlurEvent<Button>> listener) {
return getEventBus().addListener(BlurEvent.class,
(ComponentEventListener) listener, registration -> {
if (isFeatureFlagEnabled(FeatureFlags.EXAMPLE)) {
registration.setDisabledUpdateMode(
DisabledUpdateMode.ALWAYS);
}
});
}

/**
* Sets the given string as the text content of this component.
* <p>
Expand Down Expand Up @@ -427,4 +472,22 @@ private void updateThemeAttribute() {
getThemeNames().remove("icon");
}
}

/**
* Checks whether the give feature flag is active.
*
* @param feature
* the feature flag to check
* @return {@code true} if the feature flag is active, {@code false}
* otherwise
*/
private boolean isFeatureFlagEnabled(Feature feature) {
UI ui = UI.getCurrent();
if (ui == null) {
return false;
}

return FeatureFlags.get(ui.getSession().getService().getContext())
.isEnabled(feature);
}
}

0 comments on commit d897a39

Please sign in to comment.