dhre-ui-kit 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.
- package/README.md +1 -0
- package/package.json +104 -0
- package/src/App.tsx +146 -0
- package/src/assets/images/calenderDots.svg +1 -0
- package/src/assets/index.ts +2 -0
- package/src/components/autoOtpReader/AutoOtpReader.test.tsx +56 -0
- package/src/components/autoOtpReader/AutoOtpReader.tsx +79 -0
- package/src/components/autoOtpReader/index.ts +1 -0
- package/src/components/badge/Badge.styles.ts +20 -0
- package/src/components/badge/Badge.test.tsx +45 -0
- package/src/components/badge/Badge.tsx +47 -0
- package/src/components/badge/index.ts +1 -0
- package/src/components/bottomDrawer/BottomDrawer.interface.ts +12 -0
- package/src/components/bottomDrawer/BottomDrawer.styles.ts +31 -0
- package/src/components/bottomDrawer/BottomDrawer.test.tsx +19 -0
- package/src/components/bottomDrawer/BottomDrawer.tsx +43 -0
- package/src/components/bottomDrawer/index.ts +1 -0
- package/src/components/button/Button.interface.ts +18 -0
- package/src/components/button/Button.styles.ts +78 -0
- package/src/components/button/Button.test.tsx +43 -0
- package/src/components/button/Button.tsx +53 -0
- package/src/components/button/index.ts +1 -0
- package/src/components/cards/Cards.interface.ts +13 -0
- package/src/components/cards/Cards.styles.ts +59 -0
- package/src/components/cards/Cards.test.tsx +41 -0
- package/src/components/cards/Cards.tsx +69 -0
- package/src/components/cards/index.ts +1 -0
- package/src/components/checkBox/CheckBox.interface.ts +10 -0
- package/src/components/checkBox/CheckBox.styles.ts +36 -0
- package/src/components/checkBox/CheckBox.test.tsx +46 -0
- package/src/components/checkBox/CheckBox.tsx +42 -0
- package/src/components/checkBox/index.ts +1 -0
- package/src/components/customDropdown/CustomDropdown.interface.ts +15 -0
- package/src/components/customDropdown/CustomDropdown.styles.tsx +46 -0
- package/src/components/customDropdown/CustomDropdown.test.tsx +30 -0
- package/src/components/customDropdown/CustomDropdown.tsx +106 -0
- package/src/components/customDropdown/index.ts +1 -0
- package/src/components/customInputText/CustomInputText.interface.ts +20 -0
- package/src/components/customInputText/CustomInputText.styles.ts +56 -0
- package/src/components/customInputText/CustomInputText.test.tsx +37 -0
- package/src/components/customInputText/CustomInputText.tsx +120 -0
- package/src/components/customInputText/index.ts +1 -0
- package/src/components/customText/CustomText.tsx +31 -0
- package/src/components/customText/index.ts +1 -0
- package/src/components/customTooltip/CustomTooltip.interface.ts +14 -0
- package/src/components/customTooltip/CustomTooltip.styles.ts +23 -0
- package/src/components/customTooltip/CustomTooltip.test.tsx +18 -0
- package/src/components/customTooltip/CustomTooltip.tsx +46 -0
- package/src/components/customTooltip/index.ts +1 -0
- package/src/components/customTypography/CustomTypography.tsx +25 -0
- package/src/components/fileUpload/FileUpload.styles.ts +41 -0
- package/src/components/fileUpload/FileUpload.test.tsx +39 -0
- package/src/components/fileUpload/FileUpload.tsx +65 -0
- package/src/components/fileUpload/index.ts +1 -0
- package/src/components/notification/Notification.interface.ts +61 -0
- package/src/components/notification/Notification.styles.ts +33 -0
- package/src/components/notification/Notification.test.tsx +116 -0
- package/src/components/notification/Notification.tsx +156 -0
- package/src/components/pdfView/PdfView.test.tsx +10 -0
- package/src/components/pdfView/PdfView.tsx +18 -0
- package/src/components/pdfView/index.ts +1 -0
- package/src/components/popup/PopUp.interface.ts +9 -0
- package/src/components/popup/PopUp.styles.ts +52 -0
- package/src/components/popup/PopUp.test.tsx +35 -0
- package/src/components/popup/PopUp.tsx +60 -0
- package/src/components/popup/index.ts +1 -0
- package/src/components/progressBar/ProgressBar.test.tsx +78 -0
- package/src/components/progressBar/ProgressBar.tsx +93 -0
- package/src/components/progressBar/index.ts +1 -0
- package/src/components/property360View/Property360View.test.tsx +24 -0
- package/src/components/property360View/Property360View.tsx +19 -0
- package/src/components/property360View/index.ts +1 -0
- package/src/components/rating/Rating.styles.ts +13 -0
- package/src/components/rating/Rating.test.tsx +61 -0
- package/src/components/rating/Rating.tsx +96 -0
- package/src/components/rating/index.ts +1 -0
- package/src/components/tabs/Tabs.interface.ts +13 -0
- package/src/components/tabs/Tabs.styles.ts +36 -0
- package/src/components/tabs/Tabs.test.tsx +20 -0
- package/src/components/tabs/Tabs.tsx +65 -0
- package/src/components/tabs/index.ts +1 -0
- package/src/components/tags/Tags.interface.ts +12 -0
- package/src/components/tags/Tags.styles.ts +36 -0
- package/src/components/tags/Tags.test.tsx +22 -0
- package/src/components/tags/Tags.tsx +76 -0
- package/src/components/tags/index.ts +1 -0
- package/src/components/toggleButton/ToggleButton.styles.ts +40 -0
- package/src/components/toggleButton/ToggleButton.test.tsx +33 -0
- package/src/components/toggleButton/ToggleButton.tsx +78 -0
- package/src/components/toggleButton/index.ts +1 -0
- package/src/index.ts +19 -0
- package/src/theme/ThemeContext.tsx +32 -0
- package/src/theme/colors.ts +156 -0
- package/src/theme/fonts.ts +303 -0
- package/src/theme/themes.ts +20 -0
- package/src/theme/withThemeProvider.tsx +14 -0
- package/src/utils/appEnum.ts +17 -0
- package/src/utils/commonStyles.ts +17 -0
- package/src/utils/constants.ts +17 -0
- package/src/utils/deviceUtils.ts +9 -0
- package/src/utils/regex.ts +1 -0
- package/src/utils/stylehelper.ts +13 -0
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import commonStyles from '../../utils/commonStyles';
|
|
2
|
+
import React, {useEffect} from 'react';
|
|
3
|
+
import {View} from 'react-native';
|
|
4
|
+
import * as Progress from 'react-native-progress';
|
|
5
|
+
import {ShapeType} from '../../utils/appEnum';
|
|
6
|
+
|
|
7
|
+
interface CustomProgressBarProps {
|
|
8
|
+
value: number; // Current progress value (0 to 100)
|
|
9
|
+
type: ShapeType.LINE | ShapeType.CIRCLE; // Type of progress bar
|
|
10
|
+
width: number; // Width of the progress bar
|
|
11
|
+
height?: number; // Optional height (default behavior depends on library)
|
|
12
|
+
fillColor: string; // Color of the progress fill
|
|
13
|
+
unfilledColor: string; // Color of the unfilled area
|
|
14
|
+
borderWidth?: number; // Optional border width
|
|
15
|
+
borderRadius?: number; // Optional border radius
|
|
16
|
+
circleSize?: number; // Size of the circle progress bar (if type is CIRCLE)
|
|
17
|
+
thickness?: number; // Thickness of the circle progress bar (if type is CIRCLE)
|
|
18
|
+
progress: number; // filled area
|
|
19
|
+
setProgress: React.Dispatch<React.SetStateAction<number>>;
|
|
20
|
+
increment?: number; // Optional increment value for progress update
|
|
21
|
+
interval?: number; // Optional interval time (ms) for progress update
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const CustomProgressBar: React.FC<CustomProgressBarProps> = ({
|
|
25
|
+
value,
|
|
26
|
+
type,
|
|
27
|
+
width,
|
|
28
|
+
height,
|
|
29
|
+
fillColor,
|
|
30
|
+
unfilledColor,
|
|
31
|
+
borderWidth,
|
|
32
|
+
borderRadius,
|
|
33
|
+
circleSize,
|
|
34
|
+
thickness,
|
|
35
|
+
progress,
|
|
36
|
+
setProgress,
|
|
37
|
+
increment = 1, // Default increment to 1
|
|
38
|
+
interval = 100, // Default interval to 100ms
|
|
39
|
+
}) => {
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
const progressInterval = setInterval(() => {
|
|
42
|
+
if (progress < value) {
|
|
43
|
+
setProgress((prevProgress: number) => prevProgress + increment);
|
|
44
|
+
} else {
|
|
45
|
+
clearInterval(progressInterval);
|
|
46
|
+
}
|
|
47
|
+
}, interval);
|
|
48
|
+
|
|
49
|
+
return () => clearInterval(progressInterval);
|
|
50
|
+
}, [progress, value, increment, interval]);
|
|
51
|
+
|
|
52
|
+
const renderProgressBar = () => {
|
|
53
|
+
switch (type) {
|
|
54
|
+
case ShapeType.LINE:
|
|
55
|
+
return (
|
|
56
|
+
<Progress.Bar
|
|
57
|
+
progress={progress / 100}
|
|
58
|
+
width={width}
|
|
59
|
+
height={height}
|
|
60
|
+
color={fillColor}
|
|
61
|
+
unfilledColor={unfilledColor}
|
|
62
|
+
borderWidth={borderWidth}
|
|
63
|
+
borderRadius={borderRadius}
|
|
64
|
+
testID="custom-progress-bar-line"
|
|
65
|
+
/>
|
|
66
|
+
);
|
|
67
|
+
case ShapeType.CIRCLE:
|
|
68
|
+
return (
|
|
69
|
+
<Progress.Circle
|
|
70
|
+
progress={progress / 100}
|
|
71
|
+
size={circleSize}
|
|
72
|
+
thickness={thickness}
|
|
73
|
+
color={fillColor}
|
|
74
|
+
unfilledColor={unfilledColor}
|
|
75
|
+
borderWidth={borderWidth}
|
|
76
|
+
showsText={true}
|
|
77
|
+
formatText={() => `${value}%`}
|
|
78
|
+
testID="custom-progress-bar-circle"
|
|
79
|
+
/>
|
|
80
|
+
);
|
|
81
|
+
default:
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
return (
|
|
87
|
+
<View style={commonStyles.centeredContainer} testID="custom-progress-bar">
|
|
88
|
+
{renderProgressBar()}
|
|
89
|
+
</View>
|
|
90
|
+
);
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
export default CustomProgressBar;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {default} from './ProgressBar';
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import {render} from '@testing-library/react-native';
|
|
3
|
+
import Property360View from './Property360View';
|
|
4
|
+
import WebView from 'react-native-webview';
|
|
5
|
+
|
|
6
|
+
describe('Property360View Component', () => {
|
|
7
|
+
it('should render the WebView with the correct URI', () => {
|
|
8
|
+
const imageURI = 'https://example.com/360image.jpg';
|
|
9
|
+
const {getByTestId} = render(<Property360View imageURI={imageURI} />);
|
|
10
|
+
const propertyView = getByTestId('PROPERTY_360_VIEW');
|
|
11
|
+
expect(propertyView).toBeTruthy();
|
|
12
|
+
expect(WebView).toHaveBeenCalledWith(
|
|
13
|
+
expect.objectContaining({
|
|
14
|
+
source: {uri: imageURI},
|
|
15
|
+
}),
|
|
16
|
+
{},
|
|
17
|
+
);
|
|
18
|
+
});
|
|
19
|
+
it('should apply the commonStyles.flexOne style to the View and WebView components', () => {
|
|
20
|
+
const imageURI = 'https://example.com/360image.jpg';
|
|
21
|
+
const {getByTestId} = render(<Property360View imageURI={imageURI} />);
|
|
22
|
+
expect(getByTestId('PROPERTY_360_VIEW')).toBeTruthy();
|
|
23
|
+
});
|
|
24
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import {View} from 'react-native';
|
|
3
|
+
import WebView from 'react-native-webview';
|
|
4
|
+
import commonStyles from '../../utils/commonStyles';
|
|
5
|
+
|
|
6
|
+
const Property360View = ({imageURI}: {imageURI: string}) => {
|
|
7
|
+
return (
|
|
8
|
+
<View style={commonStyles.flexOne} testID="PROPERTY_360_VIEW">
|
|
9
|
+
<WebView
|
|
10
|
+
source={{
|
|
11
|
+
uri: imageURI,
|
|
12
|
+
}}
|
|
13
|
+
style={commonStyles.flexOne}
|
|
14
|
+
/>
|
|
15
|
+
</View>
|
|
16
|
+
);
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export default Property360View;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {default} from './Property360View';
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import {horizontalScale} from '../../utils/stylehelper';
|
|
2
|
+
import {StyleSheet} from 'react-native';
|
|
3
|
+
|
|
4
|
+
const styles = StyleSheet.create({
|
|
5
|
+
container: {
|
|
6
|
+
flexDirection: 'row',
|
|
7
|
+
alignItems: 'center',
|
|
8
|
+
},
|
|
9
|
+
star: {
|
|
10
|
+
marginHorizontal: horizontalScale(2),
|
|
11
|
+
},
|
|
12
|
+
});
|
|
13
|
+
export default styles;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import {render, screen} from '@testing-library/react-native';
|
|
3
|
+
import StarRating from './Rating';
|
|
4
|
+
import {CalenderDots} from '../../assets';
|
|
5
|
+
|
|
6
|
+
describe('Star component', () => {
|
|
7
|
+
it('renders a full star for a rating of 1', () => {
|
|
8
|
+
const rating = 1;
|
|
9
|
+
const count = 5;
|
|
10
|
+
const size = 20;
|
|
11
|
+
|
|
12
|
+
render(
|
|
13
|
+
<StarRating
|
|
14
|
+
rating={rating}
|
|
15
|
+
count={count}
|
|
16
|
+
size={size}
|
|
17
|
+
fullStarIcon={<CalenderDots />}
|
|
18
|
+
halfStarIcon={<CalenderDots />}
|
|
19
|
+
/>,
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
const stars = screen.getAllByTestId('star');
|
|
23
|
+
expect(stars.length).toBe(1);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('renders a half star for a rating of 1.5', () => {
|
|
27
|
+
const rating = 1.5;
|
|
28
|
+
const count = 5;
|
|
29
|
+
const size = 20;
|
|
30
|
+
|
|
31
|
+
render(
|
|
32
|
+
<StarRating
|
|
33
|
+
rating={rating}
|
|
34
|
+
count={count}
|
|
35
|
+
size={size}
|
|
36
|
+
fullStarIcon={<CalenderDots />}
|
|
37
|
+
halfStarIcon={<CalenderDots />}
|
|
38
|
+
/>,
|
|
39
|
+
);
|
|
40
|
+
const stars = screen.getAllByTestId('star');
|
|
41
|
+
expect(stars.length).toBe(1);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('renders empty stars for a rating below 1', () => {
|
|
45
|
+
const rating = 0.5;
|
|
46
|
+
const count = 5;
|
|
47
|
+
const size = 20;
|
|
48
|
+
|
|
49
|
+
render(
|
|
50
|
+
<StarRating
|
|
51
|
+
rating={rating}
|
|
52
|
+
count={count}
|
|
53
|
+
size={size}
|
|
54
|
+
fullStarIcon={<CalenderDots />}
|
|
55
|
+
halfStarIcon={<CalenderDots />}
|
|
56
|
+
/>,
|
|
57
|
+
);
|
|
58
|
+
const stars = screen.getAllByTestId('star');
|
|
59
|
+
expect(stars.length).toBe(1);
|
|
60
|
+
});
|
|
61
|
+
});
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import {View, ViewStyle} from 'react-native';
|
|
3
|
+
import styles from './Rating.styles';
|
|
4
|
+
|
|
5
|
+
interface StarProps {
|
|
6
|
+
rating: number;
|
|
7
|
+
count: number;
|
|
8
|
+
size: number;
|
|
9
|
+
fullStarIcon: React.ReactElement;
|
|
10
|
+
halfStarIcon: React.ReactElement;
|
|
11
|
+
fullStarColor?: string;
|
|
12
|
+
emptyStarColor?: string;
|
|
13
|
+
starStyle?: ViewStyle;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const splitRating = (rating: number): [number, number] => {
|
|
17
|
+
const isDecimal = rating !== Math.floor(rating);
|
|
18
|
+
if (isDecimal) {
|
|
19
|
+
const integerPart = Math.floor(rating);
|
|
20
|
+
const decimalPart = rating - integerPart;
|
|
21
|
+
return [integerPart, decimalPart];
|
|
22
|
+
} else {
|
|
23
|
+
return [rating, 0];
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const drawStar = (
|
|
28
|
+
starIndex: number,
|
|
29
|
+
ratingArray: [number, number],
|
|
30
|
+
size: number,
|
|
31
|
+
fullStarIcon: React.ReactElement,
|
|
32
|
+
halfStarIcon: React.ReactElement,
|
|
33
|
+
fullStarColor: string,
|
|
34
|
+
emptyStarColor: string,
|
|
35
|
+
): JSX.Element => {
|
|
36
|
+
const [fullStars, decimalPart] = ratingArray;
|
|
37
|
+
|
|
38
|
+
if (starIndex <= fullStars) {
|
|
39
|
+
return React.cloneElement(fullStarIcon, {
|
|
40
|
+
style: {height: size, width: size},
|
|
41
|
+
fill: fullStarColor,
|
|
42
|
+
testID: `star-${starIndex}`,
|
|
43
|
+
});
|
|
44
|
+
} else if (starIndex === fullStars + 1 && decimalPart >= 0.5) {
|
|
45
|
+
return React.cloneElement(halfStarIcon, {
|
|
46
|
+
style: {height: size, width: size},
|
|
47
|
+
fill: fullStarColor,
|
|
48
|
+
testID: `star-${starIndex}`,
|
|
49
|
+
});
|
|
50
|
+
} else {
|
|
51
|
+
return React.cloneElement(fullStarIcon, {
|
|
52
|
+
style: {height: size, width: size},
|
|
53
|
+
fill: emptyStarColor,
|
|
54
|
+
testID: `star-${starIndex}`,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const Star: React.FC<StarProps> = ({
|
|
60
|
+
rating,
|
|
61
|
+
count,
|
|
62
|
+
size,
|
|
63
|
+
fullStarIcon,
|
|
64
|
+
halfStarIcon,
|
|
65
|
+
fullStarColor = 'gold',
|
|
66
|
+
emptyStarColor = 'gray',
|
|
67
|
+
starStyle,
|
|
68
|
+
}) => {
|
|
69
|
+
const stars: React.ReactElement[] = [];
|
|
70
|
+
for (let i = 0; i < count; i++) {
|
|
71
|
+
const starIndex = i + 1;
|
|
72
|
+
const ratingArray = splitRating(rating);
|
|
73
|
+
const starType = drawStar(
|
|
74
|
+
starIndex,
|
|
75
|
+
ratingArray,
|
|
76
|
+
size,
|
|
77
|
+
fullStarIcon,
|
|
78
|
+
halfStarIcon,
|
|
79
|
+
fullStarColor,
|
|
80
|
+
emptyStarColor,
|
|
81
|
+
);
|
|
82
|
+
stars.push(
|
|
83
|
+
<View key={i} style={[styles.star, starStyle]}>
|
|
84
|
+
{starType}
|
|
85
|
+
</View>,
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return (
|
|
90
|
+
<View style={styles.container} testID="star">
|
|
91
|
+
{stars}
|
|
92
|
+
</View>
|
|
93
|
+
);
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
export default Star;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {default} from './Rating';
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import {ReactElement} from 'react';
|
|
2
|
+
import {ViewStyle} from 'react-native';
|
|
3
|
+
|
|
4
|
+
export interface TabsProps {
|
|
5
|
+
tabs: {label: string}[];
|
|
6
|
+
onTabPress?: (index: number) => void;
|
|
7
|
+
testID?: string;
|
|
8
|
+
hideTitle?: boolean;
|
|
9
|
+
hideIcon?: boolean;
|
|
10
|
+
initialTab?: number;
|
|
11
|
+
icon?: ReactElement;
|
|
12
|
+
iconStyle?: ViewStyle;
|
|
13
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import {DHRE_COLORS_TEXT} from '../../theme/colors';
|
|
2
|
+
import {horizontalScale, verticalScale} from '../../utils/stylehelper';
|
|
3
|
+
import {StyleSheet} from 'react-native';
|
|
4
|
+
|
|
5
|
+
const styles = StyleSheet.create({
|
|
6
|
+
tabsContainer: {
|
|
7
|
+
flexDirection: 'row',
|
|
8
|
+
alignItems: 'center',
|
|
9
|
+
},
|
|
10
|
+
tabItem: {
|
|
11
|
+
flex: 1,
|
|
12
|
+
flexDirection: 'row',
|
|
13
|
+
alignItems: 'center',
|
|
14
|
+
justifyContent: 'center',
|
|
15
|
+
paddingVertical: verticalScale(6),
|
|
16
|
+
paddingHorizontal: horizontalScale(12),
|
|
17
|
+
marginHorizontal: horizontalScale(8),
|
|
18
|
+
borderRadius: 50,
|
|
19
|
+
gap: horizontalScale(8),
|
|
20
|
+
backgroundColor: DHRE_COLORS_TEXT.DH_BLACK,
|
|
21
|
+
},
|
|
22
|
+
label: {
|
|
23
|
+
color: DHRE_COLORS_TEXT.DH_WHITE,
|
|
24
|
+
},
|
|
25
|
+
activeTab: {
|
|
26
|
+
backgroundColor: DHRE_COLORS_TEXT.DH_BLACK,
|
|
27
|
+
},
|
|
28
|
+
inactiveTab: {
|
|
29
|
+
backgroundColor: DHRE_COLORS_TEXT.DH_WHITE,
|
|
30
|
+
},
|
|
31
|
+
inActiveLabel: {
|
|
32
|
+
color: DHRE_COLORS_TEXT.DH_TEXTLIGHTEST,
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
export default styles;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import {it} from '@jest/globals';
|
|
3
|
+
import {render} from '@testing-library/react-native';
|
|
4
|
+
import Tabs from './Tabs';
|
|
5
|
+
|
|
6
|
+
const mockProps = {
|
|
7
|
+
tabs: [{label: 'Tabs'}],
|
|
8
|
+
hideTitle: false,
|
|
9
|
+
hideIcon: false,
|
|
10
|
+
initialTab: 1,
|
|
11
|
+
onTabPress: jest.fn(),
|
|
12
|
+
testID: 'TABS-COMPONENT',
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
describe('Tabs Component', () => {
|
|
16
|
+
it('Component renders correctly', () => {
|
|
17
|
+
const {getByTestId} = render(<Tabs {...mockProps} />);
|
|
18
|
+
expect(getByTestId('TABS-COMPONENT')).toBeTruthy();
|
|
19
|
+
});
|
|
20
|
+
});
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import React, {useState} from 'react';
|
|
2
|
+
import {ScrollView, Pressable, View} from 'react-native';
|
|
3
|
+
import CustomTypography from '../customTypography/CustomTypography';
|
|
4
|
+
import styles from './Tabs.styles';
|
|
5
|
+
import {TabsProps} from './Tabs.interface';
|
|
6
|
+
import {DHRE_COLORS_TEXT} from '../../theme/colors';
|
|
7
|
+
|
|
8
|
+
const Tabs: React.FC<TabsProps> = ({
|
|
9
|
+
tabs,
|
|
10
|
+
onTabPress,
|
|
11
|
+
testID,
|
|
12
|
+
hideTitle = false,
|
|
13
|
+
hideIcon = true,
|
|
14
|
+
initialTab = 0,
|
|
15
|
+
icon: Icon,
|
|
16
|
+
iconStyle,
|
|
17
|
+
}) => {
|
|
18
|
+
const [activeTab, setActiveTab] = useState(initialTab);
|
|
19
|
+
|
|
20
|
+
const handleTabPress = (index: number) => {
|
|
21
|
+
setActiveTab(index);
|
|
22
|
+
if (onTabPress) {
|
|
23
|
+
onTabPress(index);
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<ScrollView
|
|
29
|
+
testID={testID}
|
|
30
|
+
horizontal
|
|
31
|
+
showsHorizontalScrollIndicator={false}
|
|
32
|
+
contentContainerStyle={styles.tabsContainer}>
|
|
33
|
+
{tabs?.map((tab, index) => (
|
|
34
|
+
<Pressable
|
|
35
|
+
testID={`Press-Tab-${index}`}
|
|
36
|
+
key={index}
|
|
37
|
+
onPress={() => handleTabPress(index)}
|
|
38
|
+
style={[
|
|
39
|
+
styles.tabItem,
|
|
40
|
+
activeTab === index ? styles.activeTab : styles.inactiveTab,
|
|
41
|
+
]}>
|
|
42
|
+
{!hideIcon && Icon && (
|
|
43
|
+
<View style={iconStyle}>
|
|
44
|
+
{React.cloneElement(Icon, {
|
|
45
|
+
fill:
|
|
46
|
+
activeTab === index
|
|
47
|
+
? DHRE_COLORS_TEXT.DH_WHITE
|
|
48
|
+
: DHRE_COLORS_TEXT.DH_TEXTLIGHTEST,
|
|
49
|
+
})}
|
|
50
|
+
</View>
|
|
51
|
+
)}
|
|
52
|
+
{!hideTitle && (
|
|
53
|
+
<CustomTypography
|
|
54
|
+
variant={activeTab === index ? 'H8semiBold' : 'H8medium'}
|
|
55
|
+
style={activeTab === index ? styles.label : styles.inActiveLabel}
|
|
56
|
+
text={tab?.label}
|
|
57
|
+
/>
|
|
58
|
+
)}
|
|
59
|
+
</Pressable>
|
|
60
|
+
))}
|
|
61
|
+
</ScrollView>
|
|
62
|
+
);
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
export default Tabs;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {default} from './Tabs';
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import {ReactElement} from 'react';
|
|
2
|
+
import {ViewStyle} from 'react-native';
|
|
3
|
+
|
|
4
|
+
export interface TagsProps {
|
|
5
|
+
tags: string[];
|
|
6
|
+
tagStyles?: ViewStyle;
|
|
7
|
+
handleSetSelectedTagsOptions: (tags: string[]) => void;
|
|
8
|
+
handleTagsChange: (tag: string) => void;
|
|
9
|
+
testID?: string;
|
|
10
|
+
icon?: ReactElement;
|
|
11
|
+
iconStyle?: ViewStyle;
|
|
12
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import {DHRE_COLORS_BG, DHRE_COLORS_TEXT} from '../../theme/colors';
|
|
2
|
+
import {horizontalScale, verticalScale} from '../../utils/stylehelper';
|
|
3
|
+
import {StyleSheet} from 'react-native';
|
|
4
|
+
|
|
5
|
+
const styles = StyleSheet.create({
|
|
6
|
+
container: {
|
|
7
|
+
width: '100%',
|
|
8
|
+
},
|
|
9
|
+
contentContainer: {
|
|
10
|
+
flexWrap: 'wrap',
|
|
11
|
+
flexDirection: 'row',
|
|
12
|
+
},
|
|
13
|
+
tag: {
|
|
14
|
+
paddingVertical: verticalScale(8),
|
|
15
|
+
paddingHorizontal: horizontalScale(12),
|
|
16
|
+
borderRadius: 20,
|
|
17
|
+
margin: horizontalScale(8),
|
|
18
|
+
flexDirection: 'row',
|
|
19
|
+
alignItems: 'center',
|
|
20
|
+
},
|
|
21
|
+
selectedTag: {
|
|
22
|
+
backgroundColor: DHRE_COLORS_TEXT.DH_BLACK,
|
|
23
|
+
},
|
|
24
|
+
tagText: {
|
|
25
|
+
color: DHRE_COLORS_TEXT.DH_WHITE,
|
|
26
|
+
marginRight: horizontalScale(8),
|
|
27
|
+
},
|
|
28
|
+
tagUnSelected: {
|
|
29
|
+
backgroundColor: DHRE_COLORS_BG.DH_GREY1,
|
|
30
|
+
},
|
|
31
|
+
tagUnselectedText: {
|
|
32
|
+
color: DHRE_COLORS_TEXT.DH_TEXTLIGHT,
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
export default styles;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import {it} from '@jest/globals';
|
|
3
|
+
import {fireEvent, render} from '@testing-library/react-native';
|
|
4
|
+
import Tags from './Tags';
|
|
5
|
+
|
|
6
|
+
const customStyle = {backgroundColor: 'red', padding: 20};
|
|
7
|
+
|
|
8
|
+
const mockProps = {
|
|
9
|
+
tags: ['One', 'Two', 'Three'],
|
|
10
|
+
tagStyles: customStyle,
|
|
11
|
+
handleSetSelectedTagsOptions: jest.fn(),
|
|
12
|
+
handleTagsChange: jest.fn(),
|
|
13
|
+
testID: 'TAGS-COMPONENT',
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
describe('Tags Component', () => {
|
|
17
|
+
it('Component renders correctly', () => {
|
|
18
|
+
const {getByTestId} = render(<Tags {...mockProps} />);
|
|
19
|
+
expect(getByTestId('TAGS-COMPONENT')).toBeTruthy();
|
|
20
|
+
fireEvent.press(getByTestId('TAGS-COMPONENT-One-BTN'));
|
|
21
|
+
});
|
|
22
|
+
});
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import React, {useState} from 'react';
|
|
2
|
+
import {ScrollView, Pressable} from 'react-native';
|
|
3
|
+
import CustomTypography from '../customTypography/CustomTypography';
|
|
4
|
+
import {TagsProps} from './Tags.interface';
|
|
5
|
+
import styles from './Tags.styles';
|
|
6
|
+
|
|
7
|
+
const Tags: React.FC<TagsProps> = ({
|
|
8
|
+
tags,
|
|
9
|
+
tagStyles,
|
|
10
|
+
handleSetSelectedTagsOptions,
|
|
11
|
+
handleTagsChange,
|
|
12
|
+
testID,
|
|
13
|
+
icon,
|
|
14
|
+
iconStyle,
|
|
15
|
+
}) => {
|
|
16
|
+
const [selectedTags, setSelectedTags] = useState<string[]>([]);
|
|
17
|
+
|
|
18
|
+
const toggleTag = (tag: string) => {
|
|
19
|
+
setSelectedTags(prevSelectedTags => {
|
|
20
|
+
const selectedTagsSet = new Set(prevSelectedTags);
|
|
21
|
+
selectedTagsSet.has(tag)
|
|
22
|
+
? selectedTagsSet.delete(tag)
|
|
23
|
+
: selectedTagsSet.add(tag);
|
|
24
|
+
handleSetSelectedTagsOptions([...selectedTagsSet]);
|
|
25
|
+
return [...selectedTagsSet];
|
|
26
|
+
});
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const removeTag = (tag: string) => {
|
|
30
|
+
setSelectedTags(prevSelectedTags =>
|
|
31
|
+
prevSelectedTags.filter(t => t !== tag),
|
|
32
|
+
);
|
|
33
|
+
handleTagsChange(tag);
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<ScrollView
|
|
38
|
+
testID={testID}
|
|
39
|
+
style={styles.container}
|
|
40
|
+
contentContainerStyle={styles.contentContainer}
|
|
41
|
+
horizontal={false}>
|
|
42
|
+
{tags?.map(tag => (
|
|
43
|
+
<Pressable
|
|
44
|
+
testID={`${testID}-${tag}-BTN`}
|
|
45
|
+
key={tag}
|
|
46
|
+
onPress={() => toggleTag(tag)}
|
|
47
|
+
style={[
|
|
48
|
+
styles.tag,
|
|
49
|
+
tagStyles,
|
|
50
|
+
selectedTags.includes(tag)
|
|
51
|
+
? styles.selectedTag
|
|
52
|
+
: styles.tagUnSelected,
|
|
53
|
+
]}>
|
|
54
|
+
<CustomTypography
|
|
55
|
+
variant={selectedTags.includes(tag) ? 'H9semiBold' : 'P4regular'}
|
|
56
|
+
style={
|
|
57
|
+
selectedTags.includes(tag)
|
|
58
|
+
? styles.tagText
|
|
59
|
+
: styles.tagUnselectedText
|
|
60
|
+
}
|
|
61
|
+
text={tag}
|
|
62
|
+
/>
|
|
63
|
+
{selectedTags.includes(tag) && icon && (
|
|
64
|
+
<Pressable
|
|
65
|
+
testID={`${testID}-${tag}-PRESS`}
|
|
66
|
+
onPress={() => removeTag(tag)}>
|
|
67
|
+
{React.cloneElement(icon, {style: [icon.props.style, iconStyle]})}
|
|
68
|
+
</Pressable>
|
|
69
|
+
)}
|
|
70
|
+
</Pressable>
|
|
71
|
+
))}
|
|
72
|
+
</ScrollView>
|
|
73
|
+
);
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
export default Tags;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {default} from './Tags';
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import {DHRE_COLORS_TEXT} from '../../theme/colors';
|
|
2
|
+
import {horizontalScale, verticalScale} from '../../utils/stylehelper';
|
|
3
|
+
import {StyleSheet} from 'react-native';
|
|
4
|
+
|
|
5
|
+
const styles = StyleSheet.create({
|
|
6
|
+
container: {
|
|
7
|
+
width: horizontalScale(70),
|
|
8
|
+
height: verticalScale(25),
|
|
9
|
+
borderRadius: 15,
|
|
10
|
+
justifyContent: 'center',
|
|
11
|
+
},
|
|
12
|
+
track: {
|
|
13
|
+
position: 'absolute',
|
|
14
|
+
top: 0,
|
|
15
|
+
left: 0,
|
|
16
|
+
right: 0,
|
|
17
|
+
bottom: 0,
|
|
18
|
+
justifyContent: 'center',
|
|
19
|
+
borderRadius: 15,
|
|
20
|
+
},
|
|
21
|
+
thumb: {
|
|
22
|
+
position: 'absolute',
|
|
23
|
+
width: horizontalScale(20),
|
|
24
|
+
height: verticalScale(20),
|
|
25
|
+
borderRadius: 16,
|
|
26
|
+
justifyContent: 'center',
|
|
27
|
+
alignItems: 'center',
|
|
28
|
+
backgroundColor: DHRE_COLORS_TEXT.DH_WHITE,
|
|
29
|
+
left: horizontalScale(2),
|
|
30
|
+
},
|
|
31
|
+
text: {
|
|
32
|
+
color: DHRE_COLORS_TEXT.DH_WHITE,
|
|
33
|
+
textAlign: 'left',
|
|
34
|
+
marginHorizontal: horizontalScale(10),
|
|
35
|
+
},
|
|
36
|
+
textRight: {
|
|
37
|
+
textAlign: 'right',
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
export default styles;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import {render, screen, fireEvent} from '@testing-library/react-native';
|
|
3
|
+
import CustomSwitch from './ToggleButton';
|
|
4
|
+
|
|
5
|
+
describe('CustomSwitch', () => {
|
|
6
|
+
it('renders correctly with default type', () => {
|
|
7
|
+
const onValueChangeMock = jest.fn();
|
|
8
|
+
render(<CustomSwitch onValueChange={onValueChangeMock} value={false} />);
|
|
9
|
+
|
|
10
|
+
const switchElement = screen.getByTestId('custom-switch');
|
|
11
|
+
expect(switchElement).toBeTruthy();
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('renders correctly with custom type', () => {
|
|
15
|
+
const onValueChangeMock = jest.fn();
|
|
16
|
+
render(<CustomSwitch onValueChange={onValueChangeMock} value={true} />);
|
|
17
|
+
|
|
18
|
+
const switchElement = screen.getByTestId('custom-switch');
|
|
19
|
+
expect(switchElement).toBeTruthy();
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('toggles switch on press', () => {
|
|
23
|
+
const onValueChangeMock = jest.fn();
|
|
24
|
+
const {getByTestId} = render(
|
|
25
|
+
<CustomSwitch onValueChange={onValueChangeMock} value={false} />,
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
const switchElement = getByTestId('custom-switch');
|
|
29
|
+
fireEvent.press(switchElement);
|
|
30
|
+
|
|
31
|
+
expect(onValueChangeMock).toHaveBeenCalledWith(true);
|
|
32
|
+
});
|
|
33
|
+
});
|