mirror of
https://github.com/sveltejs/svelte.git
synced 2024-12-01 17:30:59 +01:00
[fix] style manager transition regression (#7831)
This commit is contained in:
parent
ed078e31fe
commit
07d6d179ab
@ -154,7 +154,13 @@ export function get_root_for_style(node: Node): ShadowRoot | Document {
|
||||
return node.ownerDocument;
|
||||
}
|
||||
|
||||
export function append_stylesheet(node: ShadowRoot | Document, style: HTMLStyleElement) {
|
||||
export function append_empty_stylesheet(node: Node) {
|
||||
const style_element = element('style') as HTMLStyleElement;
|
||||
append_stylesheet(get_root_for_style(node), style_element);
|
||||
return style_element.sheet as CSSStyleSheet;
|
||||
}
|
||||
|
||||
function append_stylesheet(node: ShadowRoot | Document, style: HTMLStyleElement) {
|
||||
append((node as Document).head || node, style);
|
||||
return style.sheet as CSSStyleSheet;
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { append_stylesheet, detach, element, get_root_for_style } from './dom';
|
||||
import { append_empty_stylesheet, detach, get_root_for_style } from './dom';
|
||||
import { raf } from './environment';
|
||||
|
||||
interface StyleInformation {
|
||||
style_element: HTMLStyleElement;
|
||||
stylesheet: CSSStyleSheet;
|
||||
rules: Record<string, true>;
|
||||
}
|
||||
|
||||
@ -20,8 +20,8 @@ function hash(str: string) {
|
||||
return hash >>> 0;
|
||||
}
|
||||
|
||||
function create_style_information(doc: Document | ShadowRoot) {
|
||||
const info = { style_element: element('style'), rules: {} };
|
||||
function create_style_information(doc: Document | ShadowRoot, node: Element & ElementCSSInlineStyle) {
|
||||
const info = { stylesheet: append_empty_stylesheet(node), rules: {} };
|
||||
managed_styles.set(doc, info);
|
||||
return info;
|
||||
}
|
||||
@ -39,10 +39,9 @@ export function create_rule(node: Element & ElementCSSInlineStyle, a: number, b:
|
||||
const name = `__svelte_${hash(rule)}_${uid}`;
|
||||
const doc = get_root_for_style(node);
|
||||
|
||||
const { style_element, rules } = managed_styles.get(doc) || create_style_information(doc);
|
||||
const { stylesheet, rules } = managed_styles.get(doc) || create_style_information(doc, node);
|
||||
|
||||
if (!rules[name]) {
|
||||
const stylesheet = append_stylesheet(doc, style_element);
|
||||
rules[name] = true;
|
||||
stylesheet.insertRule(`@keyframes ${name} ${rule}`, stylesheet.cssRules.length);
|
||||
}
|
||||
@ -72,8 +71,9 @@ export function clear_rules() {
|
||||
raf(() => {
|
||||
if (active) return;
|
||||
managed_styles.forEach(info => {
|
||||
const { style_element } = info;
|
||||
detach(style_element);
|
||||
const { ownerNode } = info.stylesheet;
|
||||
// there is no ownerNode if it runs on jsdom.
|
||||
if (ownerNode) detach(ownerNode);
|
||||
});
|
||||
managed_styles.clear();
|
||||
});
|
||||
|
@ -117,8 +117,12 @@ describe('runtime (puppeteer)', function() {
|
||||
load(id) {
|
||||
if (id === 'main') {
|
||||
return `
|
||||
import SvelteComponent from ${JSON.stringify(path.join(__dirname, 'samples', dir, 'main.svelte'))};
|
||||
import config from ${JSON.stringify(path.join(__dirname, 'samples', dir, '_config.js'))};
|
||||
import SvelteComponent from ${JSON.stringify(
|
||||
path.join(__dirname, 'samples', dir, 'main.svelte')
|
||||
)};
|
||||
import config from ${JSON.stringify(
|
||||
path.join(__dirname, 'samples', dir, '_config.js')
|
||||
)};
|
||||
import * as assert from 'assert';
|
||||
|
||||
export default async function (target) {
|
||||
@ -140,6 +144,14 @@ describe('runtime (puppeteer)', function() {
|
||||
|
||||
const component = new SvelteComponent(options);
|
||||
|
||||
const waitUntil = async (fn, ms = 500) => {
|
||||
const start = new Date().getTime();
|
||||
do {
|
||||
if (fn()) return;
|
||||
await new Promise(resolve => window.setTimeout(resolve, 1));
|
||||
} while (new Date().getTime() <= start + ms);
|
||||
};
|
||||
|
||||
if (config.html) {
|
||||
assert.htmlEqual(target.innerHTML, config.html);
|
||||
}
|
||||
@ -150,6 +162,7 @@ describe('runtime (puppeteer)', function() {
|
||||
component,
|
||||
target,
|
||||
window,
|
||||
waitUntil,
|
||||
});
|
||||
|
||||
component.$destroy();
|
||||
|
@ -0,0 +1,17 @@
|
||||
export default {
|
||||
skip_if_ssr: true,
|
||||
skip_if_hydrate: true,
|
||||
skip_if_hydrate_from_ssr: true,
|
||||
test: async ({ component, assert, window, waitUntil }) => {
|
||||
assert.htmlEqual(window.document.head.innerHTML, '');
|
||||
component.visible = true;
|
||||
assert.htmlEqual(window.document.head.innerHTML, '<style></style>');
|
||||
await waitUntil(() => window.document.head.innerHTML === '');
|
||||
assert.htmlEqual(window.document.head.innerHTML, '');
|
||||
|
||||
component.visible = false;
|
||||
assert.htmlEqual(window.document.head.innerHTML, '<style></style>');
|
||||
await waitUntil(() => window.document.head.innerHTML === '');
|
||||
assert.htmlEqual(window.document.head.innerHTML, '');
|
||||
}
|
||||
};
|
@ -0,0 +1,16 @@
|
||||
<script>
|
||||
export let visible;
|
||||
|
||||
function foo() {
|
||||
return {
|
||||
duration: 10,
|
||||
css: t => {
|
||||
return `opacity: ${t}`;
|
||||
}
|
||||
};
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if visible}
|
||||
<div transition:foo></div>
|
||||
{/if}
|
@ -0,0 +1,14 @@
|
||||
export default {
|
||||
test: async ({ assert, component, window, waitUntil }) => {
|
||||
component.visible = true;
|
||||
await waitUntil(() => window.document.head.querySelector('style').sheet.rules.length === 2);
|
||||
assert.equal(window.document.head.querySelector('style').sheet.rules.length, 2);
|
||||
await waitUntil(() => window.document.head.querySelector('style') === null);
|
||||
assert.equal(window.document.head.querySelector('style'), null);
|
||||
component.visible = false;
|
||||
await waitUntil(() => window.document.head.querySelector('style').sheet.rules.length === 2);
|
||||
assert.equal(window.document.head.querySelector('style').sheet.rules.length, 2);
|
||||
await waitUntil(() => window.document.head.querySelector('style') === null);
|
||||
assert.equal(window.document.head.querySelector('style'), null);
|
||||
}
|
||||
};
|
@ -0,0 +1,20 @@
|
||||
<script>
|
||||
export let visible;
|
||||
|
||||
function foo() {
|
||||
return {
|
||||
duration: 10,
|
||||
css: t => {
|
||||
return `opacity: ${t}`;
|
||||
}
|
||||
};
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if visible}
|
||||
<div transition:foo></div>
|
||||
{/if}
|
||||
|
||||
{#if !visible}
|
||||
<div transition:foo></div>
|
||||
{/if}
|
@ -1,14 +0,0 @@
|
||||
export default {
|
||||
skip_if_ssr: true,
|
||||
skip_if_hydrate: true,
|
||||
skip_if_hydrate_from_ssr: true,
|
||||
test({ raf, assert, component, window }) {
|
||||
component.visible = true;
|
||||
raf.tick(100);
|
||||
component.visible = false;
|
||||
raf.tick(200);
|
||||
raf.tick(0);
|
||||
|
||||
assert.htmlEqual(window.document.head.innerHTML, '');
|
||||
}
|
||||
};
|
@ -1,14 +0,0 @@
|
||||
<script>
|
||||
export let visible = false;
|
||||
|
||||
function foo() {
|
||||
return {
|
||||
duration: 100,
|
||||
css: () => ''
|
||||
};
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if visible}
|
||||
<div transition:foo></div>
|
||||
{/if}
|
Loading…
Reference in New Issue
Block a user