@agilant/toga-blox 1.0.5

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 (138) hide show
  1. package/Dockerfile +9 -0
  2. package/README.md +69 -0
  3. package/assets/Logo.png +0 -0
  4. package/assets/compass-card-image-2.png +0 -0
  5. package/assets/compass-card-image-3.png +0 -0
  6. package/assets/compass-card-image-4.png +0 -0
  7. package/assets/compass-card-image.png +0 -0
  8. package/assets/compass-logo.png +0 -0
  9. package/assets/compass-tech-hero-bg.png +0 -0
  10. package/assets/contact-image.png +0 -0
  11. package/assets/green-laptop.png +0 -0
  12. package/assets/heroImage.png +0 -0
  13. package/assets/placeholder-no-image-available.png +0 -0
  14. package/assets/team.png +0 -0
  15. package/declarations.d.ts +4 -0
  16. package/docker-compose.yml +22 -0
  17. package/global.css +4 -0
  18. package/index.js +4 -0
  19. package/nodemon.json +5 -0
  20. package/package.json +70 -0
  21. package/postcss.config.js +6 -0
  22. package/src/components/Badge/Badge.stories.tsx +284 -0
  23. package/src/components/Badge/Badge.test.tsx +185 -0
  24. package/src/components/Badge/Badge.tsx +137 -0
  25. package/src/components/Badge/Badge.types.tsx +28 -0
  26. package/src/components/Badge/badgeClassNames.tsx +152 -0
  27. package/src/components/Badge/index.ts +2 -0
  28. package/src/components/Card/Card.stories.tsx +91 -0
  29. package/src/components/Card/Card.test.tsx +53 -0
  30. package/src/components/Card/Card.tsx +30 -0
  31. package/src/components/Card/Card.types.ts +11 -0
  32. package/src/components/Card/DUMMYPRODUCTDATA.json +670 -0
  33. package/src/components/Card/cardClassNames.ts +49 -0
  34. package/src/components/Card/index.ts +3 -0
  35. package/src/components/Card/templates/CompassCardTemplate.tsx +58 -0
  36. package/src/components/Card/templates/HorizontalCardTemplate.tsx +184 -0
  37. package/src/components/Card/templates/VerticalCardTemplate.tsx +154 -0
  38. package/src/components/Footer/ContactInfoItem.tsx +20 -0
  39. package/src/components/Footer/DUMMYFOOTERDATA.json +132 -0
  40. package/src/components/Footer/Footer.stories.tsx +292 -0
  41. package/src/components/Footer/Footer.test.tsx +90 -0
  42. package/src/components/Footer/Footer.tsx +159 -0
  43. package/src/components/Footer/Footer.types.tsx +61 -0
  44. package/src/components/Footer/footerClassNames.tsx +57 -0
  45. package/src/components/FormButton/FormButton.stories.tsx +199 -0
  46. package/src/components/FormButton/FormButton.test.tsx +73 -0
  47. package/src/components/FormButton/FormButton.tsx +116 -0
  48. package/src/components/FormButton/FormButton.types.ts +32 -0
  49. package/src/components/FormButton/formButtonClassNames.tsx +153 -0
  50. package/src/components/FormButton/index.ts +2 -0
  51. package/src/components/GenericList/DUMMYLISTDATA.json +560 -0
  52. package/src/components/GenericList/GenericList.stories.tsx +104 -0
  53. package/src/components/GenericList/GenericList.test.tsx +29 -0
  54. package/src/components/GenericList/GenericList.tsx +146 -0
  55. package/src/components/GenericList/genericListClassNames.tsx +8 -0
  56. package/src/components/GenericList/templates/DummyDataList.tsx +23 -0
  57. package/src/components/GenericList/templates/DynamicIconList.tsx +74 -0
  58. package/src/components/HamburgerButton/HamburgerButton.tsx +68 -0
  59. package/src/components/HamburgerButton/HamburgerButton.types.tsx +6 -0
  60. package/src/components/HamburgerButton/index.ts +2 -0
  61. package/src/components/Header/DUMMYICONDATA.json +136 -0
  62. package/src/components/Header/Header.stories.tsx +521 -0
  63. package/src/components/Header/Header.test.tsx +323 -0
  64. package/src/components/Header/Header.tsx +289 -0
  65. package/src/components/Header/Header.types.ts +52 -0
  66. package/src/components/Header/headerClassNames.tsx +50 -0
  67. package/src/components/Header/headerContext.tsx +125 -0
  68. package/src/components/Header/index.ts +2 -0
  69. package/src/components/Hero/Hero.stories.tsx +69 -0
  70. package/src/components/Hero/Hero.test.tsx +109 -0
  71. package/src/components/Hero/Hero.tsx +58 -0
  72. package/src/components/Hero/Hero.types.ts +9 -0
  73. package/src/components/Hero/index.ts +2 -0
  74. package/src/components/Icon/Icon.stories.tsx +227 -0
  75. package/src/components/Icon/Icon.test.tsx +53 -0
  76. package/src/components/Icon/Icon.tsx +208 -0
  77. package/src/components/Icon/Icon.types.ts +24 -0
  78. package/src/components/Icon/iconClassNames.ts +79 -0
  79. package/src/components/Icon/index.ts +2 -0
  80. package/src/components/Image/Image.stories.tsx +79 -0
  81. package/src/components/Image/Image.test.tsx +87 -0
  82. package/src/components/Image/Image.tsx +49 -0
  83. package/src/components/Image/Image.types.ts +11 -0
  84. package/src/components/Image/index.ts +2 -0
  85. package/src/components/Input/Input.stories.tsx +651 -0
  86. package/src/components/Input/Input.test.tsx +90 -0
  87. package/src/components/Input/Input.tsx +226 -0
  88. package/src/components/Input/Input.types.ts +52 -0
  89. package/src/components/Input/InputMemoTypes.tsx +32 -0
  90. package/src/components/Input/index.ts +2 -0
  91. package/src/components/Input/inputClassNames.tsx +169 -0
  92. package/src/components/MobileMenu/MobileMenu.tsx +41 -0
  93. package/src/components/MobileMenu/MobileMenu.types.tsx +30 -0
  94. package/src/components/MobileMenu/index.ts +2 -0
  95. package/src/components/Nav/DUMMYNAVDATA.json +234 -0
  96. package/src/components/Nav/Nav.stories.tsx +181 -0
  97. package/src/components/Nav/Nav.test.tsx +89 -0
  98. package/src/components/Nav/Nav.tsx +242 -0
  99. package/src/components/Nav/Nav.types.tsx +35 -0
  100. package/src/components/Nav/index.ts +2 -0
  101. package/src/components/Nav/navClassNames.tsx +192 -0
  102. package/src/components/Page/TableDataDummy.tsx +216 -0
  103. package/src/components/Page/ViewPageTemplate.stories.tsx +546 -0
  104. package/src/components/Page/ViewPageTemplate.test.tsx +361 -0
  105. package/src/components/Page/ViewPageTemplate.tsx +10 -0
  106. package/src/components/Page/ViewPageTemplate.types.ts +6 -0
  107. package/src/components/Page/index.ts +2 -0
  108. package/src/components/PageSection/PageSection.stories.tsx +114 -0
  109. package/src/components/PageSection/PageSection.tsx +12 -0
  110. package/src/components/PageSection/PageSection.types.ts +6 -0
  111. package/src/components/PageSection/PageSections.test.tsx +88 -0
  112. package/src/components/PageSection/index.ts +2 -0
  113. package/src/components/Text/Text.stories.tsx +60 -0
  114. package/src/components/Text/Text.test.tsx +52 -0
  115. package/src/components/Text/Text.tsx +80 -0
  116. package/src/components/Text/Text.types.ts +12 -0
  117. package/src/components/Text/index.ts +2 -0
  118. package/src/components/Toaster/Toaster.stories.tsx +122 -0
  119. package/src/components/Toaster/Toaster.test.tsx +61 -0
  120. package/src/components/Toaster/Toaster.tsx +80 -0
  121. package/src/components/Toaster/Toaster.types.ts +12 -0
  122. package/src/components/Toaster/index.ts +2 -0
  123. package/src/hoc/index.ts +2 -0
  124. package/src/hoc/styling/withStoryBook.tsx +19 -0
  125. package/src/main.css +3 -0
  126. package/src/setupTests.ts +1 -0
  127. package/src/userHoc/index.ts +1 -0
  128. package/src/userHoc/withMemo.tsx +20 -0
  129. package/src/utils/assertTagName.tsx +7 -0
  130. package/src/utils/generateAccordionItem.tsx +102 -0
  131. package/src/utils/generateFooterContacts.tsx +75 -0
  132. package/src/utils/generateNavMenu.tsx +54 -0
  133. package/src/utils/generateSocialList.tsx +34 -0
  134. package/src/utils/getFontAwesomeIcon.tsx +25 -0
  135. package/src/utils/inputValidation.tsx +26 -0
  136. package/tailwind.config.js +32 -0
  137. package/tsconfig.json +25 -0
  138. package/vite.config.ts +33 -0
