33 | ? P
34 | : never;
35 |
36 | /**
37 | * Use prop
38 | * @template P Props type
39 | */
40 | export type UseProp =
41 | | keyof JSX.IntrinsicElements
42 | | React.ComponentType
;
43 |
44 | /**
45 | * Remove use props from object `T` if they're present
46 | * @template T Object
47 | */
48 | export type WithoutUseProps = Without;
49 |
50 | /**
51 | * Remove ref prop from object `T` if it's present
52 | * @template T Object
53 | */
54 | export type WithoutRef = Without;
55 |
56 | /**
57 | * Grab components passed to the `use` prop and return their props
58 | * @template T Component type
59 | */
60 | export type InheritedProps = WithoutUseProps<
61 | UnionToIntersection>
62 | >;
63 |
64 | /**
65 | * Props of a component created with `use()`
66 | * @template T The type of the `use` prop
67 | */
68 | export type UseProps = React.ClassAttributes &
69 | InheritedProps & {
70 | readonly use?: T | T[];
71 | readonly useNext?: T | T[];
72 | };
73 |
74 | /**
75 | * Component created with `use()`
76 | * @template T Component type passed to `use(...components)`
77 | */
78 | export type UseComponent = {
79 | (props: WithoutRef> & UseProps): React.ReactElement<
80 | any
81 | > | null;
82 | uses: T[];
83 | propTypes?: any;
84 | defaultProps?: any;
85 | displayName?: string;
86 | };
87 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "reuse",
3 | "version": "2.0.0",
4 | "description": "Reuse different React components to create new ones",
5 | "license": "MIT",
6 | "repository": "diegohaz/reuse",
7 | "main": "dist/cjs/index.js",
8 | "module": "dist/es/index.js",
9 | "types": "dist/ts/src",
10 | "author": {
11 | "name": "Haz",
12 | "email": "hazdiego@gmail.com",
13 | "url": "https://github.com/diegohaz"
14 | },
15 | "files": [
16 | "dist",
17 | "src"
18 | ],
19 | "scripts": {
20 | "test": "jest",
21 | "coverage": "npm test -- --coverage",
22 | "postcoverage": "opn coverage/lcov-report/index.html",
23 | "type-check": "tsc --noEmit",
24 | "lint": "eslint . --ext js,ts,tsx",
25 | "clean": "rimraf dist",
26 | "prebuild": "npm run clean",
27 | "build:ts": "tsc --emitDeclarationOnly",
28 | "build:babel": "babel src -x .js,.ts,.tsx",
29 | "build:cjs": "cross-env BABEL_ENV=cjs npm run build:babel -- -d dist/cjs",
30 | "build:es": "cross-env BABEL_ENV=es npm run build:babel -- -d dist/es",
31 | "build": "npm run build:ts && npm run build:cjs && npm run build:es",
32 | "prerelease": "npm run lint && npm test && npm run build",
33 | "release": "standard-version",
34 | "postpublish": "git push origin master --follow-tags"
35 | },
36 | "husky": {
37 | "hooks": {
38 | "pre-commit": "lint-staged"
39 | }
40 | },
41 | "lint-staged": {
42 | "*.{js,ts,tsx}": [
43 | "eslint --fix --ext js,ts,tsx",
44 | "git add"
45 | ]
46 | },
47 | "keywords": [
48 | "reuse",
49 | "react"
50 | ],
51 | "dependencies": {},
52 | "devDependencies": {
53 | "@babel/cli": "7.2.3",
54 | "@babel/core": "7.2.2",
55 | "@babel/preset-env": "7.3.1",
56 | "@babel/preset-react": "7.0.0",
57 | "@babel/preset-typescript": "7.1.0",
58 | "@types/jest": "23.3.13",
59 | "@types/react": "16.8.1",
60 | "@types/react-test-renderer": "16.0.3",
61 | "@typescript-eslint/eslint-plugin": "1.2.0",
62 | "@typescript-eslint/parser": "1.2.0",
63 | "babel-core": "7.0.0-bridge.0",
64 | "babel-eslint": "10.0.1",
65 | "babel-jest": "24.0.0",
66 | "cross-env": "5.2.0",
67 | "eslint": "5.13.0",
68 | "eslint-config-airbnb": "17.1.0",
69 | "eslint-config-prettier": "4.0.0",
70 | "eslint-plugin-import": "2.16.0",
71 | "eslint-plugin-jsx-a11y": "6.2.1",
72 | "eslint-plugin-prettier": "3.0.1",
73 | "eslint-plugin-react": "7.12.4",
74 | "husky": "1.3.1",
75 | "jest": "24.0.0",
76 | "lint-staged": "8.1.3",
77 | "opn-cli": "4.0.0",
78 | "prettier": "1.16.4",
79 | "react": "16.7.0",
80 | "react-dom": "16.7.0",
81 | "react-test-renderer": "16.7.0",
82 | "react-testing-library": "5.4.4",
83 | "rimraf": "2.6.3",
84 | "standard-version": "4.4.0",
85 | "typescript": "3.1.6"
86 | },
87 | "peerDependencies": {
88 | "react": "*"
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change Log
2 |
3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4 |
5 |
6 | # [2.0.0](https://github.com/diegohaz/reuse/compare/v1.3.0...v2.0.0) (2019-02-05)
7 |
8 |
9 | ### Bug Fixes
10 |
11 | * Fix inconsistencies with `styled-components` ([#11](https://github.com/diegohaz/reuse/issues/11)) ([7746694](https://github.com/diegohaz/reuse/commit/7746694))
12 |
13 |
14 | ### BREAKING CHANGES
15 |
16 | * Now `use()` always return an enhanced component.
17 |
18 |
19 |
20 |
21 | # [1.3.0](https://github.com/diegohaz/reuse/compare/v1.2.3...v1.3.0) (2018-12-02)
22 |
23 |
24 | ### Features
25 |
26 | * Support new `React.ExoticComponent` type ([#8](https://github.com/diegohaz/reuse/issues/8)) ([37e4718](https://github.com/diegohaz/reuse/commit/37e4718))
27 |
28 |
29 |
30 |
31 | ## [1.2.3](https://github.com/diegohaz/reuse/compare/v1.2.2...v1.2.3) (2018-11-05)
32 |
33 |
34 | ### Bug Fixes
35 |
36 | * **typescript:** Fix `UseComponent` properties ([514d036](https://github.com/diegohaz/reuse/commit/514d036))
37 |
38 |
39 |
40 |
41 | ## [1.2.2](https://github.com/diegohaz/reuse/compare/v1.2.1...v1.2.2) (2018-11-05)
42 |
43 |
44 | ### Bug Fixes
45 |
46 | * **typescript:** Fix `UseComponent` properties ([84e0a7d](https://github.com/diegohaz/reuse/commit/84e0a7d))
47 |
48 |
49 |
50 |
51 | ## [1.2.1](https://github.com/diegohaz/reuse/compare/v1.2.0...v1.2.1) (2018-11-05)
52 |
53 |
54 | ### Bug Fixes
55 |
56 | * **typescript:** Add missing component properties to `UseComponent` ([605b2e4](https://github.com/diegohaz/reuse/commit/605b2e4))
57 |
58 |
59 |
60 |
61 | # [1.2.0](https://github.com/diegohaz/reuse/compare/v1.1.1...v1.2.0) (2018-10-18)
62 |
63 |
64 | ### Features
65 |
66 | * Expose `uses` static property on component ([#7](https://github.com/diegohaz/reuse/issues/7)) ([ccf960e](https://github.com/diegohaz/reuse/commit/ccf960e))
67 |
68 |
69 |
70 |
71 | ## [1.1.1](https://github.com/diegohaz/reuse/compare/v1.1.0...v1.1.1) (2018-10-18)
72 |
73 |
74 |
75 |
76 | # [1.1.0](https://github.com/diegohaz/reuse/compare/v1.0.5...v1.1.0) (2018-10-18)
77 |
78 |
79 | ### Features
80 |
81 | * **typescript:** Export types ([a997d3d](https://github.com/diegohaz/reuse/commit/a997d3d))
82 |
83 |
84 |
85 |
86 | ## [1.0.5](https://github.com/diegohaz/reuse/compare/v1.0.4...v1.0.5) (2018-10-18)
87 |
88 |
89 | ### Bug Fixes
90 |
91 | * **typescript:** Fix `ref` prop type ([5d5124c](https://github.com/diegohaz/reuse/commit/5d5124c))
92 |
93 |
94 |
95 |
96 | ## [1.0.4](https://github.com/diegohaz/reuse/compare/v1.0.3...v1.0.4) (2018-09-27)
97 |
98 |
99 | ### Bug Fixes
100 |
101 | * Revert [#3](https://github.com/diegohaz/reuse/issues/3) ([e63d768](https://github.com/diegohaz/reuse/commit/e63d768))
102 |
103 |
104 |
105 |
106 | ## [1.0.3](https://github.com/diegohaz/reuse/compare/v1.0.2...v1.0.3) (2018-09-27)
107 |
108 |
109 | ### Bug Fixes
110 |
111 | * Make it work with styled-components v4 ([#5](https://github.com/diegohaz/reuse/issues/5)) ([8ba7751](https://github.com/diegohaz/reuse/commit/8ba7751))
112 | * Render the last non-empty element ([#3](https://github.com/diegohaz/reuse/issues/3)) ([c7a8c58](https://github.com/diegohaz/reuse/commit/c7a8c58))
113 |
114 |
115 |
116 |
117 | ## [1.0.2](https://github.com/diegohaz/reuse/compare/v1.0.1...v1.0.2) (2018-09-24)
118 |
119 |
120 | ### Bug Fixes
121 |
122 | * Always render the last string element ([852b7b9](https://github.com/diegohaz/reuse/commit/852b7b9))
123 |
124 |
125 |
126 |
127 | ## 1.0.1 (2018-09-24)
128 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | Reuse different React components to create new ones
11 | Play on CodeSandbox
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 | ## Installation
30 |
31 | ```sh
32 | npm i reuse
33 | ```
34 |
35 | > Thanks to [@eldargab](https://github.com/eldargab) for the package name on npm.
36 |
37 | ## Why
38 |
39 | This enables **(sub)[atomic design](http://bradfrost.com/blog/post/atomic-web-design/)** approach.
40 |
41 | When using classic CSS, we have a powerful way to compose "stylesheet components" by applying multiple class names to our HTML elements (`.btn`, `.large`, `.rounded` etc.). But, by doing that in React, which has its own component structure, we'll have conflicting component structures.
42 |
43 | **Reuse** solves it by combining React components together as if they were CSS classes. This also means that not only style will be composed, but also JavaScript behavior, like React lifecycle methods and event handlers.
44 |
45 | ## Usage
46 |
47 | Reuse simply exports a factory method that returns a React component. You can leverage that method in two ways: [augmentation](#augmentation) and [combination](#combination).
48 |
49 | ### Examples
50 |
51 | - [Simple](https://codesandbox.io/s/github/diegohaz/reuse/tree/master/examples/simple)
52 | - [PaperRoundedButton](https://codesandbox.io/s/github/diegohaz/reuse/tree/master/examples/paper-rounded-button)
53 | - [Styled Components](https://codesandbox.io/s/github/diegohaz/reuse/tree/master/examples/styled-components)
54 |
55 | ### Augmentation
56 |
57 | The component returned by the `use` factory will expect a `use` prop:
58 |
59 | ```jsx
60 | import use from "reuse";
61 |
62 | const Box = use();
63 |
64 | ; // null
65 | ; //
66 | ; //
67 | ```
68 |
69 | You can create the component with a default element:
70 |
71 | ```jsx
72 | const Box = use("div");
73 |
74 | ; //
75 | ; //
76 | ```
77 |
78 | You can create the component with another component. **Just make sure to render the `use` prop as the underlying element and pass the other props down** (at least, when `use` isn't a string – HTML element):
79 |
80 | ```jsx
81 | import React from "react";
82 | import use from "reuse";
83 |
84 | // grab the `use` prop and pass down other props
85 | const Base = ({ use: T = "div", ...props }) => ;
86 |
87 | const Box = use(Base);
88 |
89 | ; //
90 | ; //
91 |
92 | const BoxSpan = use(Box, "span");
93 | ; //
94 | ```
95 |
96 | > You can use `Base` to filter custom props when `use` is a string using [@emotion/is-prop-valid](https://github.com/emotion-js/emotion/tree/master/next-packages/is-prop-valid), for example.
97 |
98 | ### Combination
99 |
100 | Let's create some components:
101 |
102 | ```jsx
103 |
104 | // Using styled-components
105 | const Paper = styled(use("div"))`
106 | box-shadow: 0 2px 4px rgba(0, 0, 0, 0.30);
107 | `;
108 |
109 | // Using class names
110 | const Rounded = use(({ use: T, ...props }) => (
111 |
115 | ), "div");
116 |
117 | // Using inline styles
118 | const Button = use(({ use: T, ...props }) => (
119 |
129 | ), "button");
130 | ```
131 |
132 | Once you have a few of those components, you can combine them using the same `use` methods:
133 |
134 | ```jsx
135 | import use from "reuse";
136 | import { Rounded, Paper, Button } from "../components";
137 |
138 | // with factory
139 | const RoundedPaperButton = use(Rounded, Paper, Button);
140 | ; //
141 | ; //
142 |
143 | // with prop
144 | //
145 | //
146 | ```
147 |
148 | Note that the underlying HTML element will always be based on the last component you pass to `use`.
149 |
150 | ## FAQ
151 |
152 |
153 | How does this compare to render props and HOCs?
154 |
155 | These are equivalent implementations:
156 |
157 | **Render props**
158 | ```jsx
159 |
160 | {paperProps => (
161 |
162 | {roundedProps => (
163 |
168 | )}
169 |
170 | )}
171 |
172 | ```
173 |
174 | **High-order components**
175 | ```jsx
176 | withPaper(withRounded(withButton(props => Button)));
177 | ```
178 |
179 | **Reuse**
180 | ```jsx
181 | use(Paper, Rounded, Button);
182 | // or
183 |
184 | ```
185 |
186 | When using render props or HOCs, you have to stick with their static (HOC) or dynamic implementation (render prop). With Reuse, besides simplicity, you can use both depending on your needs.
187 |
188 |
189 |
190 |
191 | ## License
192 |
193 | MIT © [Haz](https://github.com/diegohaz)
194 |
--------------------------------------------------------------------------------
/test/index.test.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import use from "../src";
3 | import { getTreeJson } from "./testUtils";
4 |
5 | // Serialize `[Function]`
6 | expect.addSnapshotSerializer({
7 | test: val => typeof val === "function",
8 | print: val => val.name
9 | });
10 |
11 | // Serialize `React.forwardRef`
12 | expect.addSnapshotSerializer({
13 | test: val =>
14 | val &&
15 | typeof val.$$typeof === "symbol" &&
16 | val.$$typeof.toString() === "Symbol(react.forward_ref)",
17 | print: () => "ForwardRef"
18 | });
19 |
20 | type UseProps = { use?: React.ReactType; children?: React.ReactNode };
21 |
22 | test("use component", () => {
23 | const Box = ({ use: T = "div", ...props }: UseProps) => ;
24 | const UseBox = use(Box);
25 | expect(getTreeJson()).toMatchInlineSnapshot(`
26 |
27 |
28 |
29 | `);
30 | });
31 |
32 | test("different underlying element", () => {
33 | const Box = ({ use: T = "div", ...props }: UseProps) => ;
34 | const BoxSpan = use(Box, "span");
35 | expect(getTreeJson()).toMatchInlineSnapshot(`
36 |
39 |
40 |
41 | `);
42 | });
43 |
44 | test("use component without default use prop", () => {
45 | const Box = ({ use: T, ...props }: UseProps) => (T ? : null);
46 | const UseBox = use(Box);
47 | expect(getTreeJson()).toMatchInlineSnapshot(``);
48 | });
49 |
50 | test("two different components", () => {
51 | const Box1 = ({ use: T = "div", ...props }) => ;
52 | const Box2 = ({ use: T = "span", ...props }) => ;
53 | const Box1Box2 = use(Box1, Box2);
54 | expect(getTreeJson()).toMatchInlineSnapshot(`
55 |
58 |
59 |
60 |
61 |
62 | `);
63 | });
64 |
65 | test("two different components rendering the same", () => {
66 | const Box = ({ use: T = "div", ...props }: UseProps) => ;
67 | const Box1: React.SFC = props => ;
68 | const Box2: React.SFC = props => ;
69 | const Box1Box2 = use(Box1, Box2);
70 | expect(getTreeJson()).toMatchInlineSnapshot(`
71 |
74 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 | `);
85 | });
86 |
87 | test("two components and a different underlying element", () => {
88 | const Box1 = ({ use: T = "div", ...props }) => ;
89 | const Box2 = ({ use: T = "span", ...props }) => ;
90 | const Box1Box2 = use(Box1, Box2, "a");
91 | expect(getTreeJson()).toMatchInlineSnapshot(`
92 |
101 |
104 |
105 |
106 |
107 | `);
108 | });
109 |
110 | test("use component with custom prop", () => {
111 | const Box = ({ use: T = "div", ...props }: UseProps & { foo: string }) => (
112 |
113 | );
114 | const UseBox = use(Box);
115 | expect(getTreeJson()).toMatchInlineSnapshot(`
116 |
119 |
122 |
123 | `);
124 | });
125 |
126 | test("two different components with custom props", () => {
127 | const Box1 = ({ use: T = "div", ...props }: UseProps & { foo: string }) => (
128 |
129 | );
130 | const Box2 = ({ use: T = "span", ...props }: UseProps & { bar: string }) => (
131 |
132 | );
133 | const Box1Box2 = use(Box1, Box2);
134 | expect(getTreeJson()).toMatchInlineSnapshot(`
135 |
140 |
144 |
148 |
149 |
150 | `);
151 | });
152 |
153 | test("use component with use prop", () => {
154 | const Box = ({ use: T = "div", ...props }: UseProps) => ;
155 | const UseBox = use(Box);
156 | expect(getTreeJson()).toMatchInlineSnapshot(`
157 |
160 |
161 |
162 | `);
163 | });
164 |
165 | test("use component with same use prop", () => {
166 | const Box = ({ use: T = "div", ...props }: UseProps) => ;
167 | const UseBox = use(Box);
168 | expect(getTreeJson()).toMatchInlineSnapshot(`
169 |
172 |
173 |
174 | `);
175 | });
176 |
177 | test("two different components with custom props and use prop", () => {
178 | const Box1 = ({ use: T = "div", ...props }: UseProps & { foo: string }) => (
179 |
180 | );
181 | const Box2 = ({ use: T = "span", ...props }: UseProps & { bar: string }) => (
182 |
183 | );
184 | const Box1Box2 = use(Box1, Box2);
185 | expect(getTreeJson())
186 | .toMatchInlineSnapshot(`
187 |
198 |
203 |
207 |
208 |
209 | `);
210 | });
211 |
212 | test("use component with use prop as other component", () => {
213 | const Box1 = ({ use: T = "div", ...props }: UseProps) => ;
214 | const Box2 = ({ use: T = "span", ...props }: UseProps) => ;
215 | const UseBox1 = use(Box1);
216 | expect(getTreeJson()).toMatchInlineSnapshot(`
217 |
220 |
221 |
222 |
223 |
224 | `);
225 | });
226 |
227 | test("use component with use prop as other use component", () => {
228 | const Box1 = ({ use: T = "div", ...props }: UseProps) => ;
229 | const Box2 = ({ use: T = "span", ...props }: UseProps) => ;
230 | const UseBox1 = use(Box1);
231 | const UseBox2 = use(Box2);
232 | expect(getTreeJson()).toMatchInlineSnapshot(`
233 |
236 |
237 |
238 |
239 |
240 | `);
241 | });
242 |
243 | test("use component with use prop as multiple component", () => {
244 | const Box1 = ({ use: T = "div", ...props }: UseProps) => ;
245 | const Box2 = ({ use: T = "span", ...props }: UseProps) => ;
246 | const Box3 = ({ use: T = "a", ...props }: UseProps) => ;
247 | const UseBox1 = use(Box1);
248 | expect(getTreeJson()).toMatchInlineSnapshot(`
249 |
258 |
261 |
262 |
263 |
264 |
265 |
266 | `);
267 | });
268 |
269 | test("use component with custom prop with use prop as multiple component", () => {
270 | const Box1 = ({ use: T = "div", ...props }: UseProps & { foo: string }) => (
271 |
272 | );
273 | const Box2 = ({ use: T = "span", ...props }: UseProps & { bar: string }) => (
274 |
275 | );
276 | const Box3 = ({ use: T = "a", ...props }: UseProps) => ;
277 | const UseBox1 = use(Box1);
278 | expect(getTreeJson())
279 | .toMatchInlineSnapshot(`
280 |
291 |
296 |
300 |
304 |
305 |
306 |
307 | `);
308 | });
309 |
310 | test("nested use", () => {
311 | const Box1 = ({ use: T = "div", ...props }: UseProps) => ;
312 | const Box2 = ({ use: T = "span", ...props }: UseProps) => ;
313 | const UseBox1 = use(Box1);
314 | const UseBox1Box2 = use(UseBox1, Box2);
315 | expect(getTreeJson()).toMatchInlineSnapshot(`
316 |
319 |
320 |
321 |
322 |
323 | `);
324 | });
325 |
326 | test("nested use with custom prop", () => {
327 | const Box1 = ({ use: T = "div", ...props }: UseProps & { foo: string }) => (
328 |
329 | );
330 | const Box2 = ({ use: T = "span", ...props }: UseProps) => ;
331 | const UseBox1 = use(Box1);
332 | const UseBox1Box2 = use(UseBox1, Box2);
333 | expect(getTreeJson()).toMatchInlineSnapshot(`
334 |
338 |
341 |
344 |
345 |
346 | `);
347 | });
348 |
349 | test("really nested use", () => {
350 | const Box = ({ use: T = "div", ...props }: UseProps) => ;
351 | const UseBox = use(use(use(use(use(Box)))));
352 | expect(getTreeJson()).toMatchInlineSnapshot(`
353 |
354 |
355 |
356 | `);
357 | });
358 |
359 | test("really nested use with use prop", () => {
360 | const Box = ({ use: T = "div", ...props }: UseProps) => ;
361 | const UseBox = use(use(use(use(use(Box)))));
362 | expect(getTreeJson()).toMatchInlineSnapshot(`
363 |
366 |
367 |
368 | `);
369 | });
370 |
371 | test("empty use", () => {
372 | const Empty = use();
373 | expect(getTreeJson()).toMatchInlineSnapshot(`null`);
374 | });
375 |
376 | test("string use", () => {
377 | const Div = use("div");
378 | expect(getTreeJson()).toMatchInlineSnapshot(``);
379 | });
380 |
381 | test("string use with children", () => {
382 | const Div = use("div");
383 | expect(getTreeJson(Div
)).toMatchInlineSnapshot(`
384 |
387 | `);
388 | });
389 |
390 | test("empty use with use prop", () => {
391 | const Empty = use();
392 | expect(getTreeJson()).toMatchInlineSnapshot(``);
393 | });
394 |
395 | test("empty use after string use", () => {
396 | const Empty = use();
397 | const Button = use("button");
398 | const ButtonEmpty1Empty2 = use(use(Button), Empty);
399 | expect(getTreeJson()).toMatchInlineSnapshot(`null`);
400 | });
401 |
402 | test("empty use after component use", () => {
403 | const Box = ({ use: T = "div", ...props }: UseProps) => ;
404 | const Button = use(Box, "button");
405 | const ButtonEmptyEmpty = use(Button, use());
406 | expect(getTreeJson()).toMatchInlineSnapshot(`
407 |
410 | `);
411 | });
412 |
413 | test("empty custom component after two use components", () => {
414 | const Box = ({ use: T, ...props }: UseProps) => (T ? : null);
415 | const Div = use("div");
416 | const Button = use("button");
417 | const DivButtonBox = use(Div, Button, Box);
418 | expect(getTreeJson()).toMatchInlineSnapshot(``);
419 | });
420 |
421 | test("custom component with default use prop after two use components", () => {
422 | const Box = ({ use: T = "a", ...props }: UseProps) => ;
423 | const Div = use("div");
424 | const Button = use("button");
425 | const DivButtonBox = use(Div, Button, Box);
426 | expect(getTreeJson()).toMatchInlineSnapshot(`
427 |
428 |
429 |
430 | `);
431 | });
432 |
433 | test("use prop with empty component after two use components", () => {
434 | const Box = ({ use: T, ...props }: UseProps) => (T ? : null);
435 | const Div = use("div");
436 | const Button = use("button");
437 | const DivButton = use(Div, Button);
438 | expect(getTreeJson()).toMatchInlineSnapshot(``);
439 | });
440 |
441 | test("use prop with string after two use components", () => {
442 | const Div = use("div");
443 | const Button = use("button");
444 | const DivButton = use(Div, Button);
445 | expect(getTreeJson()).toMatchInlineSnapshot(
446 | ``
447 | );
448 | });
449 |
450 | test("use prop with custom component with default use prop after two use components", () => {
451 | const Box = ({ use: T = "a", ...props }: UseProps) => ;
452 | const Div = use("div");
453 | const Button = use("button");
454 | const DivButton = use(Div, Button);
455 | expect(getTreeJson()).toMatchInlineSnapshot(`
456 |
457 |
458 |
459 | `);
460 | });
461 |
462 | test("render the last use", () => {
463 | const Box = ({ use: T = "a", ...props }: UseProps) => ;
464 | const Div = use(Box, "div");
465 | const Button = use(Box, "button");
466 | const DivButton = use(Div, Button);
467 | expect(getTreeJson()).toMatchInlineSnapshot(`
468 |
471 |
474 |
475 |
476 |
477 | `);
478 | });
479 |
--------------------------------------------------------------------------------