Skip to content

Commit b7f77af

Browse files
committed
[FIX] topbar: close font size dropdown and keep focus on the grid
Pressing Tab in the font size editor could move focus to the hidden “add more rows” footer. The browser then auto-scrolled to that footer while the grid viewport state stayed unchanged, making the footer look like it was floating above the grid. We now handle Tab on the font size input: we prevent the default navigation, close the dropdown, and redirect focus back to the grid composer instead of the footer. This keeps the layout stable when tabbing from the toolbar. Alternative approaches considered but discarded: - Closing on blur and refocusing the grid: clicking the arrow caused the input to blur first, so the blur handler closed the dropdown and the arrow click immediately reopened it, making it impossible to close the dropdown. - Using mousedown to distinguish internal clicks: this fired before selecting an item in the dropdown, so the dropdown closed too early and the item click was never applied. Task: 5263792 X-original-commit: 99a94ac
1 parent 62b85c8 commit b7f77af

File tree

6 files changed

+44
-2
lines changed

6 files changed

+44
-2
lines changed

src/components/grid_add_rows_footer/grid_add_rows_footer.xml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77
<button
88
t-on-click="onConfirm"
99
t-att-disabled="state.errorFlag"
10-
class="o-button flex-grow-0 me-2">
10+
class="o-button flex-grow-0 me-2"
11+
tabindex="-1">
1112
Add
1213
</button>
1314
<input
@@ -19,6 +20,7 @@
1920
t-on-keydown.stop="onKeydown"
2021
t-on-pointerdown.stop=""
2122
t-on-input.stop="onInput"
23+
tabindex="-1"
2224
/>
2325
<span>more rows at the bottom</span>
2426
<ValidationMessages t-if="state.errorFlag" messages="errorMessages" msgType="'error'"/>

src/components/number_editor/number_editor.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import {
99
useState,
1010
} from "@odoo/owl";
1111
import { clip } from "../../helpers/index";
12+
import { Store, useStore } from "../../store_engine";
13+
import { DOMFocusableElementStore } from "../../stores/DOM_focus_store";
1214
import { isChildEvent } from "../helpers/dom_helpers";
1315
import { Popover, PopoverProps } from "../popover";
1416

@@ -56,7 +58,11 @@ export class NumberEditor extends Component<Props, SpreadsheetChildEnv> {
5658
private rootEditorRef = useRef("NumberEditor");
5759
private valueListRef = useRef("numberList");
5860

61+
private DOMFocusableElementStore!: Store<DOMFocusableElementStore>;
62+
5963
setup() {
64+
this.DOMFocusableElementStore = useStore(DOMFocusableElementStore);
65+
6066
useExternalListener(window, "click", this.onExternalClick, { capture: true });
6167
onWillUpdateProps((nextProps) => {
6268
if (this.inputRef.el && document.activeElement !== this.inputRef.el) {
@@ -133,5 +139,12 @@ export class NumberEditor extends Component<Props, SpreadsheetChildEnv> {
133139
}
134140
this.props.onToggle?.();
135141
}
142+
if (ev.key === "Tab") {
143+
ev.preventDefault();
144+
ev.stopPropagation();
145+
this.closeList();
146+
this.DOMFocusableElementStore.focus();
147+
return;
148+
}
136149
}
137150
}

src/components/number_editor/number_editor.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
class="o-number-editor d-flex align-items-center"
66
t-att-class="props.class"
77
t-att-title="props.title"
8-
t-on-click="this.toggleList">
8+
t-on-click.stop="this.toggleList">
99
<input
1010
type="number"
1111
t-att-min="props.min"

tests/grid/__snapshots__/grid_component.test.ts.snap

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,13 @@ exports[`Grid component simple rendering snapshot 1`] = `
2929
>
3030
<button
3131
class="o-button flex-grow-0 me-2"
32+
tabindex="-1"
3233
>
3334
Add
3435
</button>
3536
<input
3637
class="o-grid-add-rows-input o-input mt-0 me-2"
38+
tabindex="-1"
3739
type="text"
3840
value="100"
3941
/>

tests/spreadsheet/__snapshots__/spreadsheet_component.test.ts.snap

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -835,11 +835,13 @@ exports[`Simple Spreadsheet Component simple rendering snapshot 1`] = `
835835
>
836836
<button
837837
class="o-button flex-grow-0 me-2"
838+
tabindex="-1"
838839
>
839840
Add
840841
</button>
841842
<input
842843
class="o-grid-add-rows-input o-input mt-0 me-2"
844+
tabindex="-1"
843845
type="text"
844846
value="100"
845847
/>
@@ -1842,11 +1844,13 @@ exports[`components take the small screen into account 1`] = `
18421844
>
18431845
<button
18441846
class="o-button flex-grow-0 me-2"
1847+
tabindex="-1"
18451848
>
18461849
Add
18471850
</button>
18481851
<input
18491852
class="o-grid-add-rows-input o-input mt-0 me-2"
1853+
tabindex="-1"
18501854
type="text"
18511855
value="100"
18521856
/>

tests/top_bar_component.test.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import {
3030
doubleClick,
3131
getElComputedStyle,
3232
getTextNodes,
33+
keyDown,
3334
simulateClick,
3435
triggerMouseEvent,
3536
} from "./test_helpers/dom_helper";
@@ -451,6 +452,26 @@ describe("TopBar component", () => {
451452
expect(getStyle(model, "A1").fontSize).toBe(8);
452453
});
453454

455+
test("Tab from font size editor closes the dropdown and moves focus to grid", async () => {
456+
const { fixture } = await mountSpreadsheet();
457+
const input = fixture.querySelector("input.o-font-size") as HTMLInputElement;
458+
input.focus();
459+
await nextTick();
460+
expect(fixture.querySelector(".o-popover .o-text-options")).toBeTruthy();
461+
await keyDown({ key: "Tab" });
462+
expect(fixture.querySelector(".o-popover .o-text-options")).toBeFalsy();
463+
const composerEl = fixture.querySelector<HTMLElement>(".o-grid-composer .o-composer")!;
464+
expect(document.activeElement).toBe(composerEl);
465+
});
466+
467+
test("Clicking the number editor dropdown arrow focuses the input", async () => {
468+
const { fixture } = await mountSpreadsheet();
469+
const input = fixture.querySelector("input.o-font-size") as HTMLInputElement;
470+
const icon = fixture.querySelectorAll(".o-number-editor .o-icon")[0] as HTMLElement;
471+
await click(icon);
472+
expect(document.activeElement).toBe(input);
473+
});
474+
454475
test("prevents default behavior of mouse wheel event on font size input", async () => {
455476
await mountParent();
456477
const fontSizeInput = fixture.querySelector("input.o-font-size") as HTMLInputElement;

0 commit comments

Comments
 (0)