property-practice-ui 0.0.1

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 (189) hide show
  1. package/.prettierignore +3 -0
  2. package/.prettierrc +4 -0
  3. package/.storybook/main.ts +30 -0
  4. package/.storybook/preview.ts +32 -0
  5. package/.storybook/vitest.setup.ts +6 -0
  6. package/CHANGELOG.md +7 -0
  7. package/dist/index.js +10148 -0
  8. package/dist/index.mjs +10148 -0
  9. package/eslint.config.mjs +4 -0
  10. package/package.json +63 -0
  11. package/postcss.config.js +6 -0
  12. package/src/atoms/ArrowButton/ArrowButton.stories.tsx +52 -0
  13. package/src/atoms/ArrowButton/ArrowButton.tsx +71 -0
  14. package/src/atoms/Button/Button.stories.tsx +45 -0
  15. package/src/atoms/Button/Button.tsx +91 -0
  16. package/src/atoms/CheckboxItem/CheckboxItem.tsx +49 -0
  17. package/src/atoms/Description/Description.tsx +43 -0
  18. package/src/atoms/ExtendedButton/ExtendedButton.stories.tsx +61 -0
  19. package/src/atoms/ExtendedButton/ExtendedButton.tsx +40 -0
  20. package/src/atoms/FeatureItem/FeatureItem.stories.tsx +25 -0
  21. package/src/atoms/FeatureItem/FeatureItem.tsx +90 -0
  22. package/src/atoms/FormContainer/FormContainer.tsx +34 -0
  23. package/src/atoms/Header/Header.stories.tsx +22 -0
  24. package/src/atoms/Header/Header.tsx +53 -0
  25. package/src/atoms/Input/Input.stories.tsx +23 -0
  26. package/src/atoms/Input/Input.tsx +28 -0
  27. package/src/atoms/Label/Label.stories.tsx +28 -0
  28. package/src/atoms/Label/Label.tsx +53 -0
  29. package/src/atoms/Loader/Loader.tsx +49 -0
  30. package/src/atoms/Pill/Pill.stories.tsx +30 -0
  31. package/src/atoms/Pill/Pill.tsx +82 -0
  32. package/src/atoms/RadioItem/RadioItem.stories.tsx +25 -0
  33. package/src/atoms/RadioItem/RadioItem.tsx +54 -0
  34. package/src/atoms/SecondaryInput/SecondaryInput.stories.tsx +30 -0
  35. package/src/atoms/SecondaryInput/SecondaryInput.tsx +125 -0
  36. package/src/atoms/SocialButton/SocialButton.stories.tsx +79 -0
  37. package/src/atoms/SocialButton/SocialButton.tsx +90 -0
  38. package/src/atoms/TermsCheckbox/TermsCheckbox.stories.tsx +62 -0
  39. package/src/atoms/TermsCheckbox/TermsCheckbox.tsx +150 -0
  40. package/src/atoms/Text/Text.stories.tsx +32 -0
  41. package/src/atoms/Text/Text.tsx +49 -0
  42. package/src/atoms/TextButton/TextButton.stories.tsx +77 -0
  43. package/src/atoms/TextButton/TextButton.tsx +78 -0
  44. package/src/atoms/Textarea/Textarea.tsx +36 -0
  45. package/src/atoms/ToggleButton/ToggleButton.stories.tsx +32 -0
  46. package/src/atoms/ToggleButton/ToggleButton.tsx +106 -0
  47. package/src/atoms/index.ts +20 -0
  48. package/src/components/DynamicInput.tsx +54 -0
  49. package/src/components/FileUpload.tsx +123 -0
  50. package/src/components/Filter/Filter.tsx +33 -0
  51. package/src/components/ModeSwitch.tsx +66 -0
  52. package/src/components/NavMenu.tsx +83 -0
  53. package/src/components/SearchBar/Search.stories.tsx +25 -0
  54. package/src/components/SearchBar/Search.tsx +40 -0
  55. package/src/components/SortBy/SortBy.stories.tsx +27 -0
  56. package/src/components/SortBy/SortBy.tsx +45 -0
  57. package/src/components/Spinner.tsx +30 -0
  58. package/src/components/Table/Table.stories.tsx +56 -0
  59. package/src/components/Table/Table.tsx +25 -0
  60. package/src/components/TableInner/TableInner.tsx +27 -0
  61. package/src/components/TableInner/tableInner.stories.tsx +21 -0
  62. package/src/components/TableList.tsx +195 -0
  63. package/src/components/TableRow/TableRow.stories.tsx +55 -0
  64. package/src/components/TableRow/TableRow.tsx +343 -0
  65. package/src/components/Tabs/Tabs.stories.tsx +42 -0
  66. package/src/components/Tabs/Tabs.tsx +56 -0
  67. package/src/components/Toast.tsx +192 -0
  68. package/src/components/TopMenu.tsx +62 -0
  69. package/src/index.ts +20 -0
  70. package/src/molecules/Accordion/Accordion.stories.tsx +54 -0
  71. package/src/molecules/Accordion/Accordion.tsx +45 -0
  72. package/src/molecules/AccordionContent/AccordionContent.tsx +16 -0
  73. package/src/molecules/AccordionHeader/AccordionHeader.stories.tsx +31 -0
  74. package/src/molecules/AccordionHeader/AccordionHeader.tsx +67 -0
  75. package/src/molecules/Address/Address.stories.tsx +116 -0
  76. package/src/molecules/Address/Address.tsx +136 -0
  77. package/src/molecules/CTAContainer/CTAContainer.stories.tsx +67 -0
  78. package/src/molecules/CTAContainer/CTAContainer.tsx +98 -0
  79. package/src/molecules/Checkbox/Checkbox.tsx +60 -0
  80. package/src/molecules/ContentCard/ContentCard.stories.tsx +31 -0
  81. package/src/molecules/ContentCard/ContentCard.tsx +80 -0
  82. package/src/molecules/DocumentAccordionHeader/DocumentAccordionHeader.tsx +50 -0
  83. package/src/molecules/DocumentAccordionRow/DocumentAccordionRow.tsx +66 -0
  84. package/src/molecules/Dropdown/Dropdown.stories.tsx +39 -0
  85. package/src/molecules/Dropdown/Dropdown.tsx +89 -0
  86. package/src/molecules/EmptyState/EmptyState.stories.tsx +26 -0
  87. package/src/molecules/EmptyState/EmptyState.tsx +49 -0
  88. package/src/molecules/FAQAccordion/FAQAccordion.stories.tsx +63 -0
  89. package/src/molecules/FAQAccordion/FAQAccordion.tsx +98 -0
  90. package/src/molecules/FeatureContainer/FeatureContainer.stories.tsx +40 -0
  91. package/src/molecules/FeatureContainer/FeatureContainer.tsx +59 -0
  92. package/src/molecules/FileButton/FileButton.tsx +54 -0
  93. package/src/molecules/Input/Input.stories.tsx +30 -0
  94. package/src/molecules/Input/Input.tsx +31 -0
  95. package/src/molecules/InputContainer/InputContainer.tsx +60 -0
  96. package/src/molecules/JutaBrand/JutaBrand.stories.tsx +43 -0
  97. package/src/molecules/JutaBrand/JutaBrand.tsx +74 -0
  98. package/src/molecules/Modal/Modal.tsx +71 -0
  99. package/src/molecules/OverviewRowItem/OverviewRowItem.stories.tsx +23 -0
  100. package/src/molecules/OverviewRowItem/OverviewRowItem.tsx +39 -0
  101. package/src/molecules/PDFPreviewer/PDFPreviewer.tsx +42 -0
  102. package/src/molecules/PageLayout/PageLayout.tsx +60 -0
  103. package/src/molecules/ProductInfo/ProductInfo.stories.tsx +50 -0
  104. package/src/molecules/ProductInfo/ProductInfo.tsx +90 -0
  105. package/src/molecules/RadioGroup/RadioGroup.stories.tsx +47 -0
  106. package/src/molecules/RadioGroup/RadioGroup.tsx +55 -0
  107. package/src/molecules/RatesChart/RatesChart.stories.tsx +61 -0
  108. package/src/molecules/RatesChart/RatesChart.tsx +185 -0
  109. package/src/molecules/SideNav/SideNav.stories.tsx +64 -0
  110. package/src/molecules/SideNav/SideNav.tsx +140 -0
  111. package/src/molecules/SidePanel/SidePanel.stories.tsx +87 -0
  112. package/src/molecules/SidePanel/SidePanel.tsx +138 -0
  113. package/src/molecules/StepperHeaderTab/StepperHeaderTab.stories.tsx +23 -0
  114. package/src/molecules/StepperHeaderTab/StepperHeaderTab.tsx +43 -0
  115. package/src/molecules/Textarea/Textarea.tsx +29 -0
  116. package/src/molecules/index.ts +23 -0
  117. package/src/organism/ContactForm/ContactForm.stories.tsx +15 -0
  118. package/src/organism/ContactForm/ContactForm.tsx +54 -0
  119. package/src/organism/DocumentListAccordion/DocumentListAccordion.tsx +96 -0
  120. package/src/organism/FeatureCarousel/FeatureCarousel.stories.tsx +81 -0
  121. package/src/organism/FeatureCarousel/FeatureCarousel.tsx +162 -0
  122. package/src/organism/Footer/Footer.stories.tsx +77 -0
  123. package/src/organism/Footer/Footer.tsx +231 -0
  124. package/src/organism/Header/Header.stories.tsx +51 -0
  125. package/src/organism/Header/Header.tsx +76 -0
  126. package/src/organism/OverviewList/OverviewList.stories.tsx +43 -0
  127. package/src/organism/OverviewList/OverviewList.tsx +56 -0
  128. package/src/organism/ToastProvider/ToastProvider.tsx +40 -0
  129. package/src/organism/VersionLabel/VersionLabel.tsx +9 -0
  130. package/src/organism/index.ts +9 -0
  131. package/src/styles/tailwind.css +8 -0
  132. package/src/templates/AboutUs/AboutUs.stories.tsx +60 -0
  133. package/src/templates/AboutUs/AboutUs.tsx +97 -0
  134. package/src/templates/Contact/Contact.stories.tsx +62 -0
  135. package/src/templates/Contact/Contact.tsx +125 -0
  136. package/src/templates/FAQ/FAQ.stories.tsx +82 -0
  137. package/src/templates/FAQ/FAQ.tsx +91 -0
  138. package/src/templates/Features/Features.stories.tsx +94 -0
  139. package/src/templates/Features/Features.tsx +79 -0
  140. package/src/templates/Hero/Hero.stories.tsx +105 -0
  141. package/src/templates/Hero/Hero.tsx +139 -0
  142. package/src/templates/OtherProducts/OtherProducts.stories.tsx +77 -0
  143. package/src/templates/OtherProducts/OtherProducts.tsx +86 -0
  144. package/src/templates/index.ts +7 -0
  145. package/src/tokens/animations.ts +11 -0
  146. package/src/tokens/breakpoints.ts +7 -0
  147. package/src/tokens/colors.stories.tsx +77 -0
  148. package/src/tokens/colors.ts +59 -0
  149. package/src/tokens/radii.ts +6 -0
  150. package/src/tokens/sizes.ts +8 -0
  151. package/src/tokens/spaces.ts +21 -0
  152. package/src/types.ts +20 -0
  153. package/stories/Button.stories.ts +54 -0
  154. package/stories/Button.tsx +41 -0
  155. package/stories/Configure.mdx +364 -0
  156. package/stories/Header.stories.ts +34 -0
  157. package/stories/Header.tsx +71 -0
  158. package/stories/Page.stories.ts +33 -0
  159. package/stories/Page.tsx +91 -0
  160. package/stories/TopMenu.stories.tsx +51 -0
  161. package/stories/assets/accessibility.png +0 -0
  162. package/stories/assets/accessibility.svg +1 -0
  163. package/stories/assets/addon-library.png +0 -0
  164. package/stories/assets/assets.png +0 -0
  165. package/stories/assets/avif-test-image.avif +0 -0
  166. package/stories/assets/context.png +0 -0
  167. package/stories/assets/discord.svg +1 -0
  168. package/stories/assets/docs.png +0 -0
  169. package/stories/assets/figma-plugin.png +0 -0
  170. package/stories/assets/github.svg +1 -0
  171. package/stories/assets/share.png +0 -0
  172. package/stories/assets/styling.png +0 -0
  173. package/stories/assets/testing.png +0 -0
  174. package/stories/assets/theming.png +0 -0
  175. package/stories/assets/tutorials.svg +1 -0
  176. package/stories/assets/youtube.svg +1 -0
  177. package/stories/button.css +30 -0
  178. package/stories/header.css +32 -0
  179. package/stories/page.css +68 -0
  180. package/tailwind.config.js +34 -0
  181. package/tsconfig.json +8 -0
  182. package/types/index.ts +5 -0
  183. package/types/inputAttributes.ts +16 -0
  184. package/types/menuItem.ts +17 -0
  185. package/types/orderType.ts +2 -0
  186. package/types/tableListItem.ts +7 -0
  187. package/types/toast.ts +1 -0
  188. package/vitest.config.ts +37 -0
  189. package/vitest.shims.d.ts +1 -0
