@yak/solid 0.1.0 → 0.2.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.
@@ -1,270 +0,0 @@
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 { expect, it, vi } from "vitest";
6
- import { YakThemeProvider } from "../context/index.ts";
7
- import { css } from "../cssLiteral.ts";
8
- import { mergeCssProp } from "../internals/mergeCssProp.ts";
9
- import { styled as styledFn } from "../styled.ts";
10
- import { renderInto } from "./testUtils.tsx";
11
-
12
- // This transform is usually done by the SWC plugin.
13
- // However this `styled.test.tsx` does not compile
14
- // the code before testing
15
- const styled = Object.assign(styledFn, {
16
- div: styledFn("div"),
17
- input: styledFn("input"),
18
- button: styledFn("button"),
19
- }) as typeof styledFn;
20
-
21
- it("should render a literal element", () => {
22
- const Component = styled.input``;
23
- const container = renderInto(() => <Component />);
24
- expect(container.innerHTML).toBe("<input>");
25
- });
26
-
27
- it("should render a literal element with styles", () => {
28
- const Component = styled.input("cssClass");
29
- const container = renderInto(() => <Component />);
30
- expect(container.querySelector("input")?.className).toBe("cssClass");
31
- });
32
-
33
- it("should forward properties", () => {
34
- const Component = styled.input("cssClass");
35
- const container = renderInto(() => <Component type="text" />);
36
- const input = container.querySelector("input")!;
37
- expect(input.className).toBe("cssClass");
38
- expect(input.getAttribute("type")).toBe("text");
39
- });
40
-
41
- it("should forward children", () => {
42
- const Component = styled.div``;
43
- const container = renderInto(() => (
44
- <Component>
45
- <button>Click me!</button>
46
- </Component>
47
- ));
48
- expect(container.innerHTML).toBe("<div><button>Click me!</button></div>");
49
- });
50
-
51
- it("should filter out properties starting with $", () => {
52
- const Component = styled.input``;
53
- const container = renderInto(() => <Component $forwardedProp="notForwarded" />);
54
- const input = container.querySelector("input")!;
55
- expect(input.getAttributeNames()).toEqual([]);
56
- });
57
-
58
- it("should filter out properties starting with $ when passing to custom components", () => {
59
- let forwardedKeys: string[] | null = null;
60
- const Custom = (props) => {
61
- forwardedKeys = Object.keys(props).filter((key) => props[key] !== undefined);
62
- return null;
63
- };
64
- const StyledCustom = styled(Custom)``;
65
- renderInto(() => <StyledCustom $forwardedProp="notForwarded" />);
66
- expect(forwardedKeys).not.toContain("$forwardedProp");
67
- expect(forwardedKeys).not.toContain("theme");
68
- });
69
-
70
- it("should pass the generated class to custom components", () => {
71
- let receivedClass: string | undefined;
72
- const Custom = (props) => {
73
- receivedClass = props.class;
74
- return null;
75
- };
76
- const StyledCustom = styled(Custom)("customClass");
77
- renderInto(() => <StyledCustom />);
78
- expect(receivedClass).toBe("customClass");
79
- });
80
-
81
- it("should merge the class names of a styled(styled()) chain in order", () => {
82
- const Inner = styled.input("innerClass");
83
- const Outer = styled(Inner)("outerClass");
84
- const container = renderInto(() => <Outer />);
85
- expect(container.querySelector("input")?.className).toBe("innerClass outerClass");
86
- });
87
-
88
- it("should render a styled(styled()) chain as a single element", () => {
89
- const Inner = styled.div("innerClass");
90
- const Outer = styled(Inner)("outerClass");
91
- const container = renderInto(() => <Outer />);
92
- expect(container.querySelectorAll("div").length).toBe(1);
93
- });
94
-
95
- it("should execute each dynamic style function exactly once per render", () => {
96
- const innerFn = vi.fn(() => css("innerDynamic"));
97
- const Inner = styled.input("innerClass", innerFn);
98
- const Outer = styled(Inner)("outerClass");
99
- const container = renderInto(() => <Outer />);
100
- expect(innerFn).toHaveBeenCalledTimes(1);
101
- expect(container.querySelector("input")?.className).toBe("innerClass innerDynamic outerClass");
102
- });
103
-
104
- it("should support attrs objects", () => {
105
- const Component = styled.button.attrs({ type: "button" })("cssClass");
106
- const container = renderInto(() => <Component />);
107
- const button = container.querySelector("button")!;
108
- expect(button.getAttribute("type")).toBe("button");
109
- expect(button.className).toBe("cssClass");
110
- });
111
-
112
- it("should support attrs functions receiving props", () => {
113
- const Component = styled.input.attrs((props) => ({
114
- "aria-label": props.$text,
115
- }))``;
116
- const container = renderInto(() => <Component $text="hello world" />);
117
- const input = container.querySelector("input")!;
118
- expect(input.getAttribute("aria-label")).toBe("hello world");
119
- expect(input.getAttribute("$text")).toBe(null);
120
- });
121
-
122
- it("should preserve attrs order across styled() levels", () => {
123
- const SubmitButton = styled.button.attrs({ type: "submit" })("submit_btn");
124
- const ResetButton = styled(SubmitButton).attrs({ type: "reset" })("reset_btn");
125
- const container = renderInto(() => <ResetButton />);
126
- const button = container.querySelector("button")!;
127
- expect(button.getAttribute("type")).toBe("reset");
128
- expect(button.className).toBe("submit_btn reset_btn");
129
- });
130
-
131
- it("should flatten chains through a get-only proxy (solid-refresh dev wrapper)", () => {
132
- const SubmitButton = styled.button.attrs({ type: "submit" })("submit_btn");
133
- // solid-refresh registers components behind a Proxy that forwards property
134
- // reads but defines no `has` trap — chain detection must still see the
135
- // yak component behind it
136
- const Registered = new Proxy(function HMRComp() {}, {
137
- get: (_, property) => (SubmitButton as never as Record<PropertyKey, unknown>)[property],
138
- });
139
- const ResetButton = styled(Registered as typeof SubmitButton).attrs({ type: "reset" })(
140
- "reset_btn",
141
- );
142
- const container = renderInto(() => <ResetButton />);
143
- const button = container.querySelector("button")!;
144
- expect(button.getAttribute("type")).toBe("reset");
145
- expect(button.className).toBe("submit_btn reset_btn");
146
- });
147
-
148
- it("should run parent attrs functions before own attrs functions", () => {
149
- const order: string[] = [];
150
- const Parent = styled.input.attrs(() => {
151
- order.push("parent");
152
- return { "aria-label": "parent" };
153
- })``;
154
- const Child = styled(Parent).attrs(() => {
155
- order.push("child");
156
- return {};
157
- })``;
158
- renderInto(() => <Child />);
159
- expect(order).toEqual(["parent", "child"]);
160
- expect(order.filter((entry) => entry === "parent").length).toBe(1);
161
- });
162
-
163
- it("should merge a user provided class", () => {
164
- const Component = styled.input("cssClass");
165
- const container = renderInto(() => <Component class="userClass" />);
166
- expect(container.querySelector("input")?.className).toBe("userClass cssClass");
167
- });
168
-
169
- it("should merge a user provided style with dynamic styles", () => {
170
- const Component = styled.input("cssClass", {
171
- style: { "--x": () => "10px" },
172
- });
173
- const container = renderInto(() => <Component style={{ padding: "5px" }} />);
174
- const input = container.querySelector("input")!;
175
- expect(input.style.getPropertyValue("--x")).toBe("10px");
176
- expect(input.style.padding).toBe("5px");
177
- });
178
-
179
- it("should set css variables from dynamic props", () => {
180
- const Component = styled.input("cssClass", {
181
- style: { "--width": (props) => `${props.$width * 2}px` },
182
- });
183
- const container = renderInto(() => <Component $width={10} />);
184
- expect(container.querySelector("input")?.style.getPropertyValue("--width")).toBe("20px");
185
- });
186
-
187
- it("should support conditional dynamic class functions", () => {
188
- const Component = styled.input("base", (props) => props.$active && css("active"));
189
- const active = renderInto(() => <Component $active />);
190
- const inactive = renderInto(() => <Component />);
191
- expect(active.querySelector("input")?.className).toBe("base active");
192
- expect(inactive.querySelector("input")?.className).toBe("base");
193
- });
194
-
195
- it("should provide the theme to dynamic style functions", () => {
196
- const Component = styled.input("base", {
197
- style: { "--color": (props) => props.theme().color },
198
- });
199
- const container = renderInto(() => (
200
- <YakThemeProvider theme={{ color: "red" }}>
201
- <Component />
202
- </YakThemeProvider>
203
- ));
204
- expect(container.querySelector("input")?.style.getPropertyValue("--color")).toBe("red");
205
- });
206
-
207
- it("should provide the theme to attrs functions", () => {
208
- const Component = styled.input.attrs((props) => ({
209
- "aria-label": props.theme().label,
210
- }))``;
211
- const container = renderInto(() => (
212
- <YakThemeProvider theme={{ label: "themed" }}>
213
- <Component />
214
- </YakThemeProvider>
215
- ));
216
- expect(container.querySelector("input")?.getAttribute("aria-label")).toBe("themed");
217
- });
218
-
219
- it("should merge the css prop with class and style (mergeCssProp)", () => {
220
- const result = mergeCssProp(
221
- { class: "test-class", style: { padding: "5px" } },
222
- css("cssPropClass"),
223
- );
224
- expect(result).toEqual({
225
- class: "test-class cssPropClass",
226
- style: { padding: "5px" },
227
- });
228
- });
229
-
230
- it("should not emit an empty style object from mergeCssProp", () => {
231
- const result = mergeCssProp({}, css("onlyClass"));
232
- expect(result).toEqual({ class: "onlyClass" });
233
- });
234
-
235
- it("should forward unrelated props through mergeCssProp", () => {
236
- // the compiled output spreads only mergeCssProp's result onto the element,
237
- // so onClick, data-*, children etc. must survive the merge
238
- const onClick = () => {};
239
- const result = mergeCssProp(
240
- { "data-testid": "button", onClick, children: "label", class: "user" },
241
- css("cssPropClass"),
242
- );
243
- expect(result).toEqual({
244
- "data-testid": "button",
245
- onClick,
246
- children: "label",
247
- class: "user cssPropClass",
248
- });
249
- });
250
-
251
- it("should apply no styles for a falsy css prop", () => {
252
- // e.g. `css={on && css`...`}` with `on` being false
253
- const result = mergeCssProp({ class: "user" }, false);
254
- expect(result).toEqual({ class: "user" });
255
- });
256
-
257
- it("should forward a `component` prop to wrapped components", () => {
258
- // polymorphic targets (e.g. router links) accept a `component` prop -
259
- // it must reach them instead of being consumed by the render mechanism
260
- const Polymorphic = (props: any) => (
261
- <a data-received={props.component} class={props.class}>
262
- {props.children}
263
- </a>
264
- );
265
- const Styled = styledFn(Polymorphic)("styledClass");
266
- const container = renderInto(() => <Styled component="button">link</Styled>);
267
- const anchor = container.querySelector("a");
268
- expect(anchor?.getAttribute("data-received")).toBe("button");
269
- expect(anchor?.className).toContain("styledClass");
270
- });