dhre-component-lib 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 (60) hide show
  1. package/jest.config.ts +10 -0
  2. package/package.json +47 -0
  3. package/rollup.config.mjs +37 -0
  4. package/setupTests.ts +1 -0
  5. package/src/__mocks__/styleMock.js +1 -0
  6. package/src/components/Avatar/Avatar.tsx +26 -0
  7. package/src/components/Avatar/index.ts +1 -0
  8. package/src/components/Badge/Badge.css +15 -0
  9. package/src/components/Badge/Badge.tsx +25 -0
  10. package/src/components/Badge/index.ts +1 -0
  11. package/src/components/BreadCrumb/BreadCrumb.tsx +36 -0
  12. package/src/components/BreadCrumb/Breadcrumb.css +21 -0
  13. package/src/components/BreadCrumb/index.ts +0 -0
  14. package/src/components/Button/Button.css +92 -0
  15. package/src/components/Button/Button.tsx +22 -0
  16. package/src/components/Button/index.ts +1 -0
  17. package/src/components/Checkbox/Checkbox.test.tsx +61 -0
  18. package/src/components/Checkbox/Checkbox.tsx +35 -0
  19. package/src/components/Checkbox/index.ts +1 -0
  20. package/src/components/Circular_Progress/CircularProgress.css +19 -0
  21. package/src/components/Circular_Progress/CircularProgress.test.tsx +47 -0
  22. package/src/components/Circular_Progress/CircularProgress.tsx +37 -0
  23. package/src/components/Circular_Progress/index.ts +1 -0
  24. package/src/components/Divider/Divider.test.tsx +19 -0
  25. package/src/components/Divider/Divider.tsx +24 -0
  26. package/src/components/Divider/index.ts +1 -0
  27. package/src/components/Enum.ts +19 -0
  28. package/src/components/InputTextField/InputTextField.test.tsx +48 -0
  29. package/src/components/InputTextField/InputTextField.tsx +46 -0
  30. package/src/components/InputTextField/index.ts +1 -0
  31. package/src/components/Link/Link.test.tsx +47 -0
  32. package/src/components/Link/Link.tsx +33 -0
  33. package/src/components/Link/index.ts +1 -0
  34. package/src/components/Modal/Modal.test.tsx +51 -0
  35. package/src/components/Modal/Modal.tsx +19 -0
  36. package/src/components/Modal/index.ts +1 -0
  37. package/src/components/Notification/Notification.css +20 -0
  38. package/src/components/Notification/Notification.test.tsx +53 -0
  39. package/src/components/Notification/Notification.tsx +42 -0
  40. package/src/components/Notification/index.ts +1 -0
  41. package/src/components/PdfView/PdfView.test.tsx +79 -0
  42. package/src/components/PdfView/PdfView.tsx +93 -0
  43. package/src/components/PdfView/index.ts +1 -0
  44. package/src/components/Progress/Progress.test.tsx +35 -0
  45. package/src/components/Progress/Progress.tsx +35 -0
  46. package/src/components/Progress/index.ts +1 -0
  47. package/src/components/RadioButton/RadioButton.test.tsx +38 -0
  48. package/src/components/RadioButton/RadioButton.tsx +43 -0
  49. package/src/components/RadioButton/index.ts +1 -0
  50. package/src/components/Switch/Switch.tsx +38 -0
  51. package/src/components/Switch/index.ts +1 -0
  52. package/src/components/Tag/Tag.css +15 -0
  53. package/src/components/Tag/Tag.tsx +25 -0
  54. package/src/components/Tag/index.ts +1 -0
  55. package/src/components/Tooltip/Tooltip.css +38 -0
  56. package/src/components/Tooltip/Tooltip.tsx +34 -0
  57. package/src/components/Tooltip/index.ts +1 -0
  58. package/src/components/index.ts +11 -0
  59. package/src/index.ts +1 -0
  60. package/tsconfig.json +121 -0
