dhre-component-lib 0.0.9 → 0.1.0

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 (78) hide show
  1. package/package.json +3 -2
  2. package/src/__mocks__/styleMock.js +1 -0
  3. package/src/components/Avatar/Avatar.test.tsx +53 -0
  4. package/src/components/Avatar/Avatar.tsx +28 -0
  5. package/src/components/Avatar/index.ts +1 -0
  6. package/src/components/Badge/Badge.module.scss +14 -0
  7. package/src/components/Badge/Badge.test.tsx +59 -0
  8. package/src/components/Badge/Badge.tsx +25 -0
  9. package/src/components/Badge/index.ts +1 -0
  10. package/src/components/BreadCrumb/BreadCrumb.test.tsx +90 -0
  11. package/src/components/BreadCrumb/BreadCrumb.tsx +45 -0
  12. package/src/components/BreadCrumb/Breadcrumb.module.scss +20 -0
  13. package/src/components/BreadCrumb/index.ts +1 -0
  14. package/src/components/Button/Button.module.scss +66 -0
  15. package/src/components/Button/Button.test.tsx +49 -0
  16. package/src/components/Button/Button.tsx +29 -0
  17. package/src/components/Button/index.ts +1 -0
  18. package/src/components/Checkbox/Checkbox.test.tsx +93 -0
  19. package/src/components/Checkbox/Checkbox.tsx +35 -0
  20. package/src/components/Checkbox/index.ts +1 -0
  21. package/src/components/CircularProgress/CircularProgress.module.scss +19 -0
  22. package/src/components/CircularProgress/CircularProgress.test.tsx +39 -0
  23. package/src/components/CircularProgress/CircularProgress.tsx +37 -0
  24. package/src/components/CircularProgress/index.ts +1 -0
  25. package/src/components/Divider/Divider.test.tsx +44 -0
  26. package/src/components/Divider/Divider.tsx +24 -0
  27. package/src/components/Divider/index.ts +1 -0
  28. package/src/components/Enum.ts +19 -0
  29. package/src/components/InputTextField/InputTextField.test.tsx +118 -0
  30. package/src/components/InputTextField/InputTextField.tsx +48 -0
  31. package/src/components/InputTextField/index.ts +1 -0
  32. package/src/components/Link/Link.test.tsx +55 -0
  33. package/src/components/Link/Link.tsx +33 -0
  34. package/src/components/Link/index.ts +1 -0
  35. package/src/components/Map/Directions.tsx +36 -0
  36. package/src/components/Map/GoogleMap.module.scss +5 -0
  37. package/src/components/Map/GoogleMap.tsx +186 -0
  38. package/src/components/Map/GoogleMapsLoader.tsx +12 -0
  39. package/src/components/Map/index.ts +2 -0
  40. package/src/components/Modal/Modal.module.scss +26 -0
  41. package/src/components/Modal/Modal.test.tsx +74 -0
  42. package/src/components/Modal/Modal.tsx +39 -0
  43. package/src/components/Modal/index.ts +1 -0
  44. package/src/components/Notification/Notification.module.scss +20 -0
  45. package/src/components/Notification/Notification.test.tsx +53 -0
  46. package/src/components/Notification/Notification.tsx +42 -0
  47. package/src/components/Notification/index.ts +1 -0
  48. package/src/components/OtpInput/OtpInput.module.scss +49 -0
  49. package/src/components/OtpInput/OtpInput.test.tsx +53 -0
  50. package/src/components/OtpInput/OtpInput.tsx +137 -0
  51. package/src/components/OtpInput/index.ts +1 -0
  52. package/src/components/PdfView/PdfView.module.scss +69 -0
  53. package/src/components/PdfView/PdfView.test.tsx +52 -0
  54. package/src/components/PdfView/PdfView.tsx +116 -0
  55. package/src/components/PdfView/index.ts +1 -0
  56. package/src/components/Progress/Progress.test.tsx +43 -0
  57. package/src/components/Progress/Progress.tsx +35 -0
  58. package/src/components/Progress/index.ts +1 -0
  59. package/src/components/RadioButton/RadioButton.test.tsx +56 -0
  60. package/src/components/RadioButton/RadioButton.tsx +43 -0
  61. package/src/components/RadioButton/index.ts +1 -0
  62. package/src/components/Switch/Switch.test.tsx +83 -0
  63. package/src/components/Switch/Switch.tsx +38 -0
  64. package/src/components/Switch/index.ts +1 -0
  65. package/src/components/Tag/Tag.css +14 -0
  66. package/src/components/Tag/Tag.test.tsx +61 -0
  67. package/src/components/Tag/Tag.tsx +19 -0
  68. package/src/components/Tag/index.ts +1 -0
  69. package/src/components/Tooltip/Tooltip.module.scss +37 -0
  70. package/src/components/Tooltip/Tooltip.test.tsx +68 -0
  71. package/src/components/Tooltip/Tooltip.tsx +38 -0
  72. package/src/components/Tooltip/index.ts +1 -0
  73. package/src/components/index.ts +15 -0
  74. package/src/index.ts +1 -0
  75. package/src/theme/Typography/typography.scss +117 -0
  76. package/src/theme/colors/colors.scss +22 -0
  77. package/src/theme/colors.ts +3 -0
  78. package/src/typings.d.ts +1 -0
