@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.
- package/assets/cable.jpg +0 -0
- package/assets/card-1.jpg +0 -0
- package/assets/cat-logo.png +0 -0
- package/assets/item.jpg +0 -0
- package/assets/map.png +0 -0
- package/coverage/base.css +224 -0
- package/coverage/block-navigation.js +87 -0
- package/coverage/clover.xml +953 -0
- package/coverage/coverage-final.json +74 -0
- package/coverage/favicon.png +0 -0
- package/coverage/index.html +551 -0
- package/coverage/prettify.css +1 -0
- package/coverage/prettify.js +2 -0
- package/coverage/sort-arrow-sprite.png +0 -0
- package/coverage/sorter.js +196 -0
- package/coverage/toga-blox-npm/index.html +131 -0
- package/coverage/toga-blox-npm/postcss.config.js.html +103 -0
- package/coverage/toga-blox-npm/src/components/Badge/Badge.stories.tsx.html +793 -0
- package/coverage/toga-blox-npm/src/components/Badge/Badge.tsx.html +247 -0
- package/coverage/toga-blox-npm/src/components/Badge/index.html +131 -0
- package/coverage/toga-blox-npm/src/components/Card/Card.stories.tsx.html +787 -0
- package/coverage/toga-blox-npm/src/components/Card/Card.tsx.html +163 -0
- package/coverage/toga-blox-npm/src/components/Card/index.html +131 -0
- package/coverage/toga-blox-npm/src/components/Card/templates/CategoryCardTemplate.tsx.html +343 -0
- package/coverage/toga-blox-npm/src/components/Card/templates/CompassCardTemplate.tsx.html +259 -0
- package/coverage/toga-blox-npm/src/components/Card/templates/CounterContentCardTemplate.tsx.html +685 -0
- package/coverage/toga-blox-npm/src/components/Card/templates/HorizontalCardTemplate.tsx.html +637 -0
- package/coverage/toga-blox-npm/src/components/Card/templates/ItemCardTemplate.tsx.html +550 -0
- package/coverage/toga-blox-npm/src/components/Card/templates/KitContentCardTemplate.tsx.html +469 -0
- package/coverage/toga-blox-npm/src/components/Card/templates/ShippingAddressCardTemplate.tsx.html +418 -0
- package/coverage/toga-blox-npm/src/components/Card/templates/VerticalCardTemplate.tsx.html +592 -0
- package/coverage/toga-blox-npm/src/components/Card/templates/index.html +221 -0
- package/coverage/toga-blox-npm/src/components/CounterButton/CounterButton.stories.tsx.html +358 -0
- package/coverage/toga-blox-npm/src/components/CounterButton/CounterButton.tsx.html +385 -0
- package/coverage/toga-blox-npm/src/components/CounterButton/index.html +131 -0
- package/coverage/toga-blox-npm/src/components/Description/Description.stories.tsx.html +286 -0
- package/coverage/toga-blox-npm/src/components/Description/Description.tsx.html +124 -0
- package/coverage/toga-blox-npm/src/components/Description/index.html +131 -0
- package/coverage/toga-blox-npm/src/components/DropDownIconButton/DropDownIconButton.stories.tsx.html +676 -0
- package/coverage/toga-blox-npm/src/components/DropDownIconButton/DropDownIconButton.tsx.html +346 -0
- package/coverage/toga-blox-npm/src/components/DropDownIconButton/index.html +131 -0
- package/coverage/toga-blox-npm/src/components/Footer/ContactInfoItem.tsx.html +139 -0
- package/coverage/toga-blox-npm/src/components/Footer/Footer.stories.tsx.html +934 -0
- package/coverage/toga-blox-npm/src/components/Footer/Footer.tsx.html +373 -0
- package/coverage/toga-blox-npm/src/components/Footer/index.html +146 -0
- package/coverage/toga-blox-npm/src/components/FormButton/FormButton.stories.tsx.html +952 -0
- package/coverage/toga-blox-npm/src/components/FormButton/FormButton.tsx.html +343 -0
- package/coverage/toga-blox-npm/src/components/FormButton/index.html +131 -0
- package/coverage/toga-blox-npm/src/components/GenericList/GenericList.stories.tsx.html +376 -0
- package/coverage/toga-blox-npm/src/components/GenericList/GenericList.tsx.html +520 -0
- package/coverage/toga-blox-npm/src/components/GenericList/index.html +131 -0
- package/coverage/toga-blox-npm/src/components/GenericList/templates/DummyDataList.tsx.html +154 -0
- package/coverage/toga-blox-npm/src/components/GenericList/templates/DynamicIconList.tsx.html +250 -0
- package/coverage/toga-blox-npm/src/components/GenericList/templates/index.html +131 -0
- package/coverage/toga-blox-npm/src/components/GetSupport/GetSupport.stories.tsx.html +325 -0
- package/coverage/toga-blox-npm/src/components/GetSupport/GetSupport.tsx.html +262 -0
- package/coverage/toga-blox-npm/src/components/GetSupport/index.html +131 -0
- package/coverage/toga-blox-npm/src/components/HamburgerButton/Hamburger.stories.tsx.html +760 -0
- package/coverage/toga-blox-npm/src/components/HamburgerButton/HamburgerButton.tsx.html +232 -0
- package/coverage/toga-blox-npm/src/components/HamburgerButton/index.html +131 -0
- package/coverage/toga-blox-npm/src/components/Header/Header.stories.tsx.html +1633 -0
- package/coverage/toga-blox-npm/src/components/Header/Header.tsx.html +814 -0
- package/coverage/toga-blox-npm/src/components/Header/headerContext.tsx.html +460 -0
- package/coverage/toga-blox-npm/src/components/Header/index.html +146 -0
- package/coverage/toga-blox-npm/src/components/Hero/Hero.stories.tsx.html +289 -0
- package/coverage/toga-blox-npm/src/components/Hero/Hero.tsx.html +259 -0
- package/coverage/toga-blox-npm/src/components/Hero/index.html +131 -0
- package/coverage/toga-blox-npm/src/components/IconButton/IconButton.stories.tsx.html +673 -0
- package/coverage/toga-blox-npm/src/components/IconButton/IconButton.tsx.html +313 -0
- package/coverage/toga-blox-npm/src/components/IconButton/index.html +131 -0
- package/coverage/toga-blox-npm/src/components/Image/Image.stories.tsx.html +322 -0
- package/coverage/toga-blox-npm/src/components/Image/Image.tsx.html +226 -0
- package/coverage/toga-blox-npm/src/components/Image/index.html +131 -0
- package/coverage/toga-blox-npm/src/components/Input/Input.stories.tsx.html +1621 -0
- package/coverage/toga-blox-npm/src/components/Input/Input.tsx.html +568 -0
- package/coverage/toga-blox-npm/src/components/Input/InputMemoTypes.tsx.html +181 -0
- package/coverage/toga-blox-npm/src/components/Input/index.html +146 -0
- package/coverage/toga-blox-npm/src/components/MobileMenu/MobileMenu.tsx.html +208 -0
- package/coverage/toga-blox-npm/src/components/MobileMenu/index.html +116 -0
- package/coverage/toga-blox-npm/src/components/Nav/Nav.stories.tsx.html +628 -0
- package/coverage/toga-blox-npm/src/components/Nav/Nav.tsx.html +622 -0
- package/coverage/toga-blox-npm/src/components/Nav/index.html +131 -0
- package/coverage/toga-blox-npm/src/components/Page/TableDataDummy.tsx.html +733 -0
- package/coverage/toga-blox-npm/src/components/Page/ViewPageTemplate.stories.tsx.html +1714 -0
- package/coverage/toga-blox-npm/src/components/Page/ViewPageTemplate.tsx.html +115 -0
- package/coverage/toga-blox-npm/src/components/Page/index.html +146 -0
- package/coverage/toga-blox-npm/src/components/PageSection/PageSection.stories.tsx.html +433 -0
- package/coverage/toga-blox-npm/src/components/PageSection/PageSection.tsx.html +121 -0
- package/coverage/toga-blox-npm/src/components/PageSection/index.html +131 -0
- package/coverage/toga-blox-npm/src/components/SearchInput/SearchInput.stories.tsx.html +517 -0
- package/coverage/toga-blox-npm/src/components/SearchInput/SearchInput.tsx.html +325 -0
- package/coverage/toga-blox-npm/src/components/SearchInput/index.html +131 -0
- package/coverage/toga-blox-npm/src/components/Slider/Slider.stories.tsx.html +349 -0
- package/coverage/toga-blox-npm/src/components/Slider/Slider.tsx.html +502 -0
- package/coverage/toga-blox-npm/src/components/Slider/index.html +131 -0
- package/coverage/toga-blox-npm/src/components/Submenus/AdminSubmenu.tsx.html +136 -0
- package/coverage/toga-blox-npm/src/components/Submenus/AlertSubmenu.tsx.html +253 -0
- package/coverage/toga-blox-npm/src/components/Submenus/AlertSubmenuItem.tsx.html +202 -0
- package/coverage/toga-blox-npm/src/components/Submenus/index.html +146 -0
- package/coverage/toga-blox-npm/src/components/Text/Text.stories.tsx.html +235 -0
- package/coverage/toga-blox-npm/src/components/Text/Text.tsx.html +172 -0
- package/coverage/toga-blox-npm/src/components/Text/index.html +131 -0
- package/coverage/toga-blox-npm/src/components/Toaster/Toaster.stories.tsx.html +445 -0
- package/coverage/toga-blox-npm/src/components/Toaster/Toaster.tsx.html +301 -0
- package/coverage/toga-blox-npm/src/components/Toaster/index.html +131 -0
- package/coverage/toga-blox-npm/src/hoc/styling/index.html +116 -0
- package/coverage/toga-blox-npm/src/hoc/styling/withStoryBook.tsx.html +142 -0
- package/coverage/toga-blox-npm/src/userHoc/index.html +116 -0
- package/coverage/toga-blox-npm/src/userHoc/withMemo.tsx.html +145 -0
- package/coverage/toga-blox-npm/src/utils/assertTagName.tsx.html +106 -0
- package/coverage/toga-blox-npm/src/utils/generateAccordionItem.tsx.html +373 -0
- package/coverage/toga-blox-npm/src/utils/generateFooterContacts.tsx.html +295 -0
- package/coverage/toga-blox-npm/src/utils/generateNavMenu.tsx.html +247 -0
- package/coverage/toga-blox-npm/src/utils/generateSocialList.tsx.html +187 -0
- package/coverage/toga-blox-npm/src/utils/getFontAwesomeIcon.tsx.html +145 -0
- package/coverage/toga-blox-npm/src/utils/index.html +206 -0
- package/coverage/toga-blox-npm/src/utils/inputValidation.tsx.html +163 -0
- package/coverage/toga-blox-npm/tailwind.config.js.html +205 -0
- package/declarations.d.ts +1 -1
- package/package.json +5 -6
- package/src/components/Badge/Badge.stories.tsx +110 -207
- package/src/components/Badge/Badge.test.tsx +40 -41
- package/src/components/Badge/Badge.tsx +29 -99
- package/src/components/Badge/Badge.types.tsx +12 -23
- package/src/components/Card/Card.stories.tsx +166 -22
- package/src/components/Card/Card.test.tsx +3 -3
- package/src/components/Card/Card.tsx +12 -16
- package/src/components/Card/DUMMYPRODUCTDATA.json +381 -225
- package/src/components/Card/templates/CategoryCardTemplate.tsx +86 -0
- package/src/components/Card/templates/CompassCardTemplate.tsx +1 -1
- package/src/components/Card/templates/CounterContentCardTemplate.tsx +200 -0
- package/src/components/Card/templates/ItemCardTemplate.tsx +155 -0
- package/src/components/Card/templates/KitContentCardTemplate.tsx +128 -0
- package/src/components/Card/templates/ShippingAddressCardTemplate.tsx +111 -0
- package/src/components/Card/templates/VerticalCardTemplate.tsx +100 -85
- package/src/components/CounterButton/CounterButton.tsx +1 -1
- package/src/components/Description/Description.stories.tsx +67 -0
- package/src/components/Description/Description.tsx +13 -0
- package/src/components/Description/Description.types.ts +9 -0
- package/src/components/DropDownIconButton/DropDownIconButton.stories.tsx +197 -0
- package/src/components/DropDownIconButton/DropDownIconButton.test.tsx +90 -0
- package/src/components/DropDownIconButton/DropDownIconButton.tsx +87 -0
- package/src/components/DropDownIconButton/DropDownIconButton.types.ts +21 -0
- package/src/components/DropDownIconButton/index.ts +2 -0
- package/src/components/Footer/ContactInfoItem.tsx +8 -10
- package/src/components/Footer/Footer.stories.tsx +13 -22
- package/src/components/Footer/Footer.tsx +25 -88
- package/src/components/Footer/Footer.types.tsx +2 -2
- package/src/components/FormButton/FormButton.stories.tsx +101 -226
- package/src/components/FormButton/FormButton.test.tsx +1 -1
- package/src/components/FormButton/FormButton.tsx +34 -42
- package/src/components/FormButton/FormButton.types.ts +7 -13
- package/src/components/GenericList/GenericList.stories.tsx +5 -12
- package/src/components/GenericList/GenericList.tsx +91 -92
- package/src/components/GenericList/templates/DynamicIconList.tsx +45 -64
- package/src/components/GetSupport/GetSupport.stories.tsx +80 -0
- package/src/components/GetSupport/GetSupport.test.tsx +62 -0
- package/src/components/GetSupport/GetSupport.tsx +59 -0
- package/src/components/GetSupport/GetSupport.types.ts +11 -0
- package/src/components/HamburgerButton/Hamburger.stories.tsx +225 -0
- package/src/components/HamburgerButton/HamburgerButton.tsx +37 -56
- package/src/components/HamburgerButton/HamburgerButton.types.tsx +2 -1
- package/src/components/Header/Header.stories.tsx +3 -5
- package/src/components/Header/Header.tsx +37 -83
- package/src/components/Hero/Hero.stories.tsx +1 -2
- package/src/components/IconButton/IconButton.stories.tsx +196 -0
- package/src/components/{Icon/Icon.test.tsx → IconButton/IconButton.test.tsx} +3 -4
- package/src/components/IconButton/IconButton.tsx +76 -0
- package/src/components/IconButton/IconButton.types.ts +28 -0
- package/src/components/IconButton/index.ts +2 -0
- package/src/components/Image/Image.tsx +1 -1
- package/src/components/Input/Input.stories.tsx +10 -12
- package/src/components/Input/Input.test.tsx +2 -3
- package/src/components/Input/Input.tsx +41 -104
- package/src/components/MobileMenu/MobileMenu.types.tsx +0 -2
- package/src/components/Nav/DUMMYNAVDATA.json +4 -4
- package/src/components/Nav/Nav.tsx +49 -112
- package/src/components/Nav/Nav.types.tsx +14 -2
- package/src/components/Page/ViewPageTemplate.stories.tsx +2 -2
- package/src/components/Page/ViewPageTemplate.test.tsx +1 -1
- package/src/components/Page/ViewPageTemplate.types.ts +1 -1
- package/src/components/PageSection/PageSection.stories.tsx +3 -1
- package/src/components/PageSection/PageSections.test.tsx +2 -1
- package/src/components/SearchInput/SearchInput.stories.tsx +144 -0
- package/src/components/SearchInput/SearchInput.tsx +81 -0
- package/src/components/SearchInput/SearchInput.types.ts +28 -0
- package/src/components/Slider/Slider.stories.tsx +88 -0
- package/src/components/Slider/Slider.tsx +139 -0
- package/src/components/Slider/Slider.types.ts +21 -0
- package/src/components/Submenus/AdminSubmenu.tsx +17 -0
- package/src/components/Submenus/AlertSubmenu.tsx +56 -0
- package/src/components/Submenus/AlertSubmenuItem.tsx +39 -0
- package/src/components/Submenus/types.tsx +32 -0
- package/src/components/Toaster/Toaster.stories.tsx +4 -6
- package/src/components/Toaster/Toaster.test.tsx +3 -4
- package/src/components/Toaster/Toaster.tsx +9 -17
- package/src/components/Toaster/Toaster.types.ts +2 -2
- package/src/utils/assertTagName.tsx +1 -1
- package/src/utils/generateAccordionItem.tsx +7 -13
- package/src/utils/generateFooterContacts.tsx +4 -9
- package/src/utils/getFontAwesomeIcon.tsx +8 -13
- package/tailwind.config.js +11 -3
- package/src/components/Badge/badgeClassNames.tsx +0 -173
- package/src/components/Card/cardClassNames.ts +0 -49
- package/src/components/Footer/footerClassNames.tsx +0 -57
- package/src/components/FormButton/formButtonClassNames.tsx +0 -153
- package/src/components/GenericList/genericListClassNames.tsx +0 -8
- package/src/components/Header/headerClassNames.tsx +0 -50
- package/src/components/Icon/Icon.stories.tsx +0 -227
- package/src/components/Icon/Icon.tsx +0 -208
- package/src/components/Icon/Icon.types.ts +0 -24
- package/src/components/Icon/iconClassNames.ts +0 -79
- package/src/components/Icon/index.ts +0 -2
- package/src/components/Input/inputClassNames.tsx +0 -169
- 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 {
|
|
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:
|
|
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:
|
|
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:
|
|
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 {
|
|
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={
|
|
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
|
|
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
|
|
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
|
-
<
|
|
48
|
+
<span
|
|
57
49
|
data-testid="clearMessage"
|
|
58
50
|
onClick={handleClearMessage}
|
|
59
51
|
className={`cursor-pointer ${textColorClass}`}
|
|
60
|
-
|
|
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=
|
|
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:
|
|
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 {
|
|
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
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
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
|
-
|
|
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:
|
|
22
|
+
icon: getFontAwesomeIcon("phone"),
|
|
28
23
|
text: contact.phone,
|
|
29
24
|
},
|
|
30
25
|
{
|
|
31
|
-
icon:
|
|
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={
|
|
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
|
-
|
|
10
|
-
const
|
|
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 (
|
|
16
|
-
|
|
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;
|