dhre-component-lib 0.1.6 → 0.1.7
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.
- package/dist/components/Avatar/Avatar.d.ts +9 -0
- package/dist/components/Avatar/Avatar.js +6 -0
- package/dist/components/Avatar/Avatar.test.d.ts +1 -0
- package/dist/components/Avatar/Avatar.test.js +39 -0
- package/dist/components/Avatar/index.d.ts +1 -0
- package/dist/components/Avatar/index.js +1 -0
- package/dist/components/Badge/Badge.d.ts +8 -0
- package/dist/components/Badge/Badge.js +6 -0
- package/dist/components/Badge/Badge.test.d.ts +1 -0
- package/dist/components/Badge/Badge.test.js +44 -0
- package/dist/components/Badge/index.d.ts +2 -0
- package/dist/components/Badge/index.js +2 -0
- package/dist/components/BreadCrumb/BreadCrumb.d.ts +14 -0
- package/dist/components/BreadCrumb/BreadCrumb.js +8 -0
- package/dist/components/BreadCrumb/BreadCrumb.test.d.ts +1 -0
- package/dist/components/BreadCrumb/BreadCrumb.test.js +55 -0
- package/dist/components/BreadCrumb/index.d.ts +2 -0
- package/dist/components/BreadCrumb/index.js +2 -0
- package/dist/components/Button/Button.d.ts +10 -0
- package/dist/components/Button/Button.js +6 -0
- package/dist/components/Button/Button.test.d.ts +1 -0
- package/dist/components/Button/Button.test.js +36 -0
- package/dist/components/Button/index.d.ts +2 -0
- package/dist/components/Button/index.js +2 -0
- package/dist/components/Checkbox/Checkbox.d.ts +11 -0
- package/dist/components/Checkbox/Checkbox.js +7 -0
- package/dist/components/Checkbox/Checkbox.test.d.ts +1 -0
- package/dist/components/Checkbox/Checkbox.test.js +59 -0
- package/dist/components/Checkbox/index.d.ts +1 -0
- package/dist/components/Checkbox/index.js +1 -0
- package/dist/components/CircularProgress/CircularProgress.d.ts +10 -0
- package/dist/components/CircularProgress/CircularProgress.js +15 -0
- package/dist/components/CircularProgress/CircularProgress.test.d.ts +1 -0
- package/dist/components/CircularProgress/CircularProgress.test.js +31 -0
- package/dist/components/CircularProgress/index.d.ts +2 -0
- package/dist/components/CircularProgress/index.js +2 -0
- package/dist/components/Divider/Divider.d.ts +8 -0
- package/dist/components/Divider/Divider.js +7 -0
- package/dist/components/Divider/Divider.test.d.ts +1 -0
- package/dist/components/Divider/Divider.test.js +30 -0
- package/dist/components/Divider/index.d.ts +1 -0
- package/dist/components/Divider/index.js +1 -0
- package/dist/components/Enum.d.ts +17 -0
- package/dist/components/Enum.js +17 -0
- package/dist/components/InputTextField/InputTextField.d.ts +16 -0
- package/dist/components/InputTextField/InputTextField.js +9 -0
- package/dist/components/InputTextField/InputTextField.test.d.ts +1 -0
- package/dist/components/InputTextField/InputTextField.test.js +98 -0
- package/dist/components/InputTextField/index.d.ts +1 -0
- package/dist/components/InputTextField/index.js +1 -0
- package/dist/components/Link/Link.d.ts +10 -0
- package/dist/components/Link/Link.js +6 -0
- package/dist/components/Link/Link.test.d.ts +1 -0
- package/dist/components/Link/Link.test.js +47 -0
- package/dist/components/Link/index.d.ts +1 -0
- package/dist/components/Link/index.js +1 -0
- package/dist/components/Map/Directions.d.ts +13 -0
- package/dist/components/Map/Directions.js +9 -0
- package/dist/components/Map/GoogleMap.d.ts +12 -0
- package/dist/components/Map/GoogleMap.js +104 -0
- package/dist/components/Map/GoogleMapsLoader.d.ts +4 -0
- package/dist/components/Map/GoogleMapsLoader.js +10 -0
- package/dist/components/Map/index.d.ts +3 -0
- package/dist/components/Map/index.js +3 -0
- package/dist/components/Modal/Modal.d.ts +10 -0
- package/dist/components/Modal/Modal.js +14 -0
- package/dist/components/Modal/Modal.test.d.ts +1 -0
- package/dist/components/Modal/Modal.test.js +52 -0
- package/dist/components/Modal/index.d.ts +2 -0
- package/dist/components/Modal/index.js +2 -0
- package/dist/components/Notification/Notification.d.ts +10 -0
- package/dist/components/Notification/Notification.js +21 -0
- package/dist/components/Notification/Notification.test.d.ts +1 -0
- package/dist/components/Notification/Notification.test.js +40 -0
- package/dist/components/Notification/index.d.ts +2 -0
- package/dist/components/Notification/index.js +2 -0
- package/dist/components/OtpInput/OtpInput.d.ts +14 -0
- package/dist/components/OtpInput/OtpInput.js +68 -0
- package/dist/components/OtpInput/OtpInput.test.d.ts +1 -0
- package/dist/components/OtpInput/OtpInput.test.js +36 -0
- package/dist/components/OtpInput/index.d.ts +2 -0
- package/dist/components/OtpInput/index.js +2 -0
- package/dist/components/PdfView/PdfView.d.ts +18 -0
- package/dist/components/PdfView/PdfView.js +59 -0
- package/dist/components/PdfView/PdfView.test.d.ts +1 -0
- package/dist/components/PdfView/PdfView.test.js +41 -0
- package/dist/components/PdfView/index.d.ts +2 -0
- package/dist/components/PdfView/index.js +2 -0
- package/dist/components/Progress/Progress.d.ts +12 -0
- package/dist/components/Progress/Progress.js +9 -0
- package/dist/components/Progress/Progress.test.d.ts +1 -0
- package/dist/components/Progress/Progress.test.js +34 -0
- package/dist/components/Progress/index.d.ts +1 -0
- package/dist/components/Progress/index.js +1 -0
- package/dist/components/RadioButton/RadioButton.d.ts +14 -0
- package/dist/components/RadioButton/RadioButton.js +7 -0
- package/dist/components/RadioButton/RadioButton.test.d.ts +1 -0
- package/dist/components/RadioButton/RadioButton.test.js +29 -0
- package/dist/components/RadioButton/index.d.ts +1 -0
- package/dist/components/RadioButton/index.js +1 -0
- package/dist/components/Switch/Switch.d.ts +13 -0
- package/dist/components/Switch/Switch.js +10 -0
- package/dist/components/Switch/Switch.test.d.ts +1 -0
- package/dist/components/Switch/Switch.test.js +45 -0
- package/dist/components/Switch/index.d.ts +1 -0
- package/dist/components/Switch/index.js +1 -0
- package/dist/components/Tag/Tag.d.ts +9 -0
- package/dist/components/Tag/Tag.js +6 -0
- package/dist/components/Tag/Tag.test.d.ts +1 -0
- package/dist/components/Tag/Tag.test.js +43 -0
- package/dist/components/Tag/index.d.ts +1 -0
- package/dist/components/Tag/index.js +1 -0
- package/dist/components/Tooltip/Tooltip.d.ts +9 -0
- package/dist/components/Tooltip/Tooltip.js +11 -0
- package/dist/components/Tooltip/Tooltip.test.d.ts +1 -0
- package/dist/components/Tooltip/Tooltip.test.js +48 -0
- package/dist/components/Tooltip/index.d.ts +2 -0
- package/dist/components/Tooltip/index.js +2 -0
- package/dist/components/index.d.ts +15 -0
- package/dist/components/index.js +15 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/theme/colors.d.ts +1 -0
- package/dist/theme/colors.js +2 -0
- package/package.json +16 -7
- package/src/components/Badge/index.ts +2 -1
- package/src/components/BreadCrumb/index.ts +2 -1
- package/src/components/Button/index.ts +2 -1
- package/src/components/CircularProgress/index.ts +2 -1
- package/src/components/Map/index.ts +1 -0
- package/src/components/Modal/index.ts +2 -1
- package/src/components/Notification/index.ts +2 -1
- package/src/components/OtpInput/index.ts +2 -1
- package/src/components/PdfView/index.ts +2 -1
- package/src/components/Tooltip/index.ts +2 -1
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render, screen, fireEvent } from '@testing-library/react';
|
|
3
|
+
import CustomInputField from './InputTextField';
|
|
4
|
+
import { INPUT_TYPES } from '../Enum';
|
|
5
|
+
const renderWithProps = (props = {}) => {
|
|
6
|
+
const defaultProps = {
|
|
7
|
+
label: 'Test Label',
|
|
8
|
+
value: 'Test Value',
|
|
9
|
+
type: INPUT_TYPES.TEXT,
|
|
10
|
+
placeholder: 'Enter text...',
|
|
11
|
+
onChange: jest.fn(),
|
|
12
|
+
className: 'custom-input-wrapper',
|
|
13
|
+
inputClassName: 'custom-input',
|
|
14
|
+
labelClassName: 'custom-label',
|
|
15
|
+
error: '',
|
|
16
|
+
errorClassName: 'custom-error',
|
|
17
|
+
id: 'test-input-field',
|
|
18
|
+
...props
|
|
19
|
+
};
|
|
20
|
+
return render(React.createElement(CustomInputField, { ...defaultProps }));
|
|
21
|
+
};
|
|
22
|
+
describe('CustomInputField Component', () => {
|
|
23
|
+
// it('renders without crashing', () => {
|
|
24
|
+
// renderWithProps();
|
|
25
|
+
// // Check if the label correctly associates with the input by using the id
|
|
26
|
+
// const input = screen.getByLabelText('Test Label');
|
|
27
|
+
// expect(input).toBeInTheDocument();
|
|
28
|
+
// });
|
|
29
|
+
it('renders the label correctly', () => {
|
|
30
|
+
renderWithProps();
|
|
31
|
+
expect(screen.getByText('Test Label')).toHaveClass('custom-label');
|
|
32
|
+
});
|
|
33
|
+
it('renders the input with the correct value', () => {
|
|
34
|
+
renderWithProps();
|
|
35
|
+
expect(screen.getByDisplayValue('Test Value')).toHaveClass('custom-input');
|
|
36
|
+
});
|
|
37
|
+
it('calls the onChange handler when input value changes', () => {
|
|
38
|
+
const mockOnChange = jest.fn();
|
|
39
|
+
renderWithProps({ onChange: mockOnChange });
|
|
40
|
+
const input = screen.getByDisplayValue('Test Value');
|
|
41
|
+
fireEvent.change(input, { target: { value: 'New Value' } });
|
|
42
|
+
expect(mockOnChange).toHaveBeenCalledTimes(1);
|
|
43
|
+
});
|
|
44
|
+
it('displays the error message correctly', () => {
|
|
45
|
+
renderWithProps({ error: 'Error message' });
|
|
46
|
+
expect(screen.getByText('Error message')).toHaveClass('custom-error');
|
|
47
|
+
});
|
|
48
|
+
it('renders the input with the correct type', () => {
|
|
49
|
+
renderWithProps({ type: INPUT_TYPES.EMAIL });
|
|
50
|
+
expect(screen.getByPlaceholderText('Enter text...')).toHaveAttribute('type', INPUT_TYPES.EMAIL);
|
|
51
|
+
});
|
|
52
|
+
// it('applies the passed className to the wrapper div', () => {
|
|
53
|
+
// renderWithProps();
|
|
54
|
+
// // Check the class name of the wrapper div
|
|
55
|
+
// const wrapper = screen.getByLabelText('Test Label').parentElement?.parentElement;
|
|
56
|
+
// expect(wrapper).toHaveClass('custom-input-wrapper');
|
|
57
|
+
// });
|
|
58
|
+
it('applies additional props to the input field', () => {
|
|
59
|
+
renderWithProps();
|
|
60
|
+
expect(screen.getByPlaceholderText('Enter text...')).toBeInTheDocument();
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
describe('CustomInputField Component - Default Values', () => {
|
|
64
|
+
it('renders the input field with default value when value prop is not provided', () => {
|
|
65
|
+
render(React.createElement(CustomInputField, { label: "Test Label", type: INPUT_TYPES.TEXT, value: "", onChange: jest.fn() }));
|
|
66
|
+
expect(screen.getByPlaceholderText('')).toHaveValue('');
|
|
67
|
+
});
|
|
68
|
+
it('renders the input field with default type when type prop is not provided', () => {
|
|
69
|
+
render(React.createElement(CustomInputField, { label: "Test Label", value: "Test Value", type: INPUT_TYPES.TEXT, onChange: jest.fn() }));
|
|
70
|
+
expect(screen.getByDisplayValue('Test Value')).toHaveAttribute('type', INPUT_TYPES.TEXT);
|
|
71
|
+
});
|
|
72
|
+
it('renders the input field with default placeholder when placeholder prop is not provided', () => {
|
|
73
|
+
render(React.createElement(CustomInputField, { label: "Test Label", value: "Test Value", type: INPUT_TYPES.TEXT, onChange: jest.fn() }));
|
|
74
|
+
expect(screen.getByDisplayValue('Test Value')).toHaveAttribute('placeholder', '');
|
|
75
|
+
});
|
|
76
|
+
// it('applies default class names if className, inputClassName, or labelClassName are not provided', () => {
|
|
77
|
+
// render(<CustomInputField label="Test Label" value="Test Value" type={INPUT_TYPES.TEXT} onChange={jest.fn()} />);
|
|
78
|
+
// // Get the label element
|
|
79
|
+
// const label = screen.getByText('Test Label');
|
|
80
|
+
// const input = screen.getByDisplayValue('Test Value');
|
|
81
|
+
// // Get the parent wrapper div
|
|
82
|
+
// const wrapper = label.closest('div');
|
|
83
|
+
// // Check if the wrapper div has default class names or no class names
|
|
84
|
+
// if (wrapper) {
|
|
85
|
+
// // Expect no additional class names or specific default class names
|
|
86
|
+
// expect(wrapper).toHaveClass('default-wrapper-class'); // Replace with actual default class
|
|
87
|
+
// }
|
|
88
|
+
// // Check if the input field has default input class names or no class names
|
|
89
|
+
// expect(input).toHaveClass('default-input-class'); // Replace with actual default class
|
|
90
|
+
// // Check if the label has default label class names or no class names
|
|
91
|
+
// expect(label).toHaveClass('default-label-class'); // Replace with actual default class
|
|
92
|
+
// });
|
|
93
|
+
it('does not render the error message if error prop is not provided', () => {
|
|
94
|
+
render(React.createElement(CustomInputField, { label: "Test Label", value: "Test Value", type: INPUT_TYPES.TEXT, onChange: jest.fn() }));
|
|
95
|
+
const errorMessage = screen.queryByText('Error message');
|
|
96
|
+
expect(errorMessage).not.toBeInTheDocument();
|
|
97
|
+
});
|
|
98
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "./InputTextField";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "./InputTextField";
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { LINK_TARGET } from '../Enum';
|
|
3
|
+
const Link = ({ href, target = LINK_TARGET.Self, rel, className = '', children, ...rest }) => {
|
|
4
|
+
return (React.createElement("a", { href: href, target: target, rel: rel, className: className, ...rest }, children));
|
|
5
|
+
};
|
|
6
|
+
export default Link;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render, screen } from '@testing-library/react';
|
|
3
|
+
import Link from './Link';
|
|
4
|
+
import { LINK_TARGET } from '../Enum';
|
|
5
|
+
describe('Link Component', () => {
|
|
6
|
+
const defaultProps = {
|
|
7
|
+
href: 'https://example.com',
|
|
8
|
+
target: LINK_TARGET.Self,
|
|
9
|
+
rel: 'noopener noreferrer',
|
|
10
|
+
className: 'custom-link',
|
|
11
|
+
children: 'Click here',
|
|
12
|
+
};
|
|
13
|
+
it('renders the link with the correct href', () => {
|
|
14
|
+
render(React.createElement(Link, { ...defaultProps }));
|
|
15
|
+
expect(screen.getByText('Click here')).toHaveAttribute('href', 'https://example.com');
|
|
16
|
+
});
|
|
17
|
+
it('renders the link with the correct target attribute', () => {
|
|
18
|
+
render(React.createElement(Link, { ...defaultProps }));
|
|
19
|
+
expect(screen.getByText('Click here')).toHaveAttribute('target', LINK_TARGET.Self);
|
|
20
|
+
});
|
|
21
|
+
it('renders the link with the correct rel attribute', () => {
|
|
22
|
+
render(React.createElement(Link, { ...defaultProps }));
|
|
23
|
+
expect(screen.getByText('Click here')).toHaveAttribute('rel', 'noopener noreferrer');
|
|
24
|
+
});
|
|
25
|
+
it('renders the link with the correct className', () => {
|
|
26
|
+
render(React.createElement(Link, { ...defaultProps }));
|
|
27
|
+
expect(screen.getByText('Click here')).toHaveClass('custom-link');
|
|
28
|
+
});
|
|
29
|
+
it('renders the link with the correct children', () => {
|
|
30
|
+
render(React.createElement(Link, { ...defaultProps }));
|
|
31
|
+
expect(screen.getByText('Click here')).toBeInTheDocument();
|
|
32
|
+
});
|
|
33
|
+
it('renders with default target when not specified', () => {
|
|
34
|
+
const propsWithoutTarget = {
|
|
35
|
+
href: defaultProps.href,
|
|
36
|
+
rel: defaultProps.rel,
|
|
37
|
+
className: defaultProps.className,
|
|
38
|
+
children: defaultProps.children,
|
|
39
|
+
};
|
|
40
|
+
render(React.createElement(Link, { ...propsWithoutTarget }));
|
|
41
|
+
expect(screen.getByText('Click here')).toHaveAttribute('target', LINK_TARGET.Self);
|
|
42
|
+
});
|
|
43
|
+
it('applies additional props to the link element', () => {
|
|
44
|
+
render(React.createElement(Link, { ...defaultProps, "data-testid": "custom-link" }));
|
|
45
|
+
expect(screen.getByTestId('custom-link')).toBeInTheDocument();
|
|
46
|
+
});
|
|
47
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "./Link";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "./Link";
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
interface GetDirectionActionProps {
|
|
3
|
+
title: string;
|
|
4
|
+
titleClasses?: string;
|
|
5
|
+
location: string;
|
|
6
|
+
locationClasses?: string;
|
|
7
|
+
hours: string;
|
|
8
|
+
hoursClasses?: string;
|
|
9
|
+
directionsLink: string;
|
|
10
|
+
buttonName?: string;
|
|
11
|
+
}
|
|
12
|
+
declare const GetDirectionAction: React.FC<GetDirectionActionProps>;
|
|
13
|
+
export default GetDirectionAction;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
const GetDirectionAction = ({ title, titleClasses, location, locationClasses, hours, hoursClasses, directionsLink, buttonName = 'Get Directions', }) => {
|
|
3
|
+
return (React.createElement("div", { className: "direction-action-container" },
|
|
4
|
+
React.createElement("h3", { className: titleClasses }, title),
|
|
5
|
+
React.createElement("p", { className: locationClasses }, location),
|
|
6
|
+
React.createElement("p", { className: hoursClasses }, hours),
|
|
7
|
+
React.createElement("a", { href: directionsLink, target: "_blank", rel: "noopener noreferrer", className: "directions-link" }, buttonName)));
|
|
8
|
+
};
|
|
9
|
+
export default GetDirectionAction;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
export interface MapComponentProps {
|
|
3
|
+
containerClassName?: string;
|
|
4
|
+
zoom?: number;
|
|
5
|
+
mapOptions?: google.maps.MapOptions;
|
|
6
|
+
radius?: number;
|
|
7
|
+
googleMapsUrls: string[];
|
|
8
|
+
buttonText: string;
|
|
9
|
+
buttonClassName: string;
|
|
10
|
+
}
|
|
11
|
+
declare const MapComponent: React.FC<MapComponentProps>;
|
|
12
|
+
export default MapComponent;
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import React, { useState, useRef, useEffect } from "react";
|
|
2
|
+
import { GoogleMap, Marker, Circle, DirectionsRenderer, } from "@react-google-maps/api";
|
|
3
|
+
import { useGoogleMapsLoader } from "./GoogleMapsLoader";
|
|
4
|
+
import styles from './GoogleMap.module.scss';
|
|
5
|
+
const MapComponent = ({ containerClassName = "map-container", zoom = 18, mapOptions = {
|
|
6
|
+
zoomControl: true,
|
|
7
|
+
tilt: 0,
|
|
8
|
+
gestureHandling: "auto",
|
|
9
|
+
mapTypeId: "roadmap",
|
|
10
|
+
}, radius = 5000, googleMapsUrls, buttonText, buttonClassName, }) => {
|
|
11
|
+
const [location, setLocation] = useState(null);
|
|
12
|
+
const [mapVisible, setMapVisible] = useState(false);
|
|
13
|
+
const [directionsResponse, setDirectionsResponse] = useState(null);
|
|
14
|
+
const mapRef = useRef(null);
|
|
15
|
+
const directionsService = useRef(null);
|
|
16
|
+
const directionsRenderer = useRef(null);
|
|
17
|
+
const handleLocationRequest = () => {
|
|
18
|
+
if ("geolocation" in navigator) {
|
|
19
|
+
navigator.geolocation.getCurrentPosition(({ coords }) => {
|
|
20
|
+
const { latitude, longitude } = coords;
|
|
21
|
+
setLocation({ latitude, longitude });
|
|
22
|
+
setMapVisible(true);
|
|
23
|
+
}, () => {
|
|
24
|
+
alert("Failed to get location. Please try again.");
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
alert("Geolocation is not supported by this browser.");
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
const handleMarkerClick = (lat, lng) => {
|
|
32
|
+
if (location) {
|
|
33
|
+
const directionsServiceInstance = directionsService.current || new google.maps.DirectionsService();
|
|
34
|
+
const directionsRendererInstance = directionsRenderer.current || new google.maps.DirectionsRenderer();
|
|
35
|
+
if (!directionsService.current)
|
|
36
|
+
directionsService.current = directionsServiceInstance;
|
|
37
|
+
if (!directionsRenderer.current)
|
|
38
|
+
directionsRenderer.current = directionsRendererInstance;
|
|
39
|
+
const directionsRequest = {
|
|
40
|
+
origin: { lat: location.latitude, lng: location.longitude },
|
|
41
|
+
destination: { lat, lng },
|
|
42
|
+
travelMode: google.maps.TravelMode.DRIVING,
|
|
43
|
+
};
|
|
44
|
+
directionsService.current.route(directionsRequest, (result, status) => {
|
|
45
|
+
if (status === google.maps.DirectionsStatus.OK) {
|
|
46
|
+
setDirectionsResponse(result);
|
|
47
|
+
if (mapRef.current && directionsRenderer.current) {
|
|
48
|
+
directionsRenderer.current.setMap(mapRef.current);
|
|
49
|
+
directionsRenderer.current.setDirections(result);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
alert("Failed to get directions. Please try again.");
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
const extractCoordinatesFromUrl = (url) => {
|
|
59
|
+
const regex = /@(-?\d+\.\d+),(-?\d+\.\d+)/;
|
|
60
|
+
const match = url.match(regex);
|
|
61
|
+
if (match) {
|
|
62
|
+
return {
|
|
63
|
+
lat: parseFloat(match[1]),
|
|
64
|
+
lng: parseFloat(match[2]),
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
return null;
|
|
68
|
+
};
|
|
69
|
+
const { isLoaded } = useGoogleMapsLoader();
|
|
70
|
+
useEffect(() => { }, [isLoaded]);
|
|
71
|
+
useEffect(() => { }, [mapVisible]);
|
|
72
|
+
if (!isLoaded) {
|
|
73
|
+
return React.createElement("div", null, "Loading map...");
|
|
74
|
+
}
|
|
75
|
+
if (mapVisible && location) {
|
|
76
|
+
return (React.createElement("div", { className: containerClassName },
|
|
77
|
+
React.createElement("button", { onClick: handleLocationRequest, className: buttonClassName }, buttonText),
|
|
78
|
+
React.createElement(GoogleMap, { mapContainerStyle: styles.mapContainerStyle, center: { lat: location.latitude, lng: location.longitude }, zoom: zoom, options: mapOptions, onLoad: (map) => {
|
|
79
|
+
mapRef.current = map;
|
|
80
|
+
directionsRenderer.current = new google.maps.DirectionsRenderer();
|
|
81
|
+
directionsRenderer.current.setMap(map);
|
|
82
|
+
} },
|
|
83
|
+
React.createElement(Marker, { position: { lat: location.latitude, lng: location.longitude }, icon: "http://maps.google.com/mapfiles/ms/icons/red-dot.png" }),
|
|
84
|
+
React.createElement(Circle, { center: { lat: location.latitude, lng: location.longitude }, radius: radius, options: {
|
|
85
|
+
strokeColor: "#FF0000",
|
|
86
|
+
strokeOpacity: 0.8,
|
|
87
|
+
strokeWeight: 2,
|
|
88
|
+
fillColor: "#FF0000",
|
|
89
|
+
fillOpacity: 0,
|
|
90
|
+
} }),
|
|
91
|
+
googleMapsUrls.map((url, index) => {
|
|
92
|
+
const coord = extractCoordinatesFromUrl(url);
|
|
93
|
+
if (!coord)
|
|
94
|
+
return null;
|
|
95
|
+
return (React.createElement(Marker, { key: index, position: { lat: coord.lat, lng: coord.lng }, title: `Sample Location ${index + 1}`, icon: "http://maps.google.com/mapfiles/ms/icons/blue-dot.png", onClick: () => handleMarkerClick(coord.lat, coord.lng) }));
|
|
96
|
+
}),
|
|
97
|
+
directionsResponse && (React.createElement(DirectionsRenderer, { directions: directionsResponse, options: { suppressMarkers: true } })))));
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
return (React.createElement("div", { className: containerClassName },
|
|
101
|
+
React.createElement("button", { onClick: handleLocationRequest, className: buttonClassName }, buttonText)));
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
export default MapComponent;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { useJsApiLoader } from '@react-google-maps/api';
|
|
2
|
+
const GOOGLE_MAPS_API_KEY = process.env.NEXT_PUBLIC_GOOGLE_MAP_API; // Ensure you are using environment variable
|
|
3
|
+
export const useGoogleMapsLoader = () => {
|
|
4
|
+
return useJsApiLoader({
|
|
5
|
+
googleMapsApiKey: GOOGLE_MAPS_API_KEY, // Replace with your API key
|
|
6
|
+
libraries: ['places', 'drawing', 'geometry'], // Include all libraries you need
|
|
7
|
+
language: 'en',
|
|
8
|
+
region: 'US',
|
|
9
|
+
});
|
|
10
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
interface ModalProps {
|
|
3
|
+
isOpen: boolean;
|
|
4
|
+
onClose: () => void;
|
|
5
|
+
children: React.ReactNode;
|
|
6
|
+
modalOverlayClassname?: string;
|
|
7
|
+
modalContentClassname?: string;
|
|
8
|
+
}
|
|
9
|
+
declare const Modal: React.FC<ModalProps>;
|
|
10
|
+
export default Modal;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import styles from './Modal.module.scss';
|
|
3
|
+
const Modal = ({ isOpen, onClose, children, modalOverlayClassname = '', modalContentClassname = '' }) => {
|
|
4
|
+
if (!isOpen)
|
|
5
|
+
return null;
|
|
6
|
+
const handleOverlayClick = (event) => {
|
|
7
|
+
if (event.target === event.currentTarget) {
|
|
8
|
+
onClose();
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
return (React.createElement("button", { className: `${styles.modalOverlay} ${modalOverlayClassname}`, onClick: handleOverlayClick },
|
|
12
|
+
React.createElement("div", { className: `${styles.modalContent} ${modalContentClassname}` }, children)));
|
|
13
|
+
};
|
|
14
|
+
export default Modal;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render, screen, fireEvent } from '@testing-library/react';
|
|
3
|
+
import Modal from './Modal';
|
|
4
|
+
describe('Modal Component', () => {
|
|
5
|
+
const onCloseMock = jest.fn();
|
|
6
|
+
const modalContentText = 'This is the modal content';
|
|
7
|
+
const renderModal = (isOpen, modalOverlayClassname, modalContentClassname) => {
|
|
8
|
+
render(React.createElement(Modal, { isOpen: isOpen, onClose: onCloseMock, modalOverlayClassname: modalOverlayClassname, modalContentClassname: modalContentClassname },
|
|
9
|
+
React.createElement("div", null, modalContentText)));
|
|
10
|
+
};
|
|
11
|
+
afterEach(() => {
|
|
12
|
+
jest.clearAllMocks();
|
|
13
|
+
});
|
|
14
|
+
it('renders the modal when isOpen is true', () => {
|
|
15
|
+
renderModal(true);
|
|
16
|
+
expect(screen.getByText(modalContentText)).toBeInTheDocument();
|
|
17
|
+
});
|
|
18
|
+
it('does not render the modal when isOpen is false', () => {
|
|
19
|
+
renderModal(false);
|
|
20
|
+
expect(screen.queryByText(modalContentText)).not.toBeInTheDocument();
|
|
21
|
+
});
|
|
22
|
+
it('calls onClose when clicking on the overlay', () => {
|
|
23
|
+
renderModal(true);
|
|
24
|
+
const overlay = screen.getByText(modalContentText).closest('button');
|
|
25
|
+
fireEvent.click(overlay);
|
|
26
|
+
expect(onCloseMock).toHaveBeenCalled();
|
|
27
|
+
});
|
|
28
|
+
it('does not call onClose when clicking inside the modal content', () => {
|
|
29
|
+
renderModal(true);
|
|
30
|
+
const modalContent = screen.getByText(modalContentText);
|
|
31
|
+
fireEvent.click(modalContent);
|
|
32
|
+
expect(onCloseMock).not.toHaveBeenCalled();
|
|
33
|
+
});
|
|
34
|
+
it('applies the provided modalOverlayClassname', () => {
|
|
35
|
+
const overlayClass = 'customOverlayClass';
|
|
36
|
+
renderModal(true, overlayClass);
|
|
37
|
+
const overlay = screen.getByText(modalContentText).closest('button');
|
|
38
|
+
expect(overlay).toHaveClass(overlayClass);
|
|
39
|
+
});
|
|
40
|
+
it('applies the provided modalContentClassname', () => {
|
|
41
|
+
const contentClass = 'customContentClass';
|
|
42
|
+
renderModal(true, undefined, contentClass);
|
|
43
|
+
const modalContent = screen.getByText(modalContentText);
|
|
44
|
+
expect(modalContent.parentElement).toHaveClass(contentClass);
|
|
45
|
+
});
|
|
46
|
+
it('does not render modal when isOpen is false, even if modalOverlayClassname and modalContentClassname are provided', () => {
|
|
47
|
+
const overlayClass = 'customOverlayClass';
|
|
48
|
+
const contentClass = 'customContentClass';
|
|
49
|
+
renderModal(false, overlayClass, contentClass);
|
|
50
|
+
expect(screen.queryByText(modalContentText)).not.toBeInTheDocument();
|
|
51
|
+
});
|
|
52
|
+
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import React 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
|
+
declare const Notification: React.FC<NotificationProps>;
|
|
10
|
+
export default Notification;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import React, { useEffect } from "react";
|
|
2
|
+
import './Notification.module.scss';
|
|
3
|
+
const Notification = ({ message, severity = 'info', autoHideDuration = 6000, onClose, }) => {
|
|
4
|
+
useEffect(() => {
|
|
5
|
+
if (autoHideDuration && onClose) {
|
|
6
|
+
const timer = setTimeout(() => {
|
|
7
|
+
onClose(new Event('timeout'), 'timeout');
|
|
8
|
+
}, autoHideDuration);
|
|
9
|
+
return () => clearTimeout(timer);
|
|
10
|
+
}
|
|
11
|
+
}, [autoHideDuration, onClose]);
|
|
12
|
+
const handleClose = (event, reason) => {
|
|
13
|
+
if (onClose) {
|
|
14
|
+
onClose(event, reason);
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
return (React.createElement("div", { className: `notification ${severity}`, onClick: (e) => handleClose(e), role: "alert" },
|
|
18
|
+
React.createElement("div", { className: "notification-message" }, message),
|
|
19
|
+
React.createElement("button", { className: "close-button", onClick: (e) => handleClose(e, 'close'), "aria-label": "Close notification" }, "\u00D7")));
|
|
20
|
+
};
|
|
21
|
+
export default Notification;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
|
|
3
|
+
import Notification from './Notification';
|
|
4
|
+
describe('Notification Component', () => {
|
|
5
|
+
const defaultProps = {
|
|
6
|
+
message: 'Test Message',
|
|
7
|
+
severity: 'info',
|
|
8
|
+
autoHideDuration: 6000,
|
|
9
|
+
onClose: jest.fn(),
|
|
10
|
+
};
|
|
11
|
+
const renderNotification = (props = {}) => {
|
|
12
|
+
return render(React.createElement(Notification, { ...defaultProps, ...props }));
|
|
13
|
+
};
|
|
14
|
+
test('renders notification with the correct message', () => {
|
|
15
|
+
render(React.createElement(Notification, { message: "Test Message", severity: "info" }));
|
|
16
|
+
// Check if the element exists by querying it
|
|
17
|
+
const notificationElement = screen.getByText('Test Message');
|
|
18
|
+
expect(notificationElement).not.toBeNull(); // Equivalent to `toBeInTheDocument`
|
|
19
|
+
// Alternatively, check for the presence of the role or className
|
|
20
|
+
const alertElement = screen.getByRole('alert');
|
|
21
|
+
expect(alertElement.className).toContain('info'); // Check if className includes "info"
|
|
22
|
+
});
|
|
23
|
+
test('calls onClose when the notification is clicked', () => {
|
|
24
|
+
const onClose = jest.fn();
|
|
25
|
+
renderNotification({ onClose });
|
|
26
|
+
fireEvent.click(screen.getByRole('alert'));
|
|
27
|
+
expect(onClose).toHaveBeenCalled();
|
|
28
|
+
});
|
|
29
|
+
test('calls onClose when the close button is clicked', () => {
|
|
30
|
+
const onClose = jest.fn();
|
|
31
|
+
renderNotification({ onClose });
|
|
32
|
+
fireEvent.click(screen.getByLabelText('Close notification'));
|
|
33
|
+
expect(onClose).toHaveBeenCalledWith(expect.anything(), 'close');
|
|
34
|
+
});
|
|
35
|
+
test('calls onClose after autoHideDuration expires', async () => {
|
|
36
|
+
const onClose = jest.fn();
|
|
37
|
+
renderNotification({ onClose, autoHideDuration: 1000 });
|
|
38
|
+
await waitFor(() => expect(onClose).toHaveBeenCalledWith(expect.anything(), 'timeout'), { timeout: 1500 });
|
|
39
|
+
});
|
|
40
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import "./OtpInput.module.scss";
|
|
3
|
+
interface OTPInputProps {
|
|
4
|
+
length?: number;
|
|
5
|
+
onChange: (otp: string) => void;
|
|
6
|
+
autoFocus?: boolean;
|
|
7
|
+
onResend: () => void;
|
|
8
|
+
resendDelay?: number;
|
|
9
|
+
error?: boolean;
|
|
10
|
+
errorText?: string;
|
|
11
|
+
resendText: string;
|
|
12
|
+
}
|
|
13
|
+
declare const OTPInput: React.FC<OTPInputProps>;
|
|
14
|
+
export default OTPInput;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import React, { useEffect, useState } from "react";
|
|
2
|
+
import "./OtpInput.module.scss";
|
|
3
|
+
const OTPInput = ({ length = 4, onChange, autoFocus = false, onResend, resendDelay = 60, error = false, errorText, resendText, }) => {
|
|
4
|
+
const [otp, setOtp] = useState(Array(length).fill(""));
|
|
5
|
+
const [timer, setTimer] = useState(resendDelay);
|
|
6
|
+
const [isComplete, setIsComplete] = useState(false);
|
|
7
|
+
useEffect(() => {
|
|
8
|
+
const countdown = setInterval(() => {
|
|
9
|
+
setTimer((prevTimer) => (prevTimer > 0 ? prevTimer - 1 : 0));
|
|
10
|
+
}, 1000);
|
|
11
|
+
return () => clearInterval(countdown);
|
|
12
|
+
}, []);
|
|
13
|
+
const handleChange = (value, index) => {
|
|
14
|
+
if (isNaN(Number(value)))
|
|
15
|
+
return;
|
|
16
|
+
const newOtp = [...otp];
|
|
17
|
+
newOtp[index] = value;
|
|
18
|
+
setOtp(newOtp);
|
|
19
|
+
onChange(newOtp.join(""));
|
|
20
|
+
if (value && index < length - 1) {
|
|
21
|
+
const nextInput = document.getElementById(`otp-input-${index + 1}`);
|
|
22
|
+
if (nextInput) {
|
|
23
|
+
nextInput.focus();
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
const handleKeyDown = (e, index) => {
|
|
28
|
+
if (e.key === "Backspace" && !otp[index] && index > 0) {
|
|
29
|
+
const prevInput = document.getElementById(`otp-input-${index - 1}`);
|
|
30
|
+
if (prevInput) {
|
|
31
|
+
prevInput.focus();
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
const handleResendClick = () => {
|
|
36
|
+
onResend();
|
|
37
|
+
setOtp(Array(length).fill(""));
|
|
38
|
+
setTimer(resendDelay);
|
|
39
|
+
setIsComplete(false);
|
|
40
|
+
};
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
setIsComplete(otp.every((digit) => digit !== ""));
|
|
43
|
+
}, [otp]);
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
if (error) {
|
|
46
|
+
setTimer(0);
|
|
47
|
+
}
|
|
48
|
+
}, [error]);
|
|
49
|
+
function getReadableStatus() {
|
|
50
|
+
if (error) {
|
|
51
|
+
return "#EB0542";
|
|
52
|
+
}
|
|
53
|
+
return isComplete ? "#00B578" : "#A7A7A7";
|
|
54
|
+
}
|
|
55
|
+
return (React.createElement("div", { className: "otpMainContainer" },
|
|
56
|
+
React.createElement("div", { className: "otpInputDiv" }, Array(length)
|
|
57
|
+
.fill("")
|
|
58
|
+
.map((_, index) => (React.createElement("input", { key: index, type: "text", id: `otp-input-${index}`, value: otp[index], "data-testid": `otp-input-${index}`, role: "textbox", onChange: (e) => handleChange(e.target.value, index), onKeyDown: (e) => handleKeyDown(e, index), maxLength: 1, style: { border: `1px solid ${getReadableStatus()}` }, className: "otpInput", autoFocus: autoFocus && index === 0 })))),
|
|
59
|
+
React.createElement("div", { className: "resendContainer" },
|
|
60
|
+
error ? (React.createElement("div", { className: "errorText" }, errorText)) : (React.createElement("div", { className: "timerText" }, timer > 0 ? `00:${timer} secs` : "")),
|
|
61
|
+
React.createElement("button", { onClick: handleResendClick, disabled: timer > 0, style: {
|
|
62
|
+
border: "none",
|
|
63
|
+
background: "none",
|
|
64
|
+
cursor: timer > 0 ? "not-allowed" : "pointer",
|
|
65
|
+
} },
|
|
66
|
+
React.createElement("div", { className: "resendText" }, resendText)))));
|
|
67
|
+
};
|
|
68
|
+
export default OTPInput;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import '@testing-library/jest-dom';
|