From c1fef6532d6f174390b32aead2e182643590c72d Mon Sep 17 00:00:00 2001 From: danielwiehl Date: Wed, 9 Oct 2024 16:38:27 +0200 Subject: [PATCH] fix(toolkit/observable): position document root in CSS layer when using `fromBoundingClientRect$` CSS layers have lower priority than normal CSS declarations, and the layer name indicates the styling originates from `@scion/toolkit`. --- .../bounding-client-rect.observable.spec.ts | 47 +++++++++++++++++++ .../src/bounding-client-rect.observable.ts | 10 +++- 2 files changed, 55 insertions(+), 2 deletions(-) diff --git a/projects/scion/toolkit/observable/src/bounding-client-rect.observable.spec.ts b/projects/scion/toolkit/observable/src/bounding-client-rect.observable.spec.ts index 74d1a4eb..81a918c3 100644 --- a/projects/scion/toolkit/observable/src/bounding-client-rect.observable.spec.ts +++ b/projects/scion/toolkit/observable/src/bounding-client-rect.observable.spec.ts @@ -10,6 +10,7 @@ import {fromBoundingClientRect$} from './bounding-client-rect.observable'; import {ObserveCaptor} from '@scion/toolkit/testing'; +import {Arrays} from '@scion/toolkit/util'; const destroyAfterEach = true; const disposables = new Array<() => void>(); @@ -452,6 +453,52 @@ describe('fromBoundingClientRect$', () => { expect(emitCaptor.getLastValue()).toEqual(jasmine.objectContaining({x: x2, y: y2 - 20, width: 100, height: 100})); }); + it('should position document root element (html)', async () => { + // Precondition: Ensure document root not to be positioned so its position will be changed to 'relative'. + document.documentElement.style.setProperty('position', 'static'); + + const subscription = fromBoundingClientRect$(document.body).subscribe(); + onDestroy(() => subscription.unsubscribe()); + + // Remove style from precondition. + document.documentElement.style.removeProperty('position'); + + // Expect document root to be positioned. + expect(getComputedStyle(document.documentElement)).toEqual(jasmine.objectContaining({ + position: 'relative', + })); + }); + + it('should allow overriding positioning of document root element (html)', async () => { + // Precondition: Ensure document root not to be positioned so its position will be changed to 'relative'. + document.documentElement.style.setProperty('position', 'static'); + + const subscription = fromBoundingClientRect$(document.body).subscribe(); + onDestroy(() => subscription.unsubscribe()); + + // Remove style from precondition. + document.documentElement.style.removeProperty('position'); + + // Expect document root to be positioned. + expect(getComputedStyle(document.documentElement)).toEqual(jasmine.objectContaining({ + position: 'relative', + })); + + // Override positioning of root element. + const styleSheet = new CSSStyleSheet(); + styleSheet.insertRule(` + html { + position: absolute; + }`); + document.adoptedStyleSheets.push(styleSheet); + onDestroy(() => Arrays.remove(document.adoptedStyleSheets, styleSheet)); + + // Expect overrides to be applied. + expect(getComputedStyle(document.documentElement)).toEqual(jasmine.objectContaining({ + position: 'absolute', + })); + }); + describe('Moving element out of the viewport', async () => { it('should emit until moved the element out of the viewport (moving element to the right)', async () => { diff --git a/projects/scion/toolkit/observable/src/bounding-client-rect.observable.ts b/projects/scion/toolkit/observable/src/bounding-client-rect.observable.ts index c5c51338..e620c4fa 100644 --- a/projects/scion/toolkit/observable/src/bounding-client-rect.observable.ts +++ b/projects/scion/toolkit/observable/src/bounding-client-rect.observable.ts @@ -213,10 +213,16 @@ function ensureElementPositioned(element: HTMLElement): void { return; } - // Position the HTML root using a constructable stylesheet to not clutter its element styles. + // Declare styles for the document root element (``) in a CSS layer. + // CSS layers have lower priority than "regular" CSS declarations, and the layer name indicates the styles are from @scion/toolkit. if (element === document.documentElement) { const styleSheet = new CSSStyleSheet({}); - styleSheet.insertRule(`html { position: relative; }`); + styleSheet.insertRule(` + @layer sci-toolkit { + :root { + position: relative; + } + }`); document.adoptedStyleSheets.push(styleSheet); console?.warn?.('[@scion/toolkit] fromBoundingClientRect$ requires the document root element () to be positioned relative or absolute. Changing positioning to relative.'); }