@dhasdk/simple-ui 1.0.7 → 1.0.8

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 (227) hide show
  1. package/.babelrc +12 -0
  2. package/.storybook/main.ts +35 -0
  3. package/.storybook/preview.ts +4 -0
  4. package/BAKpostcss.config.jsBAK +15 -0
  5. package/BAKtailwind.config.mjsBAK +99 -0
  6. package/README.md +464 -16
  7. package/coverage/storybook/coverage-storybook.json +32411 -0
  8. package/coverage/storybook/lcov-report/Accordion.tsx.html +805 -0
  9. package/coverage/storybook/lcov-report/Badge.tsx.html +346 -0
  10. package/coverage/storybook/lcov-report/Breadcrumbs.tsx.html +742 -0
  11. package/coverage/storybook/lcov-report/Button.tsx.html +448 -0
  12. package/coverage/storybook/lcov-report/ButtonGroup.tsx.html +403 -0
  13. package/coverage/storybook/lcov-report/Card.tsx.html +292 -0
  14. package/coverage/storybook/lcov-report/CharacterCounter.tsx.html +253 -0
  15. package/coverage/storybook/lcov-report/CheckBox.tsx.html +1555 -0
  16. package/coverage/storybook/lcov-report/DatePicker.tsx.html +826 -0
  17. package/coverage/storybook/lcov-report/Input.tsx.html +1012 -0
  18. package/coverage/storybook/lcov-report/List.tsx.html +364 -0
  19. package/coverage/storybook/lcov-report/Modal.tsx.html +745 -0
  20. package/coverage/storybook/lcov-report/Pill.tsx.html +358 -0
  21. package/coverage/storybook/lcov-report/Search.tsx.html +997 -0
  22. package/coverage/storybook/lcov-report/SearchContent.tsx.html +235 -0
  23. package/coverage/storybook/lcov-report/SectionHeader.tsx.html +358 -0
  24. package/coverage/storybook/lcov-report/Select.tsx.html +1012 -0
  25. package/coverage/storybook/lcov-report/Shield.tsx.html +802 -0
  26. package/coverage/storybook/lcov-report/SideBarNav.tsx.html +490 -0
  27. package/coverage/storybook/lcov-report/Skeleton.tsx.html +394 -0
  28. package/coverage/storybook/lcov-report/Slider.tsx.html +385 -0
  29. package/coverage/storybook/lcov-report/Status.tsx.html +322 -0
  30. package/coverage/storybook/lcov-report/Tabs.tsx.html +610 -0
  31. package/coverage/storybook/lcov-report/Toggle.tsx.html +373 -0
  32. package/coverage/storybook/lcov-report/Tooltip.tsx.html +496 -0
  33. package/coverage/storybook/lcov-report/base.css +224 -0
  34. package/coverage/storybook/lcov-report/block-navigation.js +87 -0
  35. package/coverage/storybook/lcov-report/favicon.png +0 -0
  36. package/coverage/storybook/lcov-report/index.html +476 -0
  37. package/coverage/storybook/lcov-report/prettify.css +1 -0
  38. package/coverage/storybook/lcov-report/prettify.js +2 -0
  39. package/coverage/storybook/lcov-report/sort-arrow-sprite.png +0 -0
  40. package/coverage/storybook/lcov-report/sorter.js +196 -0
  41. package/coverage/storybook/lcov.info +2312 -0
  42. package/dist/README.md +1815 -0
  43. package/eslint.config.mjs +13 -0
  44. package/package.json +6 -7
  45. package/project.json +11 -0
  46. package/src/assets/img/Frame.svg +5 -0
  47. package/src/assets/img/backArrowRight.svg +10 -0
  48. package/src/assets/img/bc-separator.png +0 -0
  49. package/src/assets/img/calendar.png +0 -0
  50. package/src/assets/img/calendar.svg +4 -0
  51. package/src/assets/img/check.svg +5 -0
  52. package/src/assets/img/check_box.svg +10 -0
  53. package/src/assets/img/check_box_empty.svg +10 -0
  54. package/src/assets/img/check_box_fill.svg +10 -0
  55. package/src/assets/img/check_box_fill_empty.svg +10 -0
  56. package/src/assets/img/chevron-down-white.svg +2 -0
  57. package/src/assets/img/chevron-down.svg +2 -0
  58. package/src/assets/img/chevron-left.svg +1 -0
  59. package/src/assets/img/chevron-right-light.svg +4 -0
  60. package/src/assets/img/chevron-right.svg +3 -0
  61. package/src/assets/img/chevron-up-white.svg +1 -0
  62. package/src/assets/img/chevron-up.svg +1 -0
  63. package/src/assets/img/clock.svg +6 -0
  64. package/src/assets/img/close.svg +1 -0
  65. package/src/assets/img/close2.svg +6 -0
  66. package/src/assets/img/closeModal.svg +10 -0
  67. package/src/assets/img/close_icon_dark.svg +10 -0
  68. package/src/assets/img/close_small.svg +3 -0
  69. package/src/assets/img/emergency_home.svg +10 -0
  70. package/src/assets/img/first-aid-kit.svg +7 -0
  71. package/src/assets/img/heartbeat.svg +4 -0
  72. package/src/assets/img/home-gray.svg +3 -0
  73. package/src/assets/img/home.svg +3 -0
  74. package/src/assets/img/hospital.jpg +0 -0
  75. package/src/assets/img/indeterminate_check_box.svg +10 -0
  76. package/src/assets/img/indeterminate_check_box_fill.svg +10 -0
  77. package/src/assets/img/info_24_ 1d4ed8.svg +3 -0
  78. package/src/assets/img/info_24_ 2c6441.svg +3 -0
  79. package/src/assets/img/marker_check_by_default.svg +10 -0
  80. package/src/assets/img/marker_check_by_default_fill.svg +10 -0
  81. package/src/assets/img/minus-accordion.svg +5 -0
  82. package/src/assets/img/minus.svg +3 -0
  83. package/src/assets/img/open.svg +1 -0
  84. package/src/assets/img/pill-white.svg +7 -0
  85. package/src/assets/img/pill.svg +5 -0
  86. package/src/assets/img/plus-accordion.svg +5 -0
  87. package/src/assets/img/plus.svg +4 -0
  88. package/src/assets/img/prescription.svg +6 -0
  89. package/src/assets/img/search.svg +10 -0
  90. package/src/assets/img/search_icon_light.svg +10 -0
  91. package/src/assets/img/separator.svg +3 -0
  92. package/src/assets/img/stethoscope-white.svg +8 -0
  93. package/src/assets/img/stethoscope.svg +8 -0
  94. package/src/assets/img/thumb_up.svg +10 -0
  95. package/src/assets/img/vector.svg +3 -0
  96. package/src/assets/img/warning-badge-disabled.svg +11 -0
  97. package/src/assets/img/warning-badge-green.svg +11 -0
  98. package/src/assets/img/warning-badge-red.svg +11 -0
  99. package/src/assets/img/warning-badge-yellow.svg +11 -0
  100. package/src/assets/img/warning.svg +10 -0
  101. package/src/global.d.ts +13 -0
  102. package/{index.d.ts → src/index.ts} +13 -5
  103. package/src/lib/Accordian--Accordian.stories.tsx +312 -0
  104. package/src/lib/Accordion.spec.tsx +384 -0
  105. package/src/lib/Accordion.tsx +240 -0
  106. package/src/lib/AppointmentPicker.spec.tsx +138 -0
  107. package/src/lib/AppointmentPicker.tsx +97 -0
  108. package/src/lib/Badge--Badge.stories.tsx +60 -0
  109. package/src/lib/Badge.spec.tsx +70 -0
  110. package/src/lib/Badge.tsx +87 -0
  111. package/src/lib/Breadcrumbs-Breadcrumbs.stories.tsx +114 -0
  112. package/src/lib/Breadcrumbs.spec.tsx +218 -0
  113. package/src/lib/Breadcrumbs.tsx +219 -0
  114. package/src/lib/Button--Button.stories.tsx +220 -0
  115. package/src/lib/Button.spec.tsx +241 -0
  116. package/src/lib/Button.tsx +121 -0
  117. package/src/lib/ButtonGroup--ButtonGroup.stories.tsx +129 -0
  118. package/src/lib/ButtonGroup.spec.tsx +89 -0
  119. package/src/lib/ButtonGroup.tsx +107 -0
  120. package/src/lib/Card--Card.stories.tsx +113 -0
  121. package/src/lib/Card.spec.tsx +112 -0
  122. package/src/lib/Card.tsx +69 -0
  123. package/src/lib/CharacterCounter--CharacterCounter.stories.tsx +169 -0
  124. package/src/lib/CharacterCounter.spec.tsx +123 -0
  125. package/src/lib/CharacterCounter.tsx +56 -0
  126. package/src/lib/CheckBox--CheckBox.stories.tsx +107 -0
  127. package/src/lib/CheckBox.spec.tsx +412 -0
  128. package/src/lib/CheckBox.tsx +491 -0
  129. package/src/lib/DatePicker--DatePicker.stories.tsx +228 -0
  130. package/src/lib/DatePicker.spec.tsx +424 -0
  131. package/src/lib/DatePicker.tsx +247 -0
  132. package/src/lib/Input--Input.stories.tsx +449 -0
  133. package/src/lib/Input.spec.tsx +281 -0
  134. package/src/lib/Input.tsx +309 -0
  135. package/src/lib/List--List.stories.tsx +157 -0
  136. package/src/lib/List.spec.tsx +211 -0
  137. package/src/lib/List.tsx +93 -0
  138. package/src/lib/Modal--Modal.stories.tsx +454 -0
  139. package/src/lib/Modal.spec.tsx +202 -0
  140. package/src/lib/Modal.tsx +220 -0
  141. package/src/lib/Pill--Pill.stories.tsx +98 -0
  142. package/src/lib/Pill.spec.tsx +103 -0
  143. package/src/lib/Pill.tsx +91 -0
  144. package/src/lib/ProgressBar.spec.tsx +106 -0
  145. package/src/lib/ProgressBar.tsx +112 -0
  146. package/src/lib/RadioGroup.spec.tsx +84 -0
  147. package/src/lib/RadioGroup.tsx +74 -0
  148. package/src/lib/RadioIcon.tsx +13 -0
  149. package/src/lib/Search--Search.stories.tsx +67 -0
  150. package/src/lib/Search.spec.tsx +182 -0
  151. package/src/lib/Search.tsx +304 -0
  152. package/src/lib/SearchContent.tsx +51 -0
  153. package/src/lib/SectionHeader--SectionHeader.stories.tsx +98 -0
  154. package/src/lib/SectionHeader.spec.tsx +60 -0
  155. package/src/lib/SectionHeader.tsx +91 -0
  156. package/src/lib/Select--Select.stories.tsx +387 -0
  157. package/src/lib/Select.spec.tsx +493 -0
  158. package/src/lib/Select.tsx +311 -0
  159. package/src/lib/Shield--Shield.stories.tsx +196 -0
  160. package/src/lib/Shield.spec.tsx +275 -0
  161. package/src/lib/Shield.tsx +239 -0
  162. package/src/lib/SideBarNav--SideBarNav.stories.tsx +136 -0
  163. package/src/lib/SideBarNav.spec.tsx +178 -0
  164. package/src/lib/SideBarNav.tsx +135 -0
  165. package/src/lib/Skeleton--Skeleton.stories.tsx +77 -0
  166. package/src/lib/Skeleton.module.css +16 -0
  167. package/src/lib/Skeleton.spec.tsx +83 -0
  168. package/src/lib/Skeleton.tsx +103 -0
  169. package/src/lib/SkipLink.spec.tsx +76 -0
  170. package/src/lib/SkipLink.tsx +48 -0
  171. package/src/lib/Slider--Slider.stories.tsx +108 -0
  172. package/src/lib/Slider.module.css +109 -0
  173. package/src/lib/Slider.spec.tsx +67 -0
  174. package/src/lib/Slider.tsx +101 -0
  175. package/src/lib/Status--Status.stories.tsx +93 -0
  176. package/src/lib/Status.spec.tsx +118 -0
  177. package/src/lib/Status.tsx +79 -0
  178. package/src/lib/Tabs--Tabs.stories.tsx +294 -0
  179. package/src/lib/Tabs.spec.tsx +249 -0
  180. package/src/lib/Tabs.tsx +188 -0
  181. package/src/lib/Tester.spec.tsx +17 -0
  182. package/src/lib/Toggle--Toggle.stories.tsx +162 -0
  183. package/src/lib/Toggle.spec.tsx +122 -0
  184. package/src/lib/Toggle.tsx +96 -0
  185. package/src/lib/Tooltip--Tooltip.stories.tsx +315 -0
  186. package/src/lib/Tooltip.spec.tsx +307 -0
  187. package/src/lib/Tooltip.tsx +137 -0
  188. package/src/lib/bak-simple-ui.stories.tsx-bak +24 -0
  189. package/src/styles.css +190 -0
  190. package/tsconfig.json +25 -0
  191. package/tsconfig.lib.json +42 -0
  192. package/tsconfig.spec.json +29 -0
  193. package/tsconfig.storybook.json +36 -0
  194. package/vite.config.mts +87 -0
  195. package/vitest.setup.ts +12 -0
  196. package/index.css +0 -1
  197. package/index.js +0 -35
  198. package/index.mjs +0 -4981
  199. package/lib/Accordion.d.ts +0 -36
  200. package/lib/AppointmentPicker.d.ts +0 -21
  201. package/lib/Badge.d.ts +0 -11
  202. package/lib/Breadcrumbs.d.ts +0 -13
  203. package/lib/Button.d.ts +0 -15
  204. package/lib/ButtonGroup.d.ts +0 -8
  205. package/lib/Card.d.ts +0 -11
  206. package/lib/CharacterCounter.d.ts +0 -11
  207. package/lib/CheckBox.d.ts +0 -30
  208. package/lib/DatePicker.d.ts +0 -7
  209. package/lib/Input.d.ts +0 -16
  210. package/lib/List.d.ts +0 -22
  211. package/lib/Modal.d.ts +0 -18
  212. package/lib/Pill.d.ts +0 -13
  213. package/lib/ProgressBar.d.ts +0 -19
  214. package/lib/RadioGroup.d.ts +0 -15
  215. package/lib/Search.d.ts +0 -26
  216. package/lib/SearchContent.d.ts +0 -6
  217. package/lib/SectionHeader.d.ts +0 -18
  218. package/lib/Select.d.ts +0 -19
  219. package/lib/Shield.d.ts +0 -12
  220. package/lib/SideBarNav.d.ts +0 -21
  221. package/lib/Skeleton.d.ts +0 -15
  222. package/lib/SkipLink.d.ts +0 -22
  223. package/lib/Slider.d.ts +0 -14
  224. package/lib/Status.d.ts +0 -10
  225. package/lib/Tabs.d.ts +0 -23
  226. package/lib/Toggle.d.ts +0 -11
  227. package/lib/Tooltip.d.ts +0 -14
