From bce709d22a0918afc16246d947a60005f5cb46b6 Mon Sep 17 00:00:00 2001 From: Arnau Giralt Date: Thu, 22 Feb 2024 22:51:02 +0100 Subject: [PATCH] Refactor $size event behavior - Replace interval-based size update with a ResizeObserver instance - Use the body element to measure size, to avoid issues that come with using the document element - Add "overflow:scroll" to the View widget content holder element --- components/src/core/injector/core/Core.js | 7 --- .../src/core/injector/core/Core.spec.js | 16 +----- components/src/core/injector/core/launcher.js | 13 +++-- .../src/core/injector/core/launcher.spec.js | 53 +++++++++++++------ components/src/widgets/view/widget.vue | 1 + 5 files changed, 49 insertions(+), 41 deletions(-) diff --git a/components/src/core/injector/core/Core.js b/components/src/core/injector/core/Core.js index c10fd707..0d951b74 100644 --- a/components/src/core/injector/core/Core.js +++ b/components/src/core/injector/core/Core.js @@ -21,11 +21,4 @@ export default class { if (has('*', this.watchers)) this.watchers['*'].forEach(call); } - - size() { - return { - height: Math.max(document.documentElement.offsetHeight, document.documentElement.scrollHeight), - width: Math.max(document.documentElement.offsetWidth, document.documentElement.scrollWidth), - }; - } } diff --git a/components/src/core/injector/core/Core.spec.js b/components/src/core/injector/core/Core.spec.js index 41174edf..7eaf97bc 100644 --- a/components/src/core/injector/core/Core.spec.js +++ b/components/src/core/injector/core/Core.spec.js @@ -82,18 +82,4 @@ describe('Core', () => { expect(core.watchers['*'][0]).toHaveBeenCalled(); }); }); - - describe('#size', () => { - it('should return document size in proper format', () => { - jest.spyOn(global.document.documentElement, 'scrollHeight', 'get').mockReturnValue(100) - jest.spyOn(global.document.documentElement, 'scrollWidth', 'get').mockReturnValue(300) - jest.spyOn(global.document.documentElement, 'offsetHeight', 'get').mockReturnValue(200) - jest.spyOn(global.document.documentElement, 'offsetWidth', 'get').mockReturnValue(200) - - expect(core.size()).toEqual({ - width: 300, - height: 200, - }); - }); - }); -}); \ No newline at end of file +}); diff --git a/components/src/core/injector/core/launcher.js b/components/src/core/injector/core/launcher.js index 4b903975..86ec7e42 100644 --- a/components/src/core/injector/core/launcher.js +++ b/components/src/core/injector/core/launcher.js @@ -10,8 +10,12 @@ export default (injector, core, options = {}) => new Promise((resolve) => { core.state = data; if (!options?.disableAutoResizing) { - injector.emit('$size', core.size()); - setInterval(() => injector.emit('$size', core.size()), 300); + const resizeObserver = new ResizeObserver((entries) => { + const [{ contentRect }] = entries; + injector.emit('$size', { height: contentRect.height, width: contentRect.width }); + }); + + resizeObserver.observe(document.body); } resolve(); @@ -20,7 +24,10 @@ export default (injector, core, options = {}) => new Promise((resolve) => { window.addEventListener('$injector', ({ detail }) => { let { type, data } = detail; - if (type === '$size') data = core.size(); + if (type === '$size') { + const { height, width } = document.body.getBoundingClientRect(); + data = { height, width }; + } injector.emit(type, data); }); diff --git a/components/src/core/injector/core/launcher.spec.js b/components/src/core/injector/core/launcher.spec.js index ce0ad1dc..141d612c 100644 --- a/components/src/core/injector/core/launcher.spec.js +++ b/components/src/core/injector/core/launcher.spec.js @@ -1,6 +1,30 @@ import launch from './launcher'; +const resizeObserverCtorSpy = jest.fn(); +const resizeObserverObserveSpy = jest.fn(); +const resizeObserverEntriesStub = [ + { + contentRect: { + height: 400, + width: 800, + }, + }, +]; +const ResizeObserverMock = function ResizeObserverMock(callback) { + resizeObserverCtorSpy(callback); + + callback(resizeObserverEntriesStub); + + return { + observe: resizeObserverObserveSpy, + }; +}; +Object.defineProperty(global, 'ResizeObserver', { + writable: true, + value: ResizeObserverMock, +}); + describe('$init', () => { let injector; let core; @@ -9,7 +33,6 @@ describe('$init', () => { beforeEach(() => { global.window.addEventListener = jest.fn(); global.window.name = 'XXX'; - global.setInterval = jest.fn(); global.crypto = { getRandomValues: jest.fn(() => ['abc']), }; @@ -57,27 +80,22 @@ describe('$init', () => { it('should emit "$size" event', () => { handler({}, {}); - expect(injector.emit.mock.calls[2]).toEqual(['$size', 'SIZE']); - }); - - it('should get sizes from $size method', () => { - handler({}, {}); - - expect(core.size).toHaveBeenCalled(); + expect(injector.emit.mock.calls[2]).toEqual(['$size', { + height: 400, + width: 800, + }]); }); - it('should set interval', () => { + it('should create a ResizeObserver instance', () => { handler({}, {}); - expect(setInterval).toHaveBeenCalledWith(expect.any(Function), 300); + expect(resizeObserverCtorSpy).toHaveBeenCalledWith(expect.any(Function)); }); - it('should set interval for sizing', () => { + it('should call the resize observer\'s observe method with the document body', () => { handler({}, {}); - injector.emit.mock.calls = []; - setInterval.mock.calls[0][0](); - expect(injector.emit).toHaveBeenCalledWith('$size', 'SIZE'); + expect(resizeObserverObserveSpy).toHaveBeenCalledWith(document.body); }); }); @@ -104,7 +122,10 @@ describe('$init', () => { it('should fill size for $size type', () => { handler({ detail: { type: '$size' } }); - expect(injector.emit.mock.calls[2]).toEqual(['$size', 'SIZE']); + expect(injector.emit.mock.calls[2]).toEqual(['$size', { + height: 0, + width: 0, + }]); }); }); @@ -170,4 +191,4 @@ describe('$init', () => { expect(injector.emit).toHaveBeenCalledWith('$mounted'); }); }); -}); \ No newline at end of file +}); diff --git a/components/src/widgets/view/widget.vue b/components/src/widgets/view/widget.vue index 662d960b..bb212537 100644 --- a/components/src/widgets/view/widget.vue +++ b/components/src/widgets/view/widget.vue @@ -149,6 +149,7 @@ export default { &__content-holder { position: relative; grid-area: c; + overflow: scroll; } &__content {