dhre-component-lib 0.0.4 → 0.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 (42) hide show
  1. package/package.json +2 -1
  2. package/src/components/Avatar/Avatar.test.tsx +5 -4
  3. package/src/components/Avatar/Avatar.tsx +8 -6
  4. package/src/components/Badge/Badge.module.scss +14 -0
  5. package/src/components/Badge/Badge.test.tsx +8 -4
  6. package/src/components/Badge/Badge.tsx +5 -5
  7. package/src/components/BreadCrumb/BreadCrumb.tsx +16 -7
  8. package/src/components/BreadCrumb/Breadcrumb.module.scss +20 -0
  9. package/src/components/Button/Button.module.scss +66 -0
  10. package/src/components/Button/Button.test.tsx +10 -6
  11. package/src/components/Button/Button.tsx +11 -4
  12. package/src/components/Checkbox/Checkbox.test.tsx +7 -3
  13. package/src/components/CircularProgress/CircularProgress.module.scss +19 -0
  14. package/src/components/CircularProgress/CircularProgress.tsx +2 -2
  15. package/src/components/Modal/{Modal.css → Modal.module.scss} +1 -1
  16. package/src/components/Modal/Modal.test.tsx +9 -5
  17. package/src/components/Modal/Modal.tsx +17 -6
  18. package/src/components/Notification/Notification.tsx +1 -1
  19. package/src/components/OtpInput/OtpInput.module.scss +49 -0
  20. package/src/components/OtpInput/OtpInput.test.tsx +53 -0
  21. package/src/components/OtpInput/OtpInput.tsx +137 -0
  22. package/src/components/OtpInput/index.ts +1 -0
  23. package/src/components/PdfView/PdfView.module.scss +69 -0
  24. package/src/components/PdfView/PdfView.test.tsx +20 -11
  25. package/src/components/PdfView/PdfView.tsx +20 -9
  26. package/src/components/Tag/Tag.css +12 -13
  27. package/src/components/Tag/Tag.test.tsx +7 -3
  28. package/src/components/Tag/Tag.tsx +8 -14
  29. package/src/components/Tooltip/Tooltip.module.scss +37 -0
  30. package/src/components/Tooltip/Tooltip.test.tsx +35 -40
  31. package/src/components/Tooltip/Tooltip.tsx +9 -5
  32. package/src/components/index.ts +1 -0
  33. package/src/theme/Typography/typography.scss +117 -0
  34. package/src/theme/colors/colors.scss +22 -0
  35. package/src/theme/colors.ts +3 -0
  36. package/src/components/Badge/Badge.css +0 -15
  37. package/src/components/BreadCrumb/Breadcrumb.css +0 -21
  38. package/src/components/Button/Button.css +0 -92
  39. package/src/components/CircularProgress/CircularProgress.css +0 -19
  40. package/src/components/PdfView/PdfView.css +0 -70
  41. package/src/components/Tooltip/Tooltip.css +0 -38
  42. /package/src/components/Notification/{Notification.css → Notification.module.scss} +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dhre-component-lib",
3
- "version": "0.0.4",
3
+ "version": "0.0.5",
4
4
  "private": false,
5
5
  "scripts": {
6
6
  "test": "jest",
@@ -38,6 +38,7 @@
38
38
  "@emotion/styled": "^11.13.0",
39
39
  "@mui/icons-material": "^5.16.7",
40
40
  "@mui/material": "^5.16.7",
41
+ "sass": "^1.77.8",
41
42
  "tslib": "^2.6.3"
42
43
  },
