@acusti/styling 0.7.0 → 0.7.1
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/style-registry.d.ts +2 -0
- package/dist/style-registry.js +15 -1
- package/dist/style-registry.js.flow +2 -0
- package/dist/style-registry.js.map +1 -1
- package/dist/style-registry.test.d.ts +1 -0
- package/dist/style-registry.test.js +114 -0
- package/dist/style-registry.test.js.flow +6 -0
- package/dist/style-registry.test.js.map +1 -0
- package/package.json +19 -10
- package/src/style-registry.test.ts +129 -0
- package/src/style-registry.ts +16 -4
package/dist/style-registry.d.ts
CHANGED
|
@@ -14,4 +14,6 @@ type UpdatePayload = {
|
|
|
14
14
|
styles: string;
|
|
15
15
|
};
|
|
16
16
|
export declare const updateStyles: ({ ownerDocument, previousStyles, styles, }: UpdatePayload) => void;
|
|
17
|
+
export declare const getStyleRegistryKeys: () => IterableIterator<string>;
|
|
18
|
+
export declare const clearRegistry: () => void;
|
|
17
19
|
export {};
|
package/dist/style-registry.js
CHANGED
|
@@ -8,6 +8,8 @@ export const getRegisteredStyles = ({ ownerDocument, styles }) => {
|
|
|
8
8
|
return null;
|
|
9
9
|
return (_a = stylesMap.get(ownerDocument)) !== null && _a !== void 0 ? _a : null;
|
|
10
10
|
};
|
|
11
|
+
// NOTE a more idiomatic API than (register|unregister)Styles would be
|
|
12
|
+
// to make registerStyles a thunk that returns a cleanup function
|
|
11
13
|
export const registerStyles = ({ ownerDocument, styles }) => {
|
|
12
14
|
if (!styles)
|
|
13
15
|
return;
|
|
@@ -17,7 +19,15 @@ export const registerStyles = ({ ownerDocument, styles }) => {
|
|
|
17
19
|
return;
|
|
18
20
|
}
|
|
19
21
|
if (ownerDocument === 'global') {
|
|
20
|
-
|
|
22
|
+
const stylesItem = { element: null, referenceCount: 1 };
|
|
23
|
+
let stylesMap = styleRegistry.get(styles);
|
|
24
|
+
if (stylesMap) {
|
|
25
|
+
stylesMap.set(ownerDocument, stylesItem);
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
stylesMap = new Map([[ownerDocument, stylesItem]]);
|
|
29
|
+
}
|
|
30
|
+
styleRegistry.set(styles, stylesMap);
|
|
21
31
|
return;
|
|
22
32
|
}
|
|
23
33
|
const element = ownerDocument.createElement('style');
|
|
@@ -77,4 +87,8 @@ export const updateStyles = ({ ownerDocument, previousStyles, styles, }) => {
|
|
|
77
87
|
}
|
|
78
88
|
registerStyles({ ownerDocument, styles });
|
|
79
89
|
};
|
|
90
|
+
export const getStyleRegistryKeys = () => styleRegistry.keys();
|
|
91
|
+
export const clearRegistry = () => {
|
|
92
|
+
styleRegistry.clear();
|
|
93
|
+
};
|
|
80
94
|
//# sourceMappingURL=style-registry.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"style-registry.js","sourceRoot":"","sources":["../src/style-registry.ts"],"names":[],"mappings":"AAKA,MAAM,aAAa,GAAkB,IAAI,GAAG,EAAE,CAAC;AAI/C,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,EAAE,aAAa,EAAE,MAAM,EAAW,EAAE,EAAE;;IACtE,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IACzB,MAAM,SAAS,GAAG,aAAa,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAC5C,IAAI,CAAC,SAAS;QAAE,OAAO,IAAI,CAAC;IAC5B,OAAO,MAAA,SAAS,CAAC,GAAG,CAAC,aAAa,CAAC,mCAAI,IAAI,CAAC;AAChD,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,EAAE,aAAa,EAAE,MAAM,EAAW,EAAE,EAAE;IACjE,IAAI,CAAC,MAAM;QAAE,OAAO;IAEpB,MAAM,kBAAkB,GAAG,mBAAmB,CAAC,EAAE,aAAa,EAAE,MAAM,EAAE,CAAC,CAAC;IAC1E,IAAI,kBAAkB,EAAE,CAAC;QACrB,kBAAkB,CAAC,cAAc,EAAE,CAAC;QACpC,OAAO;IACX,CAAC;IAED,IAAI,aAAa,KAAK,QAAQ,EAAE,CAAC;QAC7B,aAAa,CAAC,GAAG,
|
|
1
|
+
{"version":3,"file":"style-registry.js","sourceRoot":"","sources":["../src/style-registry.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
// @vitest-environment happy-dom
|
|
2
|
+
import { beforeEach, describe, expect, it } from 'vitest';
|
|
3
|
+
import { clearRegistry, getRegisteredStyles, getStyleRegistryKeys, registerStyles, unregisterStyles, updateStyles, } from './style-registry.js';
|
|
4
|
+
describe('@acusti/styling', () => {
|
|
5
|
+
describe('style-registry.ts', () => {
|
|
6
|
+
const mockStyles = '.test { color: red; }';
|
|
7
|
+
// reset styleRegistry before each test
|
|
8
|
+
beforeEach(clearRegistry);
|
|
9
|
+
describe('registerStyles', () => {
|
|
10
|
+
it('should add styles to the registry keyed by the style string', () => {
|
|
11
|
+
const payload = { ownerDocument: document, styles: mockStyles };
|
|
12
|
+
registerStyles(payload);
|
|
13
|
+
const styleRegistryKeys = getStyleRegistryKeys();
|
|
14
|
+
const keysArray = [...styleRegistryKeys];
|
|
15
|
+
expect(keysArray.length).toBe(1);
|
|
16
|
+
expect(keysArray[0]).toBe(mockStyles);
|
|
17
|
+
const result = getRegisteredStyles(payload);
|
|
18
|
+
expect(result.element).toBeDefined();
|
|
19
|
+
expect(result.referenceCount).toBe(1);
|
|
20
|
+
});
|
|
21
|
+
it('should allow registering styles without a DOM via ownerDocument: "global"', () => {
|
|
22
|
+
const payload = Object.freeze({
|
|
23
|
+
ownerDocument: 'global',
|
|
24
|
+
styles: mockStyles,
|
|
25
|
+
});
|
|
26
|
+
registerStyles(payload);
|
|
27
|
+
const registryKeys = [...getStyleRegistryKeys()];
|
|
28
|
+
expect(registryKeys.length).toBe(1);
|
|
29
|
+
expect(registryKeys[0]).toBe(mockStyles);
|
|
30
|
+
const result = getRegisteredStyles(payload);
|
|
31
|
+
expect(result.element).toBeNull();
|
|
32
|
+
expect(result.referenceCount).toBe(1);
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
describe('getRegisteredStyles', () => {
|
|
36
|
+
it('should retrieve registered styles', () => {
|
|
37
|
+
const payload = { ownerDocument: document, styles: mockStyles };
|
|
38
|
+
registerStyles(payload);
|
|
39
|
+
const result = getRegisteredStyles(payload);
|
|
40
|
+
expect(result.element.tagName).toBe('STYLE');
|
|
41
|
+
expect(result.referenceCount).toBe(1);
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
describe('unregisterStyles', () => {
|
|
45
|
+
it('should remove styles from the registry if no other references to same styles exist', () => {
|
|
46
|
+
const payload = { ownerDocument: document, styles: mockStyles };
|
|
47
|
+
const otherPayload = Object.freeze({
|
|
48
|
+
ownerDocument: 'global',
|
|
49
|
+
styles: mockStyles,
|
|
50
|
+
});
|
|
51
|
+
registerStyles(payload);
|
|
52
|
+
registerStyles(otherPayload);
|
|
53
|
+
expect(getStyleRegistryKeys().next().value).toBe(mockStyles);
|
|
54
|
+
expect(getRegisteredStyles(payload).referenceCount).toBe(1);
|
|
55
|
+
expect(getRegisteredStyles(otherPayload).referenceCount).toBe(1);
|
|
56
|
+
unregisterStyles(payload);
|
|
57
|
+
// style registry for this style should still exist
|
|
58
|
+
expect(getStyleRegistryKeys().next().value).toBe(mockStyles);
|
|
59
|
+
// but this document’s styles item should be cleared
|
|
60
|
+
expect(getRegisteredStyles(payload)).toBeNull();
|
|
61
|
+
unregisterStyles(otherPayload);
|
|
62
|
+
// now the style registry should be empty
|
|
63
|
+
expect([...getStyleRegistryKeys()].length).toBe(0);
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
describe('updateStyles', () => {
|
|
67
|
+
it('should update styles in the registry, reusing the existing DOM element if no other reference', () => {
|
|
68
|
+
const previousStyles = '.previous { color: blue; }';
|
|
69
|
+
const payload = {
|
|
70
|
+
ownerDocument: document,
|
|
71
|
+
styles: previousStyles,
|
|
72
|
+
};
|
|
73
|
+
registerStyles(payload);
|
|
74
|
+
const previousStylesItem = getRegisteredStyles(payload);
|
|
75
|
+
expect(previousStylesItem.element.innerText).toBe(previousStyles);
|
|
76
|
+
updateStyles({
|
|
77
|
+
ownerDocument: document,
|
|
78
|
+
previousStyles,
|
|
79
|
+
styles: mockStyles,
|
|
80
|
+
});
|
|
81
|
+
const stylesItem = getRegisteredStyles({
|
|
82
|
+
ownerDocument: document,
|
|
83
|
+
styles: mockStyles,
|
|
84
|
+
});
|
|
85
|
+
expect(previousStylesItem.element).toBe(stylesItem.element);
|
|
86
|
+
expect(stylesItem.element.innerText).toBe(mockStyles);
|
|
87
|
+
expect(getRegisteredStyles(payload)).toBeNull();
|
|
88
|
+
});
|
|
89
|
+
it('should update styles in the registry, creating a new DOM element if there are other references', () => {
|
|
90
|
+
const previousStyles = '.previous { color: blue; }';
|
|
91
|
+
const payload = { ownerDocument: document, styles: previousStyles };
|
|
92
|
+
registerStyles(payload);
|
|
93
|
+
// create multiple references to the same styles and document
|
|
94
|
+
registerStyles(payload);
|
|
95
|
+
const previousStylesItem = getRegisteredStyles(payload);
|
|
96
|
+
expect(previousStylesItem.referenceCount).toBe(2);
|
|
97
|
+
expect(previousStylesItem.element.innerText).toBe(previousStyles);
|
|
98
|
+
updateStyles({
|
|
99
|
+
ownerDocument: document,
|
|
100
|
+
previousStyles,
|
|
101
|
+
styles: mockStyles,
|
|
102
|
+
});
|
|
103
|
+
const stylesItem = getRegisteredStyles({
|
|
104
|
+
ownerDocument: document,
|
|
105
|
+
styles: mockStyles,
|
|
106
|
+
});
|
|
107
|
+
expect(previousStylesItem.element).not.toBe(stylesItem.element);
|
|
108
|
+
expect(stylesItem.element.innerText).toBe(mockStyles);
|
|
109
|
+
expect(getRegisteredStyles(payload).referenceCount).toBe(1);
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
//# sourceMappingURL=style-registry.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"style-registry.test.js","sourceRoot":"","sources":["../src/style-registry.test.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@acusti/styling",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.1",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"sideEffects": false,
|
|
6
|
+
"exports": "./dist/index.js",
|
|
7
|
+
"main": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"files": [
|
|
10
|
+
"dist",
|
|
11
|
+
"src"
|
|
12
|
+
],
|
|
4
13
|
"description": "React component that renders a CSS style string as a <style> element in the <head> of the document (with global style de-duplication)",
|
|
5
14
|
"keywords": [
|
|
6
15
|
"react",
|
|
@@ -13,15 +22,9 @@
|
|
|
13
22
|
"ts",
|
|
14
23
|
"flow"
|
|
15
24
|
],
|
|
16
|
-
"
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
"main": "./dist/index.js",
|
|
20
|
-
"types": "./dist/index.d.ts",
|
|
21
|
-
"files": [
|
|
22
|
-
"dist",
|
|
23
|
-
"src"
|
|
24
|
-
],
|
|
25
|
+
"scripts": {
|
|
26
|
+
"test": "vitest"
|
|
27
|
+
},
|
|
25
28
|
"repository": {
|
|
26
29
|
"type": "git",
|
|
27
30
|
"url": "https://github.com/acusti/uikit.git",
|
|
@@ -34,7 +37,13 @@
|
|
|
34
37
|
},
|
|
35
38
|
"homepage": "https://github.com/acusti/uikit/tree/main/packages/styling#readme",
|
|
36
39
|
"devDependencies": {
|
|
40
|
+
"@testing-library/dom": "^9.3.1",
|
|
41
|
+
"@testing-library/react": "^14.0.0",
|
|
42
|
+
"@testing-library/user-event": "^14.4.3",
|
|
37
43
|
"@types/react": "^18.2.45",
|
|
44
|
+
"happy-dom": "^12.10.3",
|
|
45
|
+
"react": "^18",
|
|
46
|
+
"react-dom": "^18",
|
|
38
47
|
"typescript": "^5.3.3",
|
|
39
48
|
"vitest": "^1.1.0"
|
|
40
49
|
},
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
// @vitest-environment happy-dom
|
|
2
|
+
import { beforeEach, describe, expect, it } from 'vitest';
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
clearRegistry,
|
|
6
|
+
getRegisteredStyles,
|
|
7
|
+
getStyleRegistryKeys,
|
|
8
|
+
registerStyles,
|
|
9
|
+
unregisterStyles,
|
|
10
|
+
updateStyles,
|
|
11
|
+
} from './style-registry.js';
|
|
12
|
+
|
|
13
|
+
describe('@acusti/styling', () => {
|
|
14
|
+
describe('style-registry.ts', () => {
|
|
15
|
+
const mockStyles = '.test { color: red; }';
|
|
16
|
+
|
|
17
|
+
// reset styleRegistry before each test
|
|
18
|
+
beforeEach(clearRegistry);
|
|
19
|
+
|
|
20
|
+
describe('registerStyles', () => {
|
|
21
|
+
it('should add styles to the registry keyed by the style string', () => {
|
|
22
|
+
const payload = { ownerDocument: document, styles: mockStyles };
|
|
23
|
+
registerStyles(payload);
|
|
24
|
+
const styleRegistryKeys = getStyleRegistryKeys();
|
|
25
|
+
const keysArray = [...styleRegistryKeys];
|
|
26
|
+
expect(keysArray.length).toBe(1);
|
|
27
|
+
expect(keysArray[0]).toBe(mockStyles);
|
|
28
|
+
const result = getRegisteredStyles(payload);
|
|
29
|
+
expect(result!.element).toBeDefined();
|
|
30
|
+
expect(result!.referenceCount).toBe(1);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('should allow registering styles without a DOM via ownerDocument: "global"', () => {
|
|
34
|
+
const payload = Object.freeze({
|
|
35
|
+
ownerDocument: 'global',
|
|
36
|
+
styles: mockStyles,
|
|
37
|
+
});
|
|
38
|
+
registerStyles(payload);
|
|
39
|
+
const registryKeys = [...getStyleRegistryKeys()];
|
|
40
|
+
expect(registryKeys.length).toBe(1);
|
|
41
|
+
expect(registryKeys[0]).toBe(mockStyles);
|
|
42
|
+
const result = getRegisteredStyles(payload);
|
|
43
|
+
expect(result!.element).toBeNull();
|
|
44
|
+
expect(result!.referenceCount).toBe(1);
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
describe('getRegisteredStyles', () => {
|
|
49
|
+
it('should retrieve registered styles', () => {
|
|
50
|
+
const payload = { ownerDocument: document, styles: mockStyles };
|
|
51
|
+
registerStyles(payload);
|
|
52
|
+
const result = getRegisteredStyles(payload);
|
|
53
|
+
expect(result!.element!.tagName).toBe('STYLE');
|
|
54
|
+
expect(result!.referenceCount).toBe(1);
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
describe('unregisterStyles', () => {
|
|
59
|
+
it('should remove styles from the registry if no other references to same styles exist', () => {
|
|
60
|
+
const payload = { ownerDocument: document, styles: mockStyles };
|
|
61
|
+
const otherPayload = Object.freeze({
|
|
62
|
+
ownerDocument: 'global',
|
|
63
|
+
styles: mockStyles,
|
|
64
|
+
});
|
|
65
|
+
registerStyles(payload);
|
|
66
|
+
registerStyles(otherPayload);
|
|
67
|
+
expect(getStyleRegistryKeys().next().value).toBe(mockStyles);
|
|
68
|
+
expect(getRegisteredStyles(payload)!.referenceCount).toBe(1);
|
|
69
|
+
expect(getRegisteredStyles(otherPayload)!.referenceCount).toBe(1);
|
|
70
|
+
unregisterStyles(payload);
|
|
71
|
+
// style registry for this style should still exist
|
|
72
|
+
expect(getStyleRegistryKeys().next().value).toBe(mockStyles);
|
|
73
|
+
// but this document’s styles item should be cleared
|
|
74
|
+
expect(getRegisteredStyles(payload)).toBeNull();
|
|
75
|
+
unregisterStyles(otherPayload);
|
|
76
|
+
// now the style registry should be empty
|
|
77
|
+
expect([...getStyleRegistryKeys()].length).toBe(0);
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
describe('updateStyles', () => {
|
|
82
|
+
it('should update styles in the registry, reusing the existing DOM element if no other reference', () => {
|
|
83
|
+
const previousStyles = '.previous { color: blue; }';
|
|
84
|
+
const payload = {
|
|
85
|
+
ownerDocument: document,
|
|
86
|
+
styles: previousStyles,
|
|
87
|
+
};
|
|
88
|
+
registerStyles(payload);
|
|
89
|
+
const previousStylesItem = getRegisteredStyles(payload)!;
|
|
90
|
+
expect(previousStylesItem.element!.innerText).toBe(previousStyles);
|
|
91
|
+
updateStyles({
|
|
92
|
+
ownerDocument: document,
|
|
93
|
+
previousStyles,
|
|
94
|
+
styles: mockStyles,
|
|
95
|
+
});
|
|
96
|
+
const stylesItem = getRegisteredStyles({
|
|
97
|
+
ownerDocument: document,
|
|
98
|
+
styles: mockStyles,
|
|
99
|
+
})!;
|
|
100
|
+
expect(previousStylesItem.element).toBe(stylesItem.element);
|
|
101
|
+
expect(stylesItem.element!.innerText).toBe(mockStyles);
|
|
102
|
+
expect(getRegisteredStyles(payload)).toBeNull();
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('should update styles in the registry, creating a new DOM element if there are other references', () => {
|
|
106
|
+
const previousStyles = '.previous { color: blue; }';
|
|
107
|
+
const payload = { ownerDocument: document, styles: previousStyles };
|
|
108
|
+
registerStyles(payload);
|
|
109
|
+
// create multiple references to the same styles and document
|
|
110
|
+
registerStyles(payload);
|
|
111
|
+
const previousStylesItem = getRegisteredStyles(payload)!;
|
|
112
|
+
expect(previousStylesItem.referenceCount).toBe(2);
|
|
113
|
+
expect(previousStylesItem.element!.innerText).toBe(previousStyles);
|
|
114
|
+
updateStyles({
|
|
115
|
+
ownerDocument: document,
|
|
116
|
+
previousStyles,
|
|
117
|
+
styles: mockStyles,
|
|
118
|
+
});
|
|
119
|
+
const stylesItem = getRegisteredStyles({
|
|
120
|
+
ownerDocument: document,
|
|
121
|
+
styles: mockStyles,
|
|
122
|
+
})!;
|
|
123
|
+
expect(previousStylesItem.element).not.toBe(stylesItem.element);
|
|
124
|
+
expect(stylesItem.element!.innerText).toBe(mockStyles);
|
|
125
|
+
expect(getRegisteredStyles(payload)!.referenceCount).toBe(1);
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
});
|
package/src/style-registry.ts
CHANGED
|
@@ -14,6 +14,8 @@ export const getRegisteredStyles = ({ ownerDocument, styles }: Payload) => {
|
|
|
14
14
|
return stylesMap.get(ownerDocument) ?? null;
|
|
15
15
|
};
|
|
16
16
|
|
|
17
|
+
// NOTE a more idiomatic API than (register|unregister)Styles would be
|
|
18
|
+
// to make registerStyles a thunk that returns a cleanup function
|
|
17
19
|
export const registerStyles = ({ ownerDocument, styles }: Payload) => {
|
|
18
20
|
if (!styles) return;
|
|
19
21
|
|
|
@@ -24,10 +26,14 @@ export const registerStyles = ({ ownerDocument, styles }: Payload) => {
|
|
|
24
26
|
}
|
|
25
27
|
|
|
26
28
|
if (ownerDocument === 'global') {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
const stylesItem = { element: null, referenceCount: 1 };
|
|
30
|
+
let stylesMap = styleRegistry.get(styles);
|
|
31
|
+
if (stylesMap) {
|
|
32
|
+
stylesMap.set(ownerDocument, stylesItem);
|
|
33
|
+
} else {
|
|
34
|
+
stylesMap = new Map([[ownerDocument, stylesItem]]);
|
|
35
|
+
}
|
|
36
|
+
styleRegistry.set(styles, stylesMap);
|
|
31
37
|
return;
|
|
32
38
|
}
|
|
33
39
|
|
|
@@ -101,3 +107,9 @@ export const updateStyles = ({
|
|
|
101
107
|
|
|
102
108
|
registerStyles({ ownerDocument, styles });
|
|
103
109
|
};
|
|
110
|
+
|
|
111
|
+
export const getStyleRegistryKeys = () => styleRegistry.keys();
|
|
112
|
+
|
|
113
|
+
export const clearRegistry = () => {
|
|
114
|
+
styleRegistry.clear();
|
|
115
|
+
};
|