@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,311 @@
1
+ import { useEffect, useRef, useState, HTMLAttributes } from 'react';
2
+ import { twMerge } from 'tailwind-merge';
3
+ import chevronDown from '../assets/img/chevron-down.svg';
4
+ import chevronUp from '../assets/img/chevron-up.svg';
5
+ import chevronDownWhite from '../assets/img/chevron-down-white.svg';
6
+ import chevronUpWhite from '../assets/img/chevron-up-white.svg';
7
+
8
+ interface VariantType {
9
+ [key: string]: string;
10
+ }
11
+
12
+
13
+ const baseClasses = 'outline-hidden outline-offset-0 flex justify-between items-center w-full h-14 ' +
14
+ 'border focus:outline-4 focus:mb-2 focus:outline-[#fa89f1] shadow-xs pl-4 pr-2 py-2 bg-white ' +
15
+ 'text-base md:text-lg font-medium text-gray-700 hover:bg-gray-50 border-[#b3b3b3] h-12 mt-1 font-["Arial"] ';
16
+
17
+ const variants: VariantType = {
18
+ default: 'hover:bg-gray-200 text-black' +
19
+ 'hover:border-gray-400 disabled:bg-dha-mc-bottom-nav-background disabled:text-dha-mc-checkbox-inactive ' +
20
+ 'disabled:border-dha-mc-secondary-border disabled:border-2',
21
+ fill: 'hover:bg-[#0c2c8e] text-white bg-[#092068] ' +
22
+ 'hover:border-gray-400 disabled:bg-dha-mc-bottom-nav-background disabled:text-dha-mc-checkbox-inactive ' +
23
+ 'disabled:border-dha-mc-secondary-border disabled:border-2 py-3',
24
+ outline: 'bg-white disabled:border-dha-mc-secondary-border ' +
25
+ 'disabled:text-dha-mc-checkbox-inactive',
26
+ };
27
+
28
+ type Option = {
29
+ name: string;
30
+ value?: string;
31
+ };
32
+
33
+ export interface SelectProps extends HTMLAttributes<HTMLDivElement> {
34
+ className?: string;
35
+ classNameContainer?: string;
36
+ label?: string;
37
+ variant?: string;
38
+ options: Option[];
39
+ optionsLabel?: string;
40
+ disabled?: boolean;
41
+ error?: boolean;
42
+ width?: string;
43
+ setSelectedOption: (param: string) => void;
44
+ }
45
+
46
+ export const Select = ({
47
+ className,
48
+ classNameContainer = '',
49
+ label,
50
+ options,
51
+ optionsLabel,
52
+ disabled = false,
53
+ variant = 'default',
54
+ setSelectedOption,
55
+ error = false,
56
+ width = '',
57
+ ...props
58
+ }: SelectProps) => {
59
+
60
+ const containerRef = useRef<HTMLDivElement>(null);
61
+ const buttonRef = useRef<HTMLButtonElement>(null);
62
+ const dropdownRef = useRef<HTMLDivElement>(null); // ref to drop down portion only, determine if should display above/below
63
+ const [isOpen, setIsOpen] = useState(false);
64
+ const [isAbove, setIsAbove] = useState(false); // use to determine/display above/below button depending on avail space
65
+ const [selectedOptionName, setSelectedOptionName] = useState(optionsLabel || 'Options');
66
+ const [selectedIndex, setSelectedIndex] = useState<number | null>(null);
67
+ const buttonHeight = useRef<string>('bottom-[60px]');
68
+ const [errorCss, setErrorCss] = useState<string>('');
69
+ // const [buttonHeight, setButtonHeight] = useState<string>('bottom-[45px]');
70
+ // const [buttonHeight, setButtonHeight] = useState<number | null>(null);
71
+
72
+
73
+ useEffect (() => {
74
+ if (buttonRef.current) {
75
+ const buttonRect = buttonRef.current.getBoundingClientRect();
76
+ // setButtonHeight(buttonRect.height);
77
+ const height = Math.floor(buttonRect.height) + 2;
78
+ buttonHeight.current = 'bottom-[' + height + 'px]';
79
+
80
+
81
+ // console.log('button height: ', buttonHeight.current);
82
+ // setButtonHeight('bottom-[' + Math.floor(buttonRect.height + 4) + 'px]');
83
+ // console.log('button height: ', buttonHeight);
84
+ }
85
+ // eslint-disable-next-line react-hooks/exhaustive-deps
86
+ }, [buttonRef.current]);
87
+
88
+
89
+ // Function to calculate dropdown position
90
+ const updateDropdownPosition = () => {
91
+ if (containerRef.current && dropdownRef.current) {
92
+ const buttonRect = containerRef.current.getBoundingClientRect();
93
+ const dropdownHeight = dropdownRef.current.offsetHeight;
94
+
95
+ // console.log('dropdownHeight: ', dropdownHeight);
96
+
97
+ const windowHeight = window.innerHeight;
98
+ const documentHeight = document.body.offsetHeight;
99
+
100
+ // THIS IS THE SECRET SAUCE!
101
+ const containerHeight = windowHeight > documentHeight ? documentHeight : windowHeight;
102
+
103
+ // console.log('windowHeight: ', windowHeight);
104
+ // console.log('documentHeight: ', documentHeight);
105
+ // console.log('calculated containerHeight: ', containerHeight);
106
+ // console.log('buttonRect.bottom: ', buttonRect.bottom);
107
+
108
+ // Check if there's enough space below the button
109
+ const spaceBelow = containerHeight - buttonRect.bottom;
110
+ if (spaceBelow < dropdownHeight && buttonRect.top > dropdownHeight) {
111
+ setIsAbove(true); // Flip above if not enough space below
112
+ } else {
113
+ setIsAbove(false); // Default to below
114
+ }
115
+ }
116
+ };
117
+
118
+ // array of refs, one for each dropdown item
119
+ const itemRefs = useRef<HTMLButtonElement[]>([]);
120
+
121
+ // We might slice the array in case `options` changes
122
+ useEffect(() => {
123
+ itemRefs.current = itemRefs.current.slice(0, options.length);
124
+ }, [options]);
125
+
126
+ // Track which item is "focused" wrt arrow navigation
127
+ const [focusedIndex, setFocusedIndex] = useState<number>(0);
128
+
129
+ const toggleDropdown = () => {
130
+ setIsOpen(!isOpen);
131
+ setFocusedIndex(0);
132
+ };
133
+
134
+
135
+ // px-4
136
+ // On Error, border-2 border-[#b3b3b3]
137
+ useEffect(() => {
138
+ error ? setErrorCss('border-2 border-[#ff0004] pl-[15px]') : setErrorCss('');
139
+ }, [error]);
140
+
141
+ // Close on click outside, ESC, focus outside, etc.
142
+ useEffect(() => {
143
+
144
+ function checkIfClickedOutside(event: MouseEvent | TouchEvent) {
145
+ if (isOpen && containerRef.current && !containerRef.current.contains(event.target as Node)) {
146
+ setIsOpen(false);
147
+ }
148
+ }
149
+ function handleKeyDown(event: KeyboardEvent) {
150
+ if (isOpen && event.key === 'Escape') {
151
+ setIsOpen(false);
152
+ }
153
+ }
154
+ function handleFocusIn(event: FocusEvent) {
155
+ if (isOpen && containerRef.current && !containerRef.current.contains(event.target as Node)) {
156
+ setIsOpen(false);
157
+ }
158
+ }
159
+
160
+ document.addEventListener('mousedown', checkIfClickedOutside);
161
+ document.addEventListener('touchend', checkIfClickedOutside);
162
+ document.addEventListener('keydown', handleKeyDown);
163
+ document.addEventListener('focusin', handleFocusIn);
164
+
165
+ return () => {
166
+ document.removeEventListener('mousedown', checkIfClickedOutside);
167
+ document.removeEventListener('touchend', checkIfClickedOutside);
168
+ document.removeEventListener('keydown', handleKeyDown);
169
+ document.removeEventListener('focusin', handleFocusIn);
170
+ };
171
+
172
+ }, [isOpen]);
173
+
174
+ useEffect(() => {
175
+
176
+ if (isOpen) {
177
+ updateDropdownPosition();
178
+ // Optional: Recalculate on window resize
179
+ // window.addEventListener("resize", updateDropdownPosition);
180
+ // return () => window.removeEventListener("resize", updateDropdownPosition);
181
+ }
182
+ }, [isOpen]);
183
+
184
+ // Whenever focusedIndex changes, focus the button at that index
185
+ useEffect(() => {
186
+ if (isOpen) {
187
+ itemRefs.current[focusedIndex]?.focus();
188
+ }
189
+ }, [focusedIndex, isOpen]);
190
+
191
+ // Key Down handler for the "menu" DIV.
192
+ const handleMenuKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {
193
+ if (!isOpen) return;
194
+
195
+ switch (event.key) {
196
+ case 'ArrowDown':
197
+ event.preventDefault();
198
+ setFocusedIndex((prev) => {
199
+ const nextIndex = (prev + 1) % options.length;
200
+ setSelectedIndex(nextIndex);
201
+ return nextIndex;
202
+ });
203
+ break;
204
+ case 'ArrowUp':
205
+ event.preventDefault();
206
+ setFocusedIndex((prev) => {
207
+ const nextIndex = (prev - 1 + options.length) % options.length;
208
+ setSelectedIndex(nextIndex);
209
+ return nextIndex;
210
+ });
211
+ break;
212
+ case 'Home':
213
+ event.preventDefault();
214
+ setFocusedIndex(0);
215
+ break;
216
+ case 'End':
217
+ event.preventDefault();
218
+ setFocusedIndex(options.length - 1);
219
+ break;
220
+ default:
221
+ break;
222
+ }
223
+ };
224
+
225
+
226
+ return (
227
+ <div
228
+ className={twMerge('relative inline-block text-left w-[292px] md:w-[343px] lg:w-[600px] max-w-full', classNameContainer)}
229
+ ref={containerRef}
230
+ {...props}
231
+ aria-disabled={disabled}
232
+ >
233
+ {label ? <p className="text-black mb-2 text-base font-['Arial']">{label}</p> : ''}
234
+
235
+ <button
236
+ onClick={toggleDropdown}
237
+ ref={buttonRef}
238
+ className={twMerge(baseClasses, variants[variant] || variants.default, errorCss, className,
239
+ (isOpen && isAbove && "rounded-b-lg") || (isOpen && !isAbove && 'rounded-t-lg') || 'rounded-lg')}
240
+ aria-label={'Select options - ' + selectedOptionName}
241
+ aria-disabled={disabled}
242
+ aria-expanded={isOpen}
243
+ aria-haspopup='listbox'
244
+ disabled={disabled}
245
+ tabIndex={0}
246
+ >
247
+ {selectedOptionName}
248
+ {isOpen ? variant === 'fill' ? <img src={chevronUpWhite} alt='chevron up' /> : <img src={chevronUp} alt='chevron up' />
249
+ : variant === 'fill' ? <img src={chevronDownWhite} alt='chevron down' /> : <img src={chevronDown} alt='chevron down' /> }
250
+ </button>
251
+
252
+ {isOpen && (
253
+ <div
254
+ role="listbox"
255
+ ref={dropdownRef}
256
+ aria-label="Select option"
257
+ onKeyDown={handleMenuKeyDown}
258
+ className={twMerge("absolute w-full shadow-lg bg-white ring-1 ring-white/5 focus:outline-hidden z-10 ",
259
+ ((isOpen && isAbove) && 'rounded-t-lg pt-1') || ((isOpen && !isAbove) && 'rounded-b-lg pb-1'),
260
+ isAbove && buttonHeight.current)}
261
+ >
262
+ {/* <ul
263
+ className="list"
264
+ role="listbox"
265
+ aria-label="Select option"
266
+ onKeyDown={handleMenuKeyDown}
267
+ > */}
268
+ {options.map((item, index) => (
269
+ // <li key={item.name}>
270
+ <button
271
+ // ref={buttonRef}
272
+ key={item.name}
273
+ ref={el => {
274
+ itemRefs.current[index] = el!
275
+ }} // store ref
276
+ className='block w-full px-4 py-2 text-left text-base text-gray-700
277
+ hover:bg-[#092068]/20 focus:outline-hidden focus:bg-[#092068]/20'
278
+ role="option"
279
+ aria-selected={selectedIndex === index}
280
+ aria-label={'option ' + item.name}
281
+ onClick={() => {
282
+ setSelectedIndex(index);
283
+ setSelectedOptionName(item.name);
284
+ setSelectedOption(item.value || item.name);
285
+ setIsOpen(false);
286
+ }}
287
+ onKeyDown={(event) => {
288
+ if (
289
+ event.key === 'Enter' ||
290
+ event.code === 'Enter' ||
291
+ event.key === ' '
292
+ ) {
293
+ setSelectedIndex(index);
294
+ setSelectedOptionName(item.name);
295
+ setSelectedOption(item.value || item.name);
296
+ setIsOpen(false);
297
+ } else if (event.key === 'Escape') {
298
+ setIsOpen(false);
299
+ }
300
+ }}
301
+ >
302
+ {item.name}
303
+ </button>
304
+ // </li>
305
+ ))}
306
+ {/* </ul> */}
307
+ </div>
308
+ )}
309
+ </div>
310
+ );
311
+ };
@@ -0,0 +1,196 @@
1
+ // Shield.stories.tsx
2
+
3
+ // import { Meta, StoryFn} from '@storybook/react';
4
+ import { Shield } from './Shield';
5
+ import { Meta, StoryContext, StoryFn, StoryObj } from '@storybook/react';
6
+ import { Input, InputProps } from './Input';
7
+ import { CharacterCounter, CharacterCounterProps } from './CharacterCounter';
8
+ import { userEvent, within } from 'storybook/test';
9
+ import { expect } from 'storybook/test';
10
+ import { FC, useRef, useState } from 'react';
11
+
12
+ // Import your images
13
+ import firstAidKit from '../assets/img/first-aid-kit.svg';
14
+ import heartbeat from '../assets/img/heartbeat.svg';
15
+ import pill from '../assets/img/pill.svg';
16
+ import prescription from '../assets/img/prescription.svg';
17
+ import stethoscope from '../assets/img/stethoscope.svg';
18
+ // import { useState } from 'react';
19
+
20
+
21
+ export default {
22
+ title: 'Components/Shield',
23
+ component: Shield,
24
+ argTypes: {
25
+ variant: {
26
+ control: 'select',
27
+ options: ['default', 'icon', 'media']
28
+ },
29
+ subVariant: {
30
+ control: 'select',
31
+ options: ['default', 'gray', 'red', 'green', 'yellow', 'blue'],
32
+ }
33
+ },
34
+ parameters: {
35
+ layout: 'centered',
36
+ backgrounds: { default: 'light' },
37
+ },
38
+ } as Meta<typeof Shield>;
39
+
40
+ // DefaultShield story
41
+ export const DefaultShield = {
42
+ args: {
43
+ variant: 'default',
44
+ subVariant: 'default',
45
+ children: 'Shield!',
46
+ }
47
+ };
48
+
49
+ // DefaultBlueShield story
50
+ export const DefaultBlueShield = {
51
+ args: {
52
+ variant: 'default',
53
+ subVariant: 'blue',
54
+ children: 'Shield!',
55
+ }
56
+ };
57
+
58
+ // DefaultIconShield story
59
+ export const DefaultIconShield = {
60
+ args: {
61
+ variant: 'icon',
62
+ subVariant: 'default',
63
+ children: 'Shield!',
64
+ }
65
+ };
66
+
67
+
68
+ // WarningHalf story
69
+ // export const WarningHalfShield = {
70
+ // args: {
71
+ // variant: 'warning',
72
+ // subVariant: 'half',
73
+ // children: 'Shield!',
74
+ // }
75
+ // };
76
+
77
+ export const WarningHalfShield: StoryFn = () => {
78
+ return (
79
+ <div className='w-[600px] h-[300px] border-l border-r border-slate-300'>
80
+ <Shield variant='warning' subVariant='half'>I am a most excellent Shield, carefully designed by UX.</Shield>
81
+ </div>
82
+ );
83
+ };
84
+
85
+
86
+ export const ShieldTestClose: StoryFn = () => {
87
+ return (
88
+ <div className='w-[600px] h-[300px] border-l border-r border-slate-300'>
89
+ <Shield variant='warning' subVariant='half'>I am a most excellent Shield, carefully designed by UX.</Shield>
90
+ </div>
91
+ );
92
+ };
93
+
94
+
95
+ // Attach the play function after the story definition:
96
+ ShieldTestClose.play = async ({ canvasElement }: StoryContext) => {
97
+ const canvas = within(canvasElement);
98
+
99
+ // Example: Locate an element labeled "Close modal" and click it.
100
+ const closeButton = canvas.getByLabelText('Close modal');
101
+ await userEvent.click(closeButton);
102
+
103
+ expect(closeButton).not.toBeVisible();
104
+ };
105
+
106
+
107
+ export const WarningFullShield: StoryFn = () => {
108
+ return (
109
+ <div className='w-[600px] h-[300px] border-l border-r border-slate-300'>
110
+ <Shield variant='warning' subVariant='full'>
111
+ I am a most excellent Shield, carefully designed by UX, and implemented
112
+ by the SDK Team. For more information, check out the Documentation and
113
+ Usage examples.
114
+ </Shield>
115
+ </div>
116
+ );
117
+ };
118
+
119
+
120
+ export const HazardHalfShield: StoryFn = () => {
121
+ return (
122
+ <div className='w-[600px] h-[300px] border-l border-r border-slate-300'>
123
+ <Shield variant='hazard' subVariant='half'>I am a most excellent Shield, carefully designed by UX.</Shield>
124
+ </div>
125
+ );
126
+ };
127
+
128
+ export const HazardFullShield: StoryFn = () => {
129
+ return (
130
+ <div className='w-[600px] h-[300px] border-l border-r border-slate-300'>
131
+ <Shield variant='hazard' subVariant='full'>
132
+ I am a most excellent Shield, carefully designed by UX, and implemented
133
+ by the SDK Team. For more information, check out the Documentation and
134
+ Usage examples.
135
+ </Shield>
136
+ </div>
137
+ );
138
+ };
139
+
140
+ export const GoHalfShield: StoryFn = () => {
141
+ return (
142
+ <div className='w-[600px] h-[300px] border-l border-r border-slate-300'>
143
+ <Shield variant='go' subVariant='half'>I am a most excellent Shield, carefully designed by UX.</Shield>
144
+ </div>
145
+ );
146
+ };
147
+
148
+ export const GoFullShield: StoryFn = () => {
149
+ return (
150
+ <div className='w-[600px] h-[300px] border-l border-r border-slate-300'>
151
+ <Shield variant='go' subVariant='full'>
152
+ I am a most excellent Shield, carefully designed by UX, and implemented
153
+ by the SDK Team. For more information, check out the Documentation and
154
+ Usage examples.
155
+ </Shield>
156
+ </div>
157
+ );
158
+ };
159
+
160
+
161
+ // GreenIconShield story
162
+ export const GreenIconShield = {
163
+ args: {
164
+ variant: 'icon',
165
+ subVariant: 'green',
166
+ children: 'Shield!',
167
+ }
168
+ };
169
+
170
+ // CustomIconShield story
171
+ export const CustomIconShield = {
172
+ args: {
173
+ variant: 'icon',
174
+ subVariant: 'custom',
175
+ className: 'inline-flex items-center gap-x-1.5 rounded-full bg-red-100 px-2 py-1 text-xs font-medium text-red-600',
176
+ classNameSvg: 'fill-red-400 stroke-red-400',
177
+ children: 'Shield!',
178
+ }
179
+ };
180
+
181
+
182
+ // DefaultMediaShield story
183
+ export const DefaultMediaShield = {
184
+ args: {
185
+ variant: 'media',
186
+ subVariant: 'default',
187
+ children: <>
188
+ <h5 className='text-sm font-bold my-2'>Flu Season!</h5>
189
+ <p>Don't be caught unprepared. </p>
190
+ </>,
191
+ imagePath: 'src/assets/img/heartbeat.svg',
192
+ imageAlt: 'heartbeat icon',
193
+ classNameImage: 'mt-1',
194
+ className: 'pb-2',
195
+ },
196
+ };