@agilant/toga-blox 1.0.8 → 1.0.9

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 (215) hide show
  1. package/assets/cable.jpg +0 -0
  2. package/assets/card-1.jpg +0 -0
  3. package/assets/cat-logo.png +0 -0
  4. package/assets/item.jpg +0 -0
  5. package/assets/map.png +0 -0
  6. package/coverage/base.css +224 -0
  7. package/coverage/block-navigation.js +87 -0
  8. package/coverage/clover.xml +953 -0
  9. package/coverage/coverage-final.json +74 -0
  10. package/coverage/favicon.png +0 -0
  11. package/coverage/index.html +551 -0
  12. package/coverage/prettify.css +1 -0
  13. package/coverage/prettify.js +2 -0
  14. package/coverage/sort-arrow-sprite.png +0 -0
  15. package/coverage/sorter.js +196 -0
  16. package/coverage/toga-blox-npm/index.html +131 -0
  17. package/coverage/toga-blox-npm/postcss.config.js.html +103 -0
  18. package/coverage/toga-blox-npm/src/components/Badge/Badge.stories.tsx.html +793 -0
  19. package/coverage/toga-blox-npm/src/components/Badge/Badge.tsx.html +247 -0
  20. package/coverage/toga-blox-npm/src/components/Badge/index.html +131 -0
  21. package/coverage/toga-blox-npm/src/components/Card/Card.stories.tsx.html +787 -0
  22. package/coverage/toga-blox-npm/src/components/Card/Card.tsx.html +163 -0
  23. package/coverage/toga-blox-npm/src/components/Card/index.html +131 -0
  24. package/coverage/toga-blox-npm/src/components/Card/templates/CategoryCardTemplate.tsx.html +343 -0
  25. package/coverage/toga-blox-npm/src/components/Card/templates/CompassCardTemplate.tsx.html +259 -0
  26. package/coverage/toga-blox-npm/src/components/Card/templates/CounterContentCardTemplate.tsx.html +685 -0
  27. package/coverage/toga-blox-npm/src/components/Card/templates/HorizontalCardTemplate.tsx.html +637 -0
  28. package/coverage/toga-blox-npm/src/components/Card/templates/ItemCardTemplate.tsx.html +550 -0
  29. package/coverage/toga-blox-npm/src/components/Card/templates/KitContentCardTemplate.tsx.html +469 -0
  30. package/coverage/toga-blox-npm/src/components/Card/templates/ShippingAddressCardTemplate.tsx.html +418 -0
  31. package/coverage/toga-blox-npm/src/components/Card/templates/VerticalCardTemplate.tsx.html +592 -0
  32. package/coverage/toga-blox-npm/src/components/Card/templates/index.html +221 -0
  33. package/coverage/toga-blox-npm/src/components/CounterButton/CounterButton.stories.tsx.html +358 -0
  34. package/coverage/toga-blox-npm/src/components/CounterButton/CounterButton.tsx.html +385 -0
  35. package/coverage/toga-blox-npm/src/components/CounterButton/index.html +131 -0
  36. package/coverage/toga-blox-npm/src/components/Description/Description.stories.tsx.html +286 -0
  37. package/coverage/toga-blox-npm/src/components/Description/Description.tsx.html +124 -0
  38. package/coverage/toga-blox-npm/src/components/Description/index.html +131 -0
  39. package/coverage/toga-blox-npm/src/components/DropDownIconButton/DropDownIconButton.stories.tsx.html +676 -0
  40. package/coverage/toga-blox-npm/src/components/DropDownIconButton/DropDownIconButton.tsx.html +346 -0
  41. package/coverage/toga-blox-npm/src/components/DropDownIconButton/index.html +131 -0
  42. package/coverage/toga-blox-npm/src/components/Footer/ContactInfoItem.tsx.html +139 -0
  43. package/coverage/toga-blox-npm/src/components/Footer/Footer.stories.tsx.html +934 -0
  44. package/coverage/toga-blox-npm/src/components/Footer/Footer.tsx.html +373 -0
  45. package/coverage/toga-blox-npm/src/components/Footer/index.html +146 -0
  46. package/coverage/toga-blox-npm/src/components/FormButton/FormButton.stories.tsx.html +952 -0
  47. package/coverage/toga-blox-npm/src/components/FormButton/FormButton.tsx.html +343 -0
  48. package/coverage/toga-blox-npm/src/components/FormButton/index.html +131 -0
  49. package/coverage/toga-blox-npm/src/components/GenericList/GenericList.stories.tsx.html +376 -0
  50. package/coverage/toga-blox-npm/src/components/GenericList/GenericList.tsx.html +520 -0
  51. package/coverage/toga-blox-npm/src/components/GenericList/index.html +131 -0
  52. package/coverage/toga-blox-npm/src/components/GenericList/templates/DummyDataList.tsx.html +154 -0
  53. package/coverage/toga-blox-npm/src/components/GenericList/templates/DynamicIconList.tsx.html +250 -0
  54. package/coverage/toga-blox-npm/src/components/GenericList/templates/index.html +131 -0
  55. package/coverage/toga-blox-npm/src/components/GetSupport/GetSupport.stories.tsx.html +325 -0
  56. package/coverage/toga-blox-npm/src/components/GetSupport/GetSupport.tsx.html +262 -0
  57. package/coverage/toga-blox-npm/src/components/GetSupport/index.html +131 -0
  58. package/coverage/toga-blox-npm/src/components/HamburgerButton/Hamburger.stories.tsx.html +760 -0
  59. package/coverage/toga-blox-npm/src/components/HamburgerButton/HamburgerButton.tsx.html +232 -0
  60. package/coverage/toga-blox-npm/src/components/HamburgerButton/index.html +131 -0
  61. package/coverage/toga-blox-npm/src/components/Header/Header.stories.tsx.html +1633 -0
  62. package/coverage/toga-blox-npm/src/components/Header/Header.tsx.html +814 -0
  63. package/coverage/toga-blox-npm/src/components/Header/headerContext.tsx.html +460 -0
  64. package/coverage/toga-blox-npm/src/components/Header/index.html +146 -0
  65. package/coverage/toga-blox-npm/src/components/Hero/Hero.stories.tsx.html +289 -0
  66. package/coverage/toga-blox-npm/src/components/Hero/Hero.tsx.html +259 -0
  67. package/coverage/toga-blox-npm/src/components/Hero/index.html +131 -0
  68. package/coverage/toga-blox-npm/src/components/IconButton/IconButton.stories.tsx.html +673 -0
  69. package/coverage/toga-blox-npm/src/components/IconButton/IconButton.tsx.html +313 -0
  70. package/coverage/toga-blox-npm/src/components/IconButton/index.html +131 -0
  71. package/coverage/toga-blox-npm/src/components/Image/Image.stories.tsx.html +322 -0
  72. package/coverage/toga-blox-npm/src/components/Image/Image.tsx.html +226 -0
  73. package/coverage/toga-blox-npm/src/components/Image/index.html +131 -0
  74. package/coverage/toga-blox-npm/src/components/Input/Input.stories.tsx.html +1621 -0
  75. package/coverage/toga-blox-npm/src/components/Input/Input.tsx.html +568 -0
  76. package/coverage/toga-blox-npm/src/components/Input/InputMemoTypes.tsx.html +181 -0
  77. package/coverage/toga-blox-npm/src/components/Input/index.html +146 -0
  78. package/coverage/toga-blox-npm/src/components/MobileMenu/MobileMenu.tsx.html +208 -0
  79. package/coverage/toga-blox-npm/src/components/MobileMenu/index.html +116 -0
  80. package/coverage/toga-blox-npm/src/components/Nav/Nav.stories.tsx.html +628 -0
  81. package/coverage/toga-blox-npm/src/components/Nav/Nav.tsx.html +622 -0
  82. package/coverage/toga-blox-npm/src/components/Nav/index.html +131 -0
  83. package/coverage/toga-blox-npm/src/components/Page/TableDataDummy.tsx.html +733 -0
  84. package/coverage/toga-blox-npm/src/components/Page/ViewPageTemplate.stories.tsx.html +1714 -0
  85. package/coverage/toga-blox-npm/src/components/Page/ViewPageTemplate.tsx.html +115 -0
  86. package/coverage/toga-blox-npm/src/components/Page/index.html +146 -0
  87. package/coverage/toga-blox-npm/src/components/PageSection/PageSection.stories.tsx.html +433 -0
  88. package/coverage/toga-blox-npm/src/components/PageSection/PageSection.tsx.html +121 -0
  89. package/coverage/toga-blox-npm/src/components/PageSection/index.html +131 -0
  90. package/coverage/toga-blox-npm/src/components/SearchInput/SearchInput.stories.tsx.html +517 -0
  91. package/coverage/toga-blox-npm/src/components/SearchInput/SearchInput.tsx.html +325 -0
  92. package/coverage/toga-blox-npm/src/components/SearchInput/index.html +131 -0
  93. package/coverage/toga-blox-npm/src/components/Slider/Slider.stories.tsx.html +349 -0
  94. package/coverage/toga-blox-npm/src/components/Slider/Slider.tsx.html +502 -0
  95. package/coverage/toga-blox-npm/src/components/Slider/index.html +131 -0
  96. package/coverage/toga-blox-npm/src/components/Submenus/AdminSubmenu.tsx.html +136 -0
  97. package/coverage/toga-blox-npm/src/components/Submenus/AlertSubmenu.tsx.html +253 -0
  98. package/coverage/toga-blox-npm/src/components/Submenus/AlertSubmenuItem.tsx.html +202 -0
  99. package/coverage/toga-blox-npm/src/components/Submenus/index.html +146 -0
  100. package/coverage/toga-blox-npm/src/components/Text/Text.stories.tsx.html +235 -0
  101. package/coverage/toga-blox-npm/src/components/Text/Text.tsx.html +172 -0
  102. package/coverage/toga-blox-npm/src/components/Text/index.html +131 -0
  103. package/coverage/toga-blox-npm/src/components/Toaster/Toaster.stories.tsx.html +445 -0
  104. package/coverage/toga-blox-npm/src/components/Toaster/Toaster.tsx.html +301 -0
  105. package/coverage/toga-blox-npm/src/components/Toaster/index.html +131 -0
  106. package/coverage/toga-blox-npm/src/hoc/styling/index.html +116 -0
  107. package/coverage/toga-blox-npm/src/hoc/styling/withStoryBook.tsx.html +142 -0
  108. package/coverage/toga-blox-npm/src/userHoc/index.html +116 -0
  109. package/coverage/toga-blox-npm/src/userHoc/withMemo.tsx.html +145 -0
  110. package/coverage/toga-blox-npm/src/utils/assertTagName.tsx.html +106 -0
  111. package/coverage/toga-blox-npm/src/utils/generateAccordionItem.tsx.html +373 -0
  112. package/coverage/toga-blox-npm/src/utils/generateFooterContacts.tsx.html +295 -0
  113. package/coverage/toga-blox-npm/src/utils/generateNavMenu.tsx.html +247 -0
  114. package/coverage/toga-blox-npm/src/utils/generateSocialList.tsx.html +187 -0
  115. package/coverage/toga-blox-npm/src/utils/getFontAwesomeIcon.tsx.html +145 -0
  116. package/coverage/toga-blox-npm/src/utils/index.html +206 -0
  117. package/coverage/toga-blox-npm/src/utils/inputValidation.tsx.html +163 -0
  118. package/coverage/toga-blox-npm/tailwind.config.js.html +205 -0
  119. package/declarations.d.ts +1 -1
  120. package/package.json +5 -6
  121. package/src/components/Badge/Badge.stories.tsx +110 -207
  122. package/src/components/Badge/Badge.test.tsx +40 -41
  123. package/src/components/Badge/Badge.tsx +29 -99
  124. package/src/components/Badge/Badge.types.tsx +12 -23
  125. package/src/components/Card/Card.stories.tsx +166 -22
  126. package/src/components/Card/Card.test.tsx +3 -3
  127. package/src/components/Card/Card.tsx +12 -16
  128. package/src/components/Card/DUMMYPRODUCTDATA.json +381 -225
  129. package/src/components/Card/templates/CategoryCardTemplate.tsx +86 -0
  130. package/src/components/Card/templates/CompassCardTemplate.tsx +1 -1
  131. package/src/components/Card/templates/CounterContentCardTemplate.tsx +200 -0
  132. package/src/components/Card/templates/ItemCardTemplate.tsx +155 -0
  133. package/src/components/Card/templates/KitContentCardTemplate.tsx +128 -0
  134. package/src/components/Card/templates/ShippingAddressCardTemplate.tsx +111 -0
  135. package/src/components/Card/templates/VerticalCardTemplate.tsx +100 -85
  136. package/src/components/CounterButton/CounterButton.tsx +1 -1
  137. package/src/components/Description/Description.stories.tsx +67 -0
  138. package/src/components/Description/Description.tsx +13 -0
  139. package/src/components/Description/Description.types.ts +9 -0
  140. package/src/components/DropDownIconButton/DropDownIconButton.stories.tsx +197 -0
  141. package/src/components/DropDownIconButton/DropDownIconButton.test.tsx +90 -0
  142. package/src/components/DropDownIconButton/DropDownIconButton.tsx +87 -0
  143. package/src/components/DropDownIconButton/DropDownIconButton.types.ts +21 -0
  144. package/src/components/DropDownIconButton/index.ts +2 -0
  145. package/src/components/Footer/ContactInfoItem.tsx +8 -10
  146. package/src/components/Footer/Footer.stories.tsx +13 -22
  147. package/src/components/Footer/Footer.tsx +25 -88
  148. package/src/components/Footer/Footer.types.tsx +2 -2
  149. package/src/components/FormButton/FormButton.stories.tsx +101 -226
  150. package/src/components/FormButton/FormButton.test.tsx +1 -1
  151. package/src/components/FormButton/FormButton.tsx +34 -42
  152. package/src/components/FormButton/FormButton.types.ts +7 -13
  153. package/src/components/GenericList/GenericList.stories.tsx +5 -12
  154. package/src/components/GenericList/GenericList.tsx +91 -92
  155. package/src/components/GenericList/templates/DynamicIconList.tsx +45 -64
  156. package/src/components/GetSupport/GetSupport.stories.tsx +80 -0
  157. package/src/components/GetSupport/GetSupport.test.tsx +62 -0
  158. package/src/components/GetSupport/GetSupport.tsx +59 -0
  159. package/src/components/GetSupport/GetSupport.types.ts +11 -0
  160. package/src/components/HamburgerButton/Hamburger.stories.tsx +225 -0
  161. package/src/components/HamburgerButton/HamburgerButton.tsx +37 -56
  162. package/src/components/HamburgerButton/HamburgerButton.types.tsx +2 -1
  163. package/src/components/Header/Header.stories.tsx +3 -5
  164. package/src/components/Header/Header.tsx +37 -83
  165. package/src/components/Hero/Hero.stories.tsx +1 -2
  166. package/src/components/IconButton/IconButton.stories.tsx +196 -0
  167. package/src/components/{Icon/Icon.test.tsx → IconButton/IconButton.test.tsx} +3 -4
  168. package/src/components/IconButton/IconButton.tsx +76 -0
  169. package/src/components/IconButton/IconButton.types.ts +28 -0
  170. package/src/components/IconButton/index.ts +2 -0
  171. package/src/components/Image/Image.tsx +1 -1
  172. package/src/components/Input/Input.stories.tsx +10 -12
  173. package/src/components/Input/Input.test.tsx +2 -3
  174. package/src/components/Input/Input.tsx +41 -104
  175. package/src/components/MobileMenu/MobileMenu.types.tsx +0 -2
  176. package/src/components/Nav/DUMMYNAVDATA.json +4 -4
  177. package/src/components/Nav/Nav.tsx +49 -112
  178. package/src/components/Nav/Nav.types.tsx +14 -2
  179. package/src/components/Page/ViewPageTemplate.stories.tsx +2 -2
  180. package/src/components/Page/ViewPageTemplate.test.tsx +1 -1
  181. package/src/components/Page/ViewPageTemplate.types.ts +1 -1
  182. package/src/components/PageSection/PageSection.stories.tsx +3 -1
  183. package/src/components/PageSection/PageSections.test.tsx +2 -1
  184. package/src/components/SearchInput/SearchInput.stories.tsx +144 -0
  185. package/src/components/SearchInput/SearchInput.tsx +81 -0
  186. package/src/components/SearchInput/SearchInput.types.ts +28 -0
  187. package/src/components/Slider/Slider.stories.tsx +88 -0
  188. package/src/components/Slider/Slider.tsx +139 -0
  189. package/src/components/Slider/Slider.types.ts +21 -0
  190. package/src/components/Submenus/AdminSubmenu.tsx +17 -0
  191. package/src/components/Submenus/AlertSubmenu.tsx +56 -0
  192. package/src/components/Submenus/AlertSubmenuItem.tsx +39 -0
  193. package/src/components/Submenus/types.tsx +32 -0
  194. package/src/components/Toaster/Toaster.stories.tsx +4 -6
  195. package/src/components/Toaster/Toaster.test.tsx +3 -4
  196. package/src/components/Toaster/Toaster.tsx +9 -17
  197. package/src/components/Toaster/Toaster.types.ts +2 -2
  198. package/src/utils/assertTagName.tsx +1 -1
  199. package/src/utils/generateAccordionItem.tsx +7 -13
  200. package/src/utils/generateFooterContacts.tsx +4 -9
  201. package/src/utils/getFontAwesomeIcon.tsx +8 -13
  202. package/tailwind.config.js +11 -3
  203. package/src/components/Badge/badgeClassNames.tsx +0 -173
  204. package/src/components/Card/cardClassNames.ts +0 -49
  205. package/src/components/Footer/footerClassNames.tsx +0 -57
  206. package/src/components/FormButton/formButtonClassNames.tsx +0 -153
  207. package/src/components/GenericList/genericListClassNames.tsx +0 -8
  208. package/src/components/Header/headerClassNames.tsx +0 -50
  209. package/src/components/Icon/Icon.stories.tsx +0 -227
  210. package/src/components/Icon/Icon.tsx +0 -208
  211. package/src/components/Icon/Icon.types.ts +0 -24
  212. package/src/components/Icon/iconClassNames.ts +0 -79
  213. package/src/components/Icon/index.ts +0 -2
  214. package/src/components/Input/inputClassNames.tsx +0 -169
  215. package/src/components/Nav/navClassNames.tsx +0 -192
