Skip to content

Commit d619c8b

Browse files
authored
fix!: debounce updating overflow to improve performance (#9618) (#9639)
1 parent 58dcb3c commit d619c8b

File tree

7 files changed

+88
-47
lines changed

7 files changed

+88
-47
lines changed

packages/menu-bar/src/vaadin-menu-bar-mixin.js

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ import { DisabledMixin } from '@vaadin/a11y-base/src/disabled-mixin.js';
1010
import { FocusMixin } from '@vaadin/a11y-base/src/focus-mixin.js';
1111
import { isElementFocused, isKeyboardActive } from '@vaadin/a11y-base/src/focus-utils.js';
1212
import { KeyboardDirectionMixin } from '@vaadin/a11y-base/src/keyboard-direction-mixin.js';
13+
import { microTask } from '@vaadin/component-base/src/async.js';
1314
import { ControllerMixin } from '@vaadin/component-base/src/controller-mixin.js';
15+
import { Debouncer } from '@vaadin/component-base/src/debounce.js';
1416
import { I18nMixin } from '@vaadin/component-base/src/i18n-mixin.js';
1517
import { ResizeMixin } from '@vaadin/component-base/src/resize-mixin.js';
1618
import { SlotController } from '@vaadin/component-base/src/slot-controller.js';
@@ -347,13 +349,7 @@ export const MenuBarMixin = (superClass) =>
347349
const container = this.shadowRoot.querySelector('[part="container"]');
348350
container.addEventListener('click', this.__onButtonClick.bind(this));
349351
container.addEventListener('mouseover', (e) => this._onMouseOver(e));
350-
351-
// Delay setting container to avoid rendering buttons immediately,
352-
// which would also trigger detecting overflow and force re-layout
353-
// See https://github.com/vaadin/web-components/issues/7271
354-
queueMicrotask(() => {
355-
this._container = container;
356-
});
352+
this._container = container;
357353
}
358354

359355
/**
@@ -382,7 +378,7 @@ export const MenuBarMixin = (superClass) =>
382378
* @override
383379
*/
384380
_onResize() {
385-
this.__detectOverflow();
381+
this.__scheduleOverflow();
386382
}
387383

388384
/**
@@ -395,7 +391,7 @@ export const MenuBarMixin = (superClass) =>
395391
_themeChanged(theme, overflow, container) {
396392
if (overflow && container) {
397393
this.__renderButtons(this.items);
398-
this.__detectOverflow();
394+
this.__scheduleOverflow();
399395

400396
if (theme) {
401397
overflow.setAttribute('theme', theme);
@@ -415,7 +411,7 @@ export const MenuBarMixin = (superClass) =>
415411
*/
416412
_reverseCollapseChanged(_reverseCollapse, overflow, container) {
417413
if (overflow && container) {
418-
this.__detectOverflow();
414+
this.__scheduleOverflow();
419415
}
420416
}
421417

@@ -451,7 +447,7 @@ export const MenuBarMixin = (superClass) =>
451447
if (items !== this._oldItems) {
452448
this._oldItems = items;
453449
this.__renderButtons(items);
454-
this.__detectOverflow();
450+
this.__scheduleOverflow();
455451
}
456452

457453
if (disabled !== this._oldDisabled) {
@@ -563,11 +559,14 @@ export const MenuBarMixin = (superClass) =>
563559
}
564560

565561
/** @private */
566-
__detectOverflow() {
567-
if (!this._container) {
568-
return;
569-
}
562+
__scheduleOverflow() {
563+
this._overflowDebouncer = Debouncer.debounce(this._overflowDebouncer, microTask, () => {
564+
this.__detectOverflow();
565+
});
566+
}
570567

568+
/** @private */
569+
__detectOverflow() {
571570
const overflow = this._overflow;
572571
const buttons = this._buttons.filter((btn) => btn !== overflow);
573572
const oldOverflowCount = this.__getOverflowCount(overflow);

packages/menu-bar/test/dom/__snapshots__/menu-bar.test.snap.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ snapshots["menu-bar basic"] =
1515
aria-expanded="false"
1616
aria-haspopup="true"
1717
role="menuitem"
18-
tabindex="-1"
18+
tabindex="0"
1919
>
2020
Reports
2121
</vaadin-menu-bar-button>
@@ -31,7 +31,7 @@ snapshots["menu-bar basic"] =
3131
class="help"
3232
last-visible=""
3333
role="menuitem"
34-
tabindex="-1"
34+
tabindex="0"
3535
>
3636
<vaadin-menu-bar-item aria-selected="false">
3737
<strong>
@@ -46,7 +46,7 @@ snapshots["menu-bar basic"] =
4646
hidden=""
4747
role="menuitem"
4848
slot="overflow"
49-
tabindex="-1"
49+
tabindex="0"
5050
>
5151
<div aria-hidden="true">
5252
···

packages/menu-bar/test/dom/menu-bar.test.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { expect } from '@vaadin/chai-plugins';
2-
import { fixtureSync, nextRender } from '@vaadin/testing-helpers';
2+
import { fixtureSync, nextRender, nextResize } from '@vaadin/testing-helpers';
3+
import '../not-animated-styles.js';
34
import '../../src/vaadin-menu-bar.js';
45
import '../not-animated-styles.js';
56

@@ -32,7 +33,7 @@ describe('menu-bar', () => {
3233
className: 'help',
3334
},
3435
];
35-
await nextRender();
36+
await nextResize(menu);
3637
});
3738