@@ -0,0 +1,60 @@
1
+ import { ReactElement, ReactNode } from 'react';
2
+ import styled from 'styled-components';
3
+ import { Header } from '../../atoms';
4
+ import Spinner from '../../components/Spinner';
5
+ import { colors } from '../../tokens/colors';
6
+ import { spaces } from '../../tokens/spaces';
7
+
8
+ interface PageLayoutProps {
9
+ title: string;
10
+ children: ReactNode;
11
+ isLoading?: boolean;
12
+ rightElement?: ReactElement
13
+ }
14
+
15
+ const Container = styled.div`
16
+ width: '100%';
17
+ min-height: '100vh';
18
+ padding-bottom: ${spaces[9]};
19
+ `;
20
+
21
+ const HeaderContainer = styled.div`
22
+ padding: ${spaces[4.5]} ${spaces[6]};
23
+ display: flex;
24
+ align-items: center;
25
+ justify-content: space-between;
26
+ `;
27
+
28
+ const ContentContainer = styled.div`
29
+ border-top: 1px solid ${colors.border.hover};
30
+ `;
31
+
32
+ const SpinnerContainer = styled.div`
33
+ display: flex;
34
+ width: 100%;
35
+ padding: ${spaces[6]};
36
+ align-items: center;
37
+ justify-content: center;
38
+ `;
39
+
40
+ export const PageLayout = ({
41
+ title,
42
+ children,
43
+ isLoading = false,
44
+ rightElement
45
+ }: PageLayoutProps) => {
46
+ return (
47
+ <Container>
48
+ <HeaderContainer>
49
+ <Header color="subtle">{title}</Header> {rightElement && rightElement}
50
+ </HeaderContainer>
51
+ {isLoading ? (
52
+ <SpinnerContainer>
53
+ <Spinner />
54
+ </SpinnerContainer>
55
+ ) : (
56
+ <ContentContainer>{children}</ContentContainer>
57
+ )}
58
+ </Container>
59
+ );
60
+ };
@@ -0,0 +1,50 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite';
2
+ import { ComponentProps } from 'react';
3
+ import { ProductInfo as Component } from './ProductInfo';
4
+
5
+ const meta: Meta = {
6
+ title: 'Molecules/ProductInfo',
7
+ component: Component,
8
+ };
9
+
10
+ export default meta;
11
+
12
+ export const ProductInfo: StoryObj<Omit<ComponentProps<typeof Component>, 'onButtonClick'>> = {
13
+ args: {
14
+ image: 'https://via.placeholder.com/180',
15
+ logoImage: 'https://via.placeholder.com/200x60?text=Logo',
16
+ title: 'Affordable & Professional Online Legal Contracts',
17
+ buttonText: 'Learn More',
18
+ titleVariant: 'h2',
19
+ titleColor: 'brand',
20
+ buttonArrowVariant: 'teal',
21
+ buttonTextBgVariant: 'brand',
22
+ buttonTextVariant: 'secondary',
23
+ },
24
+ argTypes: {
25
+ image: { control: 'text' },
26
+ logoImage: { control: 'text' },
27
+ title: { control: 'text' },
28
+ buttonText: { control: 'text' },
29
+ titleVariant: {
30
+ control: 'select',
31
+ options: ['h1', 'h2', 'h3'],
32
+ },
33
+ titleColor: {
34
+ control: 'select',
35
+ options: ['brand', 'primary', 'secondary', 'tertiary', 'subtle', 'light', 'error', 'blue'],
36
+ },
37
+ buttonArrowVariant: {
38
+ control: 'select',
39
+ options: ['brand', 'teal', 'blue'],
40
+ },
41
+ buttonTextBgVariant: {
42
+ control: 'select',
43
+ options: ['primary', 'secondary', 'tertiary', 'subtle', 'blue', 'brand', 'light', 'transparent'],
44
+ },
45
+ buttonTextVariant: {
46
+ control: 'select',
47
+ options: ['brand', 'primary', 'secondary', 'tertiary', 'subtle', 'light', 'error', 'blue'],
48
+ },
49
+ },
50
+ };
@@ -0,0 +1,90 @@
1
+ import styled from 'styled-components';
2
+ import { ExtendedButton, Header } from '../../atoms';
3
+ import { colors } from '../../tokens/colors';
4
+
5
+ type TextColor = keyof typeof colors.text;
6
+
7
+ const Container = styled.div`
8
+ display: flex;
9
+ gap: 1.5rem;
10
+ align-items: center;
11
+ max-width: 600px;
12
+ `;
13
+
14
+ const ImageWrapper = styled.div`
15
+ flex-shrink: 0;
16
+ width: 180px;
17
+ height: 180px;
18
+ border-radius: 8px;
19
+ overflow: hidden;
20
+
21
+ img {
22
+ width: 100%;
23
+ height: 100%;
24
+ object-fit: cover;
25
+ }
26
+ `;
27
+
28
+ const ContentWrapper = styled.div`
29
+ display: flex;
30
+ flex-direction: column;
31
+ gap: 1rem;
32
+ `;
33
+
34
+ const LogoWrapper = styled.div`
35
+ max-width: 200px;
36
+
37
+ img {
38
+ width: 100%;
39
+ height: auto;
40
+ }
41
+ `;
42
+
43
+ interface ProductInfoItemProps {
44
+ image: string;
45
+ logoImage: string;
46
+ title: string;
47
+ buttonText: string;
48
+ onButtonClick?: () => void;
49
+ titleVariant?: 'h1' | 'h2' | 'h3';
50
+ titleColor?: TextColor;
51
+ buttonArrowVariant?: 'brand' | 'teal' | 'blue';
52
+ buttonTextBgVariant?: 'primary' | 'secondary' | 'tertiary' | 'subtle' | 'blue' | 'brand' | 'light' | 'transparent';
53
+ buttonTextVariant?: 'brand' | 'primary' | 'secondary' | 'tertiary' | 'subtle' | 'light' | 'error' | 'blue';
54
+ }
55
+
56
+ export const ProductInfo = ({
57
+ image,
58
+ logoImage,
59
+ title,
60
+ buttonText,
61
+ onButtonClick,
62
+ titleVariant = 'h2',
63
+ titleColor = 'brand',
64
+ buttonArrowVariant = 'teal',
65
+ buttonTextBgVariant = 'brand',
66
+ buttonTextVariant = 'secondary',
67
+ }: ProductInfoItemProps) => {
68
+ return (
69
+ <Container>
70
+ <ImageWrapper>
71
+ <img src={image} alt="Product" />
72
+ </ImageWrapper>
73
+ <ContentWrapper>
74
+ <LogoWrapper>
75
+ <img src={logoImage} alt="Logo" />
76
+ </LogoWrapper>
77
+ <Header variant={titleVariant} color={titleColor}>
78
+ {title}
79
+ </Header>
80
+ <ExtendedButton
81
+ text={buttonText}
82
+ onClick={onButtonClick}
83
+ arrowVariant={buttonArrowVariant}
84
+ textBgVariant={buttonTextBgVariant}
85
+ textVariant={buttonTextVariant}
86
+ />
87
+ </ContentWrapper>
88
+ </Container>
89
+ );
90
+ };
@@ -0,0 +1,47 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite';
2
+ import { ComponentProps } from 'react';
3
+ import styled from 'styled-components';
4
+ import { Option } from '../../types';
5
+ import { RadioGroup as Component } from './RadioGroup';
6
+
7
+ const meta: Meta = {
8
+ title: 'Molecules/RadioGroup',
9
+ component: Component,
10
+ };
11
+
12
+ export default meta;
13
+
14
+ const options: Option[] = [
15
+ {
16
+ label: 'Sale',
17
+ value: 'SALE',
18
+ },
19
+ {
20
+ label: 'Lease',
21
+ value: 'LEASE',
22
+ },
23
+ ];
24
+
25
+ const Container = styled.div`
26
+ padding: 20px;
27
+ `;
28
+
29
+ export const RadioGroup: StoryObj<ComponentProps<typeof Component>> = {
30
+ args: {
31
+ label: 'Radio Group',
32
+ options,
33
+ value: options[0]?.value,
34
+ },
35
+ argTypes: {
36
+ label: { control: 'text' },
37
+ value: {
38
+ control: 'select',
39
+ options: options.map((option) => option.value),
40
+ },
41
+ },
42
+ render: (args) => (
43
+ <Container>
44
+ <Component {...args} />
45
+ </Container>
46
+ ),
47
+ };
@@ -0,0 +1,55 @@
1
+ import styled from 'styled-components';
2
+ import { Label, RadioItem } from '../../atoms';
3
+ import { spaces } from '../../tokens/spaces';
4
+ import { Option } from '../../types';
5
+
6
+ type RadioGroupProps<T extends string | number> = {
7
+ label?: string;
8
+ options: readonly Option<T>[];
9
+ value: T;
10
+ onClick: (value: T) => void;
11
+ className?: string;
12
+ };
13
+
14
+ const Container = styled.div``;
15
+
16
+ const OptionsContainer = styled.div`
17
+ margin-top: ${spaces[2]};
18
+ display: flex;
19
+ flex-direction: row;
20
+ flex: 1;
21
+ gap: ${spaces[2]};
22
+ `;
23
+
24
+ const LabelContainer = styled.div`
25
+ padding-left: ${spaces[1]};
26
+ `;
27
+
28
+ export const RadioGroup = <T extends string | number>({
29
+ label,
30
+ options,
31
+ value,
32
+ onClick,
33
+ className,
34
+ }: RadioGroupProps<T>) => {
35
+ return (
36
+ <Container className={className}>
37
+ {typeof label === 'string' && (
38
+ <LabelContainer>
39
+ <Label value={label} fontWeight="600" />
40
+ </LabelContainer>
41
+ )}
42
+ <OptionsContainer>
43
+ {options.map((option, idx) => (
44
+ <RadioItem
45
+ key={idx}
46
+ isSelected={value === option.value}
47
+ label={option.label}
48
+ disabled={option.disabled}
49
+ onClick={() => onClick(option.value)}
50
+ />
51
+ ))}
52
+ </OptionsContainer>
53
+ </Container>
54
+ );
55
+ };
@@ -0,0 +1,61 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite';
2
+ import { ComponentProps, useState } from 'react';
3
+ import { RatesChart as Component } from './RatesChart';
4
+
5
+ const meta: Meta = {
6
+ title: 'Molecules/RatesChart',
7
+ component: Component,
8
+ parameters: {
9
+ layout: 'fullscreen',
10
+ },
11
+ };
12
+
13
+ export default meta;
14
+
15
+ const ratesData = [
16
+ { transactionType: 'Transaction name', price: 'R10.00' },
17
+ { transactionType: 'Transaction name', price: 'R10.00' },
18
+ { transactionType: 'Transaction name', price: 'R10.00' },
19
+ { transactionType: 'Transaction name', price: 'R10.00' },
20
+ { transactionType: 'Transaction name', price: 'R10.00' },
21
+ { transactionType: 'Transaction name', price: 'R10.00' },
22
+ ];
23
+
24
+ export const RatesChart: StoryObj<Omit<ComponentProps<typeof Component>, 'onClick'>> = {
25
+ render: (args) => {
26
+ const [isOpen, setIsOpen] = useState(true);
27
+
28
+ return (
29
+ <>
30
+ <button
31
+ onClick={() => setIsOpen(true)}
32
+ style={{
33
+ padding: '1rem 2rem',
34
+ backgroundColor: '#0d9488',
35
+ color: '#ffffff',
36
+ border: 'none',
37
+ borderRadius: '4px',
38
+ cursor: 'pointer',
39
+ fontSize: '1rem',
40
+ }}
41
+ >
42
+ Open Rates Chart
43
+ </button>
44
+ {isOpen && (
45
+ <Component
46
+ {...args}
47
+ rates={ratesData}
48
+ onClose={() => setIsOpen(false)}
49
+ />
50
+ )}
51
+ </>
52
+ );
53
+ },
54
+ args: {
55
+ title: 'Rates chart',
56
+ },
57
+ argTypes: {
58
+ title: { control: 'text' },
59
+
60
+ },
61
+ };
@@ -0,0 +1,185 @@
1
+ import { HiX } from 'react-icons/hi';
2
+ import styled from 'styled-components';
3
+ import { Header } from '../../atoms';
4
+ import { colors } from '../../tokens/colors';
5
+
6
+ const variants = ['primary', 'secondary', 'subtle'] as const;
7
+ type Variant = (typeof variants)[number];
8
+
9
+ const Overlay = styled.div`
10
+ position: fixed;
11
+ top: 0;
12
+ left: 0;
13
+ right: 0;
14
+ bottom: 0;
15
+ background-color: rgba(0, 0, 0, 0.5);
16
+ display: flex;
17
+ align-items: center;
18
+ justify-content: center;
19
+ z-index: 1000;
20
+ `;
21
+
22
+ const ChartContainer = styled.div`
23
+ background-color: #ffffff;
24
+ border-radius: 8px;
25
+ padding: 2rem;
26
+ max-width: 400px;
27
+ width: 90%;
28
+ box-shadow: 0 10px 25px rgba(0, 0, 0, 0.2);
29
+ `;
30
+
31
+ const HeaderSection = styled.div`
32
+ display: flex;
33
+ justify-content: space-between;
34
+ align-items: center;
35
+ margin-bottom: 1.5rem;
36
+ `;
37
+
38
+ const CloseButton = styled.button<{ variant: Variant }>`
39
+ background-color: ${(props) => {
40
+ switch (props.variant) {
41
+ case 'primary':
42
+ return colors.background.brand;
43
+ case 'secondary':
44
+ return colors.background.blue;
45
+ case 'subtle':
46
+ return colors.background.subtle;
47
+ default:
48
+ return colors.background.brand;
49
+ }
50
+ }};
51
+ color: #ffffff;
52
+ border: none;
53
+ border-radius: 4px;
54
+ width: 2rem;
55
+ height: 2rem;
56
+ display: flex;
57
+ align-items: center;
58
+ justify-content: center;
59
+ cursor: pointer;
60
+ transition: opacity 0.2s;
61
+
62
+ &:hover {
63
+ opacity: 0.8;
64
+ }
65
+ `;
66
+
67
+ const Table = styled.table`
68
+ width: 100%;
69
+ border-collapse: collapse;
70
+ `;
71
+
72
+ const TableHeader = styled.thead`
73
+ border-bottom: 2px solid #e5e7eb;
74
+ `;
75
+
76
+ const TableHeaderCell = styled.th<{ variant: Variant }>`
77
+ text-align: left;
78
+ padding: 0.75rem 0;
79
+ font-size: 0.875rem;
80
+ font-weight: 700;
81
+ text-transform: uppercase;
82
+ color: ${(props) => {
83
+ switch (props.variant) {
84
+ case 'primary':
85
+ return colors.text.brand;
86
+ case 'secondary':
87
+ return colors.text.blue;
88
+ case 'subtle':
89
+ return colors.text.subtle;
90
+ default:
91
+ return colors.text.brand;
92
+ }
93
+ }};
94
+ `;
95
+
96
+ const TableBody = styled.tbody``;
97
+
98
+ const TableRow = styled.tr`
99
+ border-bottom: 1px solid #f3f4f6;
100
+
101
+ &:last-child {
102
+ border-bottom: none;
103
+ }
104
+ `;
105
+
106
+ const TableCell = styled.td<{ variant: Variant }>`
107
+ padding: 0.75rem 0;
108
+ font-size: 0.875rem;
109
+ color: ${(props) => {
110
+ switch (props.variant) {
111
+ case 'primary':
112
+ case 'secondary':
113
+ return colors.text.primary;
114
+ case 'subtle':
115
+ return colors.text.subtle;
116
+ default:
117
+ return colors.text.primary;
118
+ }
119
+ }};
120
+ `;
121
+
122
+ interface RateItem {
123
+ transactionType: string;
124
+ price: string;
125
+ }
126
+
127
+ interface RatesChartProps {
128
+ title?: string;
129
+ rates: RateItem[];
130
+ onClose: () => void;
131
+ variant?: Variant;
132
+ }
133
+
134
+ export const RatesChart = ({
135
+ title = 'Rates chart',
136
+ rates,
137
+ onClose,
138
+ variant = 'primary',
139
+ }: RatesChartProps) => {
140
+ const getHeaderColor = () => {
141
+ switch (variant) {
142
+ case 'primary':
143
+ return 'brand';
144
+ case 'secondary':
145
+ return 'primary';
146
+ case 'subtle':
147
+ return 'subtle';
148
+ default:
149
+ return 'brand';
150
+ }
151
+ };
152
+
153
+ return (
154
+ <Overlay onClick={onClose}>
155
+ <ChartContainer onClick={(e) => e.stopPropagation()}>
156
+ <HeaderSection>
157
+ <Header variant="h2" color={getHeaderColor()}>
158
+ {title}
159
+ </Header>
160
+ <CloseButton onClick={onClose} variant={variant}>
161
+ <HiX size={20} />
162
+ </CloseButton>
163
+ </HeaderSection>
164
+ <Table>
165
+ <TableHeader>
166
+ <tr>
167
+ <TableHeaderCell variant={variant}>Transaction type</TableHeaderCell>
168
+ <TableHeaderCell variant={variant}>Price</TableHeaderCell>
169
+ </tr>
170
+ </TableHeader>
171
+ <TableBody>
172
+ {rates.map((rate, index) => (
173
+ <TableRow key={index}>
174
+ <TableCell variant={variant}>{rate.transactionType}</TableCell>
175
+ <TableCell variant={variant}>{rate.price}</TableCell>
176
+ </TableRow>
177
+ ))}
178
+ </TableBody>
179
+ </Table>
180
+ </ChartContainer>
181
+ </Overlay>
182
+ );
183
+ };
184
+
185
+ RatesChart.variants = variants;
@@ -0,0 +1,64 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite';
2
+ import { ComponentProps } from 'react';
3
+ import { SideNav as Component } from './SideNav';
4
+
5
+ const meta: Meta = {
6
+ title: 'Organisms/SideNav',
7
+ component: Component,
8
+ };
9
+
10
+ export default meta;
11
+
12
+ export const SideNav: StoryObj<Omit<ComponentProps<typeof Component>, 'onBackToTop'>> = {
13
+ args: {
14
+ items: [
15
+ { label: 'Home', href: '#home' },
16
+ { label: 'About', href: '#about' },
17
+ { label: 'Services', href: '#services' },
18
+ { label: 'Contact', href: '#contact' },
19
+ ],
20
+ activeItem: '#home',
21
+ triangleColor: 'primary',
22
+ dividerColor: 'primary',
23
+ activeTextBgVariant: 'transparent',
24
+ activeTextVariant: 'primary',
25
+ inactiveTextBgVariant: 'transparent',
26
+ inactiveTextVariant: 'subtle',
27
+ backToTopBgVariant: 'transparent',
28
+ backToTopTextVariant: 'subtle',
29
+ },
30
+ argTypes: {
31
+ triangleColor: {
32
+ control: 'select',
33
+ options: ['primary', 'secondary', 'hover', 'active', 'error', 'focus', 'brand'],
34
+ },
35
+ dividerColor: {
36
+ control: 'select',
37
+ options: ['primary', 'secondary', 'hover', 'active', 'error', 'focus', 'brand'],
38
+ },
39
+ activeTextBgVariant: {
40
+ control: 'select',
41
+ options: ['primary', 'secondary', 'tertiary', 'subtle', 'blue', 'brand', 'light', 'transparent'],
42
+ },
43
+ activeTextVariant: {
44
+ control: 'select',
45
+ options: ['brand', 'primary', 'secondary', 'tertiary', 'subtle', 'light', 'error', 'blue'],
46
+ },
47
+ inactiveTextBgVariant: {
48
+ control: 'select',
49
+ options: ['primary', 'secondary', 'tertiary', 'subtle', 'blue', 'brand', 'light', 'transparent'],
50
+ },
51
+ inactiveTextVariant: {
52
+ control: 'select',
53
+ options: ['brand', 'primary', 'secondary', 'tertiary', 'subtle', 'light', 'error', 'blue'],
54
+ },
55
+ backToTopBgVariant: {
56
+ control: 'select',
57
+ options: ['primary', 'secondary', 'tertiary', 'subtle', 'blue', 'brand', 'light', 'transparent'],
58
+ },
59
+ backToTopTextVariant: {
60
+ control: 'select',
61
+ options: ['brand', 'primary', 'secondary', 'tertiary', 'subtle', 'light', 'error', 'blue'],
62
+ },
63
+ },
64
+ };