@@ -0,0 +1,88 @@
1
+ import { Meta, StoryFn } from "@storybook/react";
2
+ import Slider from "./Slider";
3
+ import { SliderProps } from "./Slider.types";
4
+
5
+ export default {
6
+ title: 'Components/Slider',
7
+ component: Slider,
8
+ argTypes: {
9
+ min: {
10
+ control: 'none',
11
+ description: 'The minimum value of the range. Example: `0`',
12
+ },
13
+ title: {
14
+ control: 'none',
15
+ description: 'The title of the range. Example: `Temperature Range`',
16
+ },
17
+ max: {
18
+ control: 'none',
19
+ description: 'The maximum value of the range. Example: `100`',
20
+ },
21
+ step: {
22
+ control: 'none',
23
+ description: 'The step value for the range. Example: `1`',
24
+ },
25
+ minValue: {
26
+ control: 'none',
27
+ description: 'The initial minimum value. Example: `10`',
28
+ },
29
+ maxValue: {
30
+ control: 'none',
31
+ description: 'The initial maximum value. Example: `90`',
32
+ },
33
+ onMinChange: {
34
+ control: 'none',
35
+ description: 'Callback function when the minimum value changes. Example: `(value: number) => console.log(value)`',
36
+ },
37
+ onMaxChange: {
38
+ control: 'none',
39
+ description: 'Callback function when the maximum value changes. Example: `(value: number) => console.log(value)`',
40
+ },
41
+ additionalClasses: {
42
+ control: 'none',
43
+ description: 'Additional CSS classes for the component. Example: `my-custom-slider`',
44
+ },
45
+ },
46
+ tags: ['autodocs'],
47
+ parameters: {
48
+ layout: 'centered',
49
+ },
50
+ } as Meta;
51
+
52
+ const Template: StoryFn<SliderProps> = (args) => <Slider {...args} />;
53
+
54
+ export const Default = Template.bind({});
55
+ Default.args = {
56
+ min: 0,
57
+ max: 100,
58
+ step: 1,
59
+ minValue: 20,
60
+ maxValue: 80,
61
+ onMinChange: (value: number) => console.log('Min value changed:', value),
62
+ onMaxChange: (value: number) => console.log('Max value changed:', value),
63
+ title: 'Range',
64
+ };
65
+
66
+ export const TemperatureRange = Template.bind({});
67
+ TemperatureRange.args = {
68
+ min: -50,
69
+ max: 50,
70
+ step: 0.5,
71
+ minValue: -10,
72
+ maxValue: 30,
73
+ onMinChange: (value: number) => console.log('Min value changed:', value),
74
+ onMaxChange: (value: number) => console.log('Max value changed:', value),
75
+ title: 'Temperature Range',
76
+ };
77
+
78
+ export const PriceRange = Template.bind({});
79
+ PriceRange.args = {
80
+ min: 0,
81
+ max: 2000,
82
+ step: 1,
83
+ minValue: 900,
84
+ maxValue: 1578,
85
+ onMinChange: (value: number) => console.log('Min value changed:', value),
86
+ onMaxChange: (value: number) => console.log('Max value changed:', value),
87
+ title: 'Price Range',
88
+ };
@@ -0,0 +1,139 @@
1
+ import React, { useRef, useState } from "react";
2
+ import { SliderProps } from "./Slider.types";
3
+
4
+ const Slider: React.FC<SliderProps> = ({
5
+ min,
6
+ max,
7
+ step,
8
+ minValue,
9
+ maxValue,
10
+ onMinChange,
11
+ onMaxChange,
12
+ additionalClasses = "",
13
+ containerClasses = "p-8 border rounded-lg shadow-md bg-white max-w-lg",
14
+ title = "Range",
15
+ titleClasses = "text-xl text-black mb-4 font-bold",
16
+ minLabel = "Min Value",
17
+ maxLabel = "Max Value",
18
+ labelClasses = "text-sm text-gray-500",
19
+ inputClasses = "mt-2 text-lg font-semibold text-gray-900 border border-gray-300 rounded px-2 py-1 text-center w-20",
20
+ sliderClasses = "relative w-full h-2 bg-gray-300 rounded",
21
+ rangeClasses = "absolute h-2 bg-teal-500 rounded",
22
+ thumbClasses = "absolute w-6 h-6 bg-white border-2 border-teal-500 rounded-full cursor-pointer",
23
+ onAddSection,
24
+ }) => {
25
+ const [minThumbValue, setMinThumbValue] = useState(minValue);
26
+ const [maxThumbValue, setMaxThumbValue] = useState(maxValue);
27
+ const sliderRef = useRef<HTMLDivElement>(null);
28
+ const isDragging = useRef<"min" | "max" | null>(null);
29
+
30
+ const getPercentage = (value: number) =>
31
+ ((value - min) / (max - min)) * 100;
32
+
33
+ const handleMinChange = (value: number) => {
34
+ if (value <= maxThumbValue && value >= min) {
35
+ setMinThumbValue(value);
36
+ onMinChange(value);
37
+ }
38
+ };
39
+
40
+ const handleMaxChange = (value: number) => {
41
+ if (value >= minThumbValue && value <= max) {
42
+ setMaxThumbValue(value);
43
+ onMaxChange(value);
44
+ }
45
+ };
46
+
47
+ const handlePointerMove = (event: React.PointerEvent) => {
48
+ if (sliderRef.current && isDragging.current) {
49
+ const sliderRect = sliderRef.current.getBoundingClientRect();
50
+ const sliderWidth = sliderRect.width;
51
+ const clickX = event.clientX - sliderRect.left;
52
+ const newValue = min + (clickX / sliderWidth) * (max - min);
53
+
54
+ if (isDragging.current === "min") {
55
+ handleMinChange(Math.round(newValue / step) * step);
56
+ } else if (isDragging.current === "max") {
57
+ handleMaxChange(Math.round(newValue / step) * step);
58
+ }
59
+ }
60
+ };
61
+
62
+ const handlePointerUp = () => {
63
+ isDragging.current = null;
64
+ };
65
+
66
+ const handlePointerDown = (thumb: "min" | "max") => {
67
+ isDragging.current = thumb;
68
+ };
69
+
70
+ return (
71
+ <div className={`${containerClasses} ${additionalClasses}`}>
72
+ <h3 className={titleClasses}>{title}</h3>
73
+ <hr className="border-gray-300 mb-6" />
74
+ <div className="flex justify-between gap-4 mb-6">
75
+ <div className="flex flex-col items-center">
76
+ <span className={labelClasses}>{minLabel}</span>
77
+ <input
78
+ type="number"
79
+ value={minThumbValue}
80
+ onChange={(e) => handleMinChange(Number(e.target.value))}
81
+ className={inputClasses}
82
+ />
83
+ </div>
84
+ <div className="flex flex-col items-center">
85
+ <span className={labelClasses}>{maxLabel}</span>
86
+ <input
87
+ type="number"
88
+ value={maxThumbValue}
89
+ onChange={(e) => handleMaxChange(Number(e.target.value))}
90
+ className={inputClasses}
91
+ />
92
+ </div>
93
+ </div>
94
+ <div
95
+ className={sliderClasses}
96
+ ref={sliderRef}
97
+ onPointerMove={handlePointerMove}
98
+ onPointerUp={handlePointerUp}
99
+ onPointerCancel={handlePointerUp}
100
+ onPointerLeave={handlePointerUp}
101
+ >
102
+ <div
103
+ className={rangeClasses}
104
+ style={{
105
+ left: `${getPercentage(minThumbValue)}%`,
106
+ right: `${100 - getPercentage(maxThumbValue)}%`,
107
+ }}
108
+ />
109
+ <div
110
+ className={thumbClasses}
111
+ style={{
112
+ left: `${getPercentage(minThumbValue)}%`,
113
+ transform: "translate(-50%, -50%)",
114
+ top: "50%",
115
+ zIndex: 5,
116
+ }}
117
+ onPointerDown={() => handlePointerDown("min")}
118
+ />
119
+ <div
120
+ className={thumbClasses}
121
+ style={{
122
+ left: `${getPercentage(maxThumbValue)}%`,
123
+ transform: "translate(-50%, -50%)",
124
+ top: "50%",
125
+ zIndex: 4,
126
+ }}
127
+ onPointerDown={() => handlePointerDown("max")}
128
+ />
129
+ </div>
130
+ {onAddSection && (
131
+ <button onClick={onAddSection} className="mt-4 text-blue-500">
132
+ Add Section
133
+ </button>
134
+ )}
135
+ </div>
136
+ );
137
+ };
138
+
139
+ export default Slider;
@@ -0,0 +1,21 @@
1
+ export interface SliderProps {
2
+ min: number;
3
+ max: number;
4
+ step: number;
5
+ minValue: number;
6
+ maxValue: number;
7
+ onMinChange: (value: number) => void;
8
+ onMaxChange: (value: number) => void;
9
+ additionalClasses?: string;
10
+ containerClasses?: string;
11
+ title?: string;
12
+ titleClasses?: string;
13
+ minLabel?: string;
14
+ maxLabel?: string;
15
+ labelClasses?: string;
16
+ inputClasses?: string;
17
+ sliderClasses?: string;
18
+ rangeClasses?: string;
19
+ thumbClasses?: string;
20
+ onAddSection?: () => void;
21
+ }
@@ -0,0 +1,17 @@
1
+ import React from "react";
2
+ import { AdminSubmenuProps } from "./types";
3
+
4
+ const AdminSubmenu: React.FC<AdminSubmenuProps> = ({
5
+ callToActionChildren,
6
+ secondCallToActionChildren,
7
+ menuClasses = "p-4 bg-white rounded-lg shadow-lg",
8
+ }) => {
9
+ return (
10
+ <div className={menuClasses}>
11
+ <div>{callToActionChildren}</div>
12
+ <div>{secondCallToActionChildren}</div>
13
+ </div>
14
+ );
15
+ };
16
+
17
+ export default AdminSubmenu;
@@ -0,0 +1,56 @@
1
+ import React from "react";
2
+ import AlertSubmenuItem from "./AlertSubmenuItem";
3
+ import { AlertSubmenuProps } from "./types";
4
+
5
+ const AlertSubmenu: React.FC<AlertSubmenuProps> = ({
6
+ data,
7
+ newAlertCount,
8
+ newAlertColor,
9
+ onClearAlerts,
10
+ newAlertCountClasses = "text-gray-900 font-semibold",
11
+ clearAlertsTextClasses = "text-blue-600 hover:underline",
12
+ clearAlertsText = "Mark all as Read",
13
+ menuContainerClasses = "rounded-lg border border-stroke bg-white shadow-lg",
14
+ menuHeaderContainerClasses = "flex justify-between items-center p-4 border-b border-gray-200",
15
+ alertListContainerClasses = "overflow-y-auto max-h-[300px] divide-y divide-gray-200",
16
+ }) => {
17
+ const alertList = data.map((alert, index) => (
18
+ <AlertSubmenuItem
19
+ key={index}
20
+ link={alert.link}
21
+ title={alert.title}
22
+ subtitle={alert.subtitle}
23
+ status={alert.status}
24
+ statusColor={newAlertColor}
25
+ externalLink={alert.externalLink}
26
+ titleClasses="font-medium text-gray-900"
27
+ externalLinkClasses="text-sm text-blue-600"
28
+ subtitleClasses="text-sm text-gray-500"
29
+ itemContainerClasses="p-4 hover:bg-gray-50"
30
+ />
31
+ ));
32
+
33
+ return (
34
+ <div className={menuContainerClasses}>
35
+ <div className={menuHeaderContainerClasses}>
36
+ <span className={newAlertCountClasses}>
37
+ {newAlertCount} alerts
38
+ </span>
39
+ <button
40
+ className={clearAlertsTextClasses}
41
+ onClick={onClearAlerts}
42
+ >
43
+ {clearAlertsText}
44
+ </button>
45
+ </div>
46
+ <div className={alertListContainerClasses}>
47
+ <ul>{alertList}</ul>
48
+ <div className="text-center p-4 text-gray-500">
49
+ All up to date!
50
+ </div>
51
+ </div>
52
+ </div>
53
+ );
54
+ };
55
+
56
+ export default AlertSubmenu;
@@ -0,0 +1,39 @@
1
+ import React from "react";
2
+ import { AlertSubmenuItemProps } from "./types";
3
+
4
+ const AlertSubmenuItem: React.FC<AlertSubmenuItemProps> = ({
5
+ link = "#",
6
+ title,
7
+ externalLink,
8
+ subtitle,
9
+ status,
10
+ statusColor = "bg-blue-500",
11
+ itemContainerClasses = "flex justify-between items-center p-4",
12
+ titleClasses = "font-medium text-gray-900",
13
+ externalLinkClasses = "text-sm text-blue-600",
14
+ subtitleClasses = "text-sm text-gray-500",
15
+ }) => {
16
+ return (
17
+ <li className={itemContainerClasses}>
18
+ <a href={link} className="flex items-start gap-4">
19
+ <div className="flex-shrink-0 pt-2">
20
+ {status && (
21
+ <span
22
+ className={`inline-block w-3 h-3 rounded-full ${statusColor}`}
23
+ aria-label="New"
24
+ ></span>
25
+ )}
26
+ </div>
27
+ <div>
28
+ <h6 className={titleClasses}>{title}</h6>
29
+ {externalLink && (
30
+ <p className={externalLinkClasses}>{externalLink}</p>
31
+ )}
32
+ <p className={subtitleClasses}>{subtitle}</p>
33
+ </div>
34
+ </a>
35
+ </li>
36
+ );
37
+ };
38
+
39
+ export default AlertSubmenuItem;
@@ -0,0 +1,32 @@
1
+
2
+ export interface AlertSubmenuItemProps {
3
+ link?: string;
4
+ title: string;
5
+ externalLink?: string;
6
+ subtitle?: string;
7
+ status?: string;
8
+ statusColor?: string;
9
+ itemContainerClasses?: string;
10
+ titleClasses?: string;
11
+ externalLinkClasses?: string;
12
+ subtitleClasses?: string;
13
+ }
14
+
15
+ export interface AlertSubmenuProps<T = Record<string, any>> {
16
+ data: T[];
17
+ newAlertCount?: number;
18
+ newAlertColor?: string;
19
+ onClearAlerts?: () => void;
20
+ newAlertCountClasses?: string;
21
+ clearAlertsTextClasses?: string;
22
+ clearAlertsText?: string;
23
+ menuContainerClasses?: string;
24
+ menuHeaderContainerClasses?: string;
25
+ alertListContainerClasses?: string;
26
+ }
27
+
28
+ export interface AdminSubmenuProps {
29
+ callToActionChildren?: JSX.Element;
30
+ secondCallToActionChildren?: JSX.Element;
31
+ menuClasses?: string;
32
+ }
@@ -2,8 +2,7 @@ import React from "react";
2
2
 
