@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,185 @@
1
+ import { render, screen } from "@testing-library/react";
2
+ import { describe, expect, beforeEach, test } from "vitest";
3
+ import Badge from "./Badge";
4
+ import Text from "../Text/Text";
5
+ import Image from "../Image/Image";
6
+ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
7
+ import {
8
+ faCircleExclamation,
9
+ faCheck,
10
+ } from "@fortawesome/free-solid-svg-icons";
11
+
12
+ describe("<Badge />", () => {
13
+ beforeEach(() => {
14
+ render(
15
+ <Badge
16
+ type="span"
17
+ color="red"
18
+ borderColor="red"
19
+ hoverColor="none"
20
+ badgeSize="large"
21
+ testId="priority-badge"
22
+ mobileIcon={<FontAwesomeIcon icon={faCircleExclamation} />}
23
+ mobileIconLabel="Priority"
24
+ text={
25
+ <Text
26
+ size="md"
27
+ color="black"
28
+ text="Priority"
29
+ fontFamily="serif"
30
+ tag="h2"
31
+ additionalClasses="font-bold"
32
+ />
33
+ }
34
+ />
35
+ );
36
+ });
37
+
38
+ test("renders badge", () => {
39
+ const badge = screen.getByTestId("priority-badge");
40
+ expect(badge).toBeInTheDocument();
41
+ });
42
+
43
+ test("has correct text", () => {
44
+ expect(screen.getByTestId("priority-badge")).toHaveTextContent("Priority");
45
+ });
46
+
47
+ test("has correct background color", () => {
48
+ const badge = screen.getByTestId("priority-badge");
49
+ expect(badge).toHaveClass("bg-red-200");
50
+ });
51
+
52
+ test("has correct border color", () => {
53
+ const badge = screen.getByTestId("priority-badge");
54
+ expect(badge).toHaveClass("border-red-500");
55
+ });
56
+
57
+ });
58
+
59
+ describe("<Badge /> with icon", () => {
60
+ beforeEach(() => {
61
+ render(
62
+ <Badge
63
+ type="span"
64
+ color="green"
65
+ borderColor="green"
66
+ hoverColor="none"
67
+ badgeSize="large"
68
+ testId="icon-badge"
69
+ tagStyle={false}
70
+ cursorPointer={false}
71
+ icon={<FontAwesomeIcon icon={faCheck} />}
72
+ mobileIcon={<FontAwesomeIcon icon={faCheck} />}
73
+ mobileIconLabel="Complete"
74
+ text={
75
+ <Text
76
+ size="md"
77
+ color="black"
78
+ text="Complete"
79
+ fontFamily="serif"
80
+ tag="h2"
81
+ additionalClasses="font-bold"
82
+ />
83
+ }
84
+ />
85
+ );
86
+ });
87
+
88
+ test("renders badge with icon", () => {
89
+ const badge = screen.getByTestId("icon-badge");
90
+ expect(badge).toBeInTheDocument();
91
+ });
92
+
93
+ test("has correct text", () => {
94
+ expect(screen.getByTestId("icon-badge")).toHaveTextContent("Complete");
95
+ });
96
+
97
+ test("has correct background color", () => {
98
+ const badge = screen.getByTestId("icon-badge");
99
+ expect(badge).toHaveClass("bg-teal-100");
100
+ });
101
+
102
+ test("has correct border color", () => {
103
+ const badge = screen.getByTestId("icon-badge");
104
+ expect(badge).toHaveClass("border-teal-700");
105
+ });
106
+
107
+ test("has correct icon", () => {
108
+ const icon = screen.getByTestId("badge-icon");
109
+ expect(icon).toBeInTheDocument();
110
+ });
111
+
112
+ });
113
+
114
+ describe("<Badge /> with pill style", () => {
115
+ beforeEach(() => {
116
+ render(
117
+ <Badge
118
+ type="href"
119
+ to="#"
120
+ color="blue"
121
+ borderColor="none"
122
+ hoverColor="blue"
123
+ testId="pill-badge"
124
+ tagStyle={false}
125
+ cursorPointer={false}
126
+ icon={<FontAwesomeIcon icon={faCheck} />}
127
+ mobileIcon={<FontAwesomeIcon icon={faCheck} />}
128
+ mobileIconLabel="Complete"
129
+ text={
130
+ <>
131
+ <Text
132
+ size="md"
133
+ color="primary"
134
+ fontFamily="serif"
135
+ text="Contact Us"
136
+ tag="h2"
137
+ additionalClasses="pl-2 pb-0 font-bold"
138
+ />
139
+ <Text
140
+ size="md"
141
+ color="primary"
142
+ fontFamily="serif"
143
+ text="1-800-800-8000"
144
+ tag="p"
145
+ additionalClasses="pl-2 pt-0"
146
+ />
147
+ </>
148
+ }
149
+ image={
150
+ <Image
151
+ src="../../../assets/contact-image.png"
152
+ alt=""
153
+ background={false}
154
+ additionalClasses="flex justify-center w-12 h-12 max-md:hidden"
155
+ />
156
+ }
157
+ />
158
+ );
159
+ });
160
+
161
+ test("renders badge with pill style", () => {
162
+ const pill = screen.getByTestId("pill-badge");
163
+ expect(pill).toBeInTheDocument();
164
+ });
165
+
166
+ test("has correct text", () => {
167
+ expect(screen.getByTestId("badge-text")).toHaveTextContent("Contact Us");
168
+ });
169
+
170
+ test("has correct text", () => {
171
+ expect(screen.getByTestId("badge-text")).toHaveTextContent(
172
+ "1-800-800-8000"
173
+ );
174
+ });
175
+
176
+ test("has correct background color", () => {
177
+ const pill = screen.getByTestId("pill-badge");
178
+ expect(pill).toHaveClass("bg-blue-200");
179
+ });
180
+
181
+ test("has correct image", () => {
182
+ const pillImage = screen.getByTestId("image");
183
+ expect(pillImage).toBeInTheDocument();
184
+ });
185
+ });
@@ -0,0 +1,137 @@
1
+ import React from "react";
2
+ import { Link } from "react-router-dom";
3
+ import { BadgeTypes } from "./Badge.types";
4
+
5
+ import {
6
+ getHoverClasses,
7
+ getIconClasses,
8
+ getColorClassNames,
9
+ getBadgeClasses,
10
+ getPillStyleClasses,
11
+ getBadgeSizeClasses,
12
+ getTagClasses,
13
+ } from "./badgeClassNames";
14
+
15
+ const Badge: React.FC<BadgeTypes> = ({
16
+ onClick,
17
+ color = "blue",
18
+ badgeSize,
19
+ cursorPointer = true,
20
+ type = "span",
21
+ to,
22
+ href,
23
+ testId,
24
+ hoverColor = "blue",
25
+ borderColor,
26
+ fontColor = "black",
27
+ hasMobileStyle,
28
+ mobileIcon,
29
+ mobileIconLabel,
30
+ additionalClasses,
31
+ icon,
32
+ iconOrder = "first",
33
+ image,
34
+ tagStyle,
35
+ tagStyleIconColor,
36
+ text,
37
+ }) => {
38
+ let badgeColorClasses = getColorClassNames(color, borderColor, fontColor);
39
+ let badgeSizeClasses = getBadgeSizeClasses(badgeSize);
40
+ let hoverClasses = getHoverClasses(hoverColor, cursorPointer);
41
+ let iconClasses = getIconClasses(iconOrder, tagStyle, tagStyleIconColor);
42
+
43
+ let badgeClasses = getBadgeClasses({
44
+ badgeColorClasses,
45
+ hoverClasses,
46
+ badgeSizeClasses,
47
+ additionalClasses,
48
+ });
49
+
50
+ let pillClasses = getPillStyleClasses({
51
+ badgeColorClasses,
52
+ hoverClasses,
53
+ additionalClasses,
54
+ });
55
+
56
+ let tagClasses = getTagClasses({
57
+ badgeColorClasses,
58
+ hoverClasses,
59
+ additionalClasses,
60
+ });
61
+
62
+ const handleClick = (
63
+ e: React.MouseEvent<HTMLAnchorElement | HTMLButtonElement>
64
+ ) => {
65
+ e.preventDefault();
66
+ onClick?.(e);
67
+ };
68
+
69
+ const baseProps = {
70
+ className: image
71
+ ? pillClasses.trim()
72
+ : tagStyle
73
+ ? tagClasses.trim()
74
+ : badgeClasses.trim(),
75
+ onClick: handleClick,
76
+ type: type,
77
+ "data-testid": testId,
78
+ };
79
+
80
+ switch (type) {
81
+ case "href":
82
+ return (
83
+ <a {...baseProps} href={href}>
84
+ {renderContent()}
85
+ </a>
86
+ );
87
+ case "to":
88
+ if (to) {
89
+ return (
90
+ <Link {...baseProps} to={to}>
91
+ {renderContent()}
92
+ </Link>
93
+ );
94
+ }
95
+ break;
96
+ case "span":
97
+ return <span {...baseProps}>{renderContent()}</span>;
98
+ default:
99
+ return null;
100
+ }
101
+
102
+ function renderContent() {
103
+ return (
104
+ <>
105
+ {image && <span>{image}</span>}
106
+ {icon && (
107
+ <span
108
+ className={`${iconClasses} max-md:hidden`}
109
+ data-testid="badge-icon"
110
+ >
111
+ <>{icon}</>
112
+ </span>
113
+ )}
114
+ {mobileIcon && hasMobileStyle && (
115
+ <span
116
+ className={`${iconClasses} hidden max-md:flex max-md:p-0`}
117
+ data-testid="mobile-badge-icon"
118
+ aria-hidden="false"
119
+ aria-label={mobileIconLabel}
120
+ >
121
+ <>{mobileIcon}</>
122
+ </span>
123
+ )}
124
+ <div
125
+ className={` ${hasMobileStyle ? "max-md:hidden" : ""} ${
126
+ tagStyle ? "px-3" : ""
127
+ }`}
128
+ data-testid="badge-text"
129
+ >
130
+ {text}
131
+ </div>
132
+ </>
133
+ );
134
+ }
135
+ };
136
+
137
+ export default Badge;
@@ -0,0 +1,28 @@
1
+ import React, { ElementType } from "react";
2
+
3
+ export interface BadgeTypes {
4
+ onClick?: (
5
+ e: React.MouseEvent<HTMLButtonElement | HTMLAnchorElement>
6
+ ) => void;
7
+ text?: React.ReactNode;
8
+ cursorPointer?: boolean;
9
+ type: "href" | "to" | "span";
10
+ badgeSize?: string | undefined;
11
+ image?: React.ReactNode;
12
+ color?: string | undefined;
13
+ fontColor?: string | undefined;
14
+ borderColor?: string | undefined;
15
+ to?: string;
16
+ href?: string;
17
+ hoverColor: string | undefined;
18
+ additionalClasses?: string;
19
+ hasMobileStyle?: boolean;
20
+ mobileIcon?: JSX.Element | Element | null;
21
+ mobileIconLabel?: string;
22
+ children?: React.ReactNode;
23
+ icon?: JSX.Element | Element | null;
24
+ iconOrder?: "first" | "last";
25
+ testId?: string;
26
+ tagStyle?: boolean;
27
+ tagStyleIconColor?: string;
28
+ }
@@ -0,0 +1,152 @@
1
+ import classNames from "classNames";
2
+
3
+ export const getHoverClasses = (
4
+ hoverColor: string | undefined,
5
+ cursorPointer: boolean | undefined
6
+ ) => {
7
+ return classNames(
8
+ {
9
+ "hover:bg-red-800 hover:text-white": hoverColor === "red",
10
+ "hover:bg-teal-800 hover:text-white": hoverColor === "green",
11
+ "hover:bg-blue-800 hover:text-white": hoverColor === "blue",
12
+ "hover:bg-orange-700 hover:text-white": hoverColor === "orange",
13
+ "hover:bg-purple-700 hover:text-white": hoverColor === "purple",
14
+ "hover:bg-slate-950 hover:text-white": hoverColor === "black",
15
+
16
+ "": hoverColor === "none",
17
+ },
18
+ {
19
+ "cursor-pointer": cursorPointer === true,
20
+ "": cursorPointer === false,
21
+ }
22
+ );
23
+ };
24
+
25
+ export const getIconClasses = (
26
+ iconOrder: string | undefined,
27
+ tagStyle: boolean | undefined,
28
+ tagStyleIconColor: string | undefined
29
+ ) => {
30
+ return classNames({
31
+ "flex items-center order-first pr-2":
32
+ iconOrder === "first" && tagStyle === false,
33
+ "flex items-center order-last pl-2":
34
+ iconOrder === "last" && tagStyle === false,
35
+ "text-white bg-red-500 flex justify-center items-center w-8 h-8 rounded-full max-md:text-black max-md:bg-transparent":
36
+ tagStyle === true && tagStyleIconColor === "red",
37
+ "text-white bg-blue-600 flex justify-center items-center w-8 h-8 rounded-full max-md:text-black max-md:bg-transparent":
38
+ tagStyle === true && tagStyleIconColor === "blue",
39
+ "text-white bg-teal-700 flex justify-center items-center w-8 h-8 rounded-full max-md:text-black max-md:bg-transparent":
40
+ tagStyle === true && tagStyleIconColor === "green",
41
+ "text-white bg-orange-700 flex justify-center items-center w-8 h-8 rounded-full max-md:text-black max-md:bg-transparent":
42
+ tagStyle === true && tagStyleIconColor === "orange",
43
+ "text-white bg-purple-700 flex justify-center items-center w-8 h-8 rounded-full max-md:text-black max-md:bg-transparent":
44
+ tagStyle === true && tagStyleIconColor === "purple",
45
+ "text-white bg-slate-950 flex justify-center items-center w-8 h-8 rounded-full max-md:text-black max-md:bg-transparent":
46
+ tagStyle === true && tagStyleIconColor === "black",
47
+ });
48
+ };
49
+
50
+ export const getBadgeSizeClasses = (badgeSize: string | undefined) => {
51
+ return classNames({
52
+ "badge-sm px-4 py-2": badgeSize === "small",
53
+ "badge-md px-4 py-3": badgeSize === "medium",
54
+ "badge-lg px-6 py-3": badgeSize === "large",
55
+ });
56
+ };
57
+
58
+ export const getColorClassNames = (
59
+ color: string | undefined,
60
+ borderColor: string | undefined,
61
+ fontColor: string | undefined
62
+ ) => {
63
+ return classNames(
64
+ {
65
+ "bg-blue-200": color === "blue",
66
+ "bg-red-200": color === "red",
67
+ "bg-teal-100": color === "green",
68
+ "bg-orange-200": color === "orange",
69
+ "bg-purple-200": color === "purple",
70
+ "bg-slate-950": color === "black",
71
+ "bg-white": color === "white",
72
+ "bg-transparent border-black": color === "clear",
73
+ },
74
+ {
75
+ "border-blue-600": borderColor === "blue",
76
+ "border-teal-700": borderColor === "green",
77
+ "border-red-500": borderColor === "red",
78
+ "border-orange-500": color === "orange",
79
+ "border-slate-950": borderColor === "black",
80
+ "border-purple-500": color === "purple",
81
+ "border-transparent": borderColor === "none",
82
+ },
83
+ {
84
+ "text-white": fontColor === "white" && color !== "clear",
85
+ "text-black": fontColor === "black",
86
+ }
87
+ );
88
+ };
89
+
90
+ interface BadgeClassProps {
91
+ badgeColorClasses: string;
92
+ hoverClasses: string;
93
+ additionalClasses?: string;
94
+ badgeSizeClasses?: string;
95
+ }
96
+
97
+ interface PillStyleClassProps {
98
+ badgeColorClasses: string;
99
+ hoverClasses: string;
100
+ additionalClasses?: string;
101
+ }
102
+
103
+ interface TagProps {
104
+ badgeColorClasses: string;
105
+ hoverClasses: string;
106
+ additionalClasses?: string;
107
+ }
108
+
109
+ export const getBadgeClasses = ({
110
+ badgeColorClasses,
111
+ hoverClasses,
112
+ additionalClasses,
113
+ badgeSizeClasses,
114
+ }: BadgeClassProps) => {
115
+ return classNames(
116
+ "badge border-2 rounded-full flex justify-center items-center",
117
+ badgeColorClasses,
118
+ hoverClasses,
119
+ additionalClasses,
120
+ badgeSizeClasses
121
+ );
122
+ };
123
+
124
+ export const getPillStyleClasses = ({
125
+ badgeColorClasses,
126
+ hoverClasses,
127
+ additionalClasses,
128
+ hasMobileStyle,
129
+ }: PillStyleClassProps & { hasMobileStyle?: boolean }) => {
130
+ return classNames(
131
+ "flex justify-center items-center w-full border-2 p-2 rounded-full",
132
+ hasMobileStyle ? "max-md:w-12 max-md:h-12 max-md:p-4" : "",
133
+ badgeColorClasses,
134
+ hoverClasses,
135
+ additionalClasses
136
+ );
137
+ };
138
+
139
+ export const getTagClasses = ({
140
+ badgeColorClasses,
141
+ hoverClasses,
142
+ additionalClasses,
143
+ hasMobileStyle,
144
+ }: TagProps & { hasMobileStyle?: boolean }) => {
145
+ return classNames(
146
+ "flex justify-between items-center w-fit border-2 rounded-full",
147
+ hasMobileStyle ? "max-md:w-12 max-md:h-6 max-md:p-2" : "",
148
+ badgeColorClasses,
149
+ hoverClasses,
150
+ additionalClasses
151
+ );
152
+ };
@@ -0,0 +1,2 @@
1
+ export { default } from "./Badge";
2
+ export * from "./Badge.types";
@@ -0,0 +1,91 @@
1
+ import { Meta, StoryFn } from "@storybook/react";
2
+ import Card from "./Card";
3
+ import type { CardTypes } from ".";
4
+ import {
5
+ DUMMYPRODUCTDATA,
6
+ DUMMYCOMPASSPRODUCTDATA,
7
+ } from "./DUMMYPRODUCTDATA.json";
8
+ import HorizontalCardTemplate from "./templates/HorizontalCardTemplate";
9
+ import VerticalCardTemplate from "./templates/VerticalCardTemplate";
10
+ import CompassCardTemplate from "./templates/CompassCardTemplate";
11
+
12
+ export default {
13
+ title: "Components/Card",
14
+ component: Card,
15
+ argTypes: {
16
+ cardBorderRadius: {
17
+ control: "select",
18
+ options: ["cornered", "semiRounded", "rounded"],
19
+ description: "The border radius of the card.",
20
+ },
21
+ cardBackgroundColor: {
22
+ control: "select",
23
+ options: ["light blue", "light green", "gray", "white"],
24
+ description: "The background color of the card.",
25
+ },
26
+ cardBorderColor: {
27
+ control: "select",
28
+ options: ["blue", "green", "black", "none"],
29
+ description: "The color of the card border.",
30
+ },
31
+ cardBoxShadow: {
32
+ control: "boolean",
33
+ description: "Whether the card has box shadow or not.",
34
+ },
35
+ children: {
36
+ control: {
37
+ type: "none",
38
+ },
39
+ },
40
+ containerClasses: {
41
+ control: {
42
+ type: "none",
43
+ },
44
+ },
45
+ key: {
46
+ control: {
47
+ type: "none",
48
+ },
49
+ },
50
+ },
51
+ tags: ["autodocs"],
52
+ parameters: {
53
+ layout: "centered",
54
+ },
55
+ } as Meta;
56
+
57
+ const Template: StoryFn<CardTypes> = (args) => <Card {...args} />;
58
+
59
+ export const Default = Template.bind({});
60
+ Default.args = {
61
+ containerClasses: "flex w-full max-sm:w-1/2 max-sm:flex-col",
62
+ key: DUMMYPRODUCTDATA[0].id,
63
+ cardBorderRadius: "semiRounded",
64
+ cardBorderColor: "blue",
65
+ cardBackgroundColor: "light blue",
66
+ cardBoxShadow: false,
67
+ children: <HorizontalCardTemplate data={DUMMYPRODUCTDATA[0]} />,
68
+ };
69
+
70
+ export const VerticalCard = Template.bind({});
71
+ VerticalCard.args = {
72
+ containerClasses: "w-1/2 max-sm:w-1/2overflow-hidden rounded-md",
73
+ key: DUMMYPRODUCTDATA[0].id,
74
+ cardBorderRadius: "semiRounded",
75
+ cardBorderColor: "green",
76
+ cardBackgroundColor: "light green",
77
+ cardBoxShadow: true,
78
+ children: <VerticalCardTemplate data={DUMMYPRODUCTDATA[1]} />,
79
+ };
80
+
81
+ export const CompassCard = Template.bind({});
82
+ CompassCard.args = {
83
+ containerClasses:
84
+ "w-1/2 max-sm:w-1/2 overflow-hidden rounded-md transition ease-in-out delay-150 hover:-translate-y-1 hover:scale-110 duration-300",
85
+ key: DUMMYCOMPASSPRODUCTDATA[0].id,
86
+ cardBorderRadius: "cornered",
87
+ cardBorderColor: "green",
88
+ cardBackgroundColor: "green",
89
+ cardBoxShadow: true,
90
+ children: <CompassCardTemplate data={DUMMYCOMPASSPRODUCTDATA[0]} />,
91
+ };
@@ -0,0 +1,53 @@
1
+ import { render, screen } from "@testing-library/react";
2
+ import { describe, expect, beforeEach, test } from "vitest";
3
+ import Card from "./Card";
4
+ import { DUMMYCARDTESTDATA } from "./DUMMYPRODUCTDATA.json";
5
+ import CompassCardTemplate from "./templates/CompassCardTemplate";
6
+
7
+ describe("<Card />", () => {
8
+ beforeEach(() => {
9
+ render(
10
+ <Card
11
+ key={DUMMYCARDTESTDATA[0].id}
12
+ cardBorderRadius="cornered"
13
+ cardBorderColor="green"
14
+ cardBackgroundColor="none"
15
+ cardBoxShadow={true}
16
+ containerClasses="w-1/2 max-sm:w-1/2"
17
+ children={<CompassCardTemplate data={DUMMYCARDTESTDATA[0]} />}
18
+ />
19
+ );
20
+ });
21
+
22
+ test("renders card", () => {
23
+ const card = screen.getByTestId("card-container");
24
+ expect(card).toBeInTheDocument();
25
+ });
26
+
27
+ test("renders correct card text", () => {
28
+ const card = screen.getByTestId("card-container");
29
+ expect(card).toHaveTextContent(
30
+ "Compass Sales Lenovo TP X13 Yoga G3 Laptop"
31
+ );
32
+ });
33
+
34
+ test("renders correct card image", () => {
35
+ const card = screen.getByTestId("image");
36
+ expect(card).toHaveAttribute("src", "./assets/compass-card-image.png");
37
+ });
38
+
39
+ test("renders correct card background color for image", () => {
40
+ const card = screen.getByTestId("card-image-container");
41
+ expect(card).toHaveClass("bg-teal-500");
42
+ });
43
+
44
+ test("renders correct card border color", () => {
45
+ const card = screen.getByTestId("card-container");
46
+ expect(card).toHaveClass("border-teal-500");
47
+ });
48
+
49
+ test("renders correct card background color for text", () => {
50
+ const card = screen.getByTestId("card-text-container");
51
+ expect(card).toHaveClass("bg-slate-50");
52
+ });
53
+ });
@@ -0,0 +1,30 @@
1
+ import React from "react";
2
+ import type { CardTypes } from "./Card.types";
3
+ import { getCardStyleClasses } from "./cardClassNames";
4
+
5
+ const Card: React.FC<CardTypes> = ({
6
+ children,
7
+ containerClasses,
8
+ cardBackgroundColor,
9
+ cardBorderColor,
10
+ cardBorderRadius,
11
+ cardBoxShadow,
12
+ }) => {
13
+ let cardStyleClasses = getCardStyleClasses(
14
+ cardBorderRadius,
15
+ cardBorderColor,
16
+ cardBackgroundColor,
17
+ cardBoxShadow
18
+ );
19
+
20
+ return (
21
+ <div
22
+ className={`${containerClasses} ${cardStyleClasses}`}
23
+ data-testid="card-container"
24
+ >
25
+ {children}
26
+ </div>
27
+ );
28
+ };
29
+
30
+ export default Card;
@@ -0,0 +1,11 @@
1
+ import React from "react";
2
+
3
+ export interface CardTypes {
4
+ children?: React.ReactNode;
5
+ containerClasses?: string;
6
+ key?: string;
7
+ cardBackgroundColor?: string;
8
+ cardBorderColor?: string;
9
+ cardBorderRadius?: string;
10
+ cardBoxShadow?: boolean;
11
+ }