@usefui/components 1.5.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.
Files changed (104) hide show
  1. package/CHANGELOG.md +233 -0
  2. package/LICENSE +21 -0
  3. package/README.md +0 -0
  4. package/babel.config.js +12 -0
  5. package/dist/index.d.mts +1299 -0
  6. package/dist/index.d.ts +1299 -0
  7. package/dist/index.js +3701 -0
  8. package/dist/index.mjs +3586 -0
  9. package/package.json +44 -0
  10. package/src/__tests__/Accordion.test.tsx +106 -0
  11. package/src/__tests__/Avatar.test.tsx +89 -0
  12. package/src/__tests__/Badge.test.tsx +58 -0
  13. package/src/__tests__/Button.test.tsx +88 -0
  14. package/src/__tests__/Checkbox.test.tsx +106 -0
  15. package/src/__tests__/Collapsible.test.tsx +79 -0
  16. package/src/__tests__/Dialog.test.tsx +109 -0
  17. package/src/__tests__/Dropdown.test.tsx +159 -0
  18. package/src/__tests__/Field.test.tsx +100 -0
  19. package/src/__tests__/OTPField.test.tsx +199 -0
  20. package/src/__tests__/Overlay.test.tsx +70 -0
  21. package/src/__tests__/Page.test.tsx +98 -0
  22. package/src/__tests__/Portal.test.tsx +28 -0
  23. package/src/__tests__/Sheet.test.tsx +125 -0
  24. package/src/__tests__/Switch.test.tsx +90 -0
  25. package/src/__tests__/Tabs.test.tsx +129 -0
  26. package/src/__tests__/Toggle.test.tsx +67 -0
  27. package/src/__tests__/Toolbar.test.tsx +147 -0
  28. package/src/__tests__/Tooltip.test.tsx +88 -0
  29. package/src/accordion/Accordion.stories.tsx +89 -0
  30. package/src/accordion/hooks/index.tsx +39 -0
  31. package/src/accordion/index.tsx +170 -0
  32. package/src/avatar/Avatar.stories.tsx +62 -0
  33. package/src/avatar/index.tsx +90 -0
  34. package/src/avatar/styles/index.ts +79 -0
  35. package/src/badge/Badge.stories.tsx +60 -0
  36. package/src/badge/index.tsx +58 -0
  37. package/src/badge/styles/index.ts +109 -0
  38. package/src/button/Button.stories.tsx +47 -0
  39. package/src/button/index.tsx +79 -0
  40. package/src/button/styles/index.ts +180 -0
  41. package/src/checkbox/Checkbox.stories.tsx +100 -0
  42. package/src/checkbox/hooks/index.tsx +40 -0
  43. package/src/checkbox/index.tsx +147 -0
  44. package/src/checkbox/styles/index.ts +139 -0
  45. package/src/collapsible/Collapsible.stories.tsx +95 -0
  46. package/src/collapsible/hooks/index.tsx +50 -0
  47. package/src/collapsible/index.tsx +137 -0
  48. package/src/dialog/Dialog.stories.tsx +73 -0
  49. package/src/dialog/hooks/index.tsx +35 -0
  50. package/src/dialog/index.tsx +221 -0
  51. package/src/dialog/styles/index.ts +72 -0
  52. package/src/divider/index.ts +10 -0
  53. package/src/dropdown/Dropdown.stories.tsx +100 -0
  54. package/src/dropdown/hooks/index.tsx +64 -0
  55. package/src/dropdown/index.tsx +316 -0
  56. package/src/dropdown/styles/index.ts +90 -0
  57. package/src/field/Field.stories.tsx +146 -0
  58. package/src/field/hooks/index.tsx +28 -0
  59. package/src/field/index.tsx +183 -0
  60. package/src/field/styles/index.ts +166 -0
  61. package/src/index.ts +33 -0
  62. package/src/otp-field/OTPField.stories.tsx +50 -0
  63. package/src/otp-field/hooks/index.tsx +13 -0
  64. package/src/otp-field/index.tsx +234 -0
  65. package/src/otp-field/styles/index.ts +33 -0
  66. package/src/otp-field/types/index.ts +23 -0
  67. package/src/overlay/Overlay.stories.tsx +59 -0
  68. package/src/overlay/index.tsx +58 -0
  69. package/src/overlay/styles/index.ts +26 -0
  70. package/src/page/Page.stories.tsx +85 -0
  71. package/src/page/index.tsx +265 -0
  72. package/src/page/styles/index.ts +59 -0
  73. package/src/portal/Portal.stories.tsx +27 -0
  74. package/src/portal/index.tsx +36 -0
  75. package/src/scrollarea/Scrollarea.stories.tsx +99 -0
  76. package/src/scrollarea/index.tsx +27 -0
  77. package/src/scrollarea/styles/index.ts +71 -0
  78. package/src/sheet/Sheet.stories.tsx +86 -0
  79. package/src/sheet/hooks/index.tsx +47 -0
  80. package/src/sheet/index.tsx +190 -0
  81. package/src/sheet/styles/index.ts +69 -0
  82. package/src/switch/Switch.stories.tsx +96 -0
  83. package/src/switch/hooks/index.tsx +33 -0
  84. package/src/switch/index.tsx +122 -0
  85. package/src/switch/styles/index.ts +118 -0
  86. package/src/table/index.tsx +138 -0
  87. package/src/table/styles/index.ts +48 -0
  88. package/src/tabs/Tabs.stories.tsx +87 -0
  89. package/src/tabs/hooks/index.tsx +35 -0
  90. package/src/tabs/index.tsx +161 -0
  91. package/src/tabs/styles/index.ts +9 -0
  92. package/src/toggle/Toggle.stories.tsx +118 -0
  93. package/src/toggle/index.tsx +55 -0
  94. package/src/toggle/styles/index.ts +0 -0
  95. package/src/toolbar/Toolbar.stories.tsx +89 -0
  96. package/src/toolbar/hooks/index.tsx +35 -0
  97. package/src/toolbar/index.tsx +243 -0
  98. package/src/toolbar/styles/index.ts +129 -0
  99. package/src/tooltip/Tooltip.stories.tsx +60 -0
  100. package/src/tooltip/index.tsx +177 -0
  101. package/src/tooltip/styles/index.ts +38 -0
  102. package/src/utils/index.ts +2 -0
  103. package/tsconfig.json +18 -0
  104. package/vitest.config.ts +16 -0
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "@usefui/components",
3
+ "version": "1.5.1",
4
+ "description": "Open Source React components library",
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.mjs",
7
+ "types": "dist/index.d.ts",
8
+ "keywords": [
9
+ "react",
10
+ "typescript",
11
+ "accessibility",
12
+ "ui",
13
+ "components",
14
+ "ui-components"
15
+ ],
16
+ "author": "Nicolas Nunes",
17
+ "license": "MIT",
18
+ "peerDependencies": {
19
+ "@types/react": "*",
20
+ "@types/react-dom": "*",
21
+ "@types/styled-components": "*",
22
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0",
23
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0",
24
+ "styled-components": "^5.1.34"
25
+ },
26
+ "devDependencies": {
27
+ "@types/react": "^18.3.23",
28
+ "@types/react-dom": "^18.3.7",
29
+ "@types/styled-components": "^5.1.34",
30
+ "react": "^18.3.1",
31
+ "react-dom": "^18.3.1",
32
+ "styled-components": "^5.3.11",
33
+ "typescript": "^5.9.2",
34
+ "@usefui/core": "^1.3.3",
35
+ "@usefui/hooks": "^1.3.3",
36
+ "@usefui/tokens": "^1.5.2"
37
+ },
38
+ "scripts": {
39
+ "test": "vitest run --coverage --logHeapUsage",
40
+ "test:coverage": "open coverage/lcov-report/index.html",
41
+ "build": "tsup src/index.ts --format cjs,esm --dts",
42
+ "lint": "tsc"
43
+ }
44
+ }
@@ -0,0 +1,106 @@
1
+ import React from "react";
2
+
3
+ import { test, vi, afterEach, describe, expect } from "vitest";
4
+ import {
5
+ screen,
6
+ render,
7
+ cleanup,
8
+ waitFor,
9
+ fireEvent,
10
+ } from "@testing-library/react";
11
+ import { axe, toHaveNoViolations } from "jest-axe";
12
+
13
+ import { Accordion } from "../../src/accordion";
14
+
15
+ afterEach(async () => {
16
+ vi.clearAllMocks();
17
+ vi.resetModules();
18
+ cleanup();
19
+ });
20
+
21
+ expect.extend(toHaveNoViolations);
22
+ describe("Accordion", () => {
23
+ test("Renders without accessibility violation", async () => {
24
+ const { container } = render(
25
+ <Accordion.Root>
26
+ <Accordion>
27
+ <Accordion.Trigger value="1">1</Accordion.Trigger>
28
+ <Accordion.Content value="1" defaultOpen>
29
+ 2
30
+ </Accordion.Content>
31
+ </Accordion>
32
+ </Accordion.Root>
33
+ );
34
+
35
+ const ComponentContainer = await axe(container);
36
+ expect(ComponentContainer).toHaveNoViolations();
37
+ });
38
+ test("Renders with accessibility definition", async () => {
39
+ render(
40
+ <Accordion.Root>
41
+ <Accordion>
42
+ <Accordion.Trigger value="1">1</Accordion.Trigger>
43
+ <Accordion.Content value="1">1</Accordion.Content>
44
+ </Accordion>
45
+ </Accordion.Root>
46
+ );
47
+ const Trigger = screen.getByLabelText("button-action");
48
+
49
+ expect(Trigger).toBeDefined();
50
+ expect(() => screen.getByRole("article")).toThrow();
51
+
52
+ fireEvent.click(Trigger);
53
+ await waitFor(() => {
54
+ const Content = screen.getByRole("article");
55
+ expect(Content).toBeDefined();
56
+ expect(Content.getAttribute("aria-expanded")).toBe("true");
57
+ expect(Content.getAttribute("data-value")).toBe("1");
58
+ expect(Content.getAttribute("aria-labelledby")).toBeDefined();
59
+ expect(Content.getAttribute("id")).toBeDefined();
60
+ });
61
+ });
62
+ test("Fires the defined callback function and toggle the content when the trigger is clicked", async () => {
63
+ const onClickCallback = vi.fn();
64
+ render(
65
+ <Accordion.Root>
66
+ <Accordion>
67
+ <Accordion.Trigger
68
+ onClick={onClickCallback}
69
+ value="1"
70
+ name="trigger-1"
71
+ >
72
+ Trigger 1
73
+ </Accordion.Trigger>
74
+ <Accordion.Trigger value="2" name="trigger-2">
75
+ Trigger 2
76
+ </Accordion.Trigger>
77
+ <Accordion.Content value="1" defaultOpen>
78
+ Content 1
79
+ </Accordion.Content>
80
+ <Accordion.Content value="2">Content 2</Accordion.Content>
81
+ </Accordion>
82
+ </Accordion.Root>
83
+ );
84
+
85
+ expect(screen.getByText("Content 1")).toBeDefined();
86
+
87
+ fireEvent.click(screen.getByLabelText("trigger-2-action"));
88
+ await waitFor(() => {
89
+ expect(() => screen.getByText("Content 1")).toThrow();
90
+ expect(screen.getByText("Content 2")).toBeDefined();
91
+ });
92
+
93
+ fireEvent.click(screen.getByLabelText("trigger-1-action"));
94
+ await waitFor(() => {
95
+ expect(() => screen.getByText("Content 2")).toThrow();
96
+ expect(screen.getByText("Content 1")).toBeDefined();
97
+ expect(onClickCallback).toHaveBeenCalledTimes(1);
98
+ });
99
+
100
+ fireEvent.click(screen.getByLabelText("trigger-1-action"));
101
+ await waitFor(() => {
102
+ expect(() => screen.getByText("Content 1")).toThrow();
103
+ expect(() => screen.getByText("Content 2")).toThrow();
104
+ });
105
+ });
106
+ });
@@ -0,0 +1,89 @@
1
+ import React from "react";
2
+
3
+ import { test, vi, afterEach, describe, expect } from "vitest";
4
+ import { screen, render, cleanup } from "@testing-library/react";
5
+ import { axe, toHaveNoViolations } from "jest-axe";
6
+
7
+ import { Avatar, AvataStatusEnum } from "../../src/avatar";
8
+ import { ComponentSizeEnum, TComponentSize } from "../../../../types";
9
+
10
+ afterEach(async () => {
11
+ vi.clearAllMocks();
12
+ vi.resetModules();
13
+ cleanup();
14
+ });
15
+
16
+ expect.extend(toHaveNoViolations);
17
+ describe("Avatar", () => {
18
+ test("Renders without accessibility violation", async () => {
19
+ const { container } = render(
20
+ <Avatar
21
+ sizing={ComponentSizeEnum.Small}
22
+ status={AvataStatusEnum.Online}
23
+ />
24
+ );
25
+ const ComponentContainer = await axe(container);
26
+ expect(ComponentContainer).toHaveNoViolations();
27
+ });
28
+ test("Renders with accessibility definition", async () => {
29
+ render(
30
+ <Avatar
31
+ sizing={ComponentSizeEnum.Small}
32
+ status={AvataStatusEnum.Online}
33
+ />
34
+ );
35
+
36
+ const avatarLabel = "small-user-avatar";
37
+ const AvatarWrapper = screen.getByLabelText(avatarLabel);
38
+ const AvatarStatus = screen.getByLabelText(`${avatarLabel}-status`);
39
+
40
+ expect(AvatarWrapper).toBeDefined();
41
+ expect(AvatarWrapper.getAttribute("data-size")).toBe("small");
42
+ expect(AvatarWrapper.getAttribute("data-status")).toBe("online");
43
+
44
+ expect(AvatarStatus).toBeDefined();
45
+ expect(AvatarStatus.getAttribute("role")).toBe("img");
46
+ expect(AvatarStatus.getAttribute("data-status")).toBe("online");
47
+ });
48
+ test("Renders with Image as background", async () => {
49
+ render(<Avatar src="http://www.bui/tests" />);
50
+
51
+ const avatarLabel = "medium-user-avatar";
52
+ const AvatarWrapper = screen.getByLabelText(avatarLabel);
53
+ const AvatarImage = screen.getByLabelText("medium-user-avatar-image");
54
+
55
+ expect(AvatarWrapper).toBeDefined();
56
+ expect(AvatarImage).toBeDefined();
57
+ expect(AvatarWrapper.getAttribute("data-size")).toBe("medium");
58
+ });
59
+ test("Renders variants without accessibility violation", async () => {
60
+ const SizeVariants = [
61
+ ComponentSizeEnum.Small,
62
+ ComponentSizeEnum.Medium,
63
+ ComponentSizeEnum.Large,
64
+ ];
65
+ const StatusVariants = [
66
+ AvataStatusEnum.Online,
67
+ AvataStatusEnum.Away,
68
+ AvataStatusEnum.Busy,
69
+ AvataStatusEnum.Offline,
70
+ ];
71
+
72
+ const { container } = render(
73
+ <React.Fragment>
74
+ {StatusVariants.map((variant, key) => (
75
+ <Avatar
76
+ key={variant}
77
+ status={variant}
78
+ sizing={
79
+ (SizeVariants[key] as TComponentSize) ?? ComponentSizeEnum.Small
80
+ }
81
+ />
82
+ ))}
83
+ </React.Fragment>
84
+ );
85
+
86
+ const ComponentContainer = await axe(container);
87
+ expect(ComponentContainer).toHaveNoViolations();
88
+ });
89
+ });
@@ -0,0 +1,58 @@
1
+ import React from "react";
2
+
3
+ import { test, vi, afterEach, describe, expect } from "vitest";
4
+ import { render, cleanup } from "@testing-library/react";
5
+ import { axe, toHaveNoViolations } from "jest-axe";
6
+
7
+ import { Badge } from "../../src/badge";
8
+
9
+ afterEach(async () => {
10
+ vi.clearAllMocks();
11
+ vi.resetModules();
12
+ cleanup();
13
+ });
14
+
15
+ expect.extend(toHaveNoViolations);
16
+ const handleClick = vi.fn();
17
+
18
+ describe("Badge", () => {
19
+ test("Renders without accessibility violation", async () => {
20
+ const { container } = render(
21
+ <Badge aria-label="test-badge" onClick={handleClick}>
22
+ Test
23
+ </Badge>
24
+ );
25
+ const ComponentContainer = await axe(container);
26
+ expect(ComponentContainer).toHaveNoViolations();
27
+ });
28
+ test("Renders variants without accessibility violation", async () => {
29
+ const Variants = [
30
+ "primary",
31
+ "secondary",
32
+ "border",
33
+ "error",
34
+ "warning",
35
+ "success",
36
+ "meta",
37
+ ];
38
+ const Shapes = ["square", "smooth", "round"];
39
+
40
+ const { container } = render(
41
+ <React.Fragment>
42
+ {Variants.map((variant) => (
43
+ <Badge key={variant} variant={variant as any}>
44
+ {variant}
45
+ </Badge>
46
+ ))}
47
+ {Shapes.map((variant) => (
48
+ <Badge key={variant} variant={variant as any}>
49
+ {variant}
50
+ </Badge>
51
+ ))}
52
+ </React.Fragment>
53
+ );
54
+
55
+ const ComponentContainer = await axe(container);
56
+ expect(ComponentContainer).toHaveNoViolations();
57
+ });
58
+ });
@@ -0,0 +1,88 @@
1
+ import React from "react";
2
+
3
+ import { test, vi, afterEach, describe, expect } from "vitest";
4
+ import { screen, render, cleanup, fireEvent } from "@testing-library/react";
5
+ import { axe, toHaveNoViolations } from "jest-axe";
6
+
7
+ import { Button } from "../../src/button";
8
+ import { ComponentVariantEnum } from "../../../../types";
9
+
10
+ afterEach(async () => {
11
+ vi.clearAllMocks();
12
+ vi.resetModules();
13
+ cleanup();
14
+ });
15
+
16
+ expect.extend(toHaveNoViolations);
17
+ const handleClick = vi.fn();
18
+
19
+ describe("Button", () => {
20
+ test("Renders with accessibility definition", async () => {
21
+ render(<Button onClick={handleClick}>Test</Button>);
22
+
23
+ expect(screen.getByRole("button").getAttribute("type")).toEqual("button");
24
+ expect(screen.getByRole("button").getAttribute("tabIndex")).toEqual("0");
25
+ expect(screen.getByRole("button").getAttribute("name")).toEqual("button");
26
+ expect(screen.getByRole("button").getAttribute("aria-disabled")).toEqual(
27
+ "false"
28
+ );
29
+ expect(screen.getByRole("button").getAttribute("aria-label")).toEqual(
30
+ "button-action"
31
+ );
32
+ expect(screen.getByRole("button").getAttribute("data-size")).toEqual(
33
+ "medium"
34
+ );
35
+ expect(screen.getByRole("button").getAttribute("data-raw")).toEqual(
36
+ "false"
37
+ );
38
+ expect(screen.getByRole("button").getAttribute("aria-description")).toEqual(
39
+ "button-action:button/disabled:false"
40
+ );
41
+ });
42
+ test("Renders variants without accessibility violation", async () => {
43
+ const ButtonsVariants = [
44
+ ComponentVariantEnum.Primary,
45
+ ComponentVariantEnum.Secondary,
46
+ ComponentVariantEnum.Tertiary,
47
+ ComponentVariantEnum.Ghost,
48
+ ];
49
+
50
+ const { container } = render(
51
+ <React.Fragment>
52
+ {ButtonsVariants.map((variant) => (
53
+ <Button
54
+ key={variant}
55
+ name={`test-styled-${variant}`}
56
+ variant={variant}
57
+ >
58
+ {variant}
59
+ </Button>
60
+ ))}
61
+ </React.Fragment>
62
+ );
63
+
64
+ const ComponentContainer = await axe(container);
65
+ expect(ComponentContainer).toHaveNoViolations();
66
+
67
+ ButtonsVariants.map((variant) => {
68
+ expect(
69
+ screen
70
+ .getByLabelText(`test-styled-${variant}-action`)
71
+ .getAttribute("data-variant")
72
+ ).toBe(variant);
73
+ });
74
+ });
75
+ test("Renders without accessibility violation", async () => {
76
+ const { container } = render(
77
+ <Button name="test" onClick={handleClick}>
78
+ Test
79
+ </Button>
80
+ );
81
+ const ComponentContainer = await axe(container);
82
+ const ButtonComponent = screen.getByRole("button");
83
+
84
+ expect(ComponentContainer).toHaveNoViolations();
85
+ fireEvent.click(ButtonComponent);
86
+ expect(handleClick).toHaveBeenCalledTimes(1);
87
+ });
88
+ });
@@ -0,0 +1,106 @@
1
+ import React from "react";
2
+
3
+ import { test, vi, afterEach, describe, expect } from "vitest";
4
+ import {
5
+ screen,
6
+ render,
7
+ cleanup,
8
+ waitFor,
9
+ fireEvent,
10
+ } from "@testing-library/react";
11
+ import { axe, toHaveNoViolations } from "jest-axe";
12
+
13
+ import { Checkbox } from "../../src/checkbox";
14
+
15
+ afterEach(async () => {
16
+ vi.clearAllMocks();
17
+ vi.resetModules();
18
+ cleanup();
19
+ });
20
+
21
+ const onClickCallback = vi.fn();
22
+ const onChangeCallback = vi.fn();
23
+ const CheckboxDefault = () => {
24
+ return (
25
+ <Checkbox.Root>
26
+ <Checkbox
27
+ checked={false}
28
+ onClick={onClickCallback}
29
+ onChange={onChangeCallback}
30
+ name="test"
31
+ >
32
+ <Checkbox.Indicator />
33
+ </Checkbox>
34
+ </Checkbox.Root>
35
+ );
36
+ };
37
+
38
+ expect.extend(toHaveNoViolations);
39
+ describe("Checkbox", () => {
40
+ test("Renders without accessibility violation", async () => {
41
+ const { container } = render(<CheckboxDefault />);
42
+ const ComponentContainer = await axe(container);
43
+
44
+ expect(ComponentContainer).toHaveNoViolations();
45
+ });
46
+ test("Renders with accessibility definition", () => {
47
+ render(<CheckboxDefault />);
48
+ const CheckboxComponent = screen.getByLabelText("test-native-checkbox");
49
+
50
+ expect(CheckboxComponent.getAttribute("type")).toBe("checkbox");
51
+ expect(CheckboxComponent.getAttribute("tabindex")).toBe("0");
52
+ expect(CheckboxComponent.getAttribute("value")).toBe("unchecked");
53
+ expect(CheckboxComponent.getAttribute("aria-checked")).toBe("false");
54
+ expect(CheckboxComponent.getAttribute("aria-disabled")).toBe("false");
55
+ expect(CheckboxComponent.getAttribute("aria-required")).toBe("false");
56
+ expect(CheckboxComponent.getAttribute("data-state")).toBe("unchecked");
57
+ });
58
+ test("Toggle checked state on click and fire callback function", async () => {
59
+ render(<CheckboxDefault />);
60
+ const CheckboxComponent = screen.getByLabelText("test-checkbox");
61
+ const CheckboxNative = screen.getByLabelText("test-native-checkbox");
62
+
63
+ expect(CheckboxComponent.getAttribute("data-state")).toBe("unchecked");
64
+
65
+ expect(CheckboxNative.getAttribute("value")).toBe("unchecked");
66
+ expect(CheckboxNative.getAttribute("aria-checked")).toBe("false");
67
+
68
+ fireEvent.click(CheckboxNative);
69
+ await waitFor(() => {
70
+ expect(onClickCallback).toHaveBeenCalled();
71
+ expect(screen.getByTitle("Checked")).toBeDefined();
72
+
73
+ expect(CheckboxComponent.getAttribute("data-state")).toBe("checked");
74
+ expect(CheckboxNative.getAttribute("value")).toBe("checked");
75
+ expect(CheckboxNative.getAttribute("aria-checked")).toBe("true");
76
+ });
77
+
78
+ fireEvent.click(CheckboxNative);
79
+ await waitFor(() => {
80
+ expect(onClickCallback).toHaveBeenCalled();
81
+ expect(CheckboxComponent.getAttribute("data-state")).toBe("unchecked");
82
+ expect(CheckboxNative.getAttribute("value")).toBe("unchecked");
83
+ expect(CheckboxNative.getAttribute("aria-checked")).toBe("false");
84
+ });
85
+ });
86
+ test("Renders as checked if defaultChecked is defined", async () => {
87
+ render(
88
+ <Checkbox.Root>
89
+ <Checkbox
90
+ name="test"
91
+ defaultChecked
92
+ onClick={onClickCallback}
93
+ onChange={onChangeCallback}
94
+ >
95
+ <Checkbox.Indicator />
96
+ </Checkbox>
97
+ </Checkbox.Root>
98
+ );
99
+ const CheckboxComponent = screen.getByLabelText("test-checkbox");
100
+ const CheckboxNative = screen.getByLabelText("test-native-checkbox");
101
+
102
+ expect(CheckboxComponent.getAttribute("data-state")).toBe("checked");
103
+ expect(CheckboxNative.getAttribute("value")).toBe("checked");
104
+ expect(CheckboxNative.getAttribute("aria-checked")).toBe("true");
105
+ });
106
+ });
@@ -0,0 +1,79 @@
1
+ import React from "react";
2
+
3
+ import { test, vi, afterEach, describe, expect } from "vitest";
4
+ import {
5
+ screen,
6
+ render,
7
+ cleanup,
8
+ waitFor,
9
+ fireEvent,
10
+ } from "@testing-library/react";
11
+ import { axe, toHaveNoViolations } from "jest-axe";
12
+
13
+ import { Collapsible } from "../../src/collapsible";
14
+
15
+ afterEach(async () => {
16
+ vi.clearAllMocks();
17
+ vi.resetModules();
18
+ cleanup();
19
+ });
20
+
21
+ const onClickCallback = vi.fn();
22
+ const CollapsibleDefault = (args: {
23
+ defaultOpen?: boolean;
24
+ showFirstChild?: boolean;
25
+ }) => {
26
+ return (
27
+ <Collapsible.Root>
28
+ <Collapsible>
29
+ <Collapsible.Trigger onClick={onClickCallback}>
30
+ Trigger
31
+ </Collapsible.Trigger>
32
+ <Collapsible.Content
33
+ defaultOpen={args.defaultOpen}
34
+ showFirstChild={args.showFirstChild}
35
+ aria-label="test-content"
36
+ >
37
+ <p key={1}>Item 1</p>
38
+ <p key={2}>Item 2</p>
39
+ </Collapsible.Content>
40
+ </Collapsible>
41
+ </Collapsible.Root>
42
+ );
43
+ };
44
+
45
+ expect.extend(toHaveNoViolations);
46
+ describe("Collapsible", () => {
47
+ test("Renders without accessibility violation", async () => {
48
+ const { container } = render(<CollapsibleDefault />);
49
+ const ComponentContainer = await axe(container);
50
+ expect(ComponentContainer).toHaveNoViolations();
51
+ });
52
+ test("Renders with accessibility definition", () => {
53
+ render(<CollapsibleDefault />);
54
+ const TriggerComponent = screen.getByRole("button");
55
+
56
+ expect(TriggerComponent.getAttribute("aria-disabled")).toBe("false");
57
+ expect(TriggerComponent.getAttribute("data-expanded")).toBe("false");
58
+ expect(TriggerComponent.getAttribute("data-state")).toBe("closed");
59
+ });
60
+ test("Fires the defined callback function and toggle the content when the trigger is clicked", async () => {
61
+ render(<CollapsibleDefault showFirstChild defaultOpen />);
62
+ const TriggerComponent = screen.getByRole("button");
63
+ const FirstChild = screen.queryByText("Item 1");
64
+ const ContentContainer = screen.getByLabelText("test-content");
65
+
66
+ expect(TriggerComponent).toBeDefined();
67
+ expect(FirstChild).toBeDefined();
68
+ expect(ContentContainer.getAttribute("data-state")).toBe("open");
69
+
70
+ fireEvent.click(TriggerComponent);
71
+ await waitFor(() => {
72
+ expect(onClickCallback).toHaveBeenCalled();
73
+
74
+ expect(FirstChild).toBeDefined();
75
+ expect(ContentContainer.getAttribute("data-state")).toBe("closed");
76
+ expect(screen.queryByText("Item 2")).toBe(null);
77
+ });
78
+ });
79
+ });
@@ -0,0 +1,109 @@
1
+ import React from "react";
2
+
3
+ import { test, vi, afterEach, describe, expect } from "vitest";
4
+ import {
5
+ screen,
6
+ render,
7
+ cleanup,
8
+ waitFor,
9
+ fireEvent,
10
+ } from "@testing-library/react";
11
+ import { axe, toHaveNoViolations } from "jest-axe";
12
+
13
+ import { Dialog } from "../../src/dialog";
14
+
15
+ afterEach(async () => {
16
+ vi.clearAllMocks();
17
+ vi.resetModules();
18
+ cleanup();
19
+ });
20
+
21
+ const onClickCallback = vi.fn();
22
+ const onClickCallbackInner = vi.fn();
23
+ const onClickCallbackOverlay = vi.fn();
24
+ const DialogDefault = (args: { open?: boolean; closeOnInteract?: boolean }) => {
25
+ return (
26
+ <Dialog.Root>
27
+ <Dialog.Trigger name="external" onClick={onClickCallback}>
28
+ Ext Trigger
29
+ </Dialog.Trigger>
30
+
31
+ <Dialog open={args.open} aria-label="test-dialog">
32
+ <h4>Dialog component</h4>
33
+ <Dialog.Menu>
34
+ <Dialog.Control name="inner" onClick={onClickCallbackInner}>
35
+ Close
36
+ </Dialog.Control>
37
+ </Dialog.Menu>
38
+ </Dialog>
39
+ <Dialog.Overlay
40
+ aria-label="overlay"
41
+ closeOnInteract={Boolean(args.closeOnInteract)}
42
+ onClick={onClickCallbackOverlay}
43
+ />
44
+ </Dialog.Root>
45
+ );
46
+ };
47
+
48
+ expect.extend(toHaveNoViolations);
49
+ describe("Dialog", () => {
50
+ test("Renders without accessibility violation", async () => {
51
+ const { container } = render(<DialogDefault open />);
52
+ const ComponentContainer = await axe(container);
53
+
54
+ expect(ComponentContainer).toHaveNoViolations();
55
+ });
56
+ test("Renders with accessibility definition", async () => {
57
+ render(<DialogDefault />);
58
+ const ExtTrigger = screen.getByLabelText("external-action");
59
+
60
+ expect(ExtTrigger.getAttribute("aria-expanded")).toBe("false");
61
+ expect(ExtTrigger.getAttribute("data-state")).toBe("closed");
62
+
63
+ fireEvent.click(ExtTrigger);
64
+ await waitFor(() => {
65
+ const Content = screen.getByLabelText("test-dialog");
66
+ const InnerTrigger = screen.getByLabelText("inner-action");
67
+
68
+ expect(ExtTrigger.getAttribute("aria-expanded")).toBe("true");
69
+ expect(ExtTrigger.getAttribute("data-state")).toBe("open");
70
+
71
+ expect(InnerTrigger.getAttribute("aria-expanded")).toBe("true");
72
+ expect(InnerTrigger.getAttribute("data-state")).toBe("open");
73
+
74
+ expect(Content.getAttribute("role")).toBe("dialog");
75
+ expect(Content.getAttribute("data-state")).toBe("open");
76
+ expect(Content.getAttribute("tabIndex")).toBe("-1");
77
+ expect(Content.getAttribute("aria-labelledby")).toBeDefined();
78
+ });
79
+ });
80
+ test("Fires the defined callback functions and toggle the content when the triggers are clicked", async () => {
81
+ render(<DialogDefault />);
82
+
83
+ fireEvent.click(screen.getByLabelText("external-action"));
84
+ await waitFor(() => expect(onClickCallback).toHaveBeenCalled());
85
+
86
+ fireEvent.click(screen.getByLabelText("inner-action"));
87
+ await waitFor(() => expect(onClickCallbackInner).toHaveBeenCalled());
88
+ });
89
+ test("Renders the component by default if open is defined", async () => {
90
+ render(<DialogDefault open />);
91
+ await waitFor(() =>
92
+ expect(screen.getByLabelText("test-dialog")).toBeDefined()
93
+ );
94
+ });
95
+ test("Removes the component from the DOM if the Overlay is clicked and closeOnInteract is defined", async () => {
96
+ render(<DialogDefault open closeOnInteract />);
97
+ await waitFor(() => {
98
+ const Overlay = screen.getByLabelText("overlay");
99
+
100
+ expect(Overlay).toBeDefined();
101
+ expect(Overlay.getAttribute("aria-hidden")).toBe("true");
102
+ });
103
+
104
+ fireEvent.click(screen.getByLabelText("overlay"));
105
+ expect(onClickCallbackOverlay).toHaveBeenCalled();
106
+ expect(() => screen.getByRole("region")).toThrow();
107
+ expect(() => screen.getByLabelText("overlay")).toThrow();
108
+ });
109
+ });