@khanacademy/wonder-blocks-core 4.5.0 → 4.6.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/CHANGELOG.md +8 -0
- package/dist/es/index.js +16 -1
- package/package.json +2 -2
- package/src/__docs__/exports.use-is-mounted.stories.mdx +19 -0
- package/src/__docs__/exports.use-on-mount-effect.stories.mdx +50 -0
- package/src/components/__tests__/id-provider.test.js +4 -4
- package/src/components/__tests__/unique-id-provider.test.js +12 -13
- package/src/components/__tests__/with-ssr-placeholder.test.js +5 -6
- package/src/hooks/__tests__/use-is-mounted.test.js +38 -0
- package/src/hooks/__tests__/use-on-mount-effect.test.js +31 -0
- package/src/hooks/use-is-mounted.js +23 -0
- package/src/hooks/use-on-mount-effect.js +27 -0
- package/src/index.js +2 -0
- package/src/util/__tests__/add-style.test.js +37 -16
- package/dist/index.js +0 -1217
- package/dist/index.js.flow +0 -2
- package/docs.md +0 -5
package/CHANGELOG.md
CHANGED
package/dist/es/index.js
CHANGED
|
@@ -442,6 +442,21 @@ const useForceUpdate = () => {
|
|
|
442
442
|
return forceUpdate;
|
|
443
443
|
};
|
|
444
444
|
|
|
445
|
+
const useOnMountEffect = callback => {
|
|
446
|
+
React.useEffect(callback, []);
|
|
447
|
+
};
|
|
448
|
+
|
|
449
|
+
const useIsMounted = () => {
|
|
450
|
+
const isMounted = React.useRef(false);
|
|
451
|
+
useOnMountEffect(() => {
|
|
452
|
+
isMounted.current = true;
|
|
453
|
+
return () => {
|
|
454
|
+
isMounted.current = false;
|
|
455
|
+
};
|
|
456
|
+
});
|
|
457
|
+
return React.useCallback(() => isMounted.current, []);
|
|
458
|
+
};
|
|
459
|
+
|
|
445
460
|
const useOnline = () => {
|
|
446
461
|
const forceUpdate = useForceUpdate();
|
|
447
462
|
useEffect$1(() => {
|
|
@@ -490,4 +505,4 @@ RenderStateRoot.defaultProps = {
|
|
|
490
505
|
throwIfNested: true
|
|
491
506
|
};
|
|
492
507
|
|
|
493
|
-
export { IDProvider, RenderState, RenderStateRoot, server as Server, Text, UniqueIDProvider, View, WithSSRPlaceholder, addStyle, useForceUpdate, useOnline, useRenderState, useUniqueIdWithMock, useUniqueIdWithoutMock };
|
|
508
|
+
export { IDProvider, RenderState, RenderStateRoot, server as Server, Text, UniqueIDProvider, View, WithSSRPlaceholder, addStyle, useForceUpdate, useIsMounted, useOnMountEffect, useOnline, useRenderState, useUniqueIdWithMock, useUniqueIdWithoutMock };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@khanacademy/wonder-blocks-core",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.6.0",
|
|
4
4
|
"design": "v1",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"react-router-dom": "5.3.0"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
|
-
"wb-dev-build-settings": "^0.
|
|
28
|
+
"wb-dev-build-settings": "^0.5.0"
|
|
29
29
|
},
|
|
30
30
|
"author": "",
|
|
31
31
|
"license": "MIT"
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import {Meta} from "@storybook/addon-docs";
|
|
2
|
+
|
|
3
|
+
<Meta
|
|
4
|
+
title="Core / Exports / useIsMounted()"
|
|
5
|
+
parameters={{
|
|
6
|
+
chromatic: {
|
|
7
|
+
disableSnapshot: true,
|
|
8
|
+
},
|
|
9
|
+
}}
|
|
10
|
+
/>
|
|
11
|
+
|
|
12
|
+
# useIsMounted()
|
|
13
|
+
|
|
14
|
+
```ts
|
|
15
|
+
function useIsMounted(): () => boolean;
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
The `useIsMounted` hook returns a function that can be called to determine if
|
|
19
|
+
the component is mounted or not.
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import {Meta} from "@storybook/addon-docs";
|
|
2
|
+
|
|
3
|
+
<Meta
|
|
4
|
+
title="Core / Exports / useOnMountEffect()"
|
|
5
|
+
parameters={{
|
|
6
|
+
chromatic: {
|
|
7
|
+
disableSnapshot: true,
|
|
8
|
+
},
|
|
9
|
+
}}
|
|
10
|
+
/>
|
|
11
|
+
|
|
12
|
+
# useOnMountEffect()
|
|
13
|
+
|
|
14
|
+
```ts
|
|
15
|
+
function useOnMountEffect(callback: (void | () => void)) void;
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
The `useOnMountEffect` can be used to run an effect once on mount. This avoids
|
|
19
|
+
having to pass `useEffect` an empty deps array and disable the
|
|
20
|
+
`react-hooks/exhaustive-deps` lint.
|
|
21
|
+
|
|
22
|
+
If `callback` returns a cleanup function, it will be called when the component
|
|
23
|
+
is unmounted.
|
|
24
|
+
|
|
25
|
+
NOTE: This hook is equivalent to:
|
|
26
|
+
|
|
27
|
+
```js
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
callback();
|
|
30
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
31
|
+
}, []);
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Usage
|
|
35
|
+
|
|
36
|
+
```js
|
|
37
|
+
import * as React.from "react";
|
|
38
|
+
import {useOnMountEffect} from "@khanacademy/wonder-blocks-core";
|
|
39
|
+
|
|
40
|
+
import {useMarkConversion} from "~/path/to/use-mark-conversion.js";
|
|
41
|
+
|
|
42
|
+
const MyComponent = (props: {}): React.Node => {
|
|
43
|
+
const markConversion = useMarkConversion();
|
|
44
|
+
useOnMountEffect(() => {
|
|
45
|
+
markConversion("my-conversion"); // Will only be called once, on mount
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
return <h1>Hello, world</h1>;
|
|
49
|
+
};
|
|
50
|
+
```
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
// @flow
|
|
2
2
|
import * as React from "react";
|
|
3
|
-
import {
|
|
4
|
-
import "jest-enzyme";
|
|
3
|
+
import {render} from "@testing-library/react";
|
|
5
4
|
|
|
6
5
|
import IDProvider from "../id-provider.js";
|
|
7
6
|
|
|
@@ -13,6 +12,7 @@ jest.mock("@khanacademy/wonder-blocks-core", () => {
|
|
|
13
12
|
return {
|
|
14
13
|
...Core,
|
|
15
14
|
UniqueIDProvider: (props) =>
|
|
15
|
+
// eslint-disable-next-line testing-library/no-node-access
|
|
16
16
|
props.children({
|
|
17
17
|
get: () => mockIDENTIFIER,
|
|
18
18
|
}),
|
|
@@ -26,7 +26,7 @@ describe("UniqueDialog", () => {
|
|
|
26
26
|
const titleId = "custom-title";
|
|
27
27
|
|
|
28
28
|
// Act
|
|
29
|
-
|
|
29
|
+
render(
|
|
30
30
|
<IDProvider id={titleId} scope="component">
|
|
31
31
|
{renderDialogFn}
|
|
32
32
|
</IDProvider>,
|
|
@@ -41,7 +41,7 @@ describe("UniqueDialog", () => {
|
|
|
41
41
|
const renderDialogFn = jest.fn(() => <div />);
|
|
42
42
|
|
|
43
43
|
// Act
|
|
44
|
-
|
|
44
|
+
render(<IDProvider scope="component">{renderDialogFn}</IDProvider>);
|
|
45
45
|
|
|
46
46
|
// Assert
|
|
47
47
|
expect(renderDialogFn).toHaveBeenCalledWith(mockIDENTIFIER);
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
// @flow
|
|
2
2
|
import * as React from "react";
|
|
3
3
|
import * as ReactDOMServer from "react-dom/server.js";
|
|
4
|
-
import {
|
|
5
|
-
import "jest-enzyme";
|
|
4
|
+
import {render} from "@testing-library/react";
|
|
6
5
|
|
|
7
6
|
import View from "../view.js";
|
|
8
7
|
|
|
@@ -40,7 +39,7 @@ describe("UniqueIDProvider", () => {
|
|
|
40
39
|
);
|
|
41
40
|
|
|
42
41
|
// Act
|
|
43
|
-
|
|
42
|
+
render(nodes);
|
|
44
43
|
|
|
45
44
|
// Assert
|
|
46
45
|
// Called once; for real render.
|
|
@@ -54,15 +53,15 @@ describe("UniqueIDProvider", () => {
|
|
|
54
53
|
test("all renders get same unique id factory", () => {
|
|
55
54
|
// Arrange
|
|
56
55
|
const children = jest.fn(() => <View />);
|
|
57
|
-
const
|
|
56
|
+
const UnderTest = () => (
|
|
58
57
|
<UniqueIDProvider mockOnFirstRender={false}>
|
|
59
58
|
{children}
|
|
60
59
|
</UniqueIDProvider>
|
|
61
60
|
);
|
|
62
|
-
const
|
|
61
|
+
const {rerender} = render(<UnderTest />);
|
|
63
62
|
|
|
64
63
|
// Act
|
|
65
|
-
|
|
64
|
+
rerender(<UnderTest />);
|
|
66
65
|
|
|
67
66
|
// Assert
|
|
68
67
|
// Check our forced render worked and we rendered three times.
|
|
@@ -103,7 +102,7 @@ describe("UniqueIDProvider", () => {
|
|
|
103
102
|
);
|
|
104
103
|
|
|
105
104
|
// Act
|
|
106
|
-
|
|
105
|
+
render(nodes);
|
|
107
106
|
|
|
108
107
|
// Assert
|
|
109
108
|
// Called twice; once for initial mock render, and again for real
|
|
@@ -115,22 +114,22 @@ describe("UniqueIDProvider", () => {
|
|
|
115
114
|
test("children calls after first get same unique id factory", () => {
|
|
116
115
|
// Arrange
|
|
117
116
|
const children = jest.fn(() => null);
|
|
118
|
-
const
|
|
117
|
+
const UnderTest = () => (
|
|
119
118
|
<UniqueIDProvider mockOnFirstRender={true}>
|
|
120
119
|
{children}
|
|
121
120
|
</UniqueIDProvider>
|
|
122
121
|
);
|
|
123
|
-
const
|
|
122
|
+
const {rerender} = render(<UnderTest />);
|
|
124
123
|
|
|
125
124
|
// Act
|
|
126
|
-
|
|
125
|
+
rerender(<UnderTest />);
|
|
127
126
|
|
|
128
127
|
// Assert
|
|
129
128
|
// Check our forced render worked and we rendered three times.
|
|
130
129
|
expect(children).toHaveBeenCalledTimes(3);
|
|
131
130
|
// Check the second render gets a UniqueIDFactory instance.
|
|
132
131
|
expect(children.mock.calls[1][0]).toBeInstanceOf(UniqueIDFactory);
|
|
133
|
-
// Check the third render gets the same UniqueIDFactory instance.
|
|
132
|
+
// // Check the third render gets the same UniqueIDFactory instance.
|
|
134
133
|
expect(children.mock.calls[2][0]).toBe(children.mock.calls[1][0]);
|
|
135
134
|
});
|
|
136
135
|
});
|
|
@@ -150,7 +149,7 @@ describe("UniqueIDProvider", () => {
|
|
|
150
149
|
);
|
|
151
150
|
|
|
152
151
|
// Act
|
|
153
|
-
|
|
152
|
+
render(nodes);
|
|
154
153
|
|
|
155
154
|
// Assert
|
|
156
155
|
expect(foo).toHaveBeenCalled();
|
|
@@ -171,7 +170,7 @@ describe("UniqueIDProvider", () => {
|
|
|
171
170
|
);
|
|
172
171
|
|
|
173
172
|
// Act
|
|
174
|
-
|
|
173
|
+
render(nodes);
|
|
175
174
|
|
|
176
175
|
// Assert
|
|
177
176
|
expect(foo).toHaveBeenCalled();
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
// @flow
|
|
2
2
|
import * as React from "react";
|
|
3
3
|
import * as ReactDOMServer from "react-dom/server.js";
|
|
4
|
-
import {
|
|
5
|
-
import "jest-enzyme";
|
|
4
|
+
import {render} from "@testing-library/react";
|
|
6
5
|
|
|
7
6
|
import WithSSRPlaceholder from "../with-ssr-placeholder.js";
|
|
8
7
|
import {RenderStateRoot} from "../render-state-root.js";
|
|
@@ -23,7 +22,7 @@ describe("WithSSRPlaceholder", () => {
|
|
|
23
22
|
);
|
|
24
23
|
|
|
25
24
|
// Act
|
|
26
|
-
|
|
25
|
+
render(nodes);
|
|
27
26
|
});
|
|
28
27
|
|
|
29
28
|
// Assert
|
|
@@ -57,7 +56,7 @@ describe("WithSSRPlaceholder", () => {
|
|
|
57
56
|
);
|
|
58
57
|
|
|
59
58
|
// Act
|
|
60
|
-
|
|
59
|
+
render(nodes);
|
|
61
60
|
});
|
|
62
61
|
|
|
63
62
|
// Assert
|
|
@@ -90,7 +89,7 @@ describe("WithSSRPlaceholder", () => {
|
|
|
90
89
|
);
|
|
91
90
|
|
|
92
91
|
// Act
|
|
93
|
-
|
|
92
|
+
render(nodes);
|
|
94
93
|
});
|
|
95
94
|
|
|
96
95
|
// Assert
|
|
@@ -206,7 +205,7 @@ describe("WithSSRPlaceholder", () => {
|
|
|
206
205
|
);
|
|
207
206
|
|
|
208
207
|
// Act
|
|
209
|
-
|
|
208
|
+
render(nodes);
|
|
210
209
|
});
|
|
211
210
|
|
|
212
211
|
// Assert
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
import {renderHook} from "@testing-library/react-hooks";
|
|
3
|
+
|
|
4
|
+
import {useIsMounted} from "../use-is-mounted.js";
|
|
5
|
+
|
|
6
|
+
describe("useIsMounted", () => {
|
|
7
|
+
it("should return false on first call", () => {
|
|
8
|
+
// Arrange
|
|
9
|
+
|
|
10
|
+
// Act
|
|
11
|
+
const {result} = renderHook(useIsMounted);
|
|
12
|
+
|
|
13
|
+
// Assert
|
|
14
|
+
expect(result.current()).toBeTrue();
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it("should return true on true on subsequent calls", () => {
|
|
18
|
+
// Arrange
|
|
19
|
+
const {result, rerender} = renderHook(useIsMounted);
|
|
20
|
+
|
|
21
|
+
// Act
|
|
22
|
+
rerender();
|
|
23
|
+
|
|
24
|
+
// assert
|
|
25
|
+
expect(result.current()).toBeTrue();
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it("should return false on unmount", () => {
|
|
29
|
+
// Arrange
|
|
30
|
+
const {result, unmount} = renderHook(useIsMounted);
|
|
31
|
+
|
|
32
|
+
// Act
|
|
33
|
+
unmount();
|
|
34
|
+
|
|
35
|
+
// Assert
|
|
36
|
+
expect(result.current()).toBeFalse();
|
|
37
|
+
});
|
|
38
|
+
});
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
import {renderHook} from "@testing-library/react-hooks";
|
|
3
|
+
|
|
4
|
+
import {useOnMountEffect} from "../use-on-mount-effect.js";
|
|
5
|
+
|
|
6
|
+
describe("#useOnMountEffect", () => {
|
|
7
|
+
it("should call the callback once", () => {
|
|
8
|
+
// Arrange
|
|
9
|
+
const callback = jest.fn();
|
|
10
|
+
|
|
11
|
+
// Act
|
|
12
|
+
const {rerender} = renderHook(() => useOnMountEffect(callback));
|
|
13
|
+
rerender();
|
|
14
|
+
|
|
15
|
+
// Assert
|
|
16
|
+
expect(callback).toHaveBeenCalledTimes(1);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it("should call the cleanup function if one is returned by the callback", () => {
|
|
20
|
+
// Arrange
|
|
21
|
+
const cleanup = jest.fn();
|
|
22
|
+
const callback = jest.fn().mockReturnValue(cleanup);
|
|
23
|
+
|
|
24
|
+
// Act
|
|
25
|
+
const {unmount} = renderHook(() => useOnMountEffect(callback));
|
|
26
|
+
unmount();
|
|
27
|
+
|
|
28
|
+
// Assert
|
|
29
|
+
expect(cleanup).toHaveBeenCalled();
|
|
30
|
+
});
|
|
31
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
|
|
4
|
+
import {useOnMountEffect} from "./use-on-mount-effect.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Hook to provide a function for determining component mounted state.
|
|
8
|
+
*
|
|
9
|
+
* NOTE: Based on https://github.com/juliencrn/usehooks-ts/blob/d5f3de88cc319c790f2a4e90ba6a8904298957a5/src/useIsMounted/useIsMounted.ts
|
|
10
|
+
*
|
|
11
|
+
* @returns {() => boolean} A function that returns the component mounted state.
|
|
12
|
+
*/
|
|
13
|
+
export const useIsMounted = (): (() => boolean) => {
|
|
14
|
+
const isMounted = React.useRef<boolean>(false);
|
|
15
|
+
useOnMountEffect(() => {
|
|
16
|
+
isMounted.current = true;
|
|
17
|
+
return () => {
|
|
18
|
+
isMounted.current = false;
|
|
19
|
+
};
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
return React.useCallback(() => isMounted.current, []);
|
|
23
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Runs a callback once on mount.
|
|
6
|
+
*
|
|
7
|
+
* If the callback returns a cleanup function, it will be called when the component is unmounted.
|
|
8
|
+
*
|
|
9
|
+
* @param {() => (void | (() => void))} callback function that forces the component to update.
|
|
10
|
+
*
|
|
11
|
+
* The following code snippets are equivalent
|
|
12
|
+
* ```
|
|
13
|
+
* useOnMountEffect(() => {
|
|
14
|
+
* doTheThing();
|
|
15
|
+
* });
|
|
16
|
+
* ```
|
|
17
|
+
*
|
|
18
|
+
* ```
|
|
19
|
+
* useEffect(() => {
|
|
20
|
+
* doTheThing();
|
|
21
|
+
* // eslint-disable-next-line react-hooks/exhaustive-deps
|
|
22
|
+
* }, []);
|
|
23
|
+
*/
|
|
24
|
+
export const useOnMountEffect = (callback: () => void | (() => void)): void => {
|
|
25
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
26
|
+
React.useEffect(callback, []);
|
|
27
|
+
};
|
package/src/index.js
CHANGED
|
@@ -13,6 +13,8 @@ export {
|
|
|
13
13
|
useUniqueIdWithoutMock,
|
|
14
14
|
} from "./hooks/use-unique-id.js";
|
|
15
15
|
export {useForceUpdate} from "./hooks/use-force-update.js";
|
|
16
|
+
export {useIsMounted} from "./hooks/use-is-mounted.js";
|
|
17
|
+
export {useOnMountEffect} from "./hooks/use-on-mount-effect.js";
|
|
16
18
|
export {useOnline} from "./hooks/use-online.js";
|
|
17
19
|
export {useRenderState} from "./hooks/use-render-state.js";
|
|
18
20
|
export {RenderStateRoot} from "./components/render-state-root.js";
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
// @flow
|
|
2
2
|
import * as React from "react";
|
|
3
3
|
import {StyleSheet} from "aphrodite";
|
|
4
|
-
import {
|
|
5
|
-
import "jest-enzyme";
|
|
4
|
+
import {screen, render} from "@testing-library/react";
|
|
6
5
|
|
|
7
6
|
import addStyle from "../add-style.js";
|
|
8
7
|
|
|
@@ -27,40 +26,62 @@ describe("addStyle", () => {
|
|
|
27
26
|
});
|
|
28
27
|
|
|
29
28
|
it("should set the className if no style is provided", () => {
|
|
30
|
-
|
|
29
|
+
// Arrange
|
|
30
|
+
render(<StyledDiv className="foo" data-test-id="styled-div" />);
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
// Act
|
|
33
|
+
const div = screen.getByTestId("styled-div");
|
|
33
34
|
|
|
34
|
-
|
|
35
|
+
// Assert
|
|
36
|
+
expect(div).toHaveAttribute("class", "foo");
|
|
35
37
|
});
|
|
36
38
|
|
|
37
39
|
it("should set the className to include foo and inlineStyles", () => {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
+
// Arrange
|
|
41
|
+
render(
|
|
42
|
+
<StyledDiv
|
|
43
|
+
className="foo"
|
|
44
|
+
style={{width: "100%"}}
|
|
45
|
+
data-test-id="styled-div"
|
|
46
|
+
/>,
|
|
40
47
|
);
|
|
41
48
|
|
|
42
|
-
|
|
49
|
+
// Act
|
|
50
|
+
const div = screen.getByTestId("styled-div");
|
|
43
51
|
|
|
44
|
-
|
|
52
|
+
// Assert
|
|
53
|
+
const classNames = div.className.split(" ");
|
|
45
54
|
expect(classNames).toHaveLength(2);
|
|
46
55
|
expect(classNames[0].startsWith("inlineStyles")).toBeTruthy();
|
|
47
56
|
expect(classNames[1]).toEqual("foo");
|
|
48
57
|
});
|
|
49
58
|
|
|
50
|
-
it("should set the
|
|
51
|
-
|
|
59
|
+
it("should set the class if an stylesheet style is provided", () => {
|
|
60
|
+
// Arrange
|
|
61
|
+
render(<StyledDiv style={styles.foo} data-test-id="styled-div" />);
|
|
52
62
|
|
|
53
|
-
|
|
63
|
+
// Act
|
|
64
|
+
const div = screen.getByTestId("styled-div");
|
|
54
65
|
|
|
55
|
-
|
|
66
|
+
// Assert
|
|
67
|
+
expect(div).toHaveAttribute("class", expect.any(String));
|
|
56
68
|
});
|
|
57
69
|
|
|
58
70
|
it("should set the className if an stylesheet style is provided", () => {
|
|
59
|
-
|
|
71
|
+
// Arrange
|
|
72
|
+
render(
|
|
73
|
+
<StyledDiv
|
|
74
|
+
className="foo"
|
|
75
|
+
style={styles.foo}
|
|
76
|
+
data-test-id="styled-div"
|
|
77
|
+
/>,
|
|
78
|
+
);
|
|
60
79
|
|
|
61
|
-
|
|
80
|
+
// Act
|
|
81
|
+
const div = screen.getByTestId("styled-div");
|
|
62
82
|
|
|
63
|
-
|
|
83
|
+
// Assert
|
|
84
|
+
const classNames = div.className.split(" ");
|
|
64
85
|
expect(classNames).toHaveLength(2);
|
|
65
86
|
expect(classNames[0]).toEqual(expect.any(String));
|
|
66
87
|
expect(classNames[1]).toEqual("foo");
|