@@ -0,0 +1,315 @@
1
+ import { Meta, StoryObj, StoryFn } from '@storybook/react';
2
+ import { expect, fn, userEvent, within } from 'storybook/test';
3
+ import React, { Component, ReactNode, useState } from 'react';
4
+ import { Tooltip, TooltipContent } from './Tooltip';
5
+
6
+ // reusable helper function to test that tooltip is and is not present as appropriate
7
+ async function defaultPlay(canvasElement: HTMLElement) {
8
+ const canvas = within(canvasElement);
9
+
10
+ // Find the Paragraph inside the Tooltip component
11
+ // NOTE: Keeping the explicit timeouts as per the original code
12
+ await new Promise((resolve) => setTimeout(resolve, 250));
13
+ const paragraph = canvas.getByRole('status'); // Assuming 'status' is the correct role
14
+
15
+ // confirm text 'I am a tooltip' is not visible
16
+ expect(canvas.queryByText('I am a tooltip!')).toBeNull();
17
+
18
+ // Check initial paragraph text
19
+ // await new Promise((resolve) => setTimeout(resolve, 250));
20
+ expect(paragraph).toHaveTextContent("I am a paragraph");
21
+
22
+ // Hover over the paragraph
23
+ await new Promise((resolve) => setTimeout(resolve, 250));
24
+ await userEvent.hover(paragraph);
25
+
26
+ expect(canvas.queryByText('I am a tooltip!')).not.toBeNull();
27
+ }
28
+
29
+ export default {
30
+ title: 'Components/Tooltip',
31
+ component: Tooltip,
32
+ parameters: {
33
+ // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout
34
+ layout: 'centered',
35
+ },
36
+ argTypes: {
37
+ className: { control: 'text' },
38
+ thumbClassName: { control: 'text' },
39
+ defaultChecked: { control: 'boolean' },
40
+ disabled: { control: 'boolean' },
41
+ },
42
+ } as Meta<typeof Tooltip>;
43
+
44
+
45
+ export const Default: StoryFn = () => {
46
+
47
+ return (
48
+ <div className="grid mx-auto w-1/2 p-2 border-2 rounded-sm">
49
+ <Tooltip>
50
+ <p role="status">I am a paragraph. I am a paragraph. I am a paragraph. I am a paragraph. I am a paragraph.
51
+ I am a paragraph. I am a paragraph. I am a paragraph. I am a paragraph.</p>
52
+ <p>I'm another paragraph!</p>
53
+ <TooltipContent>I am a tooltip! <p>I'm a paragraph inside the tooltip!</p></TooltipContent>
54
+ </Tooltip>
55
+ </div>
56
+ );
57
+ };
58
+
59
+ Default.play = async ({ canvasElement }) => {
60
+ const canvas = within(canvasElement);
61
+
62
+ // Find the Paragraph inside the Tooltip component
63
+ // NOTE: Keeping the explicit timeouts as per the original code
64
+ await new Promise((resolve) => setTimeout(resolve, 250));
65
+ const paragraph = canvas.getByRole('status'); // Assuming 'status' is the correct role
66
+
67
+ // confirm text 'I am a tooltip' is not visible
68
+ expect(canvas.queryByText('I am a tooltip!')).toBeNull();
69
+
70
+ // Check initial paragraph text
71
+ // await new Promise((resolve) => setTimeout(resolve, 250));
72
+ expect(paragraph).toHaveTextContent("I am a paragraph");
73
+
74
+ // Hover over the paragraph
75
+ await new Promise((resolve) => setTimeout(resolve, 250));
76
+ await userEvent.hover(paragraph);
77
+
78
+ expect(canvas.queryByText('I am a tooltip!')).not.toBeNull();
79
+
80
+ // Move mouse out ...
81
+ await new Promise((resolve) => setTimeout(resolve, 250));
82
+ await userEvent.unhover(paragraph);
83
+ expect(canvas.queryByText('I am a tooltip!')).toBeNull();
84
+
85
+ };
86
+
87
+
88
+ export const South: StoryFn = () => {
89
+
90
+ return (
91
+ <div className="grid mx-auto w-1/2 p-2 border-2 rounded-sm">
92
+ <Tooltip>
93
+ <p role="status">I am a paragraph. I am a paragraph. I am a paragraph. I am a paragraph. I am a paragraph.
94
+ I am a paragraph. I am a paragraph. I am a paragraph. I am a paragraph.</p>
95
+ <TooltipContent
96
+ position='s'
97
+ className='bg-blue-200 border-2 border-blue-500 text-blue-700'
98
+ >I am a tooltip!</TooltipContent>
99
+ </Tooltip>
100
+ </div>
101
+ );
102
+ };
103
+
104
+ South.play = async ({ canvasElement }) => {
105
+ defaultPlay(canvasElement);
106
+ };
107
+
108
+ export const North: StoryFn = () => {
109
+
110
+ return (
111
+ <div className="grid mx-auto w-1/2 p-2 border-2 rounded-sm">
112
+ <Tooltip>
113
+ <p role="status">I am a paragraph. I am a paragraph. I am a paragraph. I am a paragraph. I am a paragraph.
114
+ I am a paragraph. I am a paragraph. I am a paragraph. I am a paragraph.</p>
115
+ <TooltipContent position='n'>I am a tooltip!</TooltipContent>
116
+ </Tooltip>
117
+ </div>
118
+ );
119
+ };
120
+
121
+ North.play = async ({ canvasElement }) => {
122
+ defaultPlay(canvasElement);
123
+ };
124
+
125
+
126
+
127
+ export const NorthWest: StoryFn = () => {
128
+
129
+ return (
130
+ <div className="grid mx-auto w-1/2 p-2 border-2 rounded-sm">
131
+ <Tooltip>
132
+ <p role="status">I am a paragraph. I am a paragraph. I am a paragraph. I am a paragraph. I am a paragraph.
133
+ I am a paragraph. I am a paragraph. I am a paragraph. I am a paragraph.</p>
134
+ <TooltipContent position='nw'>I am a tooltip!</TooltipContent>
135
+ </Tooltip>
136
+ </div>
137
+ );
138
+ };
139
+
140
+ NorthWest.play = async ({ canvasElement }) => {
141
+ defaultPlay(canvasElement);
142
+ };
143
+
144
+ export const NorthEast: StoryFn = () => {
145
+
146
+ return (
147
+ <div className="grid mx-auto w-1/2 p-2 border-2 rounded-sm">
148
+ <Tooltip>
149
+ <p role="status">I am a paragraph. I am a paragraph. I am a paragraph. I am a paragraph. I am a paragraph.
150
+ I am a paragraph. I am a paragraph. I am a paragraph. I am a paragraph.</p>
151
+ <TooltipContent position='ne'>I am a tooltip!</TooltipContent>
152
+ </Tooltip>
153
+ </div>
154
+ );
155
+ };
156
+
157
+ NorthEast.play = async ({ canvasElement }) => {
158
+ defaultPlay(canvasElement);
159
+ };
160
+
161
+ export const SouthEast: StoryFn = () => {
162
+
163
+ return (
164
+ <div className="grid mx-auto w-1/2 p-2 border-2 rounded-sm">
165
+ <Tooltip>
166
+ <p role="status">I am a paragraph. I am a paragraph. I am a paragraph. I am a paragraph. I am a paragraph.
167
+ I am a paragraph. I am a paragraph. I am a paragraph. I am a paragraph.</p>
168
+ <TooltipContent position='se'>I am a tooltip!</TooltipContent>
169
+ </Tooltip>
170
+ </div>
171
+ );
172
+ };
173
+
174
+ SouthEast.play = async ({ canvasElement }) => {
175
+ defaultPlay(canvasElement);
176
+ };
177
+
178
+ export const West: StoryFn = () => {
179
+
180
+ return (
181
+ <div className="grid mx-auto w-1/2 p-2 border-2 rounded-sm">
182
+ <Tooltip>
183
+ <p role="status">I am a paragraph. I am a paragraph. I am a paragraph. I am a paragraph. I am a paragraph.
184
+ I am a paragraph. I am a paragraph. I am a paragraph. I am a paragraph.</p>
185
+ <TooltipContent position='w'>I am a tooltip!</TooltipContent>
186
+ </Tooltip>
187
+ </div>
188
+ );
189
+ };
190
+
191
+ West.play = async ({ canvasElement }) => {
192
+ defaultPlay(canvasElement);
193
+ };
194
+
195
+ export const East: StoryFn = () => {
196
+
197
+ return (
198
+ <div className="grid mx-auto w-1/2 p-2 border-2 rounded-sm">
199
+ <Tooltip>
200
+ <p role="status">I am a paragraph. I am a paragraph. I am a paragraph. I am a paragraph. I am a paragraph.
201
+ I am a paragraph. I am a paragraph. I am a paragraph. I am a paragraph.</p>
202
+ <TooltipContent position='e'>I am a tooltip!</TooltipContent>
203
+ </Tooltip>
204
+ </div>
205
+ );
206
+ };
207
+
208
+ East.play = async ({ canvasElement }) => {
209
+ defaultPlay(canvasElement);
210
+ };
211
+
212
+ export const Center: StoryFn = () => {
213
+
214
+ return (
215
+ <div className="grid mx-auto w-1/2 p-2 border-2 rounded-sm">
216
+ <Tooltip>
217
+ <p role="status">I am a paragraph. I am a paragraph. I am a paragraph. I am a paragraph. I am a paragraph.
218
+ I am a paragraph. I am a paragraph. I am a paragraph. I am a paragraph.</p>
219
+ <TooltipContent position='c'>I am a tooltip!</TooltipContent>
220
+ </Tooltip>
221
+ </div>
222
+ );
223
+ };
224
+
225
+ Center.play = async ({ canvasElement }) => {
226
+ defaultPlay(canvasElement);
227
+ };
228
+
229
+
230
+ export const Content: StoryFn = () => {
231
+
232
+ return (
233
+ <div className="grid mx-auto w-1/2 p-2 border-2 rounded-sm">
234
+ <Tooltip>
235
+ <p role="status">I am a paragraph. I am a paragraph. I am a paragraph. I am a paragraph. I am a paragraph.
236
+ I am a paragraph. I am a paragraph. I am a paragraph. I am a paragraph.
237
+ </p>
238
+ I'm not a part of an element.
239
+ <TooltipContent>I am a tooltip! <p>I'm a paragraph inside the tooltip!</p></TooltipContent>
240
+ </Tooltip>
241
+ </div>
242
+ );
243
+ };
244
+
245
+
246
+ export const MultiContentElements: StoryFn = () => {
247
+
248
+ return (
249
+ <div className="grid mx-auto w-1/2 p-2 border-2 rounded-sm">
250
+ <Tooltip>
251
+ <>
252
+ <p role="status">I am a paragraph. I am a paragraph. I am a paragraph. I am a paragraph. I am a paragraph.
253
+ I am a paragraph. I am a paragraph. I am a paragraph. I am a paragraph.
254
+ </p>
255
+ <p>I'm a 2nd paragraph inside the tooltip</p>
256
+ </>
257
+ <TooltipContent>I am a tooltip! <p>I'm a paragraph inside the tooltip!</p></TooltipContent>
258
+ </Tooltip>
259
+ </div>
260
+ );
261
+ };
262
+
263
+
264
+ export const TooltipContentError: StoryFn = () => {
265
+
266
+ return (
267
+ <ErrorBoundary>
268
+ <div className="grid mx-auto w-1/2 p-2 border-2 rounded-sm">
269
+ <TooltipContent>I am a tooltip! <p>I'm a paragraph inside the tooltip!</p></TooltipContent>
270
+ </div>
271
+ </ErrorBoundary>
272
+ );
273
+ };
274
+
275
+
276
+
277
+
278
+
279
+
280
+ // Error Boundary definition for Error Checking
281
+
282
+ interface ErrorBoundaryProps {
283
+ children: ReactNode;
284
+ }
285
+
286
+ interface ErrorBoundaryState {
287
+ hasError: boolean;
288
+ error: Error | null;
289
+ }
290
+
291
+
292
+ class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {
293
+ constructor(props: ErrorBoundaryProps) {
294
+ super(props);
295
+ this.state = { hasError: false, error: null };
296
+ }
297
+
298
+ static getDerivedStateFromError(error: Error) {
299
+ // Update state so the next render shows the fallback UI.
300
+ return { hasError: true, error };
301
+ }
302
+
303
+ componentDidCatch(error: Error, errorInfo: any) {
304
+ // You can log the error to an error reporting service here.
305
+ console.error("Error caught in ErrorBoundary:", error, errorInfo);
306
+ }
307
+
308
+ render() {
309
+ if (this.state.hasError) {
310
+ return <div>Error: {this.state.error?.message}</div>;
311
+ }
312
+
313
+ return this.props.children;
314
+ }
315
+ }
@@ -0,0 +1,307 @@
1
+ import { render, screen, fireEvent, waitFor } from '@testing-library/react';
2
+ import { vi } from 'vitest';
3
+ // import '@testing-library/jest-dom';
4
+ import { axe } from "vitest-axe";
5
+ import { Tooltip, TooltipContent, TooltipProps, TooltipContentProps } from './Tooltip';
6
+ import { cp } from 'fs';
7
+ import { createRef } from 'react';
8
+
9
+ // getByText is synchronous
10
+ // findByText is asynchronous
11
+
12
+ describe('Tooltip Component', () => {
13
+
14
+ const renderComponent = (props: Partial<TooltipProps | TooltipContentProps> = {}) =>
15
+ render(
16
+ <Tooltip>
17
+ <button>Hover me</button>
18
+ <TooltipContent>Tooltip text</TooltipContent>
19
+ </Tooltip>
20
+ );
21
+
22
+ // PASSING
23
+ it('should render the trigger element', () => {
24
+ renderComponent();
25
+
26
+ expect(screen.getByText('Hover me')).toBeInTheDocument();
27
+ expect(screen.queryByText('Tooltip text')).not.toBeInTheDocument();
28
+ });
29
+
30
+ // PASSING
31
+ it('should have no accessibility violations when tooltip is not visible', async () => {
32
+ const { container } = renderComponent()
33
+
34
+ expect(screen.getByText('Hover me')).toBeInTheDocument();
35
+ expect(screen.queryByText('Tooltip text')).not.toBeInTheDocument();
36
+
37
+ const accessibilityResults = await axe(container);
38
+ expect(accessibilityResults).toHaveNoViolations();
39
+ });
40
+
41
+ // PASSING - USES A RE-RENDER TO FIND COMPONENT AND TEST
42
+ it('should show tooltip content on hover', async () => {
43
+
44
+ renderComponent();
45
+ const triggerButton = screen.getByText('Hover me');
46
+ const tooltipContainer = triggerButton.parentElement as HTMLElement;
47
+
48
+ fireEvent.mouseEnter(tooltipContainer);
49
+ await new Promise((resolve) => setTimeout(resolve, 100));
50
+ expect(screen.getByText('Tooltip text')).toBeInTheDocument();
51
+ });
52
+
53
+ // PASSING
54
+ it('should show tooltip content after specified delay', async () => {
55
+ render(
56
+ <Tooltip delay={250}>
57
+ <button>Hover me</button>
58
+ <TooltipContent>Tooltip text</TooltipContent>
59
+ </Tooltip>
60
+ );
61
+
62
+ const triggerButton = screen.getByText('Hover me');
63
+ const tooltipContainer = triggerButton.parentElement as HTMLElement;
64
+
65
+ fireEvent.mouseEnter(tooltipContainer);
66
+
67
+ const toolTipEl = await screen.findByText('Tooltip text', {}, {timeout: 300});
68
+ expect(toolTipEl).toBeInTheDocument();
69
+ });
70
+
71
+ // PASSING
72
+ it('should hide tooltip content on mouse leave', async () => {
73
+ renderComponent();
74
+
75
+ const tooltipContainer = screen.getByText('Hover me').parentElement as HTMLElement;
76
+
77
+ // Show tooltip
78
+ fireEvent.mouseEnter(tooltipContainer);
79
+
80
+ const toolTipEl = await screen.findByText('Tooltip text', {}, {timeout: 300});
81
+ expect(toolTipEl).toBeInTheDocument();
82
+
83
+ // Hide tooltip
84
+ fireEvent.mouseLeave(tooltipContainer);
85
+
86
+ expect(toolTipEl).not.toBeInTheDocument();
87
+ });
88
+
89
+ // PASSING
90
+ it('should show tooltip on focus and hide on blur for keyboard accessibility', async () => {
91
+ renderComponent();
92
+
93
+ const tooltipContainer = screen.getByText('Hover me').parentElement as HTMLElement;
94
+
95
+ // Show tooltip on focus
96
+ fireEvent.focus(tooltipContainer);
97
+
98
+ const toolTipEl = await screen.findByText('Tooltip text', {}, {timeout: 300});
99
+ expect(toolTipEl).toBeInTheDocument();
100
+
101
+ // Hide tooltip on blur
102
+ fireEvent.blur(tooltipContainer);
103
+
104
+ expect(screen.queryByText('Tooltip text')).not.toBeInTheDocument();
105
+ });
106
+
107
+ // PASSING
108
+ it('should apply custom className to Tooltip wrapper', () => {
109
+ render(
110
+ <Tooltip className="custom-tooltip-class">
111
+ <button>Hover me</button>
112
+ <TooltipContent>Tooltip text</TooltipContent>
113
+ </Tooltip>
114
+ );
115
+
116
+ const tooltipContainer = screen.getByText('Hover me').parentElement as HTMLElement;
117
+ expect(tooltipContainer).toHaveClass('custom-tooltip-class');
118
+ });
119
+
120
+ it('renders a tooltip with invalid children', () => {
121
+ expect(() => render(
122
+ <>
123
+ <TooltipContent/>
124
+ <Tooltip children={false} className="custom-tooltip-class"/>
125
+ </>
126
+ )).toThrowError();
127
+ });
128
+
129
+ // PASSING
130
+ it('should pass accessibility audit when open', async () => {
131
+ const { container } = renderComponent()
132
+
133
+ const triggerButton = screen.getByText('Hover me');
134
+ const tooltipContainer = triggerButton.parentElement as HTMLElement;
135
+
136
+ fireEvent.focus(triggerButton);
137
+ fireEvent.mouseEnter(tooltipContainer);
138
+
139
+ const toolTipEl = await screen.findByText('Tooltip text', {}, {timeout: 300});
140
+ expect(toolTipEl).toBeInTheDocument();
141
+
142
+ expect(screen.getByText('Tooltip text')).toBeInTheDocument();
143
+
144
+ const results = await axe(container);
145
+ expect(results).toHaveNoViolations();
146
+ });
147
+
148
+
149
+ it('should handle multiple children correctly', async () => {
150
+ render(
151
+ <Tooltip>
152
+ <div>
153
+ <button>Hover me</button>
154
+ <span>Extra content</span>
155
+ </div>
156
+ <TooltipContent>Tooltip text</TooltipContent>
157
+ </Tooltip>
158
+ );
159
+
160
+ expect(screen.getByText('Hover me')).toBeInTheDocument();
161
+ expect(screen.getByText('Extra content')).toBeInTheDocument();
162
+ expect(screen.queryByText('Tooltip text')).not.toBeInTheDocument();
163
+
164
+ fireEvent.mouseEnter(screen.getByText('Hover me').closest('div')?.parentElement as HTMLElement);
165
+
166
+ const toolTipEl = await screen.findByText('Tooltip text', {}, {timeout: 300});
167
+ expect(toolTipEl).toBeInTheDocument();
168
+ });
169
+ });
170
+
171
+
172
+ /*
173
+ const toolTipEl = await screen.findByText('Tooltip text', {}, {timeout: 300});
174
+ expect(toolTipEl).toBeInTheDocument();
175
+
176
+ expect(screen.getByText('Tooltip text')).toBeInTheDocument();
177
+
178
+ const results = await axe(container);
179
+ expect(results).toHaveNoViolations();
180
+ */
181
+
182
+ describe('TooltipContent Component', () => {
183
+
184
+ it('should apply position classes based on position prop', async () => {
185
+ const { rerender } = render(<Tooltip><p>Hover me</p><TooltipContent position="n">North tooltip</TooltipContent></Tooltip>);
186
+
187
+ // Test Northern position
188
+ const triggerButton = screen.getByText('Hover me');
189
+ const tooltipContainer = triggerButton.parentElement as HTMLElement;
190
+
191
+ fireEvent.focus(triggerButton);
192
+ fireEvent.mouseEnter(tooltipContainer);
193
+
194
+ let toolTipEl = await screen.findByText('North tooltip', {}, {timeout: 300});
195
+ expect(screen.getByText('North tooltip')).toHaveClass('left-1/2');
196
+ expect(screen.getByText('North tooltip')).toHaveClass('-translate-y-full');
197
+
198
+ // Test Southern position
199
+ rerender(<Tooltip><p>Hover me</p><TooltipContent position="s">South tooltip</TooltipContent></Tooltip>);
200
+
201
+ fireEvent.focus(triggerButton);
202
+ fireEvent.mouseEnter(tooltipContainer);
203
+
204
+ toolTipEl = await screen.findByText('South tooltip', {}, {timeout: 300});
205
+ expect(screen.getByText('South tooltip')).toHaveClass('top-full');
206
+ expect(screen.getByText('South tooltip')).toHaveClass('-translate-x-1/2');
207
+
208
+
209
+ // Test East position
210
+ rerender(<Tooltip><p>Hover me</p><TooltipContent position="e">East tooltip</TooltipContent></Tooltip>);
211
+
212
+ fireEvent.focus(triggerButton);
213
+ fireEvent.mouseEnter(tooltipContainer);
214
+
215
+ toolTipEl = await screen.findByText('East tooltip', {}, {timeout: 300});
216
+ expect(screen.getByText('East tooltip')).toHaveClass('-right-2');
217
+ expect(screen.getByText('East tooltip')).toHaveClass('translate-x-full');
218
+
219
+ // Test West position
220
+ rerender(<Tooltip><p>Hover me</p><TooltipContent position="w">West tooltip</TooltipContent></Tooltip>);
221
+
222
+ fireEvent.focus(triggerButton);
223
+ fireEvent.mouseEnter(tooltipContainer);
224
+
225
+ toolTipEl = await screen.findByText('West tooltip', {}, {timeout: 300});
226
+ expect(screen.getByText('West tooltip')).toHaveClass('-translate-x-full');
227
+ expect(screen.getByText('West tooltip')).toHaveClass('-left-2');
228
+
229
+ // Test NW Position
230
+ rerender(<Tooltip><p>Hover me</p><TooltipContent position="nw">NW tooltip</TooltipContent></Tooltip>);
231
+
232
+ fireEvent.focus(triggerButton);
233
+ fireEvent.mouseEnter(tooltipContainer);
234
+
235
+ toolTipEl = await screen.findByText('NW tooltip', {}, {timeout: 300});
236
+ expect(screen.getByText('NW tooltip')).toHaveClass('left-0');
237
+ expect(screen.getByText('NW tooltip')).toHaveClass('-translate-y-full');
238
+
239
+ // Test SE Position
240
+ rerender(<Tooltip><p>Hover me</p><TooltipContent position="se">SE tooltip</TooltipContent></Tooltip>);
241
+
242
+ fireEvent.focus(triggerButton);
243
+ fireEvent.mouseEnter(tooltipContainer);
244
+
245
+ toolTipEl = await screen.findByText('SE tooltip', {}, {timeout: 300});
246
+ expect(screen.getByText('SE tooltip')).toHaveClass('right-0');
247
+ expect(screen.getByText('SE tooltip')).toHaveClass('top-full');
248
+
249
+ // Test NE Position
250
+ rerender(<Tooltip><p>Hover me</p><TooltipContent position="ne">NE tooltip</TooltipContent></Tooltip>);
251
+
252
+ fireEvent.focus(triggerButton);
253
+ fireEvent.mouseEnter(tooltipContainer);
254
+
255
+ toolTipEl = await screen.findByText('NE tooltip', {}, {timeout: 300});
256
+ expect(screen.getByText('NE tooltip')).toHaveClass('right-0');
257
+ expect(screen.getByText('NE tooltip')).toHaveClass('-translate-y-full');
258
+
259
+ // Test Center Position
260
+ rerender(<Tooltip><p>Hover me</p><TooltipContent position="c">Center tooltip</TooltipContent></Tooltip>);
261
+
262
+ fireEvent.focus(triggerButton);
263
+ fireEvent.mouseEnter(tooltipContainer);
264
+
265
+ toolTipEl = await screen.findByText('Center tooltip', {}, {timeout: 300});
266
+ expect(screen.getByText('Center tooltip')).toHaveClass('left-1/2');
267
+ expect(screen.getByText('Center tooltip')).toHaveClass('top-1/2');
268
+ });
269
+
270
+ it('should apply custom className to TooltipContent', async () => {
271
+ render(
272
+ <Tooltip><p>Hover me</p><TooltipContent className="custom-content-class">Tooltip content</TooltipContent></Tooltip>
273
+ );
274
+ fireEvent.mouseEnter(screen.getByText('Hover me').closest('p')?.parentElement as HTMLElement);
275
+ const toolTipEl = await screen.findByText('Tooltip content', {}, {timeout: 300});
276
+ expect(toolTipEl).toHaveClass('custom-content-class');
277
+ });
278
+
279
+ // PASSING
280
+ it('should have proper ARIA attributes for accessibility', async () => {
281
+ render(<Tooltip><p>Hover me</p><TooltipContent>Accessible tooltip</TooltipContent></Tooltip>);
282
+
283
+ // hover over paragraph
284
+ fireEvent.mouseEnter(screen.getByText('Hover me').closest('p')?.parentElement as HTMLElement);
285
+
286
+ const tooltip = await screen.findByRole('tooltip');
287
+ expect(tooltip).toBeInTheDocument();
288
+ expect(tooltip).toHaveTextContent('Accessible tooltip');
289
+ });
290
+
291
+ it('should pass accessibility audit', async () => {
292
+ const { container } = render(
293
+ <Tooltip><p>Hover me</p><TooltipContent>Tooltip content</TooltipContent></Tooltip>
294
+ );
295
+
296
+ const triggerButton = screen.getByText('Hover me');
297
+ const tooltipContainer = triggerButton.parentElement as HTMLElement;
298
+
299
+ const p = await screen.findByText('Hover me', {}, {timeout: 300});
300
+ expect(p).toBeInTheDocument();
301
+
302
+ const results = await axe(container);
303
+ expect(results).toHaveNoViolations();
304
+ });
305
+
306
+ });
307
+