Skip to content

Commit ef0f3af

Browse files
committed
fix: Add focus to the dropdown list
1 parent de64190 commit ef0f3af

File tree

2 files changed

+77
-8
lines changed

2 files changed

+77
-8
lines changed

src/scripts/clipperUI/components/sectionPicker.tsx

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -55,14 +55,17 @@ export class SectionPickerClass extends ComponentBase<SectionPickerState, Sectio
5555
// so logging only when they open it is potentially the next best thing
5656
Clipper.logger.logClickEvent(Log.Click.Label.sectionPickerLocationContainer);
5757

58-
// A11y fix: Focus on the selected item when the popup opens via keyboard
59-
// Use setTimeout to ensure the popup has fully rendered before trying to focus.
60-
// requestAnimationFrame alone may fire before the external OneNotePickerComponent
61-
// has finished rendering the popup and its interactive elements.
62-
setTimeout(() => {
63-
this.focusOnSelectedSectionInPopup();
64-
}, 100);
65-
}
58+
// Move focus to the first item in the dropdown when it opens
59+
requestAnimationFrame(() => {
60+
let notebookList = document.getElementById("notebookList");
61+
if (notebookList) {
62+
let firstTreeItem = notebookList.querySelector("li[role='treeitem']") as HTMLElement;
63+
if (firstTreeItem) {
64+
firstTreeItem.focus();
65+
}
66+
}
67+
});
68+
}
6669
this.props.onPopupToggle(shouldNowBeOpen);
6770
}
6871

src/tests/clipperUI/components/sectionPicker_tests.tsx

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -882,6 +882,72 @@ export class SectionPickerSinonTests extends TestModule {
882882
done();
883883
});
884884
});
885+
886+
test("onPopupToggle should move focus to first tree item when dropdown opens", (assert: QUnitAssert) => {
887+
let done = assert.async();
888+
889+
let clipperState = MockProps.getMockClipperState();
890+
let mockNotebooks = MockProps.getMockNotebooks();
891+
initializeClipperStorage(JSON.stringify(mockNotebooks), undefined, TestConstants.defaultUserInfoAsJsonString);
892+
893+
let popupToggled = false;
894+
let component = <SectionPicker
895+
onPopupToggle={(shouldNowBeOpen: boolean) => { popupToggled = shouldNowBeOpen; }}
896+
clipperState={clipperState} />;
897+
let controllerInstance = MithrilUtils.mountToFixture(component);
898+
899+
// Open the dropdown
900+
MithrilUtils.simulateAction(() => {
901+
document.getElementById(TestConstants.Ids.sectionLocationContainer).click();
902+
});
903+
904+
// Wait for requestAnimationFrame to complete
905+
requestAnimationFrame(() => {
906+
let notebookList = document.getElementById("notebookList");
907+
ok(notebookList, "Notebook list should be present when dropdown is open");
908+
if (notebookList) {
909+
let firstTreeItem = notebookList.querySelector("li[role='treeitem']") as HTMLElement;
910+
ok(firstTreeItem, "First tree item should exist in the notebook list");
911+
}
912+
ok(popupToggled, "onPopupToggle should have been called with true");
913+
done();
914+
});
915+
});
916+
917+
test("onPopupToggle should call onPopupToggle prop with false when dropdown closes", (assert: QUnitAssert) => {
918+
let done = assert.async();
919+
920+
let clipperState = MockProps.getMockClipperState();
921+
let mockNotebooks = MockProps.getMockNotebooks();
922+
let mockSection = {
923+
section: mockNotebooks[0].sections[0],
924+
path: "Clipper Test > Full Page",
925+
parentId: mockNotebooks[0].id
926+
};
927+
initializeClipperStorage(JSON.stringify(mockNotebooks), JSON.stringify(mockSection), TestConstants.defaultUserInfoAsJsonString);
928+
929+
let lastPopupState: boolean;
930+
let component = <SectionPicker
931+
onPopupToggle={(shouldNowBeOpen: boolean) => { lastPopupState = shouldNowBeOpen; }}
932+
clipperState={clipperState} />;
933+
let controllerInstance = MithrilUtils.mountToFixture(component);
934+
935+
// Open the dropdown first
936+
MithrilUtils.simulateAction(() => {
937+
document.getElementById(TestConstants.Ids.sectionLocationContainer).click();
938+
});
939+
940+
// Close the dropdown by clicking again
941+
MithrilUtils.simulateAction(() => {
942+
document.getElementById(TestConstants.Ids.sectionLocationContainer).click();
943+
});
944+
945+
// Wait for requestAnimationFrame to complete
946+
requestAnimationFrame(() => {
947+
strictEqual(lastPopupState, false, "onPopupToggle should have been called with false when dropdown closes");
948+
done();
949+
});
950+
});
885951
}
886952
}
887953

0 commit comments

Comments
 (0)