@mantou/gem 2.2.15 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/gem.js +7 -7
- package/dist/gem.mjs +7 -7
- package/elements/base/modal-factory.d.ts +2 -1
- package/elements/base/modal-factory.d.ts.map +1 -1
- package/elements/base/route.d.ts +8 -3
- package/elements/base/route.d.ts.map +1 -1
- package/elements/base/route.js +84 -44
- package/elements/base/route.js.map +1 -1
- package/helper/theme.d.ts +8 -4
- package/helper/theme.d.ts.map +1 -1
- package/helper/theme.js +12 -9
- package/helper/theme.js.map +1 -1
- package/lib/decorators.d.ts.map +1 -1
- package/lib/decorators.js +27 -43
- package/lib/decorators.js.map +1 -1
- package/lib/history.d.ts.map +1 -1
- package/lib/history.js +19 -37
- package/lib/history.js.map +1 -1
- package/lib/reactive.d.ts +6 -5
- package/lib/reactive.d.ts.map +1 -1
- package/lib/reactive.js +85 -112
- package/lib/reactive.js.map +1 -1
- package/lib/server.d.ts +12 -0
- package/lib/server.d.ts.map +1 -1
- package/lib/server.js +2 -0
- package/lib/server.js.map +1 -1
- package/lib/store.d.ts +0 -6
- package/lib/store.d.ts.map +1 -1
- package/lib/store.js +5 -17
- package/lib/store.js.map +1 -1
- package/lib/version.d.ts +1 -1
- package/lib/version.d.ts.map +1 -1
- package/lib/version.js +1 -1
- package/lib/version.js.map +1 -1
- package/package.json +3 -4
- package/src/elements/base/route.ts +84 -49
- package/src/helper/theme.ts +16 -9
- package/src/lib/decorators.ts +34 -48
- package/src/lib/history.ts +19 -32
- package/src/lib/reactive.ts +89 -120
- package/src/lib/server.ts +14 -0
- package/src/lib/store.ts +3 -19
- package/src/lib/version.ts +1 -1
- package/src/test/elements/gesture.test.ts +13 -5
- package/src/test/elements/route.test.ts +102 -0
- package/src/test/elements/title.test.ts +7 -0
- package/src/test/gem-element/advance.test.ts +19 -0
- package/src/test/gem-element/basic.test.ts +13 -0
- package/src/test/history.test.ts +19 -0
- package/src/test/theme.test.ts +8 -1
- package/src/test/utils.test.ts +6 -0
- package/src/test/utils.ts +56 -0
|
@@ -106,12 +106,14 @@ class LifecycleGemElement extends GemElement {
|
|
|
106
106
|
}
|
|
107
107
|
renderCount = 0;
|
|
108
108
|
mountedCount = 0;
|
|
109
|
+
unmountedCount = 0;
|
|
109
110
|
|
|
110
111
|
@mounted()
|
|
111
112
|
#init = () => {
|
|
112
113
|
this.mountedCount++;
|
|
113
114
|
return () => {
|
|
114
115
|
this.renderCount = 0;
|
|
116
|
+
this.unmountedCount++;
|
|
115
117
|
};
|
|
116
118
|
};
|
|
117
119
|
|
|
@@ -155,10 +157,12 @@ describe('gem element 生命周期', () => {
|
|
|
155
157
|
expect(el2.renderCount).to.equal(1);
|
|
156
158
|
el2.remove();
|
|
157
159
|
expect(el2.mountedCount).to.equal(1);
|
|
160
|
+
expect(el2.unmountedCount).to.equal(1);
|
|
158
161
|
expect(el2.renderCount).to.equal(0);
|
|
159
162
|
container.append(el2);
|
|
160
163
|
await Promise.resolve();
|
|
161
164
|
expect(el2.mountedCount).to.equal(2);
|
|
165
|
+
expect(el2.unmountedCount).to.equal(1, '重新挂载时不再执行已经执行过的清理函数');
|
|
162
166
|
expect(el2.renderCount).to.equal(1);
|
|
163
167
|
el2.update();
|
|
164
168
|
el2.update();
|
|
@@ -169,8 +173,13 @@ describe('gem element 生命周期', () => {
|
|
|
169
173
|
expect(el3.appTitle).to.equal('test1');
|
|
170
174
|
expect(el3.appTitle2).to.equal('test2');
|
|
171
175
|
expect(el3.renderCount).to.equal(0);
|
|
176
|
+
// 挂载前更新不会造成挂载后多余的渲染
|
|
177
|
+
el3.update();
|
|
178
|
+
el3.appTitle = 'test3';
|
|
172
179
|
container.append(el3);
|
|
173
180
|
expect(el3.renderCount).to.equal(1);
|
|
181
|
+
await Promise.resolve();
|
|
182
|
+
expect(el3.renderCount).to.equal(1);
|
|
174
183
|
});
|
|
175
184
|
});
|
|
176
185
|
|
|
@@ -343,6 +352,16 @@ describe('gem element 继承', () => {
|
|
|
343
352
|
await Promise.resolve();
|
|
344
353
|
expect(window.name).to.equal(`${name}21`);
|
|
345
354
|
});
|
|
355
|
+
it('setAttribute 设置基类观察的 attribute', async () => {
|
|
356
|
+
const el: InheritGem1 = await fixture(html`<inherit-gem1></inherit-gem1>`);
|
|
357
|
+
el.setAttribute('app-title', 'c');
|
|
358
|
+
expect(el.appTitle).to.equal('c');
|
|
359
|
+
const el1: InheritGem = await fixture(html`<inherit-gem></inherit-gem>`);
|
|
360
|
+
el1.setAttribute('app-title2', 'd');
|
|
361
|
+
el1.setAttribute('app-title', 'e');
|
|
362
|
+
expect(el1.appTitle2).to.equal('d');
|
|
363
|
+
expect(el1.appTitle).to.equal('e');
|
|
364
|
+
});
|
|
346
365
|
});
|
|
347
366
|
@customElement('render-empty')
|
|
348
367
|
@shadow()
|
|
@@ -114,6 +114,19 @@ describe('基本 gem element 测试', () => {
|
|
|
114
114
|
expect(el.hasAttribute('attr')).to.equal(false);
|
|
115
115
|
});
|
|
116
116
|
|
|
117
|
+
it('setAttribute/removeAttribute/toggleAttribute 设置观察的 attribute', async () => {
|
|
118
|
+
const el: GemDemo = await fixture(html`<gem-demo></gem-demo>`);
|
|
119
|
+
el.setAttribute('attr', 'a');
|
|
120
|
+
el.toggleAttribute('disabled');
|
|
121
|
+
expect(el.attr).to.equal('a');
|
|
122
|
+
expect(el.disabled).to.equal(true);
|
|
123
|
+
el.removeAttribute('attr');
|
|
124
|
+
expect(el.attr).to.equal('');
|
|
125
|
+
expect(el.hasAttribute('attr')).to.equal(false);
|
|
126
|
+
await Promise.resolve();
|
|
127
|
+
expect(el.shadowRoot?.textContent).to.equal('attr: , disabled: true, count: 0, prop: , state: ');
|
|
128
|
+
});
|
|
129
|
+
|
|
117
130
|
it('读取 prop', async () => {
|
|
118
131
|
const el: GemDemo = await fixture(html`<gem-demo .prop=${{ value: 'prop' }}></gem-demo>`);
|
|
119
132
|
expect(el.prop).to.deep.equal({ value: 'prop' });
|
package/src/test/history.test.ts
CHANGED
|
@@ -58,6 +58,14 @@ describe('history 测试', () => {
|
|
|
58
58
|
await aTimeout(10);
|
|
59
59
|
expect(history.getParams().title).to.equal('321');
|
|
60
60
|
});
|
|
61
|
+
it('keep title when only hash changed', async () => {
|
|
62
|
+
history.replace({ title: 'Page' });
|
|
63
|
+
// 模拟应用格式化 document.title
|
|
64
|
+
document.title = 'Page | Site';
|
|
65
|
+
location.hash = '#x';
|
|
66
|
+
await aTimeout(10);
|
|
67
|
+
expect(history.getParams().title).to.equal('Page');
|
|
68
|
+
});
|
|
61
69
|
it('Native pushState/replaceState', async () => {
|
|
62
70
|
const historyLength = window.history.length;
|
|
63
71
|
window.history.pushState({}, 'title', '/a');
|
|
@@ -73,6 +81,17 @@ describe('history 测试', () => {
|
|
|
73
81
|
expect(new URLSearchParams(location.search).get('tab')).to.equal('a');
|
|
74
82
|
expect(window.history.length - historyLength).to.equal(1);
|
|
75
83
|
});
|
|
84
|
+
it('Native pushState with hash', async () => {
|
|
85
|
+
let hashChangeCount = 0;
|
|
86
|
+
const onHashChange = () => hashChangeCount++;
|
|
87
|
+
addEventListener('hashchange', onHashChange);
|
|
88
|
+
window.history.pushState({}, '', '/c#a%20b');
|
|
89
|
+
removeEventListener('hashchange', onHashChange);
|
|
90
|
+
expect(window.location.pathname).to.equal('/c');
|
|
91
|
+
expect(window.location.hash).to.equal('#a%20b');
|
|
92
|
+
expect(history.getParams().hash).to.equal('#a b');
|
|
93
|
+
expect(hashChangeCount).to.equal(1);
|
|
94
|
+
});
|
|
76
95
|
it('pushIgnoreCloseHandle', async () => {
|
|
77
96
|
const { href } = window.location;
|
|
78
97
|
let state = { open: true };
|
package/src/test/theme.test.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createTheme } from '../helper/theme';
|
|
1
|
+
import { createDecoratorTheme, createScopedTheme, createTheme } from '../helper/theme';
|
|
2
2
|
import { aTimeout, expect } from './utils';
|
|
3
3
|
|
|
4
4
|
describe('theme 测试', () => {
|
|
@@ -14,4 +14,11 @@ describe('theme 测试', () => {
|
|
|
14
14
|
expect(getComputedStyle(el).width).to.equal('1px');
|
|
15
15
|
el.remove();
|
|
16
16
|
});
|
|
17
|
+
|
|
18
|
+
it('key 作为变量名后缀', () => {
|
|
19
|
+
expect(createTheme({ textColor: 'red' }, 'k1').textColor).to.equal('var(--text-color-k1)');
|
|
20
|
+
expect(createScopedTheme({ size: '1px' }, 'k2').size).to.equal('var(--size-k2)');
|
|
21
|
+
expect(createDecoratorTheme({ size: '1px' }, 'k3').size).to.equal('var(--size-k3)');
|
|
22
|
+
expect(createTheme({ size: '1px' }).size).to.not.equal(createTheme({ size: '1px' }).size, '没有 key 时随机');
|
|
23
|
+
});
|
|
17
24
|
});
|
package/src/test/utils.test.ts
CHANGED
|
@@ -116,6 +116,12 @@ describe('utils 测试', () => {
|
|
|
116
116
|
expect(bodyStyleSheet.cssRules.item(0).cssText.startsWith('@scope (body)')).to.true;
|
|
117
117
|
expect(bodyStyleSheet).to.equal(bodyStyleSheet);
|
|
118
118
|
expect(bodyStyleSheet).not.to.equal(cssSheet[SheetToken].getStyle(document.documentElement));
|
|
119
|
+
// 更新内容时保留 light DOM 的作用域
|
|
120
|
+
cssSheet[SheetToken].setContent('div { color: blue; }');
|
|
121
|
+
cssSheet[SheetToken].updateStyle();
|
|
122
|
+
expect(bodyStyleSheet.cssRules.item(0).cssText.startsWith('@scope (body)')).to.true;
|
|
123
|
+
expect(bodyStyleSheet.cssRules.item(0).cssText).to.include('blue');
|
|
124
|
+
expect(cssSheet[SheetToken].getStyle().cssRules.item(0).style.color).to.equal('blue');
|
|
119
125
|
});
|
|
120
126
|
it('raw/css', () => {
|
|
121
127
|
const title: any = undefined;
|
package/src/test/utils.ts
CHANGED
|
@@ -13,6 +13,62 @@ export function aTimeout(ms = 0) {
|
|
|
13
13
|
return new Promise((resolve) => setTimeout(() => resolve(null), ms));
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
+
export function createFakeTimers() {
|
|
17
|
+
type Task = { at: number; every?: number; fn: () => void };
|
|
18
|
+
const tasks = new Map<number, Task>();
|
|
19
|
+
const originals = {
|
|
20
|
+
setTimeout: window.setTimeout,
|
|
21
|
+
setInterval: window.setInterval,
|
|
22
|
+
clearTimeout: window.clearTimeout,
|
|
23
|
+
clearInterval: window.clearInterval,
|
|
24
|
+
};
|
|
25
|
+
const realClearTimeout = window.clearTimeout.bind(window);
|
|
26
|
+
let now = 0;
|
|
27
|
+
let nextId = -1;
|
|
28
|
+
|
|
29
|
+
const schedule = (fn: () => void, delay = 0, every?: number) => {
|
|
30
|
+
const id = nextId--;
|
|
31
|
+
tasks.set(id, { at: now + Math.max(delay, 0), every, fn });
|
|
32
|
+
return id;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
return {
|
|
36
|
+
install: () => {
|
|
37
|
+
window.setTimeout = ((fn: () => void, delay?: number) =>
|
|
38
|
+
schedule(fn, delay)) as unknown as typeof window.setTimeout;
|
|
39
|
+
window.setInterval = ((fn: () => void, delay?: number) =>
|
|
40
|
+
schedule(fn, delay, Math.max(delay ?? 1, 1))) as unknown as typeof window.setInterval;
|
|
41
|
+
const clear = ((id: number) => {
|
|
42
|
+
if (!tasks.delete(id)) realClearTimeout(id);
|
|
43
|
+
}) as unknown as typeof window.clearTimeout;
|
|
44
|
+
window.clearTimeout = clear;
|
|
45
|
+
window.clearInterval = clear;
|
|
46
|
+
},
|
|
47
|
+
uninstall: () => {
|
|
48
|
+
window.setTimeout = originals.setTimeout;
|
|
49
|
+
window.setInterval = originals.setInterval;
|
|
50
|
+
window.clearTimeout = originals.clearTimeout;
|
|
51
|
+
window.clearInterval = originals.clearInterval;
|
|
52
|
+
tasks.clear();
|
|
53
|
+
},
|
|
54
|
+
tick: (ms: number) => {
|
|
55
|
+
const end = now + ms;
|
|
56
|
+
for (;;) {
|
|
57
|
+
let next: { id: number; task: Task } | undefined;
|
|
58
|
+
for (const [id, task] of tasks) {
|
|
59
|
+
if (task.at <= end && (!next || task.at < next.task.at)) next = { id, task };
|
|
60
|
+
}
|
|
61
|
+
if (!next) break;
|
|
62
|
+
now = next.task.at;
|
|
63
|
+
if (next.task.every === undefined) tasks.delete(next.id);
|
|
64
|
+
else next.task.at += next.task.every;
|
|
65
|
+
next.task.fn();
|
|
66
|
+
}
|
|
67
|
+
now = end;
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
16
72
|
export async function fixture(template: TemplateResult) {
|
|
17
73
|
const wrapper = document.createElement('div');
|
|
18
74
|
document.body.appendChild(wrapper);
|