@yak/react 0.0.0-stage → 10.0.0-rc.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/LICENSE +21 -0
- package/README.md +102 -2
- package/dist/context/baseContext.cjs +9 -0
- package/dist/context/baseContext.cjs.map +1 -0
- package/dist/context/baseContext.d.cts +6 -0
- package/dist/context/baseContext.d.ts +6 -0
- package/dist/context/baseContext.js +8 -0
- package/dist/context/baseContext.js.map +1 -0
- package/dist/context/index.cjs +45 -0
- package/dist/context/index.cjs.map +1 -0
- package/dist/context/index.d.cts +21 -0
- package/dist/context/index.d.ts +21 -0
- package/dist/context/index.js +15 -0
- package/dist/context/index.js.map +1 -0
- package/dist/context/index.server.cjs +47 -0
- package/dist/context/index.server.cjs.map +1 -0
- package/dist/context/index.server.d.cts +10 -0
- package/dist/context/index.server.d.ts +10 -0
- package/dist/context/index.server.js +17 -0
- package/dist/context/index.server.js.map +1 -0
- package/dist/cross-file-resolver/index.d.ts +199 -0
- package/dist/cross-file-resolver/index.js +535 -0
- package/dist/cross-file-resolver/index.js.map +1 -0
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +320 -0
- package/dist/index.d.ts +320 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/internal.cjs +544 -0
- package/dist/internal.cjs.map +1 -0
- package/dist/internal.d.cts +452 -0
- package/dist/internal.d.ts +452 -0
- package/dist/internal.js +363 -0
- package/dist/internal.js.map +1 -0
- package/dist/isolated-source-eval/index.d.ts +30 -0
- package/dist/isolated-source-eval/index.js +317 -0
- package/dist/isolated-source-eval/index.js.map +1 -0
- package/dist/isolated-source-eval/worker.js +59 -0
- package/dist/isolated-source-eval/worker.js.map +1 -0
- package/dist/jsx-dev-runtime.cjs +2 -0
- package/dist/jsx-dev-runtime.cjs.map +1 -0
- package/dist/jsx-dev-runtime.d.cts +7 -0
- package/dist/jsx-dev-runtime.d.ts +7 -0
- package/dist/jsx-dev-runtime.js +2 -0
- package/dist/jsx-dev-runtime.js.map +1 -0
- package/dist/jsx-runtime.cjs +2 -0
- package/dist/jsx-runtime.cjs.map +1 -0
- package/dist/jsx-runtime.d.cts +38 -0
- package/dist/jsx-runtime.d.ts +38 -0
- package/dist/jsx-runtime.js +2 -0
- package/dist/jsx-runtime.js.map +1 -0
- package/dist/loaders/turbo-loader.cjs +803 -0
- package/dist/loaders/turbo-loader.cjs.map +1 -0
- package/dist/loaders/turbo-loader.d.cts +15 -0
- package/dist/loaders/vite-plugin.d.ts +111 -0
- package/dist/loaders/vite-plugin.js +878 -0
- package/dist/loaders/vite-plugin.js.map +1 -0
- package/dist/loaders/webpack-loader.cjs +697 -0
- package/dist/loaders/webpack-loader.cjs.map +1 -0
- package/dist/loaders/webpack-loader.d.cts +12 -0
- package/dist/rsbuild/index.d.ts +29 -0
- package/dist/rsbuild/index.js +52 -0
- package/dist/rsbuild/index.js.map +1 -0
- package/dist/static/chunk-DK3Fl9T5.js +1 -0
- package/dist/static/index.cjs +2 -0
- package/dist/static/index.cjs.map +1 -0
- package/dist/static/index.d.cts +28 -0
- package/dist/static/index.d.ts +28 -0
- package/dist/static/index.js +2 -0
- package/dist/static/index.js.map +1 -0
- package/dist/withYak/index.cjs +208 -0
- package/dist/withYak/index.cjs.map +1 -0
- package/dist/withYak/index.d.cts +121 -0
- package/dist/withYak/index.d.ts +121 -0
- package/dist/withYak/index.js +177 -0
- package/dist/withYak/index.js.map +1 -0
- package/loaders/lib/resolveCrossFileSelectors.ts +162 -0
- package/loaders/turbo-evaluator.ts +134 -0
- package/loaders/turbo-loader.ts +180 -0
- package/loaders/vite-plugin.ts +28 -0
- package/loaders/webpack-loader.ts +44 -0
- package/package.json +214 -3
- package/rsbuild/index.ts +92 -0
- package/runtime/__tests__/atoms.test.tsx +48 -0
- package/runtime/__tests__/attrs.test.tsx +490 -0
- package/runtime/__tests__/cssLiteral.test.tsx +503 -0
- package/runtime/__tests__/cssProp.test.tsx +392 -0
- package/runtime/__tests__/cssPropTest.tsx +227 -0
- package/runtime/__tests__/foldedJsxParity.test.tsx +117 -0
- package/runtime/__tests__/styled.test.tsx +653 -0
- package/runtime/__tests__/tsconfig.json +18 -0
- package/runtime/__tests__/typeTest.tsx +374 -0
- package/runtime/atoms.tsx +36 -0
- package/runtime/context/README.md +84 -0
- package/runtime/context/baseContext.tsx +5 -0
- package/runtime/context/index.server.tsx +22 -0
- package/runtime/context/index.tsx +36 -0
- package/runtime/cssLiteral.tsx +214 -0
- package/runtime/globalStyle.tsx +54 -0
- package/runtime/index.ts +37 -0
- package/runtime/internal.ts +42 -0
- package/runtime/internals/mergeClassNames.ts +23 -0
- package/runtime/internals/mergeCssProp.ts +69 -0
- package/runtime/internals/propMarkers.ts +20 -0
- package/runtime/internals/unitPostFix.ts +34 -0
- package/runtime/jsx-dev-runtime.ts +6 -0
- package/runtime/jsx-runtime.ts +48 -0
- package/runtime/keyframes.tsx +30 -0
- package/runtime/mocks/cssLiteral.ts +74 -0
- package/runtime/mocks/globalStyle.ts +14 -0
- package/runtime/mocks/keyframes.ts +29 -0
- package/runtime/mocks/styled.ts +8 -0
- package/runtime/publicStyledApi.ts +159 -0
- package/runtime/styled.tsx +372 -0
- package/runtime/styledDom.tsx +139 -0
- package/withYak/allow-global-css.ts +94 -0
- package/withYak/index.ts +282 -0
|
@@ -0,0 +1,653 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
// We are testing internal functionality which does not match
|
|
3
|
+
// 1:1 the API exposed to the user before compilation.
|
|
4
|
+
// Therefore types are not matching and need to be ignored.
|
|
5
|
+
import { render } from "@testing-library/react";
|
|
6
|
+
import React from "react";
|
|
7
|
+
import { expect, it } from "vitest";
|
|
8
|
+
import { YakThemeProvider } from "../context";
|
|
9
|
+
import { css } from "../cssLiteral";
|
|
10
|
+
import { styled as styledFn } from "../styled";
|
|
11
|
+
import { atoms } from "../atoms";
|
|
12
|
+
|
|
13
|
+
// This transform is usually done by the SWC plugin.
|
|
14
|
+
// However this `styled.test.tsx` does not compile
|
|
15
|
+
// the code before testing
|
|
16
|
+
const styled = Object.assign(styledFn, {
|
|
17
|
+
div: styledFn("div"),
|
|
18
|
+
input: styledFn("input"),
|
|
19
|
+
a: styledFn("a"),
|
|
20
|
+
}) as typeof styledFn;
|
|
21
|
+
|
|
22
|
+
it("should render a literal element", () => {
|
|
23
|
+
const Component = styled.input``;
|
|
24
|
+
|
|
25
|
+
const { container } = render(<Component />);
|
|
26
|
+
|
|
27
|
+
expect(container).toMatchInlineSnapshot(`
|
|
28
|
+
<div>
|
|
29
|
+
<input />
|
|
30
|
+
</div>
|
|
31
|
+
`);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it("should render a literal element with styles", () => {
|
|
35
|
+
const Component = styled.input("cssClass");
|
|
36
|
+
|
|
37
|
+
const { container } = render(<Component />);
|
|
38
|
+
|
|
39
|
+
expect(container).toMatchInlineSnapshot(`
|
|
40
|
+
<div>
|
|
41
|
+
<input
|
|
42
|
+
class="cssClass"
|
|
43
|
+
/>
|
|
44
|
+
</div>
|
|
45
|
+
`);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it("should forward properties", () => {
|
|
49
|
+
const Component = styled.input("cssClass");
|
|
50
|
+
|
|
51
|
+
const { container } = render(<Component defaultValue="forwarded" />);
|
|
52
|
+
|
|
53
|
+
expect(container).toMatchInlineSnapshot(`
|
|
54
|
+
<div>
|
|
55
|
+
<input
|
|
56
|
+
class="cssClass"
|
|
57
|
+
value="forwarded"
|
|
58
|
+
/>
|
|
59
|
+
</div>
|
|
60
|
+
`);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it("should forward children", () => {
|
|
64
|
+
const Component = styled.div``;
|
|
65
|
+
|
|
66
|
+
const { container } = render(
|
|
67
|
+
<Component>
|
|
68
|
+
<button>Click me!</button>
|
|
69
|
+
</Component>,
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
expect(container).toMatchInlineSnapshot(`
|
|
73
|
+
<div>
|
|
74
|
+
<div>
|
|
75
|
+
<button>
|
|
76
|
+
Click me!
|
|
77
|
+
</button>
|
|
78
|
+
</div>
|
|
79
|
+
</div>
|
|
80
|
+
`);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it("should filter out properties starting with $", () => {
|
|
84
|
+
const Component = styled.input``;
|
|
85
|
+
|
|
86
|
+
const { container } = render(<Component $forwardedProp="notForwarded" />);
|
|
87
|
+
|
|
88
|
+
expect(container).toMatchInlineSnapshot(`
|
|
89
|
+
<div>
|
|
90
|
+
<input />
|
|
91
|
+
</div>
|
|
92
|
+
`);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it("should filter out properties starting with $ when passing to custom", () => {
|
|
96
|
+
let forwardedProps = null;
|
|
97
|
+
const Component = ({ className, style, ...props }) => {
|
|
98
|
+
forwardedProps = props;
|
|
99
|
+
return null;
|
|
100
|
+
};
|
|
101
|
+
const StyledComponent = styled(Component)``;
|
|
102
|
+
render(<StyledComponent $forwardedProp="notForwarded" />);
|
|
103
|
+
|
|
104
|
+
expect(forwardedProps).toEqual({});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it("should forward properties to the next yak component", () => {
|
|
108
|
+
const Component = styled.input.attrs(({ $text }) => ({
|
|
109
|
+
"aria-label": $text,
|
|
110
|
+
}))``;
|
|
111
|
+
const StyledComponent = styled(Component)``;
|
|
112
|
+
const { container } = render(<StyledComponent $text="hello world" />);
|
|
113
|
+
|
|
114
|
+
expect(container).toMatchInlineSnapshot(`
|
|
115
|
+
<div>
|
|
116
|
+
<input
|
|
117
|
+
aria-label="hello world"
|
|
118
|
+
/>
|
|
119
|
+
</div>
|
|
120
|
+
`);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it("should concatenate classNames", () => {
|
|
124
|
+
const Component = styled.input("className1");
|
|
125
|
+
|
|
126
|
+
const { container } = render(<Component className="className2" />);
|
|
127
|
+
|
|
128
|
+
expect(container).toMatchInlineSnapshot(`
|
|
129
|
+
<div>
|
|
130
|
+
<input
|
|
131
|
+
class="className2 className1"
|
|
132
|
+
/>
|
|
133
|
+
</div>
|
|
134
|
+
`);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it("should concatenate styles", () => {
|
|
138
|
+
const Component = styled.input``;
|
|
139
|
+
|
|
140
|
+
const { container } = render(<Component style={{ color: "red" }} />);
|
|
141
|
+
|
|
142
|
+
expect(container).toMatchInlineSnapshot(`
|
|
143
|
+
<div>
|
|
144
|
+
<input
|
|
145
|
+
style="color: red;"
|
|
146
|
+
/>
|
|
147
|
+
</div>
|
|
148
|
+
`);
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
it("should not inject a default style prop into a static wrapped component", () => {
|
|
152
|
+
let receivedProps = null;
|
|
153
|
+
const Component = (props) => {
|
|
154
|
+
receivedProps = props;
|
|
155
|
+
return null;
|
|
156
|
+
};
|
|
157
|
+
const StyledComponent = styled(Component)("staticClass");
|
|
158
|
+
|
|
159
|
+
render(<StyledComponent />);
|
|
160
|
+
|
|
161
|
+
expect("style" in receivedProps).toBe(false);
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it("should forward a style prop by reference to a static wrapped component", () => {
|
|
165
|
+
let receivedProps = null;
|
|
166
|
+
const Component = (props) => {
|
|
167
|
+
receivedProps = props;
|
|
168
|
+
return null;
|
|
169
|
+
};
|
|
170
|
+
const StyledComponent = styled(Component)("staticClass");
|
|
171
|
+
const style = { color: "red" };
|
|
172
|
+
|
|
173
|
+
render(<StyledComponent style={style} />);
|
|
174
|
+
|
|
175
|
+
// no per-render clone — keeps React.memo/PureComponent on the target working
|
|
176
|
+
expect(receivedProps.style).toBe(style);
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it("should not add class if prop is not set", () => {
|
|
180
|
+
const Component = styled.input(({ testProp }) => testProp && css("test"));
|
|
181
|
+
|
|
182
|
+
const { container } = render(<Component />);
|
|
183
|
+
|
|
184
|
+
expect(container).toMatchInlineSnapshot(`
|
|
185
|
+
<div>
|
|
186
|
+
<input />
|
|
187
|
+
</div>
|
|
188
|
+
`);
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
it("should add class if prop is set", () => {
|
|
192
|
+
const Component = styled.input(({ $testProp }) => $testProp && css("test"));
|
|
193
|
+
|
|
194
|
+
const { container } = render(<Component $testProp />);
|
|
195
|
+
|
|
196
|
+
expect(container).toMatchInlineSnapshot(`
|
|
197
|
+
<div>
|
|
198
|
+
<input
|
|
199
|
+
class="test"
|
|
200
|
+
/>
|
|
201
|
+
</div>
|
|
202
|
+
`);
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
it("should allow falsy values", () => {
|
|
206
|
+
const Component = styled.input(({ $testProp }) => $testProp && css("test"));
|
|
207
|
+
|
|
208
|
+
const { container } = render(
|
|
209
|
+
<>
|
|
210
|
+
<Component $testProp={null} />
|
|
211
|
+
<Component $testProp={false} />
|
|
212
|
+
<Component $testProp={undefined} />
|
|
213
|
+
</>,
|
|
214
|
+
);
|
|
215
|
+
|
|
216
|
+
expect(container).toMatchInlineSnapshot(`
|
|
217
|
+
<div>
|
|
218
|
+
<input />
|
|
219
|
+
<input />
|
|
220
|
+
<input />
|
|
221
|
+
</div>
|
|
222
|
+
`);
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
it("should execute runtime styles recursively", () => {
|
|
226
|
+
const Component = styled.input<{
|
|
227
|
+
$testProp: boolean;
|
|
228
|
+
}>(
|
|
229
|
+
({ $testProp }) =>
|
|
230
|
+
$testProp &&
|
|
231
|
+
css(
|
|
232
|
+
({ $testProp }) =>
|
|
233
|
+
$testProp && css(({ $testProp }) => $testProp && css("recursive-test-class")),
|
|
234
|
+
),
|
|
235
|
+
);
|
|
236
|
+
|
|
237
|
+
const { container } = render(<Component $testProp />);
|
|
238
|
+
|
|
239
|
+
expect(container).toMatchInlineSnapshot(`
|
|
240
|
+
<div>
|
|
241
|
+
<input
|
|
242
|
+
class="recursive-test-class"
|
|
243
|
+
/>
|
|
244
|
+
</div>
|
|
245
|
+
`);
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
it("should allow using refs", () => {
|
|
249
|
+
const Component = styled.input();
|
|
250
|
+
|
|
251
|
+
let elementFromRef: HTMLInputElement | null = null;
|
|
252
|
+
render(
|
|
253
|
+
<Component
|
|
254
|
+
ref={(element) => {
|
|
255
|
+
elementFromRef = element;
|
|
256
|
+
}}
|
|
257
|
+
/>,
|
|
258
|
+
);
|
|
259
|
+
|
|
260
|
+
expect(elementFromRef).toBeInstanceOf(HTMLInputElement);
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
it("should allow using nested refs", () => {
|
|
264
|
+
const BaseComponent = styled.input();
|
|
265
|
+
const Component = styled(BaseComponent)();
|
|
266
|
+
|
|
267
|
+
let elementFromRef: HTMLInputElement | null = null;
|
|
268
|
+
render(
|
|
269
|
+
<Component
|
|
270
|
+
ref={(element) => {
|
|
271
|
+
elementFromRef = element;
|
|
272
|
+
}}
|
|
273
|
+
/>,
|
|
274
|
+
);
|
|
275
|
+
|
|
276
|
+
expect(elementFromRef).toBeInstanceOf(HTMLInputElement);
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
it("should remove theme if styled element", () => {
|
|
280
|
+
const Link = styled.a((p) => p && css("test"));
|
|
281
|
+
|
|
282
|
+
const { container } = render(
|
|
283
|
+
<YakThemeProvider theme={{ color: "red" }}>
|
|
284
|
+
<Link />
|
|
285
|
+
</YakThemeProvider>,
|
|
286
|
+
);
|
|
287
|
+
|
|
288
|
+
expect(container).toMatchInlineSnapshot(`
|
|
289
|
+
<div>
|
|
290
|
+
<a
|
|
291
|
+
class="test"
|
|
292
|
+
/>
|
|
293
|
+
</div>
|
|
294
|
+
`);
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
it("should keep theme if theme is passed to element", () => {
|
|
298
|
+
const ThemePrinter = ({ theme, ...props }: { theme?: unknown }) => (
|
|
299
|
+
<pre {...props}>{JSON.stringify(theme)}</pre>
|
|
300
|
+
);
|
|
301
|
+
const Link = styled(ThemePrinter)((p) => p && css("test"));
|
|
302
|
+
|
|
303
|
+
const { container } = render(
|
|
304
|
+
<YakThemeProvider theme={{ color: "red" }}>
|
|
305
|
+
<Link theme={{ anything: "test" }} />
|
|
306
|
+
</YakThemeProvider>,
|
|
307
|
+
);
|
|
308
|
+
|
|
309
|
+
expect(container).toMatchInlineSnapshot(`
|
|
310
|
+
<div>
|
|
311
|
+
<pre
|
|
312
|
+
class="test"
|
|
313
|
+
>
|
|
314
|
+
{"anything":"test"}
|
|
315
|
+
</pre>
|
|
316
|
+
</div>
|
|
317
|
+
`);
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
it("should remove theme on wrapped element", () => {
|
|
321
|
+
const BaseComponent = styled.input((p) => p && css("test"));
|
|
322
|
+
const Component = styled(BaseComponent)((p) => p && css("test-wrapper"));
|
|
323
|
+
|
|
324
|
+
const { container } = render(
|
|
325
|
+
<YakThemeProvider theme={{ color: "red" }}>
|
|
326
|
+
<Component />
|
|
327
|
+
</YakThemeProvider>,
|
|
328
|
+
);
|
|
329
|
+
|
|
330
|
+
expect(container).toMatchInlineSnapshot(`
|
|
331
|
+
<div>
|
|
332
|
+
<input
|
|
333
|
+
class="test test-wrapper"
|
|
334
|
+
/>
|
|
335
|
+
</div>
|
|
336
|
+
`);
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
it("should not remove theme if theme is passed to wrapped element", () => {
|
|
340
|
+
const ThemePrinter = ({ theme, ...props }: { theme?: unknown }) => (
|
|
341
|
+
<pre {...props}>{JSON.stringify(theme)}</pre>
|
|
342
|
+
);
|
|
343
|
+
|
|
344
|
+
const BaseComponent = styled(ThemePrinter)((p) => p && css("test"));
|
|
345
|
+
const Component = styled(BaseComponent)((p) => p && css("test-wrapper"));
|
|
346
|
+
|
|
347
|
+
const { container } = render(
|
|
348
|
+
<YakThemeProvider theme={{ color: "red" }}>
|
|
349
|
+
<Component theme={{ anything: "test" }} />
|
|
350
|
+
</YakThemeProvider>,
|
|
351
|
+
);
|
|
352
|
+
|
|
353
|
+
expect(container).toMatchInlineSnapshot(`
|
|
354
|
+
<div>
|
|
355
|
+
<pre
|
|
356
|
+
class="test test-wrapper"
|
|
357
|
+
>
|
|
358
|
+
{"anything":"test"}
|
|
359
|
+
</pre>
|
|
360
|
+
</div>
|
|
361
|
+
`);
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
describe("dev mode - error tests", () => {
|
|
365
|
+
const originalNodeEnv = process.env.NODE_ENV;
|
|
366
|
+
const originalError = console.error;
|
|
367
|
+
|
|
368
|
+
beforeEach(() => {
|
|
369
|
+
// errors are only thrown in development mode
|
|
370
|
+
process.env.NODE_ENV = "development";
|
|
371
|
+
// prevent console.error from printing the error a second time
|
|
372
|
+
console.error = () => {};
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
afterEach(() => {
|
|
376
|
+
process.env.NODE_ENV = originalNodeEnv;
|
|
377
|
+
console.error = originalError;
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
it("should show the function body in error message when dynamic css function returns invalid value", () => {
|
|
381
|
+
const Component = styled.div("cssClass", {
|
|
382
|
+
style: {
|
|
383
|
+
"--bar": ({ $groupColor }) => $groupColor,
|
|
384
|
+
},
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
let error = null;
|
|
388
|
+
try {
|
|
389
|
+
render(<Component />);
|
|
390
|
+
} catch (e) {
|
|
391
|
+
error = e;
|
|
392
|
+
}
|
|
393
|
+
expect(error).toMatchInlineSnapshot(`
|
|
394
|
+
[Error: Dynamic CSS functions must return a string or number but returned undefined
|
|
395
|
+
|
|
396
|
+
Dynamic CSS function: ({ $groupColor }) => $groupColor
|
|
397
|
+
]
|
|
398
|
+
`);
|
|
399
|
+
});
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
describe("dynamic runtime function processing", () => {
|
|
403
|
+
let consoleSpy: any;
|
|
404
|
+
|
|
405
|
+
beforeEach(() => {
|
|
406
|
+
consoleSpy = {
|
|
407
|
+
log: [],
|
|
408
|
+
originalLog: console.log,
|
|
409
|
+
};
|
|
410
|
+
console.log = (...args) => {
|
|
411
|
+
consoleSpy.log.push(args.join(" "));
|
|
412
|
+
};
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
afterEach(() => {
|
|
416
|
+
console.log = consoleSpy.originalLog;
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
it("should execute atoms runtime functions", () => {
|
|
420
|
+
const Component = styled.div(
|
|
421
|
+
atoms("static-class", (props, classNames, style) => {
|
|
422
|
+
classNames.add("dynamic-class");
|
|
423
|
+
style.fontSize = "16px";
|
|
424
|
+
}),
|
|
425
|
+
);
|
|
426
|
+
const { container } = render(<Component />);
|
|
427
|
+
expect(container).toMatchInlineSnapshot(`
|
|
428
|
+
<div>
|
|
429
|
+
<div
|
|
430
|
+
class="static-class dynamic-class"
|
|
431
|
+
style="font-size: 16px;"
|
|
432
|
+
/>
|
|
433
|
+
</div>
|
|
434
|
+
`);
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
it("should execute combined css and atoms runtime functions", () => {
|
|
438
|
+
const Component = styled.div(
|
|
439
|
+
({ $primary }) => $primary && css("color-primary"),
|
|
440
|
+
atoms("atoms-class", (props, classNames, style) => {
|
|
441
|
+
classNames.add("atoms-dynamic");
|
|
442
|
+
style.padding = "10px";
|
|
443
|
+
}),
|
|
444
|
+
);
|
|
445
|
+
const { container } = render(<Component $primary />);
|
|
446
|
+
expect(container).toMatchInlineSnapshot(`
|
|
447
|
+
<div>
|
|
448
|
+
<div
|
|
449
|
+
class="color-primary atoms-class atoms-dynamic"
|
|
450
|
+
style="padding: 10px;"
|
|
451
|
+
/>
|
|
452
|
+
</div>
|
|
453
|
+
`);
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
it("should not duplicate runtime processing in nested components", () => {
|
|
457
|
+
const BaseComponent = styled.div(
|
|
458
|
+
atoms("base-class", () => console.log("Base runtime executed")),
|
|
459
|
+
);
|
|
460
|
+
|
|
461
|
+
const ExtendedComponent = styled(BaseComponent)(
|
|
462
|
+
atoms("extended-class", () => console.log("Extended runtime executed")),
|
|
463
|
+
);
|
|
464
|
+
|
|
465
|
+
render(<ExtendedComponent />);
|
|
466
|
+
|
|
467
|
+
expect(consoleSpy.log).toEqual(["Base runtime executed", "Extended runtime executed"]);
|
|
468
|
+
expect(consoleSpy.log).toHaveLength(2);
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
it("should execute nested runtime functions in correct order", () => {
|
|
472
|
+
const executionOrder = [];
|
|
473
|
+
|
|
474
|
+
const Level1 = styled.div(atoms(() => executionOrder.push("level1")));
|
|
475
|
+
|
|
476
|
+
const Level2 = styled(Level1)(atoms(() => executionOrder.push("level2")));
|
|
477
|
+
|
|
478
|
+
const Level3 = styled(Level2)(atoms(() => executionOrder.push("level3")));
|
|
479
|
+
|
|
480
|
+
render(<Level3 />);
|
|
481
|
+
|
|
482
|
+
expect(executionOrder).toEqual(["level1", "level2", "level3"]);
|
|
483
|
+
});
|
|
484
|
+
|
|
485
|
+
it("should allow runtime functions to see previous modifications", () => {
|
|
486
|
+
const Component = styled.div(
|
|
487
|
+
atoms("first-class", (props, classNames, style) => {
|
|
488
|
+
classNames.add("first-dynamic");
|
|
489
|
+
style.color = "red";
|
|
490
|
+
}),
|
|
491
|
+
atoms((props, classNames, style) => {
|
|
492
|
+
// Should see modifications from first atoms call
|
|
493
|
+
expect(classNames.has("first-class")).toBe(true);
|
|
494
|
+
expect(classNames.has("first-dynamic")).toBe(true);
|
|
495
|
+
expect(style.color).toBe("red");
|
|
496
|
+
|
|
497
|
+
classNames.add("second-dynamic");
|
|
498
|
+
style.backgroundColor = "blue";
|
|
499
|
+
}),
|
|
500
|
+
);
|
|
501
|
+
|
|
502
|
+
const { container } = render(<Component />);
|
|
503
|
+
expect(container).toMatchInlineSnapshot(`
|
|
504
|
+
<div>
|
|
505
|
+
<div
|
|
506
|
+
class="first-class first-dynamic second-dynamic"
|
|
507
|
+
style="color: red; background-color: blue;"
|
|
508
|
+
/>
|
|
509
|
+
</div>
|
|
510
|
+
`);
|
|
511
|
+
});
|
|
512
|
+
|
|
513
|
+
it("should provide theme to runtime functions", () => {
|
|
514
|
+
const Component = styled.div(
|
|
515
|
+
atoms((props, classNames, style) => {
|
|
516
|
+
style.color = props.theme?.primaryColor || "black";
|
|
517
|
+
classNames.add("themed");
|
|
518
|
+
}),
|
|
519
|
+
);
|
|
520
|
+
|
|
521
|
+
const { container } = render(
|
|
522
|
+
<YakThemeProvider theme={{ primaryColor: "purple" }}>
|
|
523
|
+
<Component />
|
|
524
|
+
</YakThemeProvider>,
|
|
525
|
+
);
|
|
526
|
+
|
|
527
|
+
expect(container).toMatchInlineSnapshot(`
|
|
528
|
+
<div>
|
|
529
|
+
<div
|
|
530
|
+
class="themed"
|
|
531
|
+
style="color: purple;"
|
|
532
|
+
/>
|
|
533
|
+
</div>
|
|
534
|
+
`);
|
|
535
|
+
});
|
|
536
|
+
|
|
537
|
+
it("should merge external props with runtime modifications", () => {
|
|
538
|
+
const Component = styled.div(
|
|
539
|
+
atoms("runtime-class", (props, classNames, style) => {
|
|
540
|
+
style.fontSize = "18px";
|
|
541
|
+
}),
|
|
542
|
+
);
|
|
543
|
+
|
|
544
|
+
const { container } = render(
|
|
545
|
+
<Component className="external-class" style={{ color: "green", fontSize: "14px" }} />,
|
|
546
|
+
);
|
|
547
|
+
|
|
548
|
+
expect(container).toMatchInlineSnapshot(`
|
|
549
|
+
<div>
|
|
550
|
+
<div
|
|
551
|
+
class="external-class runtime-class"
|
|
552
|
+
style="color: green; font-size: 18px;"
|
|
553
|
+
/>
|
|
554
|
+
</div>
|
|
555
|
+
`);
|
|
556
|
+
});
|
|
557
|
+
|
|
558
|
+
it("should handle conditional runtime functions", () => {
|
|
559
|
+
const Component = styled.div(
|
|
560
|
+
({ $variant }) => $variant === "primary" && css("bg-blue"),
|
|
561
|
+
atoms(
|
|
562
|
+
({ $size }, classNames) => {
|
|
563
|
+
if ($size === "large") {
|
|
564
|
+
classNames.add("large-class");
|
|
565
|
+
}
|
|
566
|
+
},
|
|
567
|
+
(props, classNames, style) => {
|
|
568
|
+
if (props.$active) {
|
|
569
|
+
classNames.add("active");
|
|
570
|
+
style.borderWidth = "2px";
|
|
571
|
+
}
|
|
572
|
+
},
|
|
573
|
+
),
|
|
574
|
+
);
|
|
575
|
+
|
|
576
|
+
const { container } = render(
|
|
577
|
+
<>
|
|
578
|
+
<Component $variant="primary" $size="large" $active />
|
|
579
|
+
<Component $variant="secondary" $size="small" />
|
|
580
|
+
</>,
|
|
581
|
+
);
|
|
582
|
+
|
|
583
|
+
expect(container).toMatchInlineSnapshot(`
|
|
584
|
+
<div>
|
|
585
|
+
<div
|
|
586
|
+
class="bg-blue large-class active"
|
|
587
|
+
style="border-width: 2px;"
|
|
588
|
+
/>
|
|
589
|
+
<div />
|
|
590
|
+
</div>
|
|
591
|
+
`);
|
|
592
|
+
});
|
|
593
|
+
|
|
594
|
+
it("should filter $ props but still use them in runtime functions", () => {
|
|
595
|
+
const Component = styled.div(
|
|
596
|
+
atoms((props, classNames, style) => {
|
|
597
|
+
if (props.$variant) {
|
|
598
|
+
classNames.add(`variant-${props.$variant}`);
|
|
599
|
+
style.padding = props.$spacing || "8px";
|
|
600
|
+
}
|
|
601
|
+
}),
|
|
602
|
+
);
|
|
603
|
+
|
|
604
|
+
const { container } = render(<Component $variant="primary" $spacing="16px" />);
|
|
605
|
+
|
|
606
|
+
const element = container.querySelector("div");
|
|
607
|
+
// Should not have $ props as DOM attributes
|
|
608
|
+
expect(element.getAttribute("$variant")).toBeNull();
|
|
609
|
+
expect(element.getAttribute("$spacing")).toBeNull();
|
|
610
|
+
|
|
611
|
+
// But should have styles/classes generated from them
|
|
612
|
+
expect(container).toMatchInlineSnapshot(`
|
|
613
|
+
<div>
|
|
614
|
+
<div
|
|
615
|
+
class="variant-primary"
|
|
616
|
+
style="padding: 16px;"
|
|
617
|
+
/>
|
|
618
|
+
</div>
|
|
619
|
+
`);
|
|
620
|
+
});
|
|
621
|
+
|
|
622
|
+
it("should handle runtime functions that return falsy values", () => {
|
|
623
|
+
const Component = styled.div(
|
|
624
|
+
({ $show }) => $show && css("block"),
|
|
625
|
+
atoms(
|
|
626
|
+
({ $variant }, classNames) => {
|
|
627
|
+
if ($variant === "primary") {
|
|
628
|
+
classNames.add(`variant-${$variant}`);
|
|
629
|
+
}
|
|
630
|
+
},
|
|
631
|
+
null,
|
|
632
|
+
undefined,
|
|
633
|
+
false && "never-added",
|
|
634
|
+
),
|
|
635
|
+
);
|
|
636
|
+
|
|
637
|
+
const { container } = render(
|
|
638
|
+
<>
|
|
639
|
+
<Component $show $variant="primary" />
|
|
640
|
+
<Component />
|
|
641
|
+
</>,
|
|
642
|
+
);
|
|
643
|
+
|
|
644
|
+
expect(container).toMatchInlineSnapshot(`
|
|
645
|
+
<div>
|
|
646
|
+
<div
|
|
647
|
+
class="block variant-primary"
|
|
648
|
+
/>
|
|
649
|
+
<div />
|
|
650
|
+
</div>
|
|
651
|
+
`);
|
|
652
|
+
});
|
|
653
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2019",
|
|
4
|
+
"lib": ["DOM"],
|
|
5
|
+
"types": ["node"],
|
|
6
|
+
"jsx": "react",
|
|
7
|
+
"moduleResolution": "bundler",
|
|
8
|
+
"allowJs": true,
|
|
9
|
+
"checkJs": true,
|
|
10
|
+
"maxNodeModuleJsDepth": 0,
|
|
11
|
+
"noEmit": true,
|
|
12
|
+
"esModuleInterop": true,
|
|
13
|
+
"forceConsistentCasingInFileNames": true,
|
|
14
|
+
"strict": true,
|
|
15
|
+
"skipLibCheck": true
|
|
16
|
+
},
|
|
17
|
+
"include": ["./*"]
|
|
18
|
+
}
|