Skip to content

Commit 0dbadc3

Browse files
corvinszcorvinszKeboo
authored
Trap keyboard navigation inside of the dialogs popup (#4032)
* test(dialoghost): add failing test for the focus not being trapped inside of the dialogs popup * fix(dialoghost): trap focus inside of the dialogs popup * test(dialoghost): stabilize focus tests by using Wait.For Replace fixed Task.Delay calls with polling assertions to improve test reliability during focus transitions. Fixes #4027 --------- Co-authored-by: corvinsz <[email protected]> Co-authored-by: Kevin Bost <[email protected]>
1 parent fa6b1cc commit 0dbadc3

File tree

2 files changed

+63
-1
lines changed

2 files changed

+63
-1
lines changed

src/MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.DialogHost.xaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -452,6 +452,7 @@
452452
Focusable="True"
453453
Foreground="{DynamicResource MaterialDesign.Brush.Foreground}"
454454
IsTabStop="False"
455+
KeyboardNavigation.TabNavigation="Cycle"
455456
Opacity="0"
456457
RenderTransformOrigin=".5,.5"
457458
Tag="{TemplateBinding DialogBackground}"

tests/MaterialDesignThemes.UITests/WPF/DialogHosts/DialogHostTests.cs

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
using static MaterialDesignThemes.UITests.MaterialDesignSpec;
66

7-
87
namespace MaterialDesignThemes.UITests.WPF.DialogHosts;
98

109
public class DialogHostTests : TestBase
@@ -514,4 +513,66 @@ await Wait.For(async () =>
514513

515514
recorder.Success();
516515
}
516+
517+
[Test]
518+
[Description("Issue 4027")]
519+
[Arguments("", new[] { Key.Tab })]
520+
[Arguments("", new[] { Key.LeftShift, Key.Tab })]
521+
[Arguments("MaterialDesignEmbeddedDialogHost", new[] { Key.Tab })]
522+
[Arguments("MaterialDesignEmbeddedDialogHost", new[] { Key.LeftShift, Key.Tab })]
523+
public async Task DialogHost_TrapsFocusInsidePopup_WhenTabbing(string dialogHostStyle, Key[] inputActions)
524+
{
525+
await using var recorder = new TestRecorder(App);
526+
527+
var dialogHost = await LoadXaml<DialogHost>("""
528+
<materialDesign:DialogHost>
529+
<Grid>
530+
<StackPanel>
531+
<TextBox />
532+
<Button x:Name="btnOpenDialog"
533+
Content="Open dialog"
534+
Command="{x:Static materialDesign:DialogHost.OpenDialogCommand}" />
535+
</StackPanel>
536+
</Grid>
537+
538+
<materialDesign:DialogHost.DialogContent>
539+
<StackPanel Width="300">
540+
<TextBox x:Name="TextBoxOne" />
541+
<TextBox x:Name="TextBoxTwo" />
542+
</StackPanel>
543+
</materialDesign:DialogHost.DialogContent>
544+
</materialDesign:DialogHost>
545+
""");
546+
547+
if (!string.IsNullOrEmpty(dialogHostStyle))
548+
{
549+
await dialogHost.RemoteExecute(SetDialogHostStyle, dialogHostStyle);
550+
}
551+
552+
var openDialogButton = await dialogHost.GetElement<Button>("btnOpenDialog");
553+
var textBoxOne = await dialogHost.GetElement<TextBox>("TextBoxOne");
554+
var textBoxTwo = await dialogHost.GetElement<TextBox>("TextBoxTwo");
555+
556+
await openDialogButton.LeftClick();
557+
558+
// By default the first focusable element should be focused
559+
await Wait.For(async () => await Assert.That(await textBoxOne.GetIsFocused()).IsTrue());
560+
561+
await textBoxOne.SendInput(new KeyboardInput(inputActions));
562+
563+
await Wait.For(async () => await Assert.That(await textBoxTwo.GetIsFocused()).IsTrue());
564+
565+
await textBoxTwo.SendInput(new KeyboardInput(inputActions));
566+
567+
await Wait.For(async () => await Assert.That(await textBoxOne.GetIsFocused()).IsTrue());
568+
569+
recorder.Success();
570+
571+
static object SetDialogHostStyle(DialogHost dialogHost, string styleName)
572+
{
573+
var style = (Style)dialogHost.FindResource(styleName);
574+
dialogHost.Style = style;
575+
return null!;
576+
}
577+
}
517578
}

0 commit comments

Comments
 (0)