Skip to content

Commit 0859ffb

Browse files
committed
focus fix
1 parent d42ce66 commit 0859ffb

6 files changed

Lines changed: 61 additions & 13 deletions

File tree

src/PrompterOne.Shared/Editor/Components/EditorSourcePanel.Search.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ private async Task OnFindQueryInputAsync(ChangeEventArgs args)
4747
{
4848
_findQuery = args.Value?.ToString() ?? string.Empty;
4949
RefreshFindMatchesForCurrentText();
50-
await FocusActiveFindMatchAsync();
50+
await FocusActiveFindMatchAsync(focusEditor: false);
5151
await InvokeAsync(StateHasChanged);
5252
}
5353

@@ -98,19 +98,19 @@ private async Task NavigateFindMatchAsync(int direction)
9898

9999
var nextIndex = (_findMatchIndex + direction + _findMatches.Count) % _findMatches.Count;
100100
_findMatchIndex = nextIndex;
101-
await FocusActiveFindMatchAsync();
101+
await FocusActiveFindMatchAsync(focusEditor: false);
102102
await InvokeAsync(StateHasChanged);
103103
}
104104

105-
private async Task FocusActiveFindMatchAsync()
105+
private async Task FocusActiveFindMatchAsync(bool focusEditor)
106106
{
107107
if (_findMatchIndex < 0 || _findMatchIndex >= _findMatches.Count)
108108
{
109109
return;
110110
}
111111

112112
var activeMatch = _findMatches[_findMatchIndex];
113-
await FocusRangeAsync(activeMatch.Start, activeMatch.End);
113+
await FocusRangeAsync(activeMatch.Start, activeMatch.End, focusEditor: focusEditor);
114114
}
115115

116116
private void ClearFindState()

src/PrompterOne.Shared/Editor/Components/EditorSourcePanel.razor.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -153,10 +153,10 @@ protected override bool ShouldRender()
153153
return true;
154154
}
155155

156-
public async Task FocusRangeAsync(int start, int end, bool revealSelection = true)
156+
public async Task FocusRangeAsync(int start, int end, bool revealSelection = true, bool focusEditor = true)
157157
{
158158
var selection = await RunSelectionInteropAsync(
159-
() => MonacoInterop.SetSelectionAsync(_editorHostRef, start, end, revealSelection),
159+
() => MonacoInterop.SetSelectionAsync(_editorHostRef, start, end, revealSelection, focusEditor),
160160
FocusSelectionFailureMessage);
161161

162162
if (selection is null)

src/PrompterOne.Shared/Editor/Services/EditorMonacoInterop.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,8 @@ public async Task<EditorSelectionViewModel> SetSelectionAsync(
7070
ElementReference host,
7171
int start,
7272
int end,
73-
bool revealSelection = true)
73+
bool revealSelection = true,
74+
bool focusEditor = true)
7475
{
7576
var module = await GetModuleAsync();
7677
if (module is null)
@@ -83,7 +84,8 @@ public async Task<EditorSelectionViewModel> SetSelectionAsync(
8384
host,
8485
start,
8586
end,
86-
revealSelection);
87+
revealSelection,
88+
focusEditor);
8789

8890
return MapSelection(result);
8991
}

src/PrompterOne.Shared/wwwroot/editor/editor-monaco.js

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -337,13 +337,13 @@ export function getSelectionState(host) {
337337
return state ? createSelectionState(state) : createEmptySelectionState();
338338
}
339339