43
44
  "peerDependencies": {
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import { render, screen, fireEvent, waitFor } from '@testing-library/react';
2
+ import { render, screen, fireEvent } from '@testing-library/react';
3
3
  import '@testing-library/jest-dom';
4
4
  import Avatar, { AvatarProps } from './Avatar';
5
5
 
@@ -44,9 +44,10 @@ describe('Avatar Component', () => {
44
44
  expect(() => fireEvent.click(imgElement)).not.toThrow();
45
45
  });
46
46
 
47
- it('matches snapshot for default rendering', () => {
48
- const { container } = render(<Avatar {...defaultProps} />);
47
+ it('renders correctly without crashing', () => {
48
+ render(<Avatar {...defaultProps} />);
49
49
 
50
- expect(container.firstChild).toMatchSnapshot();
50
+ const imgElement = screen.getByAltText(defaultProps.alt);
51
+ expect(imgElement).toBeInTheDocument();
51
52
  });
52
53
  });
@@ -14,12 +14,14 @@ const Avatar: React.FC<AvatarProps> = ({
14
14
  handleClick,
15
15
  }) => {
16
16
  return (
17
- <img
18
- src={src}
19
- alt={alt}
20
- onClick={handleClick}
21
- className={imgClassName}
22
- />
17
+ <button onClick={handleClick}>
18
+ <img
19
+ src={src}
20
+ alt={alt}
21
+
22
+ className={imgClassName}
23
+ />
24
+ </button>
23
25
  );
24
26
  };
25
27
 
@@ -0,0 +1,14 @@
1
+ .badge {
2
+ display: inline-block;
3
+ padding: 6px 12px;
4
+ background-color: red;
5
+ color: white;
6
+ border-radius: 12px;
7
+ font-size: 14px;
8
+ text-align: center;
9
+ cursor: pointer;
10
+
11
+ &:hover {
12
+ background-color: darkred;
13
+ }
14
+ }
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import { render, screen, fireEvent, waitFor } from '@testing-library/react';
2
+ import { render, screen, fireEvent } from '@testing-library/react';
3
3
  import '@testing-library/jest-dom';
4
4
  import Badge, { BadgeProps } from './Badge';
5
5
 
@@ -55,9 +55,13 @@ describe('Badge Component', () => {
55
55
  expect(badgeElement).toHaveTextContent('99');
56
56
  });
57
57
 
58
- it('matches snapshot for default rendering', () => {
59
- const { container } = render(<Badge {...defaultProps} />);
58
+ it('renders correctly with different content', () => {
59
+ const newContent = 'Updated Badge';
60
+ render(<Badge {...defaultProps} content={newContent} />);
60
61
 
61
- expect(container.firstChild).toMatchSnapshot();
62
+ const badgeElement = screen.getByText(newContent);
63
+
64
+ expect(badgeElement).toBeInTheDocument();
65
+ expect(badgeElement).toHaveTextContent(newContent);
62
66
  });
63
67
  });
@@ -1,9 +1,9 @@
1
1
  import React from "react";
2
- import './Badge.css';
2
+ import styles from './Badge.module.scss';
3
3
 
4
4
  export interface BadgeProps {
5
5
  content: string | number;
6
- badgeClassName: string;
6
+ badgeClassName?: string;
7
7
  handleClick?: () => void;
8
8
  }
9
9
 
@@ -13,12 +13,12 @@ const Badge: React.FC<BadgeProps> = ({
13
13
  handleClick,
14
14
  }) => {
15
15
  return (
16
- <span
16
+ <button
17
17
  onClick={handleClick}
18
- className={badgeClassName}
18
+ className={`${styles.badge} ${badgeClassName}`}
19
19
  >
20
20
  {content}
21
- </span>
21
+ </button>
22
22
  );
23
23
  };
24
24
 
@@ -1,5 +1,5 @@
1
1
  import React from "react";
2
- import './Breadcrumb.css';
2
+ import styles from './Breadcrumb.module.scss';
3
3
 
4
4
  export interface BreadcrumbItem {
5
5
  label: string;
@@ -11,7 +11,7 @@ export interface BreadcrumbProps {
11
11
  breadcrumbClassName: string;
12
12
  itemClassName?: string;
13
13
  separator?: string;
14
- separatorClassName?: string,
14
+ separatorClassName?: string;
15
15
  }
16
16
 
17
17
  const Breadcrumb: React.FC<BreadcrumbProps> = ({
@@ -22,12 +22,21 @@ const Breadcrumb: React.FC<BreadcrumbProps> = ({
22
22
  separatorClassName,
23
23
  }) => {
24
24
  return (
25
- <nav className={breadcrumbClassName}>
25
+ <nav className={`${styles.breadcrumb} ${breadcrumbClassName}`}>
26
26
  {items.map((item, index) => (
27
- <span key={index} className={itemClassName} onClick={item.handleClick}>
28
- {item.label}
29
- {index < items.length - 1 && <span className={separatorClassName}>{separator}</span>}
30
- </span>
27
+ <React.Fragment key={index}>
28
+ <button
29
+ className={`${styles.breadcrumbItem} ${itemClassName}`}
30
+ onClick={item.handleClick}
31
+ >
32
+ {item.label}
33
+ </button>
34
+ {index < items.length - 1 && (
35
+ <span className={`${styles.separator} ${separatorClassName}`}>
36
+ {separator}
37
+ </span>
38
+ )}
39
+ </React.Fragment>
31
40
  ))}
32
41
  </nav>
33
42
  );
@@ -0,0 +1,20 @@
1
+ .breadcrumb {
2
+ display: flex;
3
+ align-items: center;
4
+ font-size: 16px;
5
+ }
6
+
7
+ .breadcrumbItem {
8
+ color: blue;
9
+ cursor: pointer;
10
+ margin-right: 8px;
11
+
12
+ &:hover {
13
+ text-decoration: underline;
14
+ }
15
+ }
16
+
17
+ .separator {
18
+ margin: 0 8px;
19
+ color: grey;
20
+ }
@@ -0,0 +1,66 @@
1
+ /* Base button styling */
2
+ .button {
3
+ padding: 8px 16px;
4
+ font-size: 14px;
5
+ border-radius: 4px;
6
+ border: none;
7
+ cursor: pointer;
8
+ transition: background-color 0.3s ease;
9
+
10
+ &.text {
11
+ background-color: transparent;
12
+ color: inherit;
13
+ }
14
+
15
+ &.outlined {
16
+ background-color: transparent;
17
+ border: 2px solid currentColor;
18
+ }
19
+
20
+ &.contained {
21
+ background-color: currentColor;
22
+ color: #fff;
23
+
24
+ &.primary:hover {
25
+ background-color: #1565c0;
26
+ }
27
+
28
+ &.secondary:hover {
29
+ background-color: #7b1fa2;
30
+ }
31
+
32
+ &.success:hover {
33
+ background-color: #388e3c;
34
+ }
35
+
36
+ &.error:hover {
37
+ background-color: #d32f2f;
38
+ }
39
+
40
+ &.warning:hover {
41
+ background-color: #f57c00;
42
+ }
43
+
44
+ &.info:hover {
45
+ background-color: #1976d2;
46
+ }
47
+ }
48
+
49
+ &.primary {
50
+ color: #1976d2;
51
+ }
52
+
53
+ &.secondary {
54
+ color: #9c27b0;
55
+ }
56
+
57
+ &.success {
58
+ color: #4caf50;
59
+ }
60
+
61
+ &.error {
62
+ color: #f44336;
63
+ }
64
+
65
+ &.warning {
66
+ color:
@@ -48,15 +48,19 @@ describe('Button Component', () => {
48
48
  expect(handleClick).toHaveBeenCalledTimes(1); // Ensure the mock function is called once
49
49
  });
50
50
 
51
- it('renders correctly without crashing', () => {
52
- const { container } = renderButton();
53
-
54
- expect(container.firstChild).toMatchSnapshot();
55
- });
56
-
57
51
  it('does not call handleClick when the button is not clicked', () => {
58
52
  renderButton();
59
53
 
60
54
  expect(handleClick).not.toHaveBeenCalled();
61
55
  });
56
+
57
+ it('renders correctly with a different label', () => {
58
+ const newLabel = 'Submit';
59
+ renderButton({ label: newLabel });
60
+
61
+ const buttonElement = screen.getByText(newLabel);
62
+
63
+ expect(buttonElement).toBeInTheDocument();
64
+ expect(buttonElement.tagName).toBe('BUTTON');
65
+ });
62
66
  });
@@ -1,19 +1,26 @@
1
1
  import React from "react";
2
- import './Button.css';
2
+ import styles from './Button.module.scss';
3
3
 
4
4
  export interface ButtonProps {
5
5
  label: string;
6
- labelClassName: string;
6
+ variant: 'text' | 'outlined' | 'contained';
7
+ color: 'primary' | 'secondary' | 'success' | 'error' | 'warning' | 'info';
8
+ size: 'small' | 'medium' | 'large';
7
9
  handleClick: () => void;
8
10
  }
9
11
 
10
12
  const Button: React.FC<ButtonProps> = ({
11
13
  label,
12
- labelClassName,
14
+ variant,
15
+ color,
16
+ size,
13
17
  handleClick,
14
18
  }) => {
15
19
  return (
16
- <button onClick={handleClick} className={labelClassName}>
20
+ <button
21
+ onClick={handleClick}
22
+ className={`${styles.button} ${styles[variant]} ${styles[color]} ${styles[size]}`}
23
+ >
17
24
  {label}
18
25
  </button>
19
26
  );
@@ -81,9 +81,13 @@ describe('CustomCheckbox Component', () => {
81
81
  expect(handleChange).toHaveBeenCalledTimes(1);
82
82
  });
83
83
 
84
- it('renders correctly without crashing', () => {
85
- const { container } = render(<CustomCheckbox {...getDefaultProps()} />);
84
+ it('renders correctly with different label', () => {
85
+ const newLabel = 'Agree to Privacy Policy';
86
+ render(<CustomCheckbox {...getDefaultProps()} label={newLabel} />);
86
87
 
87
- expect(container.firstChild).toMatchSnapshot();
88
+ const labelElement = screen.getByText(newLabel);
89
+
90
+ expect(labelElement).toBeInTheDocument();
91
+ expect(labelElement).toHaveTextContent(newLabel);
88
92
  });
89
93
  });
@@ -0,0 +1,19 @@
1
+ .spinner {
2
+ border-style: solid;
3
+ border-radius: 50%;
4
+ border-color: transparent;
5
+ border-top-color: currentColor;
6
+ animation: spin 1s linear infinite;
7
+ }
8
+
9
+ .spinner.primary { color: #007bff; }
10
+ .spinner.secondary { color: #6c757d; }
11
+ .spinner.error { color: #dc3545; }
12
+ .spinner.success { color: #28a745; }
13
+ .spinner.info { color: #17a2b8; }
14
+ .spinner.warning { color: #ffc107; }
15
+
16
+ @keyframes spin {
17
+ 0% { transform: rotate(0deg); }
18
+ 100% { transform: rotate(360deg); }
19
+ }
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import './CircularProgress.css';
2
+ import styles from './CircularProgress.module.scss';
3
3
 
4
4
  export interface CircularProgressProps {
5
5
  value?: number;
@@ -23,7 +23,7 @@ const CircularProgress: React.FC<CircularProgressProps> = ({
23
23
 
24
24
  return (
25
25
  <div
26
- className={`spinner ${color}`}
26
+ className={`${styles.spinner} ${styles[color]}`}
27
27
  style={{
28
28
  width: typeof size === 'number' ? `${size}px` : size,
29
29
  height: typeof size === 'number' ? `${size}px` : size,
@@ -23,4 +23,4 @@
23
23
 
24
24
  .modalContent::-webkit-scrollbar {
25
25
  display: none;
26
- }
26
+ }
@@ -6,7 +6,11 @@ describe('Modal Component', () => {
6
6
  const onCloseMock = jest.fn();
7
7
  const modalContentText = 'This is the modal content';
8
8
 
9
- const renderModal = (isOpen: boolean, modalOverlayClassname?: string, modalContentClassname?: string) => {
9
+ const renderModal = (
10
+ isOpen: boolean,
11
+ modalOverlayClassname?: string,
12
+ modalContentClassname?: string
13
+ ) => {
10
14
  render(
11
15
  <Modal
12
16
  isOpen={isOpen}
@@ -35,7 +39,7 @@ describe('Modal Component', () => {
35
39
 
36
40
  it('calls onClose when clicking on the overlay', () => {
37
41
  renderModal(true);
38
- const overlay = screen.getByText(modalContentText).closest('div.modalOverlay');
42
+ const overlay = screen.getByText(modalContentText).closest('button');
39
43
  fireEvent.click(overlay!);
40
44
  expect(onCloseMock).toHaveBeenCalled();
41
45
  });
@@ -50,15 +54,15 @@ describe('Modal Component', () => {
50
54
  it('applies the provided modalOverlayClassname', () => {
51
55
  const overlayClass = 'customOverlayClass';
52
56
  renderModal(true, overlayClass);
53
- const overlay = screen.getByText(modalContentText).closest('div.modalOverlay');
57
+ const overlay = screen.getByText(modalContentText).closest('button');
54
58
  expect(overlay).toHaveClass(overlayClass);
55
59
  });
56
60
 
57
61
  it('applies the provided modalContentClassname', () => {
58
62
  const contentClass = 'customContentClass';
59
63
  renderModal(true, undefined, contentClass);
60
- const modalContent = screen.getByText(modalContentText).closest('div.modalContent');
61
- expect(modalContent).toHaveClass(contentClass);
64
+ const modalContent = screen.getByText(modalContentText);
65
+ expect(modalContent.parentElement).toHaveClass(contentClass);
62
66
  });
63
67
 
64
68
  it('does not render modal when isOpen is false, even if modalOverlayClassname and modalContentClassname are provided', () => {
@@ -1,5 +1,5 @@
1
1
  import React from "react";
2
- import "./Modal.css";
2
+ import styles from './Modal.module.scss';
3
3
 
4
4
  interface ModalProps {
5
5
  isOpen: boolean;
@@ -9,19 +9,30 @@ interface ModalProps {
9
9
  modalContentClassname?: string;
10
10
  }
11
11
 
12
- const Modal: React.FC<ModalProps> = ({ isOpen, onClose, children, modalOverlayClassname, modalContentClassname }) => {
12
+ const Modal: React.FC<ModalProps> = ({
13
+ isOpen,
14
+ onClose,
15
+ children,
16
+ modalOverlayClassname = '',
17
+ modalContentClassname = ''
18
+ }) => {
13
19
  if (!isOpen) return null;
14
20
 
15
- const handleOverlayClick = (event: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
21
+ const handleOverlayClick = (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
16
22
  if (event.target === event.currentTarget) {
17
23
  onClose();
18
24
  }
19
25
  };
20
26
 
21
27
  return (
22
- <div className={`modalOverlay ${modalOverlayClassname}`} onClick={handleOverlayClick}>
23
- <div className={`modalContent ${modalContentClassname}`}>{children}</div>
24
- </div>
28
+ <button
29
+ className={`${styles.modalOverlay} ${modalOverlayClassname}`}
30
+ onClick={handleOverlayClick}
31
+ >
32
+ <div className={`${styles.modalContent} ${modalContentClassname}`}>
33
+ {children}
34
+ </div>
35
+ </button>
25
36
  );
26
37
  };
27
38
 
@@ -1,5 +1,5 @@
1
1
  import React, { useEffect } from "react";
2
- import './Notification.css';
2
+ import './Notification.module.scss';
3
3
  export interface NotificationProps {
4
4
  message: string;
5
5
  severity?: 'error' | 'warning' | 'info' | 'success';
@@ -0,0 +1,49 @@
1
+ @import '../../theme/colors/colors.scss';
2
+
3
+ .otpMainContainer {
4
+ display: flex;
5
+ justify-content: center;
6
+ flex-direction: column;
7
+ margin: 10px;
8
+ width: 343px;
9
+ }
10
+
11
+ .otpInputDiv {
12
+ flex-direction: row;
13
+ gap: 12px;
14
+ display: flex
15
+ }
16
+
17
+ .otpInput {
18
+ width: 76px;
19
+ height: 48px;
20
+ text-align: center;
21
+ font-size: 18px;
22
+ font-weight: 400;
23
+ border-radius: 8px;
24
+ }
25
+
26
+ .resendContainer {
27
+ display: flex;
28
+ flex-direction: row;
29
+ margin-top: 10px;
30
+ justify-content: space-between;
31
+ }
32
+
33
+ .errorText {
34
+ color: $border-brand-danger;
35
+ font-weight: 400;
36
+ font-size: 14px;
37
+ }
38
+
39
+ .timerText {
40
+ color: $content-disabled;
41
+ font-weight: 400;
42
+ font-size: 14px;
43
+ }
44
+ .resendText {
45
+ color: $content-disabled;
46
+ font-weight: 700;
47
+ font-size: 14px;
48
+ }
49
+
@@ -0,0 +1,53 @@
1
+ import React from 'react';
2
+ import { render, fireEvent, screen } from '@testing-library/react';
3
+ import OTPInput from './OtpInput';
4
+ import '@testing-library/jest-dom';
5
+
6
+ describe('OTPInput Component', () => {
7
+ it('renders the correct number of input fields based on the length prop', () => {
8
+ render(
9
+ <OTPInput
10
+ length={4}
11
+ onChange={() => {}}
12
+ onResend={() => {}}
13
+ resendText="Resend OTP"
14
+ />
15
+ );
16
+ const inputs = screen.getAllByRole('textbox');
17
+ expect(inputs).toHaveLength(4);
18
+ });
19
+ it('calls onChange prop when input changes', () => {
20
+ const handleChange = jest.fn();
21
+ render(<OTPInput length={4} onChange={handleChange} onResend={() => {}} resendText="Resend OTP" />);
22
+ const inputs = screen.getAllByRole('textbox');
23
+ fireEvent.change(inputs[0], { target: { value: '1' } });
24
+ expect(handleChange).toHaveBeenCalledWith('1');
25
+ });
26
+
27
+ it('calls onResend prop when the resend button is clicked', async () => {
28
+ const handleResend = jest.fn();
29
+ render(<OTPInput length={4} onChange={() => {}} onResend={handleResend} resendText="Resend OTP" resendDelay={0} />);
30
+ const resendButton = screen.getByRole('button', { name: /resend otp/i });
31
+ fireEvent.click(resendButton);
32
+ expect(handleResend).toHaveBeenCalled();
33
+ });
34
+ it('displays error text when the error prop is true', () => {
35
+ const errorMessage = 'Invalid OTP';
36
+ render(<OTPInput length={4} onChange={() => {}} onResend={() => {}} resendText="Resend OTP" error={true} errorText={errorMessage} />);
37
+ const errorElement = screen.getByText(errorMessage);
38
+ expect(errorElement).toBeInTheDocument();
39
+ });
40
+ it('changes border color to red when the error prop is true', () => {
41
+ render(
42
+ <OTPInput
43
+ length={4}
44
+ onChange={() => {}}
45
+ onResend={() => {}}
46
+ resendText="Resend OTP"
47
+ error={true}
48
+ />
49
+ );
50
+ const firstInput = screen.getByTestId('otp-input-0');
51
+ expect(firstInput).toHaveStyle('border: 1px solid #EB0542');
52
+ });
53
+ });