3
3
  import Toaster, { ToasterTypes } from ".";
4
4
  import { Meta, StoryFn } from "@storybook/react";
5
- import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
6
- import { faCheck, faExclamation, faX } from "@fortawesome/free-solid-svg-icons";
5
+ import { getFontAwesomeIcon } from "../../utils/getFontAwesomeIcon";
7
6
 
8
7
  export default {
9
8
  title: "Components/Toaster",
@@ -60,7 +59,6 @@ export default {
60
59
 
61
60
  tags: ["autodocs"],
62
61
  parameters: {
63
- // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout
64
62
  layout: "centered",
65
63
  },
66
64
  } as Meta;
@@ -84,7 +82,7 @@ WithIconFail.args = {
84
82
  ...Default.args,
85
83
  message: "Your action failed!",
86
84
  color: "red-800",
87
- icon: <FontAwesomeIcon icon={faX} />,
85
+ icon: getFontAwesomeIcon("x"),
88
86
  additionalClasses: `border-red-500 ${minWidth}`,
89
87
  };
90
88
 
@@ -92,7 +90,7 @@ export const WithIconSuccess = Template.bind({});
92
90
  WithIconSuccess.args = {
93
91
  ...Default.args,
94
92
  color: "green-800",
95
- icon: <FontAwesomeIcon icon={faCheck} />,
93
+ icon: getFontAwesomeIcon("check"),
96
94
  additionalClasses: `border-green-500 ${minWidth}`,
97
95
  };
98
96
 
@@ -101,7 +99,7 @@ WithIconAlert.args = {
101
99
  ...Default.args,
102
100
  message: "Alert, are you sure!",
103
101
  color: "orange-800",
104
- icon: <FontAwesomeIcon icon={faExclamation} />,
102
+ icon: getFontAwesomeIcon("exclamation"),
105
103
  additionalClasses: `border-orange-500 ${minWidth}`,
106
104
  };
107
105
 
@@ -1,8 +1,7 @@
1
1
  import { render, screen, fireEvent } from "@testing-library/react";
2
2
  import { describe, expect, beforeEach, test, vi } from "vitest";
3
3
  import Toaster from "./Toaster";
4
- import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
5
- import { faCoffee, faTimes } from "@fortawesome/free-solid-svg-icons";
4
+ import { getFontAwesomeIcon } from "../../utils/getFontAwesomeIcon";
6
5
 
7
6
  describe("Toaster Component Tests", () => {
8
7
  test("renders correctly with minimal props", () => {
@@ -15,8 +14,8 @@ describe("Toaster Component Tests", () => {
15
14
  render(
16
15
  <Toaster
17
16
  message="Test Message"
18
- color="blue-500"
19
- icon={<FontAwesomeIcon icon={faCoffee} />}
17
+ color="bg-blue-500"
18
+ icon={getFontAwesomeIcon("coffee")}
20
19
  />
21
20
  );
22
21
  expect(screen.getByTestId("toaster")).toBeInTheDocument();
@@ -1,8 +1,7 @@
1
- import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
2
1
  import React from "react";
3
2
  import Button from "../FormButton/FormButton";
4
- import { faTimes } from "@fortawesome/free-solid-svg-icons";
5
3
  import { ToasterTypes } from ".";
4
+ import { getFontAwesomeIcon } from "../../utils/getFontAwesomeIcon";
6
5
 
7
6
  const Toaster: React.FC<ToasterTypes> = ({
8
7
  message,
@@ -29,13 +28,8 @@ const Toaster: React.FC<ToasterTypes> = ({
29
28
  "orange-800": "text-orange-800",
30
29
  "blue-800": "text-blue-800",
31
30
  };
32
- const iconClass = iconColorMap[color] || "";
33
- // Improved CSS Class Composition
34
- const borderClass = `border ${
35
- hasBumper ? `border-l-4 border-${color}` : "border-l-1"
36
- } `;
31
+ const borderClass = `border ${hasBumper ? `border-l-4 border-${color}` : "border-l-1"} `;
37
32
  const backgroundColorClass = fullColor ? `bg-${color}` : "bg-white";
38
-
39
33
  const textColorClass = fullColor ? "text-white" : "text-black";
40
34
 
41
35
  return (
@@ -45,29 +39,27 @@ const Toaster: React.FC<ToasterTypes> = ({
45
39
  >
46
40
  {title ? (
47
41
  <h3 className="ml-8 font-bold text-xl text-left">{title}</h3>
48
- ) : (
49
- <></>
50
- )}
42
+ ) : null}
51
43
  <div className="flex items-center justify-between">
52
- <span className={`mr-2 ${iconClass}`}>{icon}</span>
44
+ <span className={`mr-2`}>{icon}</span>
53
45
  <span className={textColorClass}>{message}</span>
54
46
  {clearMessage && !hasClearText && (
55
47
  <span className="cursor-pointer hover:bg-gray-200 py-1 px-2 rounded-md">
56
- <FontAwesomeIcon
48
+ <span
57
49
  data-testid="clearMessage"
58
50
  onClick={handleClearMessage}
59
51
  className={`cursor-pointer ${textColorClass}`}
60
- icon={faTimes}
61
- />
52
+ >
53
+ {getFontAwesomeIcon("times")}
54
+ </span>
62
55
  </span>
63
56
  )}
64
57
  {hasClearText && clearText && (
65
58
  <Button
66
59
  onClick={handleClearMessage}
67
60
  text={clearText}
68
- color="primary"
69
61
  shape="outline"
70
- type={"button"}
62
+ type="button"
71
63
  isDisabled={false}
72
64
  additionalClasses="ml-2 px-4"
73
65
  />
@@ -2,8 +2,8 @@ export interface ToasterTypes {
2
2
  message: string;
3
3
  additionalClasses?: string;
4
4
  clearMessage?: React.Dispatch<React.SetStateAction<boolean>>;
5
- color: "green-800" | "red-800" | "yellow-800" | "blue-800" | "orange-800";
6
- icon?: JSX.Element;
5
+ color: string;
6
+ icon?: JSX.Element | null;
7
7
  fullColor?: boolean;
8
8
  hasBumper?: boolean;
9
9
  title?: string;
@@ -1,4 +1,4 @@
1
- import { TagName } from "../components/Text/Text";
1
+ import { TagName } from "../components/Text/Text.types";
2
2
  function assertTagName(tag: string): asserts tag is TagName {
3
3
  if (!["span", "p", "h1", "h2", "h3", "h4", "h5"].includes(tag)) {
4
4
  throw new Error(`Invalid tag name: ${tag}`);
@@ -1,6 +1,5 @@
1
1
  import React, { Fragment, useState } from "react";
2
- import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
3
- import { faChevronDown, faChevronUp } from "@fortawesome/free-solid-svg-icons";
2
+ import { getFontAwesomeIcon } from "./getFontAwesomeIcon";
4
3
 
5
4
  type NavItem = {
6
5
  title: string;
@@ -63,21 +62,16 @@ export const Accordion: React.FC<AccordionProps> = ({
63
62
  <div className="px-6 text-left items-center h-10 select-none flex justify-between flex-row">
64
63
  <div className="flex w-3/4">{item.title}</div>
65
64
  <div className="flex w-1/2 justify-end">
66
- <FontAwesomeIcon
67
- className={`text-xs transition-transform duration-300 transform ${
68
- accordionExpanded[index]
69
- ? "rotate-180"
70
- : ""
71
- }`}
72
- icon={faChevronDown}
73
- />
65
+ {getFontAwesomeIcon(
66
+ "chevronDown",
67
+ `text-xs transition-transform duration-300 transform ${accordionExpanded[index] ? "rotate-180" : ""}`
68
+ )}
74
69
  </div>
75
70
  </div>
76
71
  </div>
77
72
  <div
78
- className={`px-6 w-full overflow-hidden transition-[max-height] duration-500 ease-in-out ${
79
- accordionExpanded[index] ? "max-h-96" : "max-h-0"
80
- }`}
73
+ className={`px-6 w-full overflow-hidden transition-[max-height] duration-500 ease-in-out ${accordionExpanded[index] ? "max-h-96" : "max-h-0"
74
+ }`}
81
75
  aria-hidden={!accordionExpanded[index]}
82
76
  tabIndex={accordionExpanded[index] ? 0 : -1} // Set tabIndex dynamically
83
77
  >
@@ -1,12 +1,7 @@
1
1
  import React from "react";
2
2
  import Text from "../components/Text/Text";
3
- import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
4
- import {
5
- faEnvelope,
6
- faLocationDot,
7
- faPhone,
8
- } from "@fortawesome/free-solid-svg-icons";
9
3
  import { ContactInfoItem } from "../components/Footer/ContactInfoItem";
4
+ import { getFontAwesomeIcon } from "./getFontAwesomeIcon";
10
5
 
11
6
  interface ContactProps {
12
7
  contactDataList: {
@@ -24,11 +19,11 @@ export const FooterContacts: React.FC<ContactProps> = ({ contactDataList }) => {
24
19
  return contactDataList.map((contact) => {
25
20
  const contactDetails = [
26
21
  {
27
- icon: faPhone,
22
+ icon: getFontAwesomeIcon("phone"),
28
23
  text: contact.phone,
29
24
  },
30
25
  {
31
- icon: faEnvelope,
26
+ icon: getFontAwesomeIcon("envelope"),
32
27
  text: contact.email,
33
28
  },
34
29
  ];
@@ -39,7 +34,7 @@ export const FooterContacts: React.FC<ContactProps> = ({ contactDataList }) => {
39
34
  data-testid="contact-info"
40
35
  className="flex flex-col pt-2 w-60"
41
36
  >
42
- <ContactInfoItem icon={faLocationDot}>
37
+ <ContactInfoItem icon={getFontAwesomeIcon("location-dot")}>
43
38
  <a
44
39
  className="flex flex-col hover:underline"
45
40
  href={contact.googleLink}
@@ -1,23 +1,18 @@
1
1
  import React from "react";
2
2
  import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
3
+ import { IconDefinition } from "@fortawesome/free-solid-svg-icons";
3
4
  import * as Icons from "@fortawesome/free-solid-svg-icons";
4
5
 
5
- export const getFontAwesomeIcon = (
6
- iconName: string
7
- ): React.ReactElement | null => {
6
+ export const getFontAwesomeIcon = (iconName: string, className = ""): React.ReactElement | null => {
8
7
  try {
9
- // Extract the required icon using bracket notation
10
- const iconKey = `fa${iconName.charAt(0).toUpperCase()}${iconName.slice(
11
- 1
12
- )}`;
13
- const icon = (Icons as any)[iconKey];
8
+ const iconKey = `fa${iconName.charAt(0).toUpperCase()}${iconName.slice(1)}`;
9
+ const icon = Icons[iconKey as keyof typeof Icons] as IconDefinition;
14
10
 
15
- if (!icon) {
16
- throw new Error(`Icon not found: ${iconName}`);
11
+ if (icon) {
12
+ return <FontAwesomeIcon icon={icon} className={className} />;
13
+ } else {
14
+ throw new Error(`Icon not found or invalid type: ${iconName}`);
17
15
  }
18
-
19
- // Render the FontAwesomeIcon with the selected icon
20
- return <FontAwesomeIcon icon={icon} />;
21
16
  } catch (error) {
22
17
  console.error(`Error loading Font Awesome icon: ${iconName}`, error);
23
18
  return null;