Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix: make dialog scoped shortcuts work (#725)
Uses API provided by Flow to make keydown events passed from the overlay to the Dialog element when the dialog has been used for listening for shortcuts with .listenOn(dialog). Depends on vaadin/flow#10264 Fixes vaadin/flow#7799, vaadin/vaadin-dialog#229 Co-authored-by: David Sosa <76832183+sosa-vaadin@users.noreply.github.com>
- Loading branch information
1 parent
0a11bd2
commit b2b4c18
Showing
4 changed files
with
349 additions
and
1 deletion.
There are no files selected for viewing
117 changes: 117 additions & 0 deletions
117
...on-tests/src/main/java/com/vaadin/flow/component/dialog/tests/DialogWithShortcutPage.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
package com.vaadin.flow.component.dialog.tests; | ||
|
||
import java.util.EventObject; | ||
|
||
import com.vaadin.flow.component.AttachEvent; | ||
import com.vaadin.flow.component.Component; | ||
import com.vaadin.flow.component.Key; | ||
import com.vaadin.flow.component.ShortcutRegistration; | ||
import com.vaadin.flow.component.Text; | ||
import com.vaadin.flow.component.dialog.Dialog; | ||
import com.vaadin.flow.component.html.Div; | ||
import com.vaadin.flow.component.html.Input; | ||
import com.vaadin.flow.component.html.NativeButton; | ||
import com.vaadin.flow.component.orderedlayout.VerticalLayout; | ||
import com.vaadin.flow.router.Route; | ||
|
||
@Route("vaadin-dialog/shortcuts") | ||
public class DialogWithShortcutPage extends VerticalLayout { | ||
|
||
public static final Key SHORTCUT_KEY = Key.KEY_X; | ||
public static final String SHORTCUT = String.join("", SHORTCUT_KEY.getKeys()); | ||
public static final String EVENT_LOG = "event-log"; | ||
public static final String UI_BUTTON = "ui-button"; | ||
public static final String MODELESS_SHORTCUT_ON_UI = "modeless-shortcut-on-ui"; | ||
public static final String MODELESS_SHORTCUT_LISTEN_ON_DIALOG = "modeless-shortcur-listen-on-dialog"; | ||
public static final String LISTEN_ON_DIALOG = "listen-on-dialog"; | ||
public static final String SHORTCUT_ON_UI = "shortcut-on-ui"; | ||
public static final String DIALOG_ID = "dialog"; | ||
public static final String REUSABLE_DIALOG = "reusable-dialog"; | ||
public static final String UI_ID = "ui-id"; | ||
public static final String DIALOG_BUTTON_MESSAGE_ID = "dialog-button-message"; | ||
private int eventCounter; | ||
private int dialogCounter; | ||
|
||
private final Div eventLog; | ||
private Dialog reusableDialog; | ||
|
||
public DialogWithShortcutPage() { | ||
eventLog = new Div(); | ||
eventLog.setId(EVENT_LOG); | ||
final NativeButton modelessWithShortcutOnUi = new NativeButton( | ||
"Modeless with shortcut on UI", | ||
e -> createAndOpenDialog(false).setModal(false)); | ||
modelessWithShortcutOnUi.setId(MODELESS_SHORTCUT_ON_UI); | ||
final NativeButton modelessWithShortcutListenOnDialog = new NativeButton( | ||
"Modeless with shortcut listenOn(dialog)", | ||
e -> createAndOpenDialog(true).setModal(false)); | ||
modelessWithShortcutListenOnDialog | ||
.setId(MODELESS_SHORTCUT_LISTEN_ON_DIALOG); | ||
final NativeButton dialogWithShortcutListenOnDialog = new NativeButton( | ||
"Dialog with shortcut listenOn(dialog)", | ||
e -> createAndOpenDialog(true)); | ||
dialogWithShortcutListenOnDialog.setId(LISTEN_ON_DIALOG); | ||
final NativeButton dialogWithShortcutOnUi = new NativeButton( | ||
"Dialog with shortcut on UI", e -> createAndOpenDialog(false)); | ||
dialogWithShortcutOnUi.setId(SHORTCUT_ON_UI); | ||
final NativeButton reusableDialogButton = new NativeButton("Reusable dialog", | ||
event -> { | ||
if (reusableDialog == null) { | ||
reusableDialog = createAndOpenDialog(true); | ||
} else { | ||
reusableDialog.open(); | ||
} | ||
}); | ||
reusableDialogButton.setId(REUSABLE_DIALOG); | ||
add(modelessWithShortcutOnUi, modelessWithShortcutListenOnDialog, | ||
dialogWithShortcutOnUi, dialogWithShortcutListenOnDialog, | ||
reusableDialogButton); | ||
|
||
NativeButton nonDialogButton = new NativeButton("Button on UI with shortcut on UI", | ||
this::onEvent); | ||
nonDialogButton.addClickShortcut(SHORTCUT_KEY); | ||
nonDialogButton.setId(UI_BUTTON); | ||
|
||
add(nonDialogButton, eventLog); | ||
} | ||
|
||
@Override | ||
protected void onAttach(AttachEvent attachEvent) { | ||
super.onAttach(attachEvent); | ||
attachEvent.getUI().setId(UI_ID); | ||
} | ||
|
||
private Dialog createAndOpenDialog(boolean listenOnDialog) { | ||
int index = dialogCounter++; | ||
final String dialogId = DIALOG_ID + index; | ||
NativeButton myDialogButton = createDialogButton(); | ||
myDialogButton.setId(dialogId + "-button"); | ||
Dialog dialog = new Dialog( | ||
new Div(new Div(new Text("" + index)), myDialogButton, | ||
new Input())); | ||
NativeButton closeButton = new NativeButton("Close", buttonClickEvent -> dialog.close()); | ||
dialog.add(closeButton); | ||
dialog.setDraggable(true); | ||
dialog.open(); | ||
dialog.setId(dialogId); | ||
final ShortcutRegistration registration = myDialogButton | ||
.addClickShortcut(SHORTCUT_KEY); | ||
if (listenOnDialog) { | ||
registration.listenOn(dialog); | ||
} | ||
return dialog; | ||
} | ||
|
||
private void onEvent(EventObject event) { | ||
final Div div = new Div(); | ||
final int index = eventCounter++; | ||
div.setText(index + "-" | ||
+ ((Component) event.getSource()).getId().orElse("NO-ID!")); | ||
div.setId(DIALOG_BUTTON_MESSAGE_ID + "-" + index); | ||
eventLog.addComponentAsFirst(div); | ||
} | ||
|
||
private NativeButton createDialogButton() { | ||
return new NativeButton("Hit " + SHORTCUT, this::onEvent); | ||
} | ||
} |
165 changes: 165 additions & 0 deletions
165
...tion-tests/src/test/java/com/vaadin/flow/component/dialog/tests/DialogWithShortcutIT.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,165 @@ | ||
package com.vaadin.flow.component.dialog.tests; | ||
|
||
import com.vaadin.flow.component.html.testbench.NativeButtonElement; | ||
import org.junit.Assert; | ||
import org.junit.Before; | ||
import org.junit.Test; | ||
import org.openqa.selenium.By; | ||
import org.openqa.selenium.Keys; | ||
import org.openqa.selenium.WebElement; | ||
import org.openqa.selenium.interactions.Actions; | ||
|
||
import com.vaadin.flow.component.dialog.testbench.DialogElement; | ||
import com.vaadin.flow.component.html.testbench.DivElement; | ||
import com.vaadin.flow.testutil.TestPath; | ||
import com.vaadin.testbench.TestBenchElement; | ||
import com.vaadin.tests.AbstractComponentIT; | ||
|
||
@TestPath("vaadin-dialog/shortcuts") | ||
public class DialogWithShortcutIT extends AbstractComponentIT { | ||
|
||
private TestBenchElement eventLog; | ||
private TestBenchElement openDialogButton; | ||
private NativeButtonElement uiLevelButton; | ||
|
||
@Before | ||
public void init() { | ||
open(); | ||
eventLog = $(DivElement.class).id(DialogWithShortcutPage.EVENT_LOG); | ||
uiLevelButton = $(NativeButtonElement.class) | ||
.id(DialogWithShortcutPage.UI_BUTTON); | ||
} | ||
|
||
// #7799 | ||
@Test | ||
public void dialogOpenedWithListenOnShortcut_sameShortcutListeningOnUi_focusDecidesWhichIsExecuted() { | ||
openDialogButton = $(NativeButtonElement.class) | ||
.id(DialogWithShortcutPage.LISTEN_ON_DIALOG); | ||
pressShortcutKey(uiLevelButton); | ||
validateLatestShortcutEvent(0, DialogWithShortcutPage.UI_BUTTON); | ||
|
||
openNewDialog(); | ||
pressShortcutKey(getFirstDialogInput()); | ||
validateLatestShortcutEventOnDialog(1, 0); | ||
|
||
pressShortcutKey(getFirstDialogInput()); | ||
validateLatestShortcutEventOnDialog(2, 0); | ||
|
||
closeDialog(); | ||
pressShortcutKey(uiLevelButton); | ||
validateLatestShortcutEvent(3, DialogWithShortcutPage.UI_BUTTON); | ||
} | ||
|
||
@Test | ||
public void dialogOpenedWithShortcutNoListenOn_sameShortcutListeningOnUi_bothExecuted() { | ||
openDialogButton = $(NativeButtonElement.class) | ||
.id(DialogWithShortcutPage.SHORTCUT_ON_UI); | ||
pressShortcutKey(uiLevelButton); | ||
validateLatestShortcutEvent(0, DialogWithShortcutPage.UI_BUTTON); | ||
|
||
openNewDialog(); | ||
|
||
pressShortcutKey(getFirstDialogInput()); | ||
// last event is on dialog | ||
validateLatestShortcutEventOnDialog(2, 0); | ||
validateShortcutEvent(1, 1, DialogWithShortcutPage.UI_BUTTON); | ||
|
||
closeDialog(); | ||
pressShortcutKey(uiLevelButton); | ||
validateLatestShortcutEvent(3, DialogWithShortcutPage.UI_BUTTON); | ||
} | ||
|
||
@Test | ||
public void dialogOpenedWithListenOnShortcut_dialogReopened_oldShortcutStillWorks() { | ||
openDialogButton = $(NativeButtonElement.class) | ||
.id(DialogWithShortcutPage.REUSABLE_DIALOG); | ||
|
||
pressShortcutKey(uiLevelButton); | ||
validateLatestShortcutEvent(0, DialogWithShortcutPage.UI_BUTTON); | ||
|
||
openNewDialog(); | ||
|
||
pressShortcutKey(getFirstDialogInput()); | ||
validateLatestShortcutEventOnDialog(1, 0); | ||
|
||
pressShortcutKey(uiLevelButton); | ||
validateLatestShortcutEvent(2, DialogWithShortcutPage.UI_BUTTON); | ||
|
||
closeDialog(); | ||
|
||
pressShortcutKey(uiLevelButton); | ||
validateLatestShortcutEvent(3, DialogWithShortcutPage.UI_BUTTON); | ||
|
||
openNewDialog(); | ||
|
||
pressShortcutKey(getFirstDialogInput()); | ||
validateLatestShortcutEventOnDialog(4, 0); | ||
} | ||
|
||
// vaadin/vaadin-dialog#229 | ||
@Test | ||
public void twoModelessDialogsOpenedWithSameShortcutKeyOnListenOn_dialogWithFocusExecuted() { | ||
openDialogButton = $(NativeButtonElement.class) | ||
.id(DialogWithShortcutPage.MODELESS_SHORTCUT_LISTEN_ON_DIALOG); | ||
|
||
openNewDialog(); | ||
openNewDialog(); | ||
|
||
pressShortcutKey(getFirstDialogInput()); | ||
validateLatestShortcutEventOnDialog(0, 0); | ||
|
||
pressShortcutKey(getDialogInput(1)); | ||
validateLatestShortcutEventOnDialog(1, 1); | ||
|
||
pressShortcutKey(getFirstDialogInput()); | ||
validateLatestShortcutEventOnDialog(2, 0); | ||
|
||
pressShortcutKey(uiLevelButton); | ||
validateLatestShortcutEvent(3, DialogWithShortcutPage.UI_BUTTON); | ||
|
||
pressShortcutKey(getDialogInput(1)); | ||
validateLatestShortcutEventOnDialog(4, 1); | ||
} | ||
|
||
private void openNewDialog() { | ||
openDialogButton.click(); | ||
} | ||
|
||
private void closeDialog() { | ||
new Actions(getDriver()).sendKeys(Keys.ESCAPE).build().perform(); | ||
} | ||
|
||
private TestBenchElement getFirstDialogInput() { | ||
return getDialogInput(0); | ||
} | ||
|
||
private TestBenchElement getDialogInput(int dialogIndex) { | ||
return $(DialogElement.class) | ||
.id(DialogWithShortcutPage.DIALOG_ID + dialogIndex).$("input") | ||
.first(); | ||
} | ||
|
||
private void pressShortcutKey(TestBenchElement elementToFocus) { | ||
elementToFocus.focus(); | ||
elementToFocus.sendKeys("x"); | ||
} | ||
|
||
private void validateLatestShortcutEventOnDialog(int eventCounter, | ||
int dialogId) { | ||
validateShortcutEvent(0, eventCounter, | ||
DialogWithShortcutPage.DIALOG_ID + dialogId + "-button"); | ||
} | ||
|
||
private void validateLatestShortcutEvent(int eventCounter, | ||
String eventSourceId) { | ||
validateShortcutEvent(0, eventCounter, eventSourceId); | ||
} | ||
|
||
private void validateShortcutEvent(int indexFromTop, int eventCounter, | ||
String eventSourceId) { | ||
final WebElement latestEvent = eventLog.findElements(By.tagName("div")) | ||
.get(indexFromTop); | ||
Assert.assertEquals("Invalid latest event", | ||
eventCounter + "-" + eventSourceId, latestEvent.getText()); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters