Skip to content

Commit 9d9cd2c

Browse files
authored
Prevent programmatic focus change from bubbling in handleFocusIn (#835)
Fixes navigator issues related to focus events. Resolves: rdar://128559835
1 parent bd891e5 commit 9d9cd2c

File tree

2 files changed

+30
-10
lines changed

2 files changed

+30
-10
lines changed

src/components/Navigator/NavigatorCard.vue

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -817,8 +817,10 @@ export default {
817817
if (!this.$refs.scroller) return false;
818818
return this.$refs.scroller.$el.contains(element);
819819
},
820-
handleFocusIn({ target }) {
820+
handleFocusIn({ target, relatedTarget }) {
821821
this.lastFocusTarget = target;
822+
// prevent scroll when focus is programmatic
823+
if (!relatedTarget) return;
822824
const positionIndex = this.getChildPositionInScroller(target);
823825
// if multiplier is 0, the item is inside the scrollarea, no need to scroll
824826
if (positionIndex === 0) return;

tests/unit/components/Navigator/NavigatorCard.spec.js

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2364,7 +2364,9 @@ describe('NavigatorCard', () => {
23642364

23652365
const fourthItem = items.at(3);
23662366
setOffsetParent(fourthItem.element, { offsetHeight: SIDEBAR_ITEM_SIZE });
2367-
fourthItem.trigger('focusin');
2367+
fourthItem.trigger('focusin', {
2368+
relatedTarget: document.body,
2369+
});
23682370
await flushPromises();
23692371
expect(scrollBySpy).toHaveBeenCalledTimes(1);
23702372
expect(scrollBySpy).toHaveBeenCalledWith({
@@ -2375,7 +2377,9 @@ describe('NavigatorCard', () => {
23752377
getChildPositionInScroller.mockReturnValueOnce(-1);
23762378
const firstItem = items.at(0);
23772379
setOffsetParent(firstItem.element, { offsetHeight: SIDEBAR_ITEM_SIZE + 50 });
2378-
firstItem.trigger('focusin');
2380+
firstItem.trigger('focusin', {
2381+
relatedTarget: document.body,
2382+
});
23792383
await flushPromises();
23802384
expect(scrollBySpy).toHaveBeenCalledTimes(2);
23812385
expect(scrollBySpy).toHaveBeenCalledWith({
@@ -2391,7 +2395,9 @@ describe('NavigatorCard', () => {
23912395
await flushPromises();
23922396
const button = wrapper.find(NavigatorCardItem).find('button');
23932397
// should be focus, but jsdom does not propagate that
2394-
button.trigger('focusin');
2398+
button.trigger('focusin', {
2399+
relatedTarget: document.body,
2400+
});
23952401
await wrapper.vm.$nextTick();
23962402
expect(wrapper.vm.lastFocusTarget).toEqual(button.element);
23972403
});
@@ -2401,7 +2407,9 @@ describe('NavigatorCard', () => {
24012407
await flushPromises();
24022408
const button = wrapper.find(NavigatorCardItem).find('button');
24032409
// should be focus, but jsdom does not propagate that
2404-
button.trigger('focusin');
2410+
button.trigger('focusin', {
2411+
relatedTarget: document.body,
2412+
});
24052413
await wrapper.vm.$nextTick();
24062414
button.trigger('focusout', {
24072415
relatedTarget: document.body,
@@ -2414,7 +2422,9 @@ describe('NavigatorCard', () => {
24142422
await flushPromises();
24152423
const button = wrapper.find(NavigatorCardItem).find('button');
24162424
// should be focus, but jsdom does not propagate that
2417-
button.trigger('focusin');
2425+
button.trigger('focusin', {
2426+
relatedTarget: null,
2427+
});
24182428
await wrapper.vm.$nextTick();
24192429
button.trigger('focusout', {
24202430
relatedTarget: null,
@@ -2439,7 +2449,9 @@ describe('NavigatorCard', () => {
24392449
// This might happen if it deletes an item, that was in focus
24402450
const button = wrapper.find(NavigatorCardItem).find('button');
24412451
// should be focus, but jsdom does not propagate that
2442-
button.trigger('focusin');
2452+
button.trigger('focusin', {
2453+
relatedTarget: document.body,
2454+
});
24432455
const focusSpy = jest.spyOn(button.element, 'focus');
24442456
await flushPromises();
24452457
// now make the component go away
@@ -2464,7 +2476,9 @@ describe('NavigatorCard', () => {
24642476
// This might happen if it deletes an item, that was in focus
24652477
const button = wrapper.find(NavigatorCardItem).find('button');
24662478
// should be focus, but jsdom does not propagate that
2467-
button.trigger('focusin');
2479+
button.trigger('focusin', {
2480+
relatedTarget: document.body,
2481+
});
24682482
button.element.focus();
24692483
// move the spy below the manual focus, so we dont count it
24702484
const focusSpy = jest.spyOn(button.element, 'focus');
@@ -2484,7 +2498,9 @@ describe('NavigatorCard', () => {
24842498
// This might happen if it deletes an item, that was in focus
24852499
const button = wrapper.find(NavigatorCardItem).find('button');
24862500
const focusSpy = jest.spyOn(button.element, 'focus');
2487-
button.trigger('focusin');
2501+
button.trigger('focusin', {
2502+
relatedTarget: document.body,
2503+
});
24882504
await flushPromises();
24892505
// trigger an update
24902506
wrapper.find(DynamicScroller).vm.$emit('update');
@@ -2500,7 +2516,9 @@ describe('NavigatorCard', () => {
25002516
// This might happen if it deletes an item, that was in focus
25012517
const button = wrapper.find(NavigatorCardItem).find('button');
25022518
// should be focus, but jsdom does not propagate that
2503-
button.trigger('focusin');
2519+
button.trigger('focusin', {
2520+
relatedTarget: document.body,
2521+
});
25042522
const focusSpy = jest.spyOn(button.element, 'focus');
25052523
await flushPromises();
25062524
// initiate a filter

0 commit comments

Comments
 (0)