@@ -0,0 +1,26 @@
1
+ .modalOverlay {
2
+ position: fixed;
3
+ top: 0;
4
+ left: 0;
5
+ width: 100%;
6
+ height: 100%;
7
+ background: rgba(0, 0, 0, 0.5);
8
+ display: flex;
9
+ align-items: center;
10
+ justify-content: center;
11
+ z-index: 1000;
12
+ }
13
+
14
+ .modalContent {
15
+ background: white;
16
+ position: relative;
17
+ width: auto;
18
+ height: auto;
19
+ max-width: 80vw;
20
+ max-height: 80vh;
21
+ overflow: scroll;
22
+ }
23
+
24
+ .modalContent::-webkit-scrollbar {
25
+ display: none;
26
+ }
@@ -0,0 +1,74 @@
1
+ import React from 'react';
2
+ import { render, screen, fireEvent } from '@testing-library/react';
3
+ import Modal from './Modal';
4
+
5
+ describe('Modal Component', () => {
6
+ const onCloseMock = jest.fn();
7
+ const modalContentText = 'This is the modal content';
8
+
9
+ const renderModal = (
10
+ isOpen: boolean,
11
+ modalOverlayClassname?: string,
12
+ modalContentClassname?: string
13
+ ) => {
14
+ render(
15
+ <Modal
16
+ isOpen={isOpen}
17
+ onClose={onCloseMock}
18
+ modalOverlayClassname={modalOverlayClassname}
19
+ modalContentClassname={modalContentClassname}
20
+ >
21
+ <div>{modalContentText}</div>
22
+ </Modal>
23
+ );
24
+ };
25
+
26
+ afterEach(() => {
27
+ jest.clearAllMocks();
28
+ });
29
+
30
+ it('renders the modal when isOpen is true', () => {
31
+ renderModal(true);
32
+ expect(screen.getByText(modalContentText)).toBeInTheDocument();
33
+ });
34
+
35
+ it('does not render the modal when isOpen is false', () => {
36
+ renderModal(false);
37
+ expect(screen.queryByText(modalContentText)).not.toBeInTheDocument();
38
+ });
39
+
40
+ it('calls onClose when clicking on the overlay', () => {
41
+ renderModal(true);
42
+ const overlay = screen.getByText(modalContentText).closest('button');
43
+ fireEvent.click(overlay!);
44
+ expect(onCloseMock).toHaveBeenCalled();
45
+ });
46
+
47
+ it('does not call onClose when clicking inside the modal content', () => {
48
+ renderModal(true);
49
+ const modalContent = screen.getByText(modalContentText);
50
+ fireEvent.click(modalContent);
51
+ expect(onCloseMock).not.toHaveBeenCalled();
52
+ });
53
+
54
+ it('applies the provided modalOverlayClassname', () => {
55
+ const overlayClass = 'customOverlayClass';
56
+ renderModal(true, overlayClass);
57
+ const overlay = screen.getByText(modalContentText).closest('button');
58
+ expect(overlay).toHaveClass(overlayClass);
59
+ });
60
+
61
+ it('applies the provided modalContentClassname', () => {
62
+ const contentClass = 'customContentClass';
63
+ renderModal(true, undefined, contentClass);
64
+ const modalContent = screen.getByText(modalContentText);
65
+ expect(modalContent.parentElement).toHaveClass(contentClass);
66
+ });
67
+
68
+ it('does not render modal when isOpen is false, even if modalOverlayClassname and modalContentClassname are provided', () => {
69
+ const overlayClass = 'customOverlayClass';
70
+ const contentClass = 'customContentClass';
71
+ renderModal(false, overlayClass, contentClass);
72
+ expect(screen.queryByText(modalContentText)).not.toBeInTheDocument();
73
+ });
74
+ });
@@ -0,0 +1,39 @@
1
+ import React from "react";
2
+ import styles from './Modal.module.scss';
3
+
4
+ interface ModalProps {
5
+ isOpen: boolean;
6
+ onClose: () => void;
7
+ children: React.ReactNode;
8
+ modalOverlayClassname?: string;
9
+ modalContentClassname?: string;
10
+ }
11
+
12
+ const Modal: React.FC<ModalProps> = ({
13
+ isOpen,
14
+ onClose,
15
+ children,
16
+ modalOverlayClassname = '',
17
+ modalContentClassname = ''
18
+ }) => {
19
+ if (!isOpen) return null;
20
+
21
+ const handleOverlayClick = (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
22
+ if (event.target === event.currentTarget) {
23
+ onClose();
24
+ }
25
+ };
26
+
27
+ return (
28
+ <button
29
+ className={`${styles.modalOverlay} ${modalOverlayClassname}`}
30
+ onClick={handleOverlayClick}
31
+ >
32
+ <div className={`${styles.modalContent} ${modalContentClassname}`}>
33
+ {children}
34
+ </div>
35
+ </button>
36
+ );
37
+ };
38
+
39
+ export default Modal;
@@ -0,0 +1 @@
1
+ export { default } from "./Modal";
@@ -0,0 +1,20 @@
1
+ .notification {
2
+ position: fixed;
3
+ bottom: 20px;
4
+ left: 20px;
5
+ width: 300px;
6
+ padding: 16px;
7
+ border-radius: 4px;
8
+ display: flex;
9
+ justify-content: space-between;
10
+ align-items: center;
11
+ cursor: pointer;
12
+ z-index: 9999;
13
+ }
14
+
15
+ .notification .close-button {
16
+ background: none;
17
+ border: none;
18
+ font-size: 20px;
19
+ cursor: pointer;
20
+ }
@@ -0,0 +1,53 @@
1
+ import React from 'react';
2
+ import { render, screen, fireEvent, waitFor } from '@testing-library/react';
3
+ import Notification, { NotificationProps } from './Notification';
4
+
5
+ describe('Notification Component', () => {
6
+ const defaultProps: NotificationProps = {
7
+ message: 'Test Message',
8
+ severity: 'info',
9
+ autoHideDuration: 6000,
10
+ onClose: jest.fn(),
11
+ };
12
+
13
+ const renderNotification = (props: Partial<NotificationProps> = {}) => {
14
+ return render(<Notification {...defaultProps} {...props} />);
15
+ };
16
+
17
+ test('renders notification with the correct message', () => {
18
+ render(<Notification message="Test Message" severity="info" />);
19
+
20
+ // Check if the element exists by querying it
21
+ const notificationElement = screen.getByText('Test Message');
22
+ expect(notificationElement).not.toBeNull(); // Equivalent to `toBeInTheDocument`
23
+
24
+ // Alternatively, check for the presence of the role or className
25
+ const alertElement = screen.getByRole('alert');
26
+ expect(alertElement.className).toContain('info'); // Check if className includes "info"
27
+ });
28
+
29
+ test('calls onClose when the notification is clicked', () => {
30
+ const onClose = jest.fn();
31
+ renderNotification({ onClose });
32
+
33
+ fireEvent.click(screen.getByRole('alert'));
34
+
35
+ expect(onClose).toHaveBeenCalled();
36
+ });
37
+
38
+ test('calls onClose when the close button is clicked', () => {
39
+ const onClose = jest.fn();
40
+ renderNotification({ onClose });
41
+
42
+ fireEvent.click(screen.getByLabelText('Close notification'));
43
+
44
+ expect(onClose).toHaveBeenCalledWith(expect.anything(), 'close');
45
+ });
46
+
47
+ test('calls onClose after autoHideDuration expires', async () => {
48
+ const onClose = jest.fn();
49
+ renderNotification({ onClose, autoHideDuration: 1000 });
50
+
51
+ await waitFor(() => expect(onClose).toHaveBeenCalledWith(expect.anything(), 'timeout'), { timeout: 1500 });
52
+ });
53
+ });
@@ -0,0 +1,42 @@
1
+ import React, { useEffect } from "react";
2
+ import './Notification.module.scss';
3
+ export interface NotificationProps {
4
+ message: string;
5
+ severity?: 'error' | 'warning' | 'info' | 'success';
6
+ autoHideDuration?: number;
7
+ onClose?: (event: React.SyntheticEvent<any> | Event, reason?: string) => void;
8
+ }
9
+
10
+ const Notification: React.FC<NotificationProps> = ({
11
+ message,
12
+ severity = 'info',
13
+ autoHideDuration = 6000,
14
+ onClose,
15
+ }) => {
16
+ useEffect(() => {
17
+ if (autoHideDuration && onClose) {
18
+ const timer = setTimeout(() => {
19
+ onClose(new Event('timeout'), 'timeout');
20
+ }, autoHideDuration);
21
+
22
+ return () => clearTimeout(timer);
23
+ }
24
+ }, [autoHideDuration, onClose]);
25
+
26
+ const handleClose = (event: React.SyntheticEvent<any> | Event, reason?: string) => {
27
+ if (onClose) {
28
+ onClose(event, reason);
29
+ }
30
+ };
31
+
32
+ return (
33
+ <div className={`notification ${severity}`} onClick={(e) => handleClose(e)} role="alert">
34
+ <div className="notification-message">{message}</div>
35
+ <button className="close-button" onClick={(e) => handleClose(e, 'close')} aria-label="Close notification">
36
+ &times;
37
+ </button>
38
+ </div>
39
+ );
40
+ };
41
+
42
+ export default Notification;
@@ -0,0 +1 @@
1
+ export { default } from "./Notification";
@@ -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
+ });
@@ -0,0 +1,137 @@
1
+ import React, { useEffect, useState } from "react";
2
+ import "./OtpInput.module.scss";
3
+ interface OTPInputProps {
4
+ length?: number;
5
+ // eslint-disable-next-line no-unused-vars
6
+ onChange: (otp: string) => void;
7
+ autoFocus?: boolean;
8
+ onResend: () => void;
9
+ resendDelay?: number;
10
+ error?: boolean;
11
+ errorText?: string;
12
+ resendText: string;
13
+ }
14
+
15
+ const OTPInput: React.FC<OTPInputProps> = ({
16
+ length = 4,
17
+ onChange,
18
+ autoFocus = false,
19
+ onResend,
20
+ resendDelay = 60,
21
+ error = false,
22
+ errorText,
23
+ resendText,
24
+ }) => {
25
+ const [otp, setOtp] = useState<string[]>(Array(length).fill(""));
26
+ const [timer, setTimer] = useState<number>(resendDelay);
27
+ const [isComplete, setIsComplete] = useState<boolean>(false);
28
+
29
+ useEffect(() => {
30
+ const countdown = setInterval(() => {
31
+ setTimer((prevTimer) => (prevTimer > 0 ? prevTimer - 1 : 0));
32
+ }, 1000);
33
+
34
+ return () => clearInterval(countdown);
35
+ }, []);
36
+ const handleChange = (value: string, index: number) => {
37
+ if (isNaN(Number(value))) return;
38
+
39
+ const newOtp = [...otp];
40
+ newOtp[index] = value;
41
+ setOtp(newOtp);
42
+ onChange(newOtp.join(""));
43
+
44
+ if (value && index < length - 1) {
45
+ const nextInput = document.getElementById(
46
+ `otp-input-${index + 1}`
47
+ ) as HTMLInputElement;
48
+ if (nextInput) {
49
+ nextInput.focus();
50
+ }
51
+ }
52
+ };
53
+
54
+ const handleKeyDown = (
55
+ e: React.KeyboardEvent<HTMLInputElement>,
56
+ index: number
57
+ ) => {
58
+ if (e.key === "Backspace" && !otp[index] && index > 0) {
59
+ const prevInput = document.getElementById(
60
+ `otp-input-${index - 1}`
61
+ ) as HTMLInputElement;
62
+ if (prevInput) {
63
+ prevInput.focus();
64
+ }
65
+ }
66
+ };
67
+
68
+ const handleResendClick = () => {
69
+ onResend();
70
+ setOtp(Array(length).fill(""));
71
+ setTimer(resendDelay);
72
+ setIsComplete(false);
73
+ };
74
+
75
+ useEffect(() => {
76
+ setIsComplete(otp.every((digit) => digit !== ""));
77
+ }, [otp]);
78
+
79
+ useEffect(() => {
80
+ if (error) {
81
+ setTimer(0);
82
+ }
83
+ }, [error]);
84
+
85
+ function getReadableStatus() {
86
+ if (error) {
87
+ return "#EB0542";
88
+ }
89
+ return isComplete ? "#00B578" : "#A7A7A7";
90
+ }
91
+ return (
92
+ <div className="otpMainContainer">
93
+ <div className="otpInputDiv">
94
+ {Array(length)
95
+ .fill("")
96
+ .map((_, index) => (
97
+ <input
98
+ key={index}
99
+ type="text"
100
+ id={`otp-input-${index}`}
101
+ value={otp[index]}
102
+ data-testid={`otp-input-${index}`}
103
+ role="textbox"
104
+ onChange={(e) => handleChange(e.target.value, index)}
105
+ onKeyDown={(e) => handleKeyDown(e, index)}
106
+ maxLength={1}
107
+ style={{ border: `1px solid ${getReadableStatus()}` }}
108
+ className="otpInput"
109
+ autoFocus={autoFocus && index === 0}
110
+ />
111
+ ))}
112
+ </div>
113
+ <div className="resendContainer">
114
+ {error ? (
115
+ <div className="errorText">{errorText}</div>
116
+ ) : (
117
+ <div className="timerText">
118
+ {timer > 0 ? `00:${timer} secs` : ""}
119
+ </div>
120
+ )}
121
+ <button
122
+ onClick={handleResendClick}
123
+ disabled={timer > 0}
124
+ style={{
125
+ border: "none",
126
+ background: "none",
127
+ cursor: timer > 0 ? "not-allowed" : "pointer",
128
+ }}
129
+ >
130
+ <div className="resendText">{resendText}</div>
131
+ </button>
132
+ </div>
133
+ </div>
134
+ );
135
+ };
136
+
137
+ export default OTPInput;
@@ -0,0 +1 @@
1
+ export { default } from "./OtpInput";
@@ -0,0 +1,69 @@
1
+ .container {
2
+ display: flex;
3
+ flex-direction: column;
4
+ align-items: center;
5
+ justify-content: center;
6
+ }
7
+
8
+ .spinner {
9
+ border: 2px solid transparent;
10
+ border-radius: 50%;
11
+ border-top: 2px solid currentColor;
12
+ animation: spin 1s linear infinite;
13
+ }
14
+
15
+ .spinnerInner {
16
+ width: 100%;
17
+ height: 100%;
18
+ border-radius: 50%;
19
+ }
20
+
21
+ .errorText {
22
+ color: red;
23
+ }
24
+
25
+ .button {
26
+ padding: 8px 16px;
27
+ border: none;
28
+ border-radius: 4px;
29
+ cursor: pointer;
30
+ font-size: 16px;
31
+
32
+ &-text {
33
+ background: none;
34
+ }
35
+
36
+ &-outlined {
37
+ border: 1px solid currentColor;
38
+ }
39
+
40
+ &-contained {
41
+ background-color: currentColor;
42
+ color: white;
43
+ }
44
+
45
+ &-primary {
46
+ color: blue;
47
+ }
48
+
49
+ &-secondary {
50
+ color: gray;
51
+ }
52
+
53
+ &-small {
54
+ font-size: 12px;
55
+ }
56
+
57
+ &-medium {
58
+ font-size: 16px;
59
+ }
60
+
61
+ &-large {
62
+ font-size: 20px;
63
+ }
64
+ }
65
+
66
+ @keyframes spin {
67
+ 0% { transform: rotate(0deg); }
68
+ 100% { transform: rotate(360deg); }
69
+ }
@@ -0,0 +1,52 @@
1
+ import React from 'react';
2
+ import { render, screen, fireEvent, waitFor } from '@testing-library/react';
3
+ import '@testing-library/jest-dom';
4
+ import PdfView from './PdfView';
5
+
6
+ // Dummy base64 content for testing
7
+ const validBase64Content = 'JVBERi0xLjQKJeLjz9MNCjEgMCBvYmoKPDwvTGluay9QYWdlcyAyIDAgUj4+CnN0YXJ0eHJlZgoyNCAwIFIKZW5kb2JqCjEgMCBvYmoKPDwvTGluay9QYWdlcyAyIDAgUj4+CnN0YXJ0eHJlZgo2IDAgUgo+';
8
+ const invalidBase64Content = 'invalid base64 content';
9
+ const errorText = 'Failed to load PDF';
10
+ const buttonText = 'View PDF';
11
+ const loadingText = 'Loading...';
12
+
13
+ // Save the original implementation of createObjectURL
14
+ const originalCreateObjectURL = global.URL.createObjectURL;
15
+
16
+ describe('PdfView Component', () => {
17
+ beforeAll(() => {
18
+ // Mock URL.createObjectURL directly
19
+ global.URL.createObjectURL = jest.fn(() => 'mock-url');
20
+ });
21
+
22
+ afterAll(() => {
23
+ // Restore the original implementation
24
+ global.URL.createObjectURL = originalCreateObjectURL;
25
+ });
26
+
27
+ it('renders correctly with default props', () => {
28
+ render(<PdfView content={validBase64Content} />);
29
+ expect(screen.getByText(buttonText)).toBeInTheDocument();
30
+ });
31
+
32
+ it('shows error message if content is invalid', () => {
33
+ render(<PdfView content={invalidBase64Content} />);
34
+ expect(screen.getByText(errorText)).toBeInTheDocument();
35
+ });
36
+
37
+ it('applies the provided class names', () => {
38
+ const className = 'custom-class';
39
+ render(<PdfView content={validBase64Content} className={className} />);
40
+ expect(screen.getByText(buttonText).parentElement).toHaveClass(className);
41
+ });
42
+
43
+
44
+
45
+ it('does not show spinner or loading text if isLoading is false', () => {
46
+ render(<PdfView content={validBase64Content} />);
47
+
48
+ // The component should not be in a loading state initially
49
+ expect(screen.queryByTestId('spinnertest')).not.toBeInTheDocument();
50
+ expect(screen.queryByText(loadingText)).not.toBeInTheDocument();
51
+ });
52
+ });