3839
it('basic', async () => {

packages/menu-bar/test/overflow.test.js

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { expect } from '@vaadin/chai-plugins';
22
import { arrowRight, fixtureSync, nextRender, nextResize, nextUpdate } from '@vaadin/testing-helpers';
3+
import sinon from 'sinon';
34
import './menu-bar-test-styles.js';
45
import '../src/vaadin-menu-bar.js';
56

@@ -29,8 +30,8 @@ describe('overflow', () => {
2930
</div>
3031
`);
3132
menu = wrapper.querySelector('vaadin-menu-bar');
32-
await nextRender(menu);
3333
menu.items = [{ text: 'Item 1' }, { text: 'Item 2' }, { text: 'Item 3' }, { text: 'Item 4' }, { text: 'Item 5' }];
34+
await nextResize(menu);
3435
buttons = menu._buttons;
3536
overflow = buttons[buttons.length - 1];
3637
});
@@ -107,9 +108,10 @@ describe('overflow', () => {
107108
expect(overflow.item.children.length).to.equal(0);
108109
});
109110

110-
it('should hide overflow button and reset its items when all buttons fit after changing items', () => {
111+
it('should hide overflow button and reset its items when all buttons fit after changing items', async () => {
111112
// See https://github.com/vaadin/vaadin-menu-bar/issues/133
112113
menu.items = [{ text: 'Item 1' }, { text: 'Item 2' }];
114+
await nextResize(menu);
113115
buttons = menu._buttons;
114116
overflow = buttons[2];
115117
assertVisible(buttons[1]);
@@ -123,7 +125,7 @@ describe('overflow', () => {
123125
await nextResize(menu);
124126
expect(overflow.hasAttribute('hidden')).to.be.true;
125127
menu.setAttribute('theme', 'big');
126-
await nextUpdate(menu);
128+
await nextResize(menu);
127129
assertHidden(buttons[3]);
128130
assertHidden(buttons[4]);
129131
expect(overflow.hasAttribute('hidden')).to.be.false;
@@ -171,8 +173,9 @@ describe('overflow', () => {
171173
});
172174

173175
describe('reverse-collapse', () => {
174-
beforeEach(() => {
176+
beforeEach(async () => {
175177
menu.reverseCollapse = true;
178+
await nextUpdate(menu);
176179
});
177180

178181
it('should show overflow button and hide the buttons which do not fit', () => {
@@ -194,8 +197,9 @@ describe('overflow', () => {
194197
expect(overflow.item.children[2]).to.deep.equal(menu.items[2]);
195198
});
196199

197-
it('should update overflow when reverseCollapse changes', () => {
200+
it('should update overflow when reverseCollapse changes', async () => {
198201
menu.reverseCollapse = false;
202+
await nextUpdate(menu);
199203
assertVisible(buttons[0]);
200204
assertVisible(buttons[1]);
201205
assertHidden(buttons[2]);
@@ -211,7 +215,7 @@ describe('overflow', () => {
211215
beforeEach(async () => {
212216
menu = fixtureSync('<vaadin-menu-bar></vaadin-menu-bar>');
213217
menu.items = [{ text: 'Item 1' }, { text: 'Item 2' }, { text: 'Item 3' }, { text: 'Item 4' }];
214-
await nextRender(menu);
218+
await nextResize(menu);
215219
buttons = menu._buttons;
216220
overflow = buttons[buttons.length - 1];
217221
});
@@ -247,12 +251,13 @@ describe('overflow', () => {
247251
await nextResize(menu);
248252

249253
menu.setAttribute('theme', 'big');
250-
await nextUpdate(menu);
254+
await nextResize(menu);
251255
expect(menu.hasAttribute('has-single-button')).to.be.true;
252256
});
253257

254-
it('should set when changing items to only have one button', () => {
258+
it('should set when changing items to only have one button', async () => {
255259
menu.items = [{ text: 'Actions' }];
260+
await nextResize(menu);
256261
expect(menu.hasAttribute('has-single-button')).to.be.true;
257262
});
258263

@@ -261,13 +266,16 @@ describe('overflow', () => {
261266
await nextResize(menu);
262267

263268
menu.items = [{ text: 'Actions' }];
269+
await nextResize(menu);
264270
expect(menu.hasAttribute('has-single-button')).to.be.true;
265271
});
266272

267-
it('should remove when changing items to have more than one button', () => {
273+
it('should remove when changing items to have more than one button', async () => {
268274
menu.items = [{ text: 'Actions' }];
275+
await nextResize(menu);
269276

270277
menu.items = [{ text: 'Edit' }, { text: 'Delete' }];
278+
await nextResize(menu);
271279
expect(menu.hasAttribute('has-single-button')).to.be.false;
272280
});
273281
});
@@ -462,4 +470,20 @@ describe('overflow', () => {
462470
expect(item.classList.contains('test-class-1')).to.be.true;
463471
});
464472
});
473+
474+
describe('performance', () => {
475+
let menu, spy;
476+
477+
beforeEach(() => {
478+
menu = fixtureSync('<vaadin-menu-bar></vaadin-menu-bar>');
479+
spy = sinon.spy(menu, '_hasOverflow', ['get', 'set']);
480+
});
481+
482+
it('should only detect overflow twice on initial render', async () => {
483+
menu.items = [{ text: 'Item 1' }, { text: 'Item 2' }, { text: 'Item 3' }, { text: 'Item 4' }, { text: 'Item 5' }];
484+
await nextResize(menu);
485+
await nextUpdate(menu);
486+
expect(spy.set.callCount).to.equal(2);
487+
});
488+
});
465489
});

0 commit comments

Comments
 (0)