Skip to content

Commit 36d6e2e

Browse files
authored
chore: add method to force LumoInjector to update styles synchronously (#10588)
1 parent 9a83d91 commit 36d6e2e

File tree

2 files changed

+50
-0
lines changed

2 files changed

+50
-0
lines changed

packages/vaadin-themable-mixin/src/lumo-injector.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,25 @@ export class LumoInjector {
9797
this.#componentsByTag.values().forEach((components) => components.forEach(removeLumoStyleSheet));
9898
}
9999

100+
/**
101+
* Forces all monitored components to re-evaluate and update their
102+
* injected styles.
103+
*
104+
* This method can be used to force LumoInjector to clean up component
105+
* styles synchonously after the Lumo stylesheet has been removed from
106+
* the root element.Without this, there may be a short FOUC, when the
107+
* Lumo styles are already removed from the root but still present in
108+
* the component Shadow DOMs, since those are removed asynchronously on
109+
* `transitionstart` (CSSPropertyObserver). This is problematic for grid
110+
* in particular, as that short period is enough to cause the virtualizer
111+
* to create excessive rows (ResizeObserver).
112+
*/
113+
forceUpdate() {
114+
for (const tagName of this.#styleSheetsByTag.keys()) {
115+
this.#updateStyleSheet(tagName);
116+
}
117+
}
118+
100119
/**
101120
* Adds a component to the list of elements monitored for style injection.
102121
* If the styles have already been detected, they are injected into the

packages/vaadin-themable-mixin/test/lumo-injection-mixin.test.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ class TestFoo extends LumoInjectionMixin(ThemableMixin(LitElement)) {
2121
2222
[part='content'] {
2323
transition: background-color 1ms linear;
24+
color: black;
2425
background-color: yellow;
2526
}
2627
`;
@@ -97,6 +98,7 @@ const TEST_FOO_STYLES = `
9798
9899
@media lumo_foo {
99100
[part='content'] {
101+
color: red;
100102
background-color: green;
101103
}
102104
}
@@ -560,6 +562,35 @@ describe('Lumo injection', () => {
560562
});
561563
});
562564

565+
describe('forceUpdate', () => {
566+
beforeEach(async () => {
567+
element = fixtureSync('<test-foo></test-foo>');
568+
await nextRender();
569+
content = element.shadowRoot.querySelector('[part="content"]');
570+
});
571+
572+
afterEach(() => {
573+
document.__lumoInjector?.disconnect();
574+
document.__lumoInjector = undefined;
575+
document.__cssPropertyObserver?.disconnect();
576+
document.__cssPropertyObserver = undefined;
577+
});
578+
579+
it('should update component styles synchronously on forceUpdate', () => {
580+
const style = document.createElement('style');
581+
style.textContent = TEST_FOO_STYLES;
582+
document.head.appendChild(style);
583+
584+
document.__lumoInjector.forceUpdate();
585+
expect(getComputedStyle(content).color).to.equal('rgb(255, 0, 0)'); // red
586+
587+
style.remove();
588+
589+
document.__lumoInjector.forceUpdate();
590+
expect(getComputedStyle(content).color).to.equal('rgb(0, 0, 0)'); // black
591+
});
592+
});
593+
563594
describe('registerStyles', () => {
564595
let style;
565596

0 commit comments

Comments
 (0)