@@ -0,0 +1,292 @@
1
+ import React from "react";
2
+ import { Meta, StoryFn } from "@storybook/react";
3
+ import Footer from "./Footer";
4
+ import { FooterProps } from "./Footer.types";
5
+ import Image from "../Image/Image";
6
+ import Text from "../Text/Text";
7
+
8
+ import { withStoryBook } from "../../hoc";
9
+
10
+ // Wrap your component with the HOC
11
+ const StoryBookViewFooter = withStoryBook(Footer);
12
+
13
+ export default {
14
+ title: "Footers/Footer",
15
+ argTypes: {
16
+ // LOGO & BACKGROUND & FOOTER BAR
17
+ logo: {
18
+ control: "none",
19
+ description: "The logo displayed in the footer",
20
+ },
21
+ logoBorderRadius: {
22
+ control: "boolean",
23
+ description: "Whether the logo has a border radius.",
24
+ },
25
+ logoHoverColor: {
26
+ control: "select",
27
+ options: ["green", "blue", "none"],
28
+ description: "Whether the logo has a background color on hover.",
29
+ },
30
+ backgroundColor: {
31
+ control: "color",
32
+ description: "Whether the footer has a background color.",
33
+ },
34
+
35
+ // TITLE AND SLOGAN
36
+ title: {
37
+ control: "text",
38
+ description: "The title displayed in the footer",
39
+ },
40
+ slogan: {
41
+ control: "text",
42
+ description: "The slogan displayed in the footer",
43
+ },
44
+ hasTitle: {
45
+ control: "boolean",
46
+ description: "Whether the footer has a title displayed",
47
+ },
48
+ hasSlogan: {
49
+ control: "boolean",
50
+ description: "Whether the footer has a slogan displayed",
51
+ },
52
+
53
+ // NAV
54
+ hasNavItems: {
55
+ control: "boolean",
56
+ description: "Whether the footer has navigation items.",
57
+ },
58
+ accordionGap: {
59
+ table: {
60
+ disable: true,
61
+ },
62
+ },
63
+ accordionParentStyle: {
64
+ table: {
65
+ disable: true,
66
+ },
67
+ },
68
+ accordionExpandedStyle: {
69
+ table: {
70
+ disable: true,
71
+ },
72
+ },
73
+
74
+ // SOCIAL
75
+ socialTitle: {
76
+ table: {
77
+ disable: true,
78
+ },
79
+ },
80
+ socialPlacement: {
81
+ control: "select",
82
+ options: ["left", "center", "right"],
83
+ description: "Where the social icons are placed.",
84
+ },
85
+ hasSocial: {
86
+ control: "boolean",
87
+ description: "Whether the footer has social icons displayed",
88
+ },
89
+
90
+ // CONTACT
91
+ contactTitle: {
92
+ table: {
93
+ disable: true,
94
+ },
95
+ },
96
+
97
+ // COPYRIGHT
98
+ copyRightTextPlacement: {
99
+ control: "select",
100
+ options: ["left", "center", "right"],
101
+ description: "Where the copyright statement is placed.",
102
+ },
103
+ copyRightText: {
104
+ table: {
105
+ disable: true,
106
+ },
107
+ description: "Whether the footer displays a copyright statement.",
108
+ },
109
+ hasCopyRight: {
110
+ control: "boolean",
111
+ description:
112
+ "Whether the footer has a copyright statement displayed",
113
+ },
114
+ },
115
+ } as Meta<FooterProps>;
116
+
117
+ const Template: StoryFn<typeof StoryBookViewFooter> = (args) => (
118
+ <StoryBookViewFooter
119
+ {...args}
120
+ storybookStyle={{
121
+ backgroundColor: args.backgroundColor,
122
+ }}
123
+ />
124
+ );
125
+
126
+ export const Default = Template.bind({});
127
+ Default.args = {
128
+ logoBorderRadius: true,
129
+ hasNavItems: false,
130
+ hasSocial: true,
131
+ logoHoverColor: "blue",
132
+ backgroundColor: "gray",
133
+ contactTitle: (
134
+ <Text
135
+ size={"md"}
136
+ color={"black"}
137
+ text="Contact Us:"
138
+ fontFamily={""}
139
+ additionalClasses="font-bold uppercase"
140
+ />
141
+ ),
142
+ copyRightText: (
143
+ <Text
144
+ size={"md"}
145
+ color={"black"}
146
+ text="Copyright © 2024 - All right reserved by Generic Company"
147
+ fontFamily={""}
148
+ additionalClasses="text-center"
149
+ />
150
+ ),
151
+ copyRightTextPlacement: "center",
152
+ socialPlacement: "center",
153
+ };
154
+
155
+ export const CompassFooter = Template.bind({});
156
+ CompassFooter.args = {
157
+ logo: (
158
+ <Image
159
+ src="../../../assets/compass-logo.png"
160
+ alt="Generic Compass Logo."
161
+ background={false}
162
+ additionalClasses=" w-40 p-2"
163
+ />
164
+ ),
165
+ title: (
166
+ <Text
167
+ size={"xl"}
168
+ color={""}
169
+ text={"Compass Website"}
170
+ fontFamily={""}
171
+ tag={"h2"}
172
+ additionalClasses="font-bold uppercase pl-2 mt-2 max-md:text-sm"
173
+ />
174
+ ),
175
+ slogan: (
176
+ <Text
177
+ size={"md"}
178
+ color={""}
179
+ text={"Great slogan goes here."}
180
+ fontFamily={""}
181
+ tag={"p"}
182
+ additionalClasses="pl-2"
183
+ />
184
+ ),
185
+ copyRightText: (
186
+ <Text
187
+ size={"md"}
188
+ color={"black"}
189
+ text="Copyright © 2024 - All right reserved by Generic Company"
190
+ fontFamily={""}
191
+ additionalClasses="text-center"
192
+ />
193
+ ),
194
+ socialTitle: (
195
+ <Text
196
+ size={"md"}
197
+ color={"black"}
198
+ text="Connect"
199
+ fontFamily={""}
200
+ additionalClasses="font-bold uppercase w-full"
201
+ />
202
+ ),
203
+ contactTitle: (
204
+ <Text
205
+ size={"md"}
206
+ color={"black"}
207
+ text="Contact Us:"
208
+ fontFamily={""}
209
+ additionalClasses="font-bold uppercase"
210
+ />
211
+ ),
212
+ logoBorderRadius: true,
213
+ hasNavItems: true,
214
+ hasSocial: true,
215
+ logoHoverColor: "green",
216
+ backgroundColor: "green",
217
+ accordionGap: "gap-1",
218
+ copyRightTextPlacement: "center",
219
+ socialPlacement: "center",
220
+ accordionParentStyle: "border-b border-black p-2 w-full",
221
+ accordionExpandedStyle: "pl-3 py-3",
222
+ };
223
+
224
+ export const AlternateMobileNavStyle = Template.bind({});
225
+ AlternateMobileNavStyle.args = {
226
+ logo: (
227
+ <Image
228
+ src="../../../assets/compass-logo.png"
229
+ alt="Generic Compass Logo."
230
+ background={false}
231
+ additionalClasses=" w-40 p-2"
232
+ />
233
+ ),
234
+ title: (
235
+ <Text
236
+ size={"xl"}
237
+ color={""}
238
+ text={"Compass Website"}
239
+ fontFamily={""}
240
+ tag={"h2"}
241
+ additionalClasses="font-bold uppercase pl-2 mt-2 max-md:text-sm"
242
+ />
243
+ ),
244
+ slogan: (
245
+ <Text
246
+ size={"md"}
247
+ color={""}
248
+ text={"Great slogan goes here."}
249
+ fontFamily={""}
250
+ tag={"p"}
251
+ additionalClasses="pl-2"
252
+ />
253
+ ),
254
+ copyRightText: (
255
+ <Text
256
+ size={"md"}
257
+ color={"black"}
258
+ text="Copyright © 2024 - All right reserved by Generic Company"
259
+ fontFamily={""}
260
+ additionalClasses="text-center"
261
+ />
262
+ ),
263
+ socialTitle: (
264
+ <Text
265
+ size={"md"}
266
+ color={"black"}
267
+ text="Connect"
268
+ fontFamily={""}
269
+ additionalClasses="font-bold uppercase w-full"
270
+ />
271
+ ),
272
+ contactTitle: (
273
+ <Text
274
+ size={"md"}
275
+ color={"black"}
276
+ text="Contact Us:"
277
+ fontFamily={""}
278
+ additionalClasses="font-bold uppercase"
279
+ />
280
+ ),
281
+ logoBorderRadius: true,
282
+ hasNavItems: true,
283
+ hasSocial: true,
284
+ accordionGap: "gap-2",
285
+ logoHoverColor: "green",
286
+ backgroundColor: "green",
287
+ copyRightTextPlacement: "center",
288
+ socialPlacement: "center",
289
+ accordionParentStyle:
290
+ "rounded-md border-black p-2 w-full bg-teal-700 text-white",
291
+ accordionExpandedStyle: "pl-2 py-3",
292
+ };
@@ -0,0 +1,90 @@
1
+ import "../../../dist/main.css";
2
+ import { render, screen, within } from "@testing-library/react";
3
+ import { describe, test, expect, beforeEach } from "vitest";
4
+
5
+ import Footer from "./Footer";
6
+ import Image from "../Image/Image";
7
+ import {
8
+ DUMMYCONTACTDATA,
9
+ DUMMYNAVDATA,
10
+ DUMMYSOCIALDATA,
11
+ } from "./DUMMYFOOTERDATA.json";
12
+
13
+ describe("<Footer /> with all props (pill, icons, input, logo)", () => {
14
+ beforeEach(() => {
15
+ render(
16
+ <Footer
17
+ logo={
18
+ <Image
19
+ src="../../../assets/Logo.png"
20
+ alt="Generic Company Logo."
21
+ background={false}
22
+ additionalClasses=" w-40 "
23
+ />
24
+ }
25
+ hasSlogan={true}
26
+ hasTitle={false}
27
+ slogan="Great slogan goes here."
28
+ backgroundColor={"gray"}
29
+ logoBorderRadius
30
+ logoHoverColor={"blue"}
31
+ hasNavItems={true}
32
+ hasSocial={true}
33
+ hasContact={true}
34
+ socialData={DUMMYSOCIALDATA}
35
+ navData={DUMMYNAVDATA}
36
+ contactData={DUMMYCONTACTDATA}
37
+ />
38
+ );
39
+ });
40
+
41
+ test("renders Footer component", () => {
42
+ expect(screen.getByTestId("footer")).toBeInTheDocument();
43
+ });
44
+
45
+ test("renders correct background color class", () => {
46
+ const footer = screen.getByTestId("footer");
47
+ expect(footer).toHaveClass("bg-slate-200");
48
+ });
49
+
50
+ test("contains correct logo from Image component", () => {
51
+ const footerLogo = screen.getByTestId("footer-logo");
52
+ const logoImage = footerLogo.querySelector("img");
53
+ expect(logoImage).toHaveAttribute("src", "../../../assets/Logo.png");
54
+ });
55
+
56
+ test("contains correct alt text from Image component", () => {
57
+ const footerLogo = screen.getByTestId("footer-logo");
58
+ const logoImage = footerLogo.querySelector("img");
59
+ expect(logoImage).toHaveAttribute("alt", "Generic Company Logo.");
60
+ });
61
+
62
+ test("contains correct slogan", () => {
63
+ const titleSloganElement = screen.getByTestId("title-slogan");
64
+
65
+ const sloganElement = within(titleSloganElement).getByTestId("slogan");
66
+ expect(sloganElement).toBeInTheDocument();
67
+ expect(sloganElement).toHaveTextContent("Great slogan goes here.");
68
+ });
69
+
70
+ test("contains correct site navigation ", () => {
71
+ expect(
72
+ screen.getByTestId("footer-site-navigation")
73
+ ).toBeInTheDocument();
74
+
75
+ expect(screen.getByTestId("About")).toBeInTheDocument();
76
+ expect(screen.getByTestId("Projects")).toBeInTheDocument();
77
+ expect(screen.getByTestId("Community")).toBeInTheDocument();
78
+ expect(screen.getByTestId("Store")).toBeInTheDocument();
79
+ });
80
+
81
+ test("contains correct social navigation ", () => {
82
+ expect(
83
+ screen.getByTestId("footer-social-navigation")
84
+ ).toBeInTheDocument();
85
+
86
+ expect(screen.getByTestId("Twitter")).toBeInTheDocument();
87
+ expect(screen.getByTestId("YouTube")).toBeInTheDocument();
88
+ expect(screen.getByTestId("Facebook")).toBeInTheDocument();
89
+ });
90
+ });
@@ -0,0 +1,159 @@
1
+ import React from "react";
2
+ import Image from "../Image/Image";
3
+
4
+ import {
5
+ DUMMYCONTACTDATA,
6
+ DUMMYNAVDATA,
7
+ DUMMYSOCIALDATA,
8
+ } from "./DUMMYFOOTERDATA.json";
9
+
10
+ import {
11
+ getLogoClasses,
12
+ getBackgroundColor,
13
+ getSocialPlacement,
14
+ getCopyRightPlacement,
15
+ } from "./footerClassNames";
16
+
17
+ import type { FooterProps } from "./Footer.types";
18
+ import { SocialList } from "../../utils/generateSocialList";
19
+ import { NavList } from "../../utils/generateNavMenu";
20
+ import { Accordion } from "../../utils/generateAccordionItem";
21
+ import { FooterContacts } from "../../utils/generateFooterContacts";
22
+
23
+ const Footer: React.FC<FooterProps> = ({
24
+ logo = (
25
+ <Image
26
+ src="../../../assets/Logo.png"
27
+ alt="Generic Company Logo."
28
+ background={false}
29
+ additionalClasses=" w-40 p-2"
30
+ />
31
+ ),
32
+ hasTitle,
33
+ hasSlogan,
34
+ title,
35
+ slogan,
36
+ backgroundColor,
37
+ logoBorderRadius,
38
+ logoHoverColor,
39
+ hasNavItems = true,
40
+ hasSocial = true,
41
+ hasContact = true,
42
+ hasCopyRight = true,
43
+ copyRightText,
44
+ socialData = DUMMYSOCIALDATA,
45
+ navData = DUMMYNAVDATA,
46
+ contactData = DUMMYCONTACTDATA,
47
+ socialTitle,
48
+ contactTitle,
49
+ copyRightTextPlacement,
50
+ socialPlacement,
51
+ accordionParentStyle = "border-2 rounded-md border-black p-2 w-full",
52
+ accordionExpandedStyle = "pl-3",
53
+ accordionGap,
54
+ }) => {
55
+ let logoClasses = getLogoClasses(logoHoverColor, logoBorderRadius);
56
+ let backgroundColorClasses = getBackgroundColor(backgroundColor);
57
+ let socialPlacementClasses = getSocialPlacement(socialPlacement);
58
+ let copyRightTextPlacementClasses = getCopyRightPlacement(
59
+ copyRightTextPlacement
60
+ );
61
+
62
+ return (
63
+ // TODO: screen reader does not read all content of footer.
64
+ // not sure if this is a storybook issue, or if it is a code issue
65
+ <footer
66
+ className={`footer flex flex-col ${backgroundColorClasses} py-6 px-6`}
67
+ data-testid="footer"
68
+ >
69
+ <div className="flex justify-between w-full max-xl:px-2 max-lg:flex max-lg:flex-col max-xl:gap-10">
70
+ <div className="max-lg:flex max-lg:justify-between max-lg:w-full">
71
+ <div className={logoClasses}>
72
+ <a tabIndex={0} href="" data-testid="footer-logo">
73
+ {logo}
74
+ </a>
75
+ </div>
76
+ <div data-testid="title-slogan">
77
+ {hasTitle ? (
78
+ <div data-testid="title">{title}</div>
79
+ ) : (
80
+ <></>
81
+ )}
82
+ {hasSlogan ? (
83
+ <div data-testid="slogan">{slogan}</div>
84
+ ) : (
85
+ <></>
86
+ )}
87
+ </div>
88
+ </div>
89
+ {hasNavItems ? (
90
+ <>
91
+ <nav
92
+ className={`flex gap-12 max-sm:hidden`}
93
+ aria-label="Site navigation."
94
+ data-testid="footer-site-navigation"
95
+ tabIndex={0}
96
+ >
97
+ <NavList navDataList={navData} />
98
+ </nav>
99
+ <nav
100
+ className={`hidden flex-col ${accordionGap} col-span-6 w-3/4 max-sm:w-full max-sm:flex`}
101
+ aria-label="Site navigation."
102
+ data-testid="footer-mobile-navigation"
103
+ >
104
+ <>
105
+ <Accordion
106
+ navData={navData}
107
+ accordionParentStyle={accordionParentStyle}
108
+ accordionExpandedStyle={
109
+ accordionExpandedStyle
110
+ }
111
+ />
112
+ </>
113
+ </nav>
114
+ </>
115
+ ) : (
116
+ <></>
117
+ )}
118
+ {hasContact ? (
119
+ <div
120
+ className="flex flex-col space-between"
121
+ data-testid="footer-contact-info"
122
+ >
123
+ {contactTitle ? contactTitle : <></>}
124
+ <FooterContacts contactDataList={contactData} />
125
+ </div>
126
+ ) : (
127
+ <></>
128
+ )}
129
+ </div>
130
+ {hasSocial ? (
131
+ <nav
132
+ className={`w-full ${socialPlacementClasses}`}
133
+ aria-hidden="false"
134
+ aria-label="Social media navigation."
135
+ data-testid="footer-social-navigation"
136
+ >
137
+ {socialTitle ? socialTitle : <></>}
138
+ <div className="grid grid-flow-col gap-3">
139
+ <SocialList socialDataList={socialData} />
140
+ </div>
141
+ </nav>
142
+ ) : (
143
+ <></>
144
+ )}
145
+ {hasCopyRight ? (
146
+ <div
147
+ className={`w-full ${copyRightTextPlacementClasses}`}
148
+ aria-hidden="false"
149
+ >
150
+ {copyRightText}
151
+ </div>
152
+ ) : (
153
+ <></>
154
+ )}
155
+ </footer>
156
+ );
157
+ };
158
+
159
+ export default Footer;
@@ -0,0 +1,61 @@
1
+ import {
2
+ DUMMYCONTACTDATA,
3
+ DUMMYNAVDATA,
4
+ DUMMYSOCIALDATA,
5
+ } from "./DUMMYFOOTERDATA.json";
6
+
7
+ export interface NavItem {
8
+ title: string;
9
+ links: Array<{ link: string; menuItem: string }>;
10
+ }
11
+
12
+ export interface SocialItem {
13
+ href: string;
14
+ xmlns: string;
15
+ ariaLabel: string;
16
+ path: string;
17
+ }
18
+
19
+
20
+ export interface FooterProps {
21
+ onClick?: (
22
+ e: React.MouseEvent<HTMLButtonElement | HTMLAnchorElement>
23
+ ) => void;
24
+ // LOGO
25
+ hasLogo?: boolean;
26
+ logo?: React.ReactNode;
27
+ backgroundColor?: string;
28
+ logoHoverColor?: string;
29
+ logoBorderRadius?: boolean;
30
+
31
+ // TITLE AND SLOGAN
32
+ hasTitle?: boolean;
33
+ hasSlogan?: boolean;
34
+ title?: React.ReactNode;
35
+ slogan?: React.ReactNode;
36
+
37
+ // NAV
38
+ hasNavItems?: boolean;
39
+ navData?: typeof DUMMYNAVDATA;
40
+ accordionParentStyle?: string;
41
+ accordionExpandedStyle?: string;
42
+ accordionGap?: string;
43
+
44
+ // SOCIAL
45
+ hasSocial?: boolean;
46
+ socialData?: typeof DUMMYSOCIALDATA;
47
+ socialTitle?: React.ReactNode;
48
+ socialPlacement?: "left" | "center" | "right";
49
+
50
+ // CONTACT
51
+ contactTitle?: React.ReactNode;
52
+ hasContact?: boolean;
53
+ contactData?: typeof DUMMYCONTACTDATA;
54
+
55
+ // COPYRIGHT
56
+ hasCopyRight?: boolean;
57
+ copyRightText?: React.ReactNode;
58
+ copyRightTextPlacement?: "left" | "center" | "right";
59
+
60
+ children?: React.ReactNode;
61
+ }
@@ -0,0 +1,57 @@
1
+ import classNames from "classNames";
2
+
3
+ export const getLogoClasses = (
4
+ logoHoverColor: string | undefined,
5
+ logoBorderRadius: boolean | undefined
6
+ ) => {
7
+ return classNames(
8
+ "flex",
9
+ "justify-center",
10
+ {
11
+ "rounded-full": logoBorderRadius === true,
12
+ "rounded-none": logoBorderRadius === false,
13
+ },
14
+ {
15
+ "hover:bg-blue-300": logoHoverColor === "blue",
16
+ "hover:bg-teal-300": logoHoverColor === "green",
17
+ }
18
+ );
19
+ };
20
+
21
+
22
+ export const getBackgroundColor = (
23
+ backgroundColor: string | undefined,
24
+ ) => {
25
+ return classNames(
26
+ {
27
+ "bg-teal-50": backgroundColor === "green",
28
+ "bg-slate-200": backgroundColor === "gray",
29
+ "bg-transparent": backgroundColor === "none",
30
+ },
31
+ );
32
+ };
33
+
34
+ export const getSocialPlacement = (
35
+ socialPlacement: string | undefined,
36
+ ) => {
37
+ return classNames(
38
+ {
39
+ "justify-start pl-4": socialPlacement === "left",
40
+ "justify-center text-center": socialPlacement === "center",
41
+ "justify-end": socialPlacement === "right",
42
+ }
43
+ );
44
+ };
45
+
46
+ export const getCopyRightPlacement = (
47
+ copyRightTextPlacement: string | undefined,
48
+ ) => {
49
+ return classNames(
50
+ {
51
+ "justify-start pl-4": copyRightTextPlacement === "left",
52
+ "justify-center": copyRightTextPlacement === "center",
53
+ "justify-end": copyRightTextPlacement === "right",
54
+ }
55
+ );
56
+ };
57
+