storybook-addon-pseudo-states 1.15.0 → 1.15.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/CHANGELOG.md +12 -0
- package/dist/cjs/PseudoStateTool.js +38 -64
- package/dist/cjs/constants.js +3 -3
- package/dist/cjs/preset/manager.js +5 -3
- package/dist/cjs/preset/preview.js +1 -1
- package/dist/cjs/rewriteStyleSheet.js +31 -55
- package/dist/cjs/rewriteStyleSheet.test.js +29 -44
- package/dist/cjs/splitSelectors.js +8 -10
- package/dist/cjs/splitSelectors.test.js +4 -4
- package/dist/cjs/withPseudoState.js +46 -95
- package/dist/esm/PseudoStateTool.js +35 -59
- package/dist/esm/constants.js +3 -3
- package/dist/esm/preset/manager.js +5 -3
- package/dist/esm/preset/preview.js +1 -1
- package/dist/esm/rewriteStyleSheet.js +29 -53
- package/dist/esm/rewriteStyleSheet.test.js +29 -44
- package/dist/esm/splitSelectors.js +8 -10
- package/dist/esm/splitSelectors.test.js +4 -4
- package/dist/esm/withPseudoState.js +45 -95
- package/package.json +3 -3
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { splitSelectors } from "./splitSelectors";
|
|
2
|
-
describe("splitSelectors",
|
|
3
|
-
test("handles basic selectors",
|
|
2
|
+
describe("splitSelectors", () => {
|
|
3
|
+
test("handles basic selectors", () => {
|
|
4
4
|
expect(splitSelectors(".a")).toEqual([".a"]);
|
|
5
5
|
expect(splitSelectors(".a, .b")).toEqual([".a", ".b"]);
|
|
6
6
|
});
|
|
7
|
-
test("supports ::slotted and :is",
|
|
7
|
+
test("supports ::slotted and :is", () => {
|
|
8
8
|
expect(splitSelectors("::slotted(:is(button, a):active)")).toEqual(["::slotted(:is(button, a):active)"]);
|
|
9
9
|
expect(splitSelectors("::slotted(:is(button, a):active), ::slotted(:is(button, a):hover)")).toEqual(["::slotted(:is(button, a):active)", "::slotted(:is(button, a):hover)"]);
|
|
10
10
|
});
|
|
11
|
-
test("supports :host",
|
|
11
|
+
test("supports :host", () => {
|
|
12
12
|
expect(splitSelectors(":host([type='secondary']) ::slotted(:is(button, a)), :host([type='primary']) ::slotted(:is(button, a):active)")).toEqual([":host([type='secondary']) ::slotted(:is(button, a))", ":host([type='primary']) ::slotted(:is(button, a):active)"]);
|
|
13
13
|
expect(splitSelectors(":host([outline]) ::slotted(:is(button, a):focus-within:focus-visible:not(:active))")).toEqual([":host([outline]) ::slotted(:is(button, a):focus-within:focus-visible:not(:active))"]);
|
|
14
14
|
});
|
|
@@ -1,112 +1,72 @@
|
|
|
1
|
-
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
|
|
2
|
-
|
|
3
|
-
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
|
|
4
|
-
|
|
5
|
-
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
6
|
-
|
|
7
|
-
function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
|
|
8
|
-
|
|
9
|
-
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
|
|
10
|
-
|
|
11
|
-
function _iterableToArrayLimit(arr, i) { var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"]; if (_i == null) return; var _arr = []; var _n = true; var _d = false; var _s, _e; try { for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }
|
|
12
|
-
|
|
13
|
-
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
|
|
14
|
-
|
|
15
|
-
function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); }
|
|
16
|
-
|
|
17
|
-
function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
|
|
18
|
-
|
|
19
|
-
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
|
|
20
|
-
|
|
21
|
-
function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); }
|
|
22
|
-
|
|
23
|
-
function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); }
|
|
24
|
-
|
|
25
|
-
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
|
|
26
|
-
|
|
27
1
|
/* eslint-env browser */
|
|
28
2
|
import { addons, useEffect } from "@storybook/addons";
|
|
29
3
|
import { DOCS_RENDERED, STORY_CHANGED, STORY_RENDERED, UPDATE_GLOBALS } from "@storybook/core-events";
|
|
30
4
|
import { PSEUDO_STATES } from "./constants";
|
|
31
5
|
import { rewriteStyleSheet } from "./rewriteStyleSheet";
|
|
32
|
-
|
|
33
|
-
|
|
6
|
+
const channel = addons.getChannel();
|
|
7
|
+
const shadowHosts = new Set(); // Drops any existing pseudo state classnames that carried over from a previously viewed story
|
|
34
8
|
// before adding the new classnames. We do this the old-fashioned way, for IE compatibility.
|
|
35
9
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
element.className = (_element$className$sp = element.className.split(" ").filter(function (classname) {
|
|
40
|
-
return classname && classname.indexOf("pseudo-") !== 0;
|
|
41
|
-
})).concat.apply(_element$className$sp, _toConsumableArray(classnames)).join(" ");
|
|
10
|
+
const applyClasses = (element, classnames) => {
|
|
11
|
+
element.className = element.className.split(" ").filter(classname => classname && classname.indexOf("pseudo-") !== 0).concat(...classnames).join(" ");
|
|
42
12
|
};
|
|
43
13
|
|
|
44
|
-
|
|
45
|
-
|
|
14
|
+
const applyParameter = (rootElement, parameter) => {
|
|
15
|
+
const map = new Map([[rootElement, new Set()]]);
|
|
46
16
|
|
|
47
|
-
|
|
48
|
-
return map.set(target, new Set([].concat(_toConsumableArray(map.get(target) || []), [state])));
|
|
49
|
-
};
|
|
17
|
+
const add = (target, state) => map.set(target, new Set([...(map.get(target) || []), state]));
|
|
50
18
|
|
|
51
|
-
Object.entries(parameter || {}).forEach(
|
|
52
|
-
|
|
53
|
-
state = _ref2[0],
|
|
54
|
-
value = _ref2[1];
|
|
19
|
+
Object.entries(parameter || {}).forEach(_ref => {
|
|
20
|
+
let [state, value] = _ref;
|
|
55
21
|
|
|
56
22
|
if (typeof value === "boolean") {
|
|
57
23
|
// default API - applying pseudo class to root element.
|
|
58
24
|
add(rootElement, value && state);
|
|
59
25
|
} else if (typeof value === "string") {
|
|
60
26
|
// explicit selectors API - applying pseudo class to a specific element
|
|
61
|
-
rootElement.querySelectorAll(value).forEach(
|
|
62
|
-
return add(el, state);
|
|
63
|
-
});
|
|
27
|
+
rootElement.querySelectorAll(value).forEach(el => add(el, state));
|
|
64
28
|
} else if (Array.isArray(value)) {
|
|
65
29
|
// explicit selectors API - we have an array (of strings) recursively handle each one
|
|
66
|
-
value.forEach(
|
|
67
|
-
return rootElement.querySelectorAll(sel).forEach(function (el) {
|
|
68
|
-
return add(el, state);
|
|
69
|
-
});
|
|
70
|
-
});
|
|
30
|
+
value.forEach(sel => rootElement.querySelectorAll(sel).forEach(el => add(el, state)));
|
|
71
31
|
}
|
|
72
32
|
});
|
|
73
|
-
map.forEach(
|
|
74
|
-
|
|
75
|
-
states.forEach(
|
|
76
|
-
return PSEUDO_STATES[key] && classnames.push("pseudo-".concat(PSEUDO_STATES[key]));
|
|
77
|
-
});
|
|
33
|
+
map.forEach((states, target) => {
|
|
34
|
+
const classnames = [];
|
|
35
|
+
states.forEach(key => PSEUDO_STATES[key] && classnames.push("pseudo-".concat(PSEUDO_STATES[key])));
|
|
78
36
|
applyClasses(target, classnames);
|
|
79
37
|
});
|
|
80
38
|
}; // Traverses ancestry to collect relevant pseudo classnames, and applies them to the shadow host.
|
|
81
39
|
// Shadow DOM can only access classes on its host. Traversing is needed to mimic the CSS cascade.
|
|
82
40
|
|
|
83
41
|
|
|
84
|
-
|
|
85
|
-
|
|
42
|
+
const updateShadowHost = shadowHost => {
|
|
43
|
+
const classnames = new Set();
|
|
86
44
|
|
|
87
|
-
for (
|
|
45
|
+
for (let element = shadowHost.parentElement; element; element = element.parentElement) {
|
|
88
46
|
if (!element.className) continue;
|
|
89
|
-
element.className.split(" ").filter(
|
|
90
|
-
return classname.indexOf("pseudo-") === 0;
|
|
91
|
-
}).forEach(function (classname) {
|
|
92
|
-
return classnames.add(classname);
|
|
93
|
-
});
|
|
47
|
+
element.className.split(" ").filter(classname => classname.indexOf("pseudo-") === 0).forEach(classname => classnames.add(classname));
|
|
94
48
|
}
|
|
95
49
|
|
|
96
50
|
applyClasses(shadowHost, classnames);
|
|
97
51
|
}; // Global decorator that rewrites stylesheets and applies classnames to render pseudo styles
|
|
98
52
|
|
|
99
53
|
|
|
100
|
-
export
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
54
|
+
export const withPseudoState = (StoryFn, _ref2) => {
|
|
55
|
+
let {
|
|
56
|
+
viewMode,
|
|
57
|
+
parameters,
|
|
58
|
+
id,
|
|
59
|
+
globals: globalsArgs
|
|
60
|
+
} = _ref2;
|
|
61
|
+
const {
|
|
62
|
+
pseudo: parameter
|
|
63
|
+
} = parameters;
|
|
64
|
+
const {
|
|
65
|
+
pseudo: globals
|
|
66
|
+
} = globalsArgs; // Sync parameter to globals, used by the toolbar (only in canvas as this
|
|
107
67
|
// doesn't make sense for docs because many stories are displayed at once)
|
|
108
68
|
|
|
109
|
-
useEffect(
|
|
69
|
+
useEffect(() => {
|
|
110
70
|
if (parameter !== globals && viewMode === "story") {
|
|
111
71
|
channel.emit(UPDATE_GLOBALS, {
|
|
112
72
|
globals: {
|
|
@@ -117,41 +77,31 @@ export var withPseudoState = function withPseudoState(StoryFn, _ref3) {
|
|
|
117
77
|
}, [parameter, viewMode]); // Convert selected states to classnames and apply them to the story root element.
|
|
118
78
|
// Then update each shadow host to redetermine its own pseudo classnames.
|
|
119
79
|
|
|
120
|
-
useEffect(
|
|
121
|
-
|
|
122
|
-
|
|
80
|
+
useEffect(() => {
|
|
81
|
+
const timeout = setTimeout(() => {
|
|
82
|
+
const element = document.getElementById(viewMode === "docs" ? "story--".concat(id) : "root");
|
|
123
83
|
applyParameter(element, globals || parameter);
|
|
124
84
|
shadowHosts.forEach(updateShadowHost);
|
|
125
85
|
}, 0);
|
|
126
|
-
return
|
|
127
|
-
return clearTimeout(timeout);
|
|
128
|
-
};
|
|
86
|
+
return () => clearTimeout(timeout);
|
|
129
87
|
}, [globals, parameter, viewMode]);
|
|
130
88
|
return StoryFn();
|
|
131
89
|
}; // Rewrite CSS rules for pseudo-states on all stylesheets to add an alternative selector
|
|
132
90
|
|
|
133
|
-
|
|
91
|
+
const rewriteStyleSheets = shadowRoot => {
|
|
134
92
|
var _shadowRoot$adoptedSt;
|
|
135
93
|
|
|
136
|
-
|
|
94
|
+
let styleSheets = shadowRoot ? shadowRoot.styleSheets : document.styleSheets;
|
|
137
95
|
if (shadowRoot !== null && shadowRoot !== void 0 && (_shadowRoot$adoptedSt = shadowRoot.adoptedStyleSheets) !== null && _shadowRoot$adoptedSt !== void 0 && _shadowRoot$adoptedSt.length) styleSheets = shadowRoot.adoptedStyleSheets;
|
|
138
|
-
Array.from(styleSheets).forEach(
|
|
139
|
-
return rewriteStyleSheet(sheet, shadowRoot, shadowHosts);
|
|
140
|
-
});
|
|
96
|
+
Array.from(styleSheets).forEach(sheet => rewriteStyleSheet(sheet, shadowRoot, shadowHosts));
|
|
141
97
|
}; // Only track shadow hosts for the current story
|
|
142
98
|
|
|
143
99
|
|
|
144
|
-
channel.on(STORY_CHANGED,
|
|
145
|
-
return shadowHosts.clear();
|
|
146
|
-
}); // Reinitialize CSS enhancements every time the story changes
|
|
100
|
+
channel.on(STORY_CHANGED, () => shadowHosts.clear()); // Reinitialize CSS enhancements every time the story changes
|
|
147
101
|
|
|
148
|
-
channel.on(STORY_RENDERED,
|
|
149
|
-
return rewriteStyleSheets();
|
|
150
|
-
}); // Reinitialize CSS enhancements every time a docs page is rendered
|
|
102
|
+
channel.on(STORY_RENDERED, () => rewriteStyleSheets()); // Reinitialize CSS enhancements every time a docs page is rendered
|
|
151
103
|
|
|
152
|
-
channel.on(DOCS_RENDERED,
|
|
153
|
-
return rewriteStyleSheets();
|
|
154
|
-
}); // IE doesn't support shadow DOM
|
|
104
|
+
channel.on(DOCS_RENDERED, () => rewriteStyleSheets()); // IE doesn't support shadow DOM
|
|
155
105
|
|
|
156
106
|
if (Element.prototype.attachShadow) {
|
|
157
107
|
// Monkeypatch the attachShadow method so we can handle pseudo styles inside shadow DOM
|
|
@@ -159,12 +109,12 @@ if (Element.prototype.attachShadow) {
|
|
|
159
109
|
|
|
160
110
|
Element.prototype.attachShadow = function attachShadow(init) {
|
|
161
111
|
// Force "open" mode, so we can access the shadowRoot
|
|
162
|
-
|
|
112
|
+
const shadowRoot = this._attachShadow({ ...init,
|
|
163
113
|
mode: "open"
|
|
164
|
-
})
|
|
114
|
+
}); // Wait for it to render and apply its styles before rewriting them
|
|
165
115
|
|
|
166
116
|
|
|
167
|
-
requestAnimationFrame(
|
|
117
|
+
requestAnimationFrame(() => {
|
|
168
118
|
rewriteStyleSheets(shadowRoot);
|
|
169
119
|
updateShadowHost(shadowRoot.host);
|
|
170
120
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "storybook-addon-pseudo-states",
|
|
3
|
-
"version": "1.15.
|
|
3
|
+
"version": "1.15.1",
|
|
4
4
|
"description": "CSS pseudo states for Storybook",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"storybook-addons",
|
|
@@ -55,8 +55,8 @@
|
|
|
55
55
|
"@storybook/components": "^6.5.0",
|
|
56
56
|
"@storybook/core-events": "^6.5.0",
|
|
57
57
|
"@storybook/theming": "^6.5.0",
|
|
58
|
-
"react": "^16.8.0 || ^17.0.0",
|
|
59
|
-
"react-dom": "^16.8.0 || ^17.0.0"
|
|
58
|
+
"react": "^16.8.0 || ^17.0.0 || ^18.0.0",
|
|
59
|
+
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
|
60
60
|
},
|
|
61
61
|
"peerDependenciesMeta": {
|
|
62
62
|
"react": {
|