340-
export function setSelection(host, start, end, revealSelection = true) {
340+
export function setSelection(host, start, end, revealSelection = true, focusEditor = true) {
341341
const state = hostStates.get(host);
342342
if (!state) {
343343
return createEmptySelectionState();
344344
}
345345

346-
applySelection(state, start ?? 0, end ?? 0, revealSelection !== false);
346+
applySelection(state, start ?? 0, end ?? 0, revealSelection !== false, undefined, undefined, focusEditor !== false);
347347
notifySelectionChanged(state, false);
348348
return createSelectionState(state);
349349
}
@@ -1473,7 +1473,7 @@ function centerSelectionLineInViewport(state) {
14731473
state.editor.focus();
14741474
}
14751475

1476-
function applySelection(state, start, end, revealSelection, selectionDirection, preservedScrollPositionOverride) {
1476+
function applySelection(state, start, end, revealSelection, selectionDirection, preservedScrollPositionOverride, focusEditor = true) {
14771477
const model = state.editor.getModel();
14781478
if (!model) {
14791479
return;
@@ -1499,7 +1499,9 @@ function applySelection(state, start, end, revealSelection, selectionDirection,
14991499

15001500
if (revealSelection) {
15011501
const scrollType = state.monaco.editor.ScrollType.Immediate;
1502-
state.editor.focus();
1502+
if (focusEditor) {
1503+
state.editor.focus();
1504+
}
15031505
revealSelectionInViewport(state, scrollType);
15041506
state.editor.render();
15051507
requestAnimationFrame(() => {
@@ -1513,7 +1515,9 @@ function applySelection(state, start, end, revealSelection, selectionDirection,
15131515
});
15141516
}
15151517
else {
1516-
state.editor.focus();
1518+
if (focusEditor) {
1519+
state.editor.focus();
1520+
}
15171521
restoreEditorScrollPosition(state, preservedScrollPosition);
15181522
}
15191523

tests/PrompterOne.Web.UITests/Editor/EditorFindFlowTests.cs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,35 @@ await UiScenarioArtifacts.CapturePageAsync(
7777
BrowserTestConstants.EditorFlow.FindSurfaceStep);
7878
});
7979

80+
[Test]
81+
public Task EditorScreen_FindBar_KeepsFocusInSearchInputWhileTyping() =>
82+
RunPageAsync(async page =>
83+
{
84+
UiScenarioArtifacts.ResetScenario(BrowserTestConstants.EditorFlow.FindFocusScenario);
85+
86+
await page.GotoAsync(BrowserTestConstants.Routes.EditorDemo);
87+
await EditorMonacoDriver.WaitUntilReadyAsync(page);
88+
89+
var input = page.GetByTestId(UiTestIds.Editor.FindInput);
90+
91+
await page.GetByTestId(UiTestIds.Editor.FindToggle).ClickAsync();
92+
await Expect(input).ToBeVisibleAsync();
93+
94+
await input.ClickAsync();
95+
await input.TypeAsync("i");
96+
await Expect(input).ToHaveValueAsync("i");
97+
await ExpectActiveElementDataTestAsync(page, UiTestIds.Editor.FindInput);
98+
99+
await input.TypeAsync("n");
100+
await Expect(input).ToHaveValueAsync("in");
101+
await ExpectActiveElementDataTestAsync(page, UiTestIds.Editor.FindInput);
102+
103+
await UiScenarioArtifacts.CapturePageAsync(
104+
page,
105+
BrowserTestConstants.EditorFlow.FindFocusScenario,
106+
BrowserTestConstants.EditorFlow.FindFocusStep);
107+
});
108+
80109
private static async Task<CssColor> ReadCssColorAsync(Microsoft.Playwright.ILocator locator, string propertyName) =>
81110
await locator.EvaluateAsync<CssColor>(
82111
"""
@@ -109,4 +138,15 @@ private static Task<double> ReadPxValueAsync(Microsoft.Playwright.ILocator locat
109138
(element, propertyName) => Number.parseFloat(getComputedStyle(element)[propertyName] || "0")
110139
""",
111140
propertyName);
141+
142+
private static async Task ExpectActiveElementDataTestAsync(Microsoft.Playwright.IPage page, string expectedTestId)
143+
{
144+
var activeElementTestId = await page.EvaluateAsync<string>(
145+
"""
146+
attributeName => document.activeElement?.getAttribute(attributeName) ?? ""
147+
""",
148+
BrowserTestConstants.Html.DataTestAttribute);
149+
150+
await Assert.That(activeElementTestId).IsEqualTo(expectedTestId);
151+
}
112152
}

tests/PrompterOne.Web.UITests/Support/BrowserTestConstants.ScreenFlows.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ public static class EditorFlow
5050
public const string ToolbarDropdownAlignmentFloatingStep = "02-floating-voice-menu-left-cluster";
5151
public const string FindSurfaceScenario = "editor-find-surface";
5252
public const string FindSurfaceStep = "01-styled-find-bar";
53+
public const string FindFocusScenario = "editor-find-focus";
54+
public const string FindFocusStep = "02-search-input-keeps-focus";
5355
public const double MinimumDateFieldWidthPx = 150;
5456
public const double MinimumFindShellBackgroundAlpha = 0.01;
5557
public const double MaximumFindButtonBackgroundAlpha = 0.2;

0 commit comments

Comments
 (0)