@@ -0,0 +1,48 @@
1
+ import React from "react";
2
+ import { render } from "@testing-library/react";
3
+ import InputTextField from "./InputTextField";
4
+
5
+ describe("InputTextField Component", () => {
6
+ it("should render without crashing", () => {
7
+ const { getByTestId } = render(
8
+ <InputTextField label="Name" data-testid="inputTextField-rendered" />
9
+ );
10
+ expect(getByTestId("inputTextField-rendered")).toBeInTheDocument();
11
+ });
12
+
13
+ it("should render with default props", () => {
14
+ const { getByLabelText } = render(<InputTextField label="Name" />);
15
+ const inputElement = getByLabelText("Name") as HTMLInputElement;
16
+
17
+ expect(inputElement).toHaveAttribute("type", "text");
18
+
19
+ expect(inputElement).toHaveClass("MuiInputBase-input");
20
+ });
21
+
22
+ it("should render with the specified type", () => {
23
+ const { getByLabelText } = render(
24
+ <InputTextField label="Password" type="password" />
25
+ );
26
+ const inputElement = getByLabelText("Password") as HTMLInputElement;
27
+
28
+ expect(inputElement).toHaveAttribute("type", "password");
29
+ });
30
+
31
+ it("should render with the specified variant", () => {
32
+ const { getByLabelText } = render(
33
+ <InputTextField label="Email" variant="outlined" />
34
+ );
35
+ const inputElement = getByLabelText("Email") as HTMLInputElement;
36
+
37
+ expect(inputElement).toHaveClass("MuiOutlinedInput-input");
38
+ });
39
+
40
+ it("should render with helper text", () => {
41
+ const { getByText } = render(
42
+ <InputTextField label="Email" helperText="Enter your email" />
43
+ );
44
+ const helperTextElement = getByText("Enter your email");
45
+
46
+ expect(helperTextElement).toBeInTheDocument();
47
+ });
48
+ });
@@ -0,0 +1,46 @@
1
+ import React from 'react';
2
+ import { INPUT_TYPES } from '../Enum';
3
+
4
+ export interface CustomInputFieldProps {
5
+ label?: string;
6
+ value?: string;
7
+ type?:string ;
8
+ placeholder?: string;
9
+ onChange?: React.ChangeEventHandler<HTMLInputElement>;
10
+ className?: string;
11
+ inputClassName?: string;
12
+ labelClassName?: string;
13
+ error?: string;
14
+ errorClassName?: string;
15
+ }
16
+
17
+ const CustomInputField: React.FC<CustomInputFieldProps> = ({
18
+ label,
19
+ value = '',
20
+ type = INPUT_TYPES.TEXT,
21
+ placeholder = '',
22
+ onChange,
23
+ className = '',
24
+ inputClassName = '',
25
+ labelClassName = '',
26
+ error = '',
27
+ errorClassName = '',
28
+ ...rest
29
+ }) => {
30
+ return (
31
+ <div className={className}>
32
+ {label && <label className={labelClassName}>{label}</label>}
33
+ <input
34
+ type={type}
35
+ value={value}
36
+ placeholder={placeholder}
37
+ onChange={onChange}
38
+ className={inputClassName}
39
+ {...rest}
40
+ />
41
+ {error && <div className={errorClassName}>{error}</div>}
42
+ </div>
43
+ );
44
+ };
45
+
46
+ export default CustomInputField;
@@ -0,0 +1 @@
1
+ export { default } from "./InputTextField";
@@ -0,0 +1,47 @@
1
+ import React from "react";
2
+ import { render, fireEvent } from "@testing-library/react";
3
+ import Link from "./Link";
4
+
5
+ describe("Link Component", () => {
6
+ it("should render without crashing", () => {
7
+ const { getByRole } = render(
8
+ <Link href="https://example.com">Test Link</Link>
9
+ );
10
+ const linkElement = getByRole("link");
11
+ expect(linkElement).toBeInTheDocument();
12
+ });
13
+
14
+ it("should render with default props", () => {
15
+ const { getByRole } = render(<Link href="https://example.com" />);
16
+ const linkElement = getByRole("link");
17
+
18
+ expect(linkElement).toHaveAttribute("href", "https://example.com");
19
+
20
+ expect(linkElement).toHaveAttribute("target", "_self");
21
+
22
+ expect(linkElement).toHaveAttribute("rel", "noopener noreferrer");
23
+
24
+ expect(linkElement).toHaveClass("MuiLink-underlineNone");
25
+ });
26
+
27
+ it('should render with target="_blank"', () => {
28
+ const { getByRole } = render(
29
+ <Link href="https://example.com" target="_blank" />
30
+ );
31
+ const linkElement = getByRole("link");
32
+
33
+ expect(linkElement).toHaveAttribute("target", "_blank");
34
+ });
35
+
36
+ it("should handle onClick event", () => {
37
+ const handleClick = jest.fn();
38
+ const { getByRole } = render(
39
+ <Link href="https://example.com" onClick={handleClick} />
40
+ );
41
+ const linkElement = getByRole("link");
42
+
43
+ fireEvent.click(linkElement);
44
+
45
+ expect(handleClick).toHaveBeenCalledTimes(1);
46
+ });
47
+ });
@@ -0,0 +1,33 @@
1
+ import React from 'react';
2
+ import { LINK_TARGET } from '../Enum';
3
+
4
+ interface LinkProps {
5
+ href: string;
6
+ target?:string ;
7
+ rel?: string;
8
+ className?: string;
9
+ children?: React.ReactNode;
10
+ }
11
+
12
+ const Link: React.FC<LinkProps> = ({
13
+ href,
14
+ target = LINK_TARGET.Self,
15
+ rel,
16
+ className = '',
17
+ children,
18
+ ...rest
19
+ }) => {
20
+ return (
21
+ <a
22
+ href={href}
23
+ target={target}
24
+ rel={rel}
25
+ className={className}
26
+ {...rest}
27
+ >
28
+ {children}
29
+ </a>
30
+ );
31
+ };
32
+
33
+ export default Link;
@@ -0,0 +1 @@
1
+ export { default } from "./Link";
@@ -0,0 +1,51 @@
1
+ import React from "react";
2
+ import { render, screen } from "@testing-library/react";
3
+ import Modal from "./Modal";
4
+
5
+ describe("Modal Component", () => {
6
+ test("renders children when open", () => {
7
+ render(
8
+ <Modal isOpen={true} onClose={() => {}}>
9
+ <div>Modal Content</div>
10
+ </Modal>
11
+ );
12
+
13
+ expect(screen.getByText("Modal Content")).toBeInTheDocument();
14
+ });
15
+
16
+ test("does not render children when not open", () => {
17
+ render(
18
+ <Modal isOpen={false} onClose={() => {}}>
19
+ <div>Modal Content</div>
20
+ </Modal>
21
+ );
22
+
23
+ expect(screen.queryByText("Modal Content")).not.toBeInTheDocument();
24
+ });
25
+
26
+ test("calls onClose when the modal is closed", () => {
27
+ const onCloseMock = jest.fn();
28
+
29
+ render(
30
+ <Modal isOpen={true} onClose={onCloseMock}>
31
+ <div>Modal Content</div>
32
+ </Modal>
33
+ );
34
+
35
+ // Manually trigger the close event
36
+ onCloseMock();
37
+
38
+ expect(onCloseMock).toHaveBeenCalledTimes(1);
39
+ });
40
+
41
+ test("applies the className to the modal container", () => {
42
+ render(
43
+ <Modal isOpen={true} onClose={() => {}} className="custom-class">
44
+ <div>Modal Content</div>
45
+ </Modal>
46
+ );
47
+
48
+ const modalContainer = screen.getByText("Modal Content").parentElement;
49
+ expect(modalContainer).toHaveClass("custom-class");
50
+ });
51
+ });
@@ -0,0 +1,19 @@
1
+ import React from "react";
2
+ import { Modal as MuiModal } from "@mui/material";
3
+
4
+ interface ModalProps {
5
+ isOpen: boolean;
6
+ onClose: () => void;
7
+ children: React.ReactNode;
8
+ className?: string;
9
+ }
10
+
11
+ const Modal: React.FC<ModalProps> = ({ isOpen, onClose, children, className, ...rest }) => {
12
+ return (
13
+ <MuiModal open={isOpen} onClose={onClose} {...rest}>
14
+ <div className={className}>{children}</div>
15
+ </MuiModal>
16
+ );
17
+ };
18
+
19
+ 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.css';
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,79 @@
1
+ import React from 'react';
2
+ import { render, screen, fireEvent, waitFor } from '@testing-library/react';
3
+ import '@testing-library/jest-dom';
4
+ import PdfView, { PdfViewerProps } from './PdfView';
5
+
6
+ // Mock the URL.createObjectURL and URL.revokeObjectURL functions
7
+ global.URL.createObjectURL = jest.fn();
8
+ global.URL.revokeObjectURL = jest.fn();
9
+
10
+ const defaultProps: PdfViewerProps = {
11
+ content: 'mockBase64Data',
12
+ buttonText: 'View PDF',
13
+ showLoadingSpinner: true, // Set this to true to test the spinner
14
+ loadingText: 'Loading...',
15
+ errorText: 'Failed to load PDF',
16
+ spinnerSize: 24,
17
+ spinnerColor: 'primary',
18
+ };
19
+
20
+ describe('PdfView Component', () => {
21
+ beforeEach(() => {
22
+ jest.clearAllMocks();
23
+ });
24
+
25
+ it('should render the PDF viewer with the default button text', () => {
26
+ render(<PdfView {...defaultProps} />);
27
+ expect(screen.getByText('View PDF')).toBeInTheDocument();
28
+ });
29
+
30
+
31
+ it('should render an error message if the PDF fails to load', async () => {
32
+ // Override the base64ToBlob method to throw an error
33
+ jest.spyOn(global, 'atob').mockImplementation(() => {
34
+ throw new Error('Error converting base64');
35
+ });
36
+
37
+ render(<PdfView {...defaultProps} />);
38
+
39
+ // Click the button to trigger the error
40
+ fireEvent.click(screen.getByText('View PDF'));
41
+
42
+ // Wait for the error message to appear
43
+ await waitFor(() => expect(screen.getByText('Failed to load PDF')).toBeInTheDocument());
44
+
45
+ jest.restoreAllMocks();
46
+ });
47
+
48
+ it('should call the onError callback when there is an error', async () => {
49
+ const mockOnError = jest.fn();
50
+
51
+ // Simulate an error in base64 to blob conversion
52
+ jest.spyOn(global, 'atob').mockImplementation(() => {
53
+ throw new Error('Error converting base64');
54
+ });
55
+
56
+ render(<PdfView {...defaultProps} onError={mockOnError} />);
57
+
58
+ // Click the button to trigger the error
59
+ fireEvent.click(screen.getByText('View PDF'));
60
+
61
+ // Wait for the error callback to be called
62
+ await waitFor(() => expect(mockOnError).toHaveBeenCalled());
63
+
64
+ jest.restoreAllMocks();
65
+ });
66
+
67
+ it('should open the PDF in a new tab when the button is clicked', async () => {
68
+ render(<PdfView {...defaultProps} />);
69
+
70
+ // Click the button to trigger PDF opening
71
+ fireEvent.click(screen.getByText('View PDF'));
72
+
73
+ // Check if URL.createObjectURL was called
74
+ await waitFor(() => expect(global.URL.createObjectURL).toHaveBeenCalled());
75
+
76
+ // Check if window.open was called
77
+ expect(global.URL.createObjectURL).toHaveBeenCalled();
78
+ });
79
+ });
@@ -0,0 +1,93 @@
1
+ import React from 'react';
2
+ import { Button, Box, CircularProgress, Typography } from '@mui/material';
3
+
4
+ export interface PdfViewerProps {
5
+ content: string;
6
+ contentType?: string;
7
+ buttonText?: string;
8
+ loadingText?: string;
9
+ errorText?: string;
10
+ onError?: (error: Error) => void;
11
+ cleanUpDelay?: number;
12
+ buttonVariant?: 'text' | 'outlined' | 'contained';
13
+ buttonColor?: 'inherit' | 'primary' | 'secondary' | 'error' | 'info' | 'success' | 'warning'; // Removed "default"
14
+ buttonSize?: 'small' | 'medium' | 'large';
15
+ showLoadingSpinner?: boolean;
16
+ spinnerSize?: number;
17
+ spinnerColor?: 'inherit' | 'primary' | 'secondary';
18
+ className?: string;
19
+ }
20
+
21
+ const PdfView: React.FC<PdfViewerProps> = ({
22
+ content,
23
+ contentType = "application/pdf",
24
+ buttonText = "View PDF",
25
+ loadingText = "Loading...",
26
+ errorText = "Failed to load PDF",
27
+ onError,
28
+ cleanUpDelay = 1000,
29
+ buttonVariant = "contained",
30
+ buttonColor = "primary", // Changed to a valid default
31
+ buttonSize = "medium",
32
+ showLoadingSpinner = false,
33
+ spinnerSize = 24,
34
+ spinnerColor = "primary",
35
+ className,
36
+ }) => {
37
+ const [isLoading, setIsLoading] = React.useState(false);
38
+ const [isError, setIsError] = React.useState(false);
39
+
40
+ const handleViewPdf = async () => {
41
+ setIsLoading(true);
42
+ setIsError(false);
43
+ try {
44
+ const blob = base64ToBlob(content, contentType);
45
+ const url = URL.createObjectURL(blob);
46
+
47
+ window.open(url, '_blank');
48
+
49
+ setTimeout(() => {
50
+ URL.revokeObjectURL(url);
51
+ }, cleanUpDelay);
52
+ } catch (error) {
53
+ setIsError(true);
54
+ if (onError) {
55
+ onError(error as Error);
56
+ } else {
57
+ console.error("Error generating PDF URL:", error);
58
+ }
59
+ } finally {
60
+ setIsLoading(false);
61
+ }
62
+ };
63
+
64
+ return (
65
+ <Box className={className} display="flex" flexDirection="column" alignItems="center" justifyContent="center">
66
+ {isLoading && showLoadingSpinner && <CircularProgress size={spinnerSize} color={spinnerColor} />}
67
+ {isError && <Typography color="error">{errorText}</Typography>}
68
+ {!isLoading && !isError && (
69
+ <Button
70
+ onClick={handleViewPdf}
71
+ variant={buttonVariant}
72
+ color={buttonColor} // Ensure only valid options are passed
73
+ size={buttonSize}
74
+ >
75
+ {buttonText}
76
+ </Button>
77
+ )}
78
+ {isLoading && <Typography>{loadingText}</Typography>}
79
+ </Box>
80
+ );
81
+ };
82
+
83
+ const base64ToBlob = (content: string, contentType: string = 'application/pdf'): Blob => {
84
+ const byteCharacters = atob(content);
85
+ const byteNumbers = new Array(byteCharacters.length);
86
+ for (let i = 0; i < byteCharacters.length; i++) {
87
+ byteNumbers[i] = byteCharacters.charCodeAt(i);
88
+ }
89
+ const byteArray = new Uint8Array(byteNumbers);
90
+ return new Blob([byteArray], { type: contentType });
91
+ };
92
+
93
+ export default PdfView;
@@ -0,0 +1 @@
1
+ export { default } from "./PdfView";
@@ -0,0 +1,35 @@
1
+ import React from "react";
2
+ import { render, screen } from "@testing-library/react";
3
+ import Progress from "./Progress";
4
+
5
+ describe("Progress Component", () => {
6
+ it("should render without crashing", () => {
7
+ render(<Progress value={50} />);
8
+ const progressElement = screen.getByRole("progressbar");
9
+ expect(progressElement).toBeInTheDocument();
10
+ });
11
+
12
+ it("should render with default props", () => {
13
+ render(<Progress value={50} />);
14
+ const progressElement = screen.getByRole("progressbar");
15
+ expect(progressElement).toHaveClass("MuiLinearProgress-colorPrimary");
16
+ });
17
+
18
+ it("should render with custom variant", () => {
19
+ render(<Progress value={50} variant="determinate" />);
20
+ const progressElement = screen.getByRole("progressbar");
21
+ expect(progressElement).toHaveClass("MuiLinearProgress-determinate");
22
+ });
23
+
24
+ it("should render with custom color", () => {
25
+ render(<Progress value={50} color="secondary" />);
26
+ const progressElement = screen.getByRole("progressbar");
27
+ expect(progressElement).toHaveClass("MuiLinearProgress-colorSecondary");
28
+ });
29
+
30
+ it("should apply custom size", () => {
31
+ render(<Progress value={50} size={150} />);
32
+ const progressElement = screen.getByRole("progressbar");
33
+ expect(progressElement).toHaveStyle({ width: "150px", height: "150px" });
34
+ });
35
+ });
@@ -0,0 +1,35 @@
1
+ import React from 'react';
2
+
3
+ interface ProgressProps {
4
+ value: number;
5
+ max: number;
6
+ className?: string;
7
+ barContainerClassName?: string;
8
+ barClassName?: string;
9
+ labelClassName?: string;
10
+ label?: string;
11
+ }
12
+
13
+ const Progress: React.FC<ProgressProps> = ({
14
+ value,
15
+ max,
16
+ className = '',
17
+ barContainerClassName = '',
18
+ barClassName = '',
19
+ labelClassName = '',
20
+ label,
21
+ ...rest
22
+ }) => {
23
+ const percentage = (value / max) * 100;
24
+
25
+ return (
26
+ <div className={className} {...rest}>
27
+ {label && <div className={labelClassName}>{label}</div>}
28
+ <div className={barContainerClassName}>
29
+ <div className={barClassName} style={{ width: `${percentage}%` }} />
30
+ </div>
31
+ </div>
32
+ );
33
+ };
34
+
35
+ export default Progress;
@@ -0,0 +1 @@
1
+ export { default } from "./Progress";
@@ -0,0 +1,38 @@
1
+ import React from "react";
2
+ import { render, screen, fireEvent } from "@testing-library/react";
3
+ import RadioButton from "./RadioButton";
4
+
5
+ describe("RadioButton Component", () => {
6
+ it("should render without crashing", () => {
7
+ render(<RadioButton value="option1" />);
8
+ const radioElement = screen.getByRole("radio");
9
+ expect(radioElement).toBeInTheDocument();
10
+ });
11
+
12
+ it("should render with default props", () => {
13
+ render(<RadioButton value="option1" />);
14
+ const radioElement = screen.getByRole("radio");
15
+ expect(radioElement).not.toBeChecked();
16
+ expect(radioElement).toHaveAttribute("value", "option1");
17
+ });
18
+
19
+ it("should handle the checked prop", () => {
20
+ render(<RadioButton value="option1" checked />);
21
+ const radioElement = screen.getByRole("radio");
22
+ expect(radioElement).toBeChecked();
23
+ });
24
+
25
+ it("should be disabled when disabled prop is true", () => {
26
+ render(<RadioButton value="option1" disabled />);
27
+ const radioElement = screen.getByRole("radio");
28
+ expect(radioElement).toBeDisabled();
29
+ });
30
+
31
+ it("should call the onChange handler when clicked", () => {
32
+ const handleChange = jest.fn();
33
+ render(<RadioButton value="option1" onChange={handleChange} />);
34
+ const radioElement = screen.getByRole("radio");
35
+ fireEvent.click(radioElement);
36
+ expect(handleChange).toHaveBeenCalledTimes(1);
37
+ });
38
+ });