cpk-ui 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 +27 -0
- package/components/modals/AlertDialog/AlertDialog.js +80 -0
- package/components/modals/AlertDialog/AlertDialog.stories.tsx +80 -0
- package/components/modals/AlertDialog/AlertDialog.tsx +194 -0
- package/components/modals/Snackbar/Snackbar.js +94 -0
- package/components/modals/Snackbar/Snackbar.stories.tsx +98 -0
- package/components/modals/Snackbar/Snackbar.tsx +174 -0
- package/components/modals/Snackbar/const.js +5 -0
- package/components/modals/Snackbar/const.ts +4 -0
- package/components/uis/Accordion/Accordion.js +11 -0
- package/components/uis/Accordion/Accordion.stories.tsx +38 -0
- package/components/uis/Accordion/Accordion.test.tsx +128 -0
- package/components/uis/Accordion/Accordion.tsx +58 -0
- package/components/uis/Accordion/AccordionItem.js +102 -0
- package/components/uis/Accordion/AccordionItem.tsx +213 -0
- package/components/uis/Button/Button.js +161 -0
- package/components/uis/Button/Button.stories.tsx +81 -0
- package/components/uis/Button/Button.test.tsx +560 -0
- package/components/uis/Button/Button.tsx +335 -0
- package/components/uis/Checkbox/Checkbox.js +70 -0
- package/components/uis/Checkbox/Checkbox.stories.tsx +46 -0
- package/components/uis/Checkbox/Checkbox.test.tsx +139 -0
- package/components/uis/Checkbox/Checkbox.tsx +150 -0
- package/components/uis/Icon/Icon.js +3780 -0
- package/components/uis/Icon/Icon.stories.tsx +45 -0
- package/components/uis/Icon/Icon.test.tsx +19 -0
- package/components/uis/Icon/Icon.tsx +3800 -0
- package/components/uis/Icon/Pretendard-Bold.otf +0 -0
- package/components/uis/Icon/Pretendard-Regular.otf +0 -0
- package/components/uis/Icon/Pretendard-Thin.otf +0 -0
- package/components/uis/Icon/cpk.ttf +0 -0
- package/components/uis/Icon/selection.json +1 -0
- package/components/uis/IconButton/IconButton.js +120 -0
- package/components/uis/IconButton/IconButton.test.tsx +165 -0
- package/components/uis/IconButton/IconButton.tsx +252 -0
- package/components/uis/LoadingIndicator/LoadingIndicator.js +24 -0
- package/components/uis/LoadingIndicator/LoadingIndicator.tsx +79 -0
- package/components/uis/Rating/Rating.js +53 -0
- package/components/uis/Rating/Rating.test.tsx +72 -0
- package/components/uis/Rating/Rating.tsx +155 -0
- package/components/uis/StatusbarBrightness/StatusBarBrightness.js +13 -0
- package/components/uis/StatusbarBrightness/StatusBarBrightness.test.tsx +41 -0
- package/components/uis/StatusbarBrightness/StatusBarBrightness.tsx +17 -0
- package/components/uis/Styled/StyledComponents.js +130 -0
- package/components/uis/Styled/StyledComponents.tsx +200 -0
- package/components/uis/SwitchToggle/SwitchToggle.js +126 -0
- package/components/uis/SwitchToggle/SwitchToggle.test.tsx +70 -0
- package/components/uis/SwitchToggle/SwitchToggle.tsx +224 -0
- package/components/uis/Typography/Typography.js +90 -0
- package/components/uis/Typography/Typography.test.tsx +58 -0
- package/components/uis/Typography/Typography.tsx +132 -0
- package/hooks/useDebouncedColorScheme.js +21 -0
- package/hooks/useDebouncedColorScheme.tsx +30 -0
- package/index.js +16 -0
- package/index.tsx +18 -0
- package/package.json +35 -0
- package/providers/ThemeProvider.js +106 -0
- package/providers/ThemeProvider.tsx +180 -0
- package/providers/index.js +62 -0
- package/providers/index.tsx +125 -0
- package/react-native.config.cjs +5 -0
- package/utils/colors.js +124 -0
- package/utils/colors.ts +127 -0
- package/utils/createCtx.js +15 -0
- package/utils/createCtx.tsx +26 -0
- package/utils/guards.js +44 -0
- package/utils/guards.ts +93 -0
- package/utils/theme.js +5 -0
- package/utils/theme.ts +33 -0
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useState } from 'react';
|
|
3
|
+
import { Animated, TouchableOpacity } from 'react-native';
|
|
4
|
+
import styled from '@emotion/native';
|
|
5
|
+
import { useTheme } from '../../../providers/ThemeProvider';
|
|
6
|
+
// Typing limitation: https://github.com/DefinitelyTyped/DefinitelyTyped/issues/12202
|
|
7
|
+
const AnimatedContainer = styled(Animated.View) `
|
|
8
|
+
flex-direction: row;
|
|
9
|
+
align-items: center;
|
|
10
|
+
`;
|
|
11
|
+
const smallContainer = {
|
|
12
|
+
width: 40,
|
|
13
|
+
height: 24,
|
|
14
|
+
borderRadius: 25,
|
|
15
|
+
padding: 5,
|
|
16
|
+
};
|
|
17
|
+
const smallCircle = {
|
|
18
|
+
width: 16,
|
|
19
|
+
height: 16,
|
|
20
|
+
borderRadius: 8,
|
|
21
|
+
};
|
|
22
|
+
const mediumContainer = {
|
|
23
|
+
width: 56,
|
|
24
|
+
height: 30,
|
|
25
|
+
borderRadius: 25,
|
|
26
|
+
padding: 5,
|
|
27
|
+
};
|
|
28
|
+
const mediumCircle = {
|
|
29
|
+
width: 20,
|
|
30
|
+
height: 20,
|
|
31
|
+
borderRadius: 10,
|
|
32
|
+
};
|
|
33
|
+
const largeContainer = {
|
|
34
|
+
width: 80,
|
|
35
|
+
height: 40,
|
|
36
|
+
borderRadius: 25,
|
|
37
|
+
padding: 5,
|
|
38
|
+
};
|
|
39
|
+
const largeCircle = {
|
|
40
|
+
width: 32,
|
|
41
|
+
height: 32,
|
|
42
|
+
borderRadius: 16,
|
|
43
|
+
};
|
|
44
|
+
export function SwitchToggle({ testID, isOn, style, styles, duration = 300, onElement, size = 'medium', offElement, onPress, }) {
|
|
45
|
+
const { theme } = useTheme();
|
|
46
|
+
const { backgroundColorOn = theme.role.primary, backgroundColorOff = theme.bg.disabled, circleColorOn = theme.text.contrast, circleColorOff = theme.text.basic, container = size === 'large'
|
|
47
|
+
? largeContainer
|
|
48
|
+
: size === 'small'
|
|
49
|
+
? smallContainer
|
|
50
|
+
: mediumContainer, circle = size === 'large'
|
|
51
|
+
? largeCircle
|
|
52
|
+
: size === 'small'
|
|
53
|
+
? smallCircle
|
|
54
|
+
: mediumCircle, button, onElementContainer, offElementContainer, } = styles ?? {};
|
|
55
|
+
const paddingLeft = container.padding || container.paddingLeft || 0;
|
|
56
|
+
const paddingRight = container.padding || container.paddingRight || 0;
|
|
57
|
+
const circlePosXStart = 0;
|
|
58
|
+
const circlePosXEnd = (container.width ?? mediumContainer.width) -
|
|
59
|
+
(circle.width ?? mediumCircle.width) -
|
|
60
|
+
(paddingRight + paddingLeft);
|
|
61
|
+
const [animXValue] = useState(new Animated.Value(isOn ? 1 : 0));
|
|
62
|
+
useEffect(() => {
|
|
63
|
+
const runAnimation = () => {
|
|
64
|
+
const option = {
|
|
65
|
+
fromValue: isOn ? 0 : 1,
|
|
66
|
+
toValue: isOn ? 1 : 0,
|
|
67
|
+
duration,
|
|
68
|
+
useNativeDriver: false,
|
|
69
|
+
};
|
|
70
|
+
Animated.timing(animXValue, option).start();
|
|
71
|
+
};
|
|
72
|
+
runAnimation();
|
|
73
|
+
}, [animXValue, isOn, duration]);
|
|
74
|
+
const CircleButton = (_jsx(Animated.View, { style: [
|
|
75
|
+
circle,
|
|
76
|
+
{
|
|
77
|
+
backgroundColor: animXValue.interpolate({
|
|
78
|
+
inputRange: [0.5, 1],
|
|
79
|
+
outputRange: [
|
|
80
|
+
circleColorOff,
|
|
81
|
+
circleColorOn,
|
|
82
|
+
],
|
|
83
|
+
}),
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
transform: [
|
|
87
|
+
{
|
|
88
|
+
translateX: animXValue.interpolate({
|
|
89
|
+
inputRange: [0, 1],
|
|
90
|
+
outputRange: [
|
|
91
|
+
circlePosXStart,
|
|
92
|
+
circlePosXEnd,
|
|
93
|
+
],
|
|
94
|
+
}),
|
|
95
|
+
},
|
|
96
|
+
],
|
|
97
|
+
},
|
|
98
|
+
button,
|
|
99
|
+
] }));
|
|
100
|
+
const OnElement = (_jsx(Animated.View, { style: [{ opacity: animXValue }, onElementContainer], children: onElement }));
|
|
101
|
+
const OffElement = (_jsx(Animated.View, { style: [
|
|
102
|
+
{
|
|
103
|
+
opacity: animXValue.interpolate({
|
|
104
|
+
inputRange: [0, 1],
|
|
105
|
+
outputRange: [1, 0],
|
|
106
|
+
}),
|
|
107
|
+
},
|
|
108
|
+
offElementContainer,
|
|
109
|
+
], children: offElement }));
|
|
110
|
+
return (_jsx(TouchableOpacity, { accessibilityRole: "switch", activeOpacity: 0.8, onPress: onPress, style: style, testID: testID, children: _jsxs(AnimatedContainer, { style: [
|
|
111
|
+
container,
|
|
112
|
+
{
|
|
113
|
+
paddingLeft,
|
|
114
|
+
paddingRight,
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
backgroundColor: animXValue.interpolate({
|
|
118
|
+
inputRange: [0, 1],
|
|
119
|
+
outputRange: [
|
|
120
|
+
backgroundColorOff,
|
|
121
|
+
backgroundColorOn,
|
|
122
|
+
],
|
|
123
|
+
}),
|
|
124
|
+
},
|
|
125
|
+
], children: [isOn ? OnElement : OffElement, CircleButton] }) }));
|
|
126
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import {Text} from 'react-native';
|
|
3
|
+
import type {RenderAPI} from '@testing-library/react-native';
|
|
4
|
+
import {fireEvent, render, waitFor} from '@testing-library/react-native';
|
|
5
|
+
import {createComponent} from '../../../../test/testUtils';
|
|
6
|
+
import {SwitchToggle} from './SwitchToggle';
|
|
7
|
+
|
|
8
|
+
let testingLib: RenderAPI;
|
|
9
|
+
|
|
10
|
+
describe('[SwitchToggle]', () => {
|
|
11
|
+
const handlePress = jest.fn();
|
|
12
|
+
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
handlePress.mockClear();
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('handles press event', async () => {
|
|
18
|
+
testingLib = render(
|
|
19
|
+
createComponent(<SwitchToggle isOn={false} onPress={handlePress} />),
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
const baseElement = await waitFor(() => testingLib.toJSON());
|
|
23
|
+
expect(baseElement).toBeTruthy();
|
|
24
|
+
|
|
25
|
+
const {getByRole} = testingLib;
|
|
26
|
+
fireEvent.press(getByRole('switch'));
|
|
27
|
+
expect(handlePress).toBeCalled();
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
const getSwitchToggle = ({isOn}: {isOn: boolean}): JSX.Element =>
|
|
31
|
+
createComponent(
|
|
32
|
+
<SwitchToggle
|
|
33
|
+
isOn={isOn}
|
|
34
|
+
offElement={<Text>off</Text>}
|
|
35
|
+
onElement={<Text>on</Text>}
|
|
36
|
+
onPress={handlePress}
|
|
37
|
+
style={{padding: 6}}
|
|
38
|
+
styles={{
|
|
39
|
+
container: {paddingLeft: 6, paddingRight: 5},
|
|
40
|
+
onElementContainer: {padding: 4},
|
|
41
|
+
offElementContainer: {padding: 3},
|
|
42
|
+
circle: {padding: 2},
|
|
43
|
+
button: {padding: 1},
|
|
44
|
+
circleColorOff: 'red',
|
|
45
|
+
circleColorOn: 'blue',
|
|
46
|
+
backgroundColorOn: 'green',
|
|
47
|
+
backgroundColorOff: 'grey',
|
|
48
|
+
}}
|
|
49
|
+
/>,
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
context('when switch toggle is on', () => {
|
|
53
|
+
it('renders as on state', async () => {
|
|
54
|
+
testingLib = render(createComponent(getSwitchToggle({isOn: true})));
|
|
55
|
+
|
|
56
|
+
const baseElement = await waitFor(() => testingLib.toJSON());
|
|
57
|
+
expect(baseElement).toBeTruthy();
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
context('when switch toggle is off', () => {
|
|
62
|
+
it('renders as off state', () => {
|
|
63
|
+
const component = createComponent(getSwitchToggle({isOn: false}));
|
|
64
|
+
testingLib = render(component);
|
|
65
|
+
|
|
66
|
+
const baseElement = testingLib.toJSON();
|
|
67
|
+
expect(baseElement).toBeTruthy();
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
});
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import React, {useEffect, useState} from 'react';
|
|
2
|
+
import type {StyleProp, ViewStyle} from 'react-native';
|
|
3
|
+
import {Animated, TouchableOpacity} from 'react-native';
|
|
4
|
+
import styled from '@emotion/native';
|
|
5
|
+
import {useTheme} from '../../../providers/ThemeProvider';
|
|
6
|
+
|
|
7
|
+
type Styles = {
|
|
8
|
+
container?: ViewStyle;
|
|
9
|
+
onElementContainer?: StyleProp<ViewStyle>;
|
|
10
|
+
offElementContainer?: StyleProp<ViewStyle>;
|
|
11
|
+
circle?: ViewStyle;
|
|
12
|
+
button?: StyleProp<ViewStyle>;
|
|
13
|
+
circleColorOff?: string;
|
|
14
|
+
circleColorOn?: string;
|
|
15
|
+
backgroundColorOn?: string;
|
|
16
|
+
backgroundColorOff?: string;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
type Props = {
|
|
20
|
+
testID?: string;
|
|
21
|
+
size?: 'small' | 'medium' | 'large';
|
|
22
|
+
isOn: boolean;
|
|
23
|
+
style?: StyleProp<ViewStyle>;
|
|
24
|
+
styles?: Styles;
|
|
25
|
+
duration?: number;
|
|
26
|
+
onElement?: any;
|
|
27
|
+
offElement?: any;
|
|
28
|
+
onPress?: () => void;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
// Typing limitation: https://github.com/DefinitelyTyped/DefinitelyTyped/issues/12202
|
|
32
|
+
|
|
33
|
+
const AnimatedContainer = styled(Animated.View)`
|
|
34
|
+
flex-direction: row;
|
|
35
|
+
align-items: center;
|
|
36
|
+
`;
|
|
37
|
+
|
|
38
|
+
const smallContainer: ViewStyle = {
|
|
39
|
+
width: 40,
|
|
40
|
+
height: 24,
|
|
41
|
+
borderRadius: 25,
|
|
42
|
+
padding: 5,
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const smallCircle: ViewStyle = {
|
|
46
|
+
width: 16,
|
|
47
|
+
height: 16,
|
|
48
|
+
borderRadius: 8,
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const mediumContainer: ViewStyle = {
|
|
52
|
+
width: 56,
|
|
53
|
+
height: 30,
|
|
54
|
+
borderRadius: 25,
|
|
55
|
+
padding: 5,
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const mediumCircle: ViewStyle = {
|
|
59
|
+
width: 20,
|
|
60
|
+
height: 20,
|
|
61
|
+
borderRadius: 10,
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const largeContainer: ViewStyle = {
|
|
65
|
+
width: 80,
|
|
66
|
+
height: 40,
|
|
67
|
+
borderRadius: 25,
|
|
68
|
+
padding: 5,
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const largeCircle: ViewStyle = {
|
|
72
|
+
width: 32,
|
|
73
|
+
height: 32,
|
|
74
|
+
borderRadius: 16,
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
export function SwitchToggle({
|
|
78
|
+
testID,
|
|
79
|
+
isOn,
|
|
80
|
+
style,
|
|
81
|
+
styles,
|
|
82
|
+
duration = 300,
|
|
83
|
+
onElement,
|
|
84
|
+
size = 'medium',
|
|
85
|
+
offElement,
|
|
86
|
+
onPress,
|
|
87
|
+
}: Props): JSX.Element {
|
|
88
|
+
const {theme} = useTheme();
|
|
89
|
+
|
|
90
|
+
const {
|
|
91
|
+
backgroundColorOn = theme.role.primary,
|
|
92
|
+
backgroundColorOff = theme.bg.disabled,
|
|
93
|
+
circleColorOn = theme.text.contrast,
|
|
94
|
+
circleColorOff = theme.text.basic,
|
|
95
|
+
container = size === 'large'
|
|
96
|
+
? largeContainer
|
|
97
|
+
: size === 'small'
|
|
98
|
+
? smallContainer
|
|
99
|
+
: mediumContainer,
|
|
100
|
+
circle = size === 'large'
|
|
101
|
+
? largeCircle
|
|
102
|
+
: size === 'small'
|
|
103
|
+
? smallCircle
|
|
104
|
+
: mediumCircle,
|
|
105
|
+
button,
|
|
106
|
+
onElementContainer,
|
|
107
|
+
offElementContainer,
|
|
108
|
+
} = styles ?? {};
|
|
109
|
+
|
|
110
|
+
const paddingLeft: number =
|
|
111
|
+
(container.padding as number) || (container.paddingLeft as number) || 0;
|
|
112
|
+
|
|
113
|
+
const paddingRight: number =
|
|
114
|
+
(container.padding as number) || (container.paddingRight as number) || 0;
|
|
115
|
+
|
|
116
|
+
const circlePosXStart = 0;
|
|
117
|
+
|
|
118
|
+
const circlePosXEnd =
|
|
119
|
+
((container.width ?? mediumContainer.width) as number) -
|
|
120
|
+
((circle.width ?? mediumCircle.width) as number) -
|
|
121
|
+
(paddingRight + paddingLeft);
|
|
122
|
+
|
|
123
|
+
const [animXValue] = useState(new Animated.Value(isOn ? 1 : 0));
|
|
124
|
+
|
|
125
|
+
useEffect(() => {
|
|
126
|
+
const runAnimation = (): void => {
|
|
127
|
+
const option = {
|
|
128
|
+
fromValue: isOn ? 0 : 1,
|
|
129
|
+
toValue: isOn ? 1 : 0,
|
|
130
|
+
duration,
|
|
131
|
+
useNativeDriver: false,
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
Animated.timing(animXValue, option).start();
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
runAnimation();
|
|
138
|
+
}, [animXValue, isOn, duration]);
|
|
139
|
+
|
|
140
|
+
const CircleButton = (
|
|
141
|
+
<Animated.View
|
|
142
|
+
style={[
|
|
143
|
+
circle,
|
|
144
|
+
{
|
|
145
|
+
backgroundColor: animXValue.interpolate({
|
|
146
|
+
inputRange: [0.5, 1],
|
|
147
|
+
outputRange: [
|
|
148
|
+
circleColorOff as string | number,
|
|
149
|
+
circleColorOn as string | number,
|
|
150
|
+
] as string[] | number[],
|
|
151
|
+
}),
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
transform: [
|
|
155
|
+
{
|
|
156
|
+
translateX: animXValue.interpolate({
|
|
157
|
+
inputRange: [0, 1],
|
|
158
|
+
outputRange: [
|
|
159
|
+
circlePosXStart as string | number,
|
|
160
|
+
circlePosXEnd as string | number,
|
|
161
|
+
] as string[] | number[],
|
|
162
|
+
}),
|
|
163
|
+
},
|
|
164
|
+
],
|
|
165
|
+
},
|
|
166
|
+
button,
|
|
167
|
+
]}
|
|
168
|
+
/>
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
const OnElement = (
|
|
172
|
+
<Animated.View style={[{opacity: animXValue}, onElementContainer]}>
|
|
173
|
+
{onElement}
|
|
174
|
+
</Animated.View>
|
|
175
|
+
);
|
|
176
|
+
|
|
177
|
+
const OffElement = (
|
|
178
|
+
<Animated.View
|
|
179
|
+
style={[
|
|
180
|
+
{
|
|
181
|
+
opacity: animXValue.interpolate({
|
|
182
|
+
inputRange: [0, 1],
|
|
183
|
+
outputRange: [1, 0],
|
|
184
|
+
}),
|
|
185
|
+
},
|
|
186
|
+
offElementContainer,
|
|
187
|
+
]}
|
|
188
|
+
>
|
|
189
|
+
{offElement}
|
|
190
|
+
</Animated.View>
|
|
191
|
+
);
|
|
192
|
+
|
|
193
|
+
return (
|
|
194
|
+
<TouchableOpacity
|
|
195
|
+
accessibilityRole="switch"
|
|
196
|
+
activeOpacity={0.8}
|
|
197
|
+
onPress={onPress}
|
|
198
|
+
style={style}
|
|
199
|
+
testID={testID}
|
|
200
|
+
>
|
|
201
|
+
<AnimatedContainer
|
|
202
|
+
style={[
|
|
203
|
+
container,
|
|
204
|
+
{
|
|
205
|
+
paddingLeft,
|
|
206
|
+
paddingRight,
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
backgroundColor: animXValue.interpolate({
|
|
210
|
+
inputRange: [0, 1],
|
|
211
|
+
outputRange: [
|
|
212
|
+
backgroundColorOff as string | number,
|
|
213
|
+
backgroundColorOn as string | number,
|
|
214
|
+
] as string[] | number[],
|
|
215
|
+
}),
|
|
216
|
+
},
|
|
217
|
+
]}
|
|
218
|
+
>
|
|
219
|
+
{isOn ? OnElement : OffElement}
|
|
220
|
+
{CircleButton}
|
|
221
|
+
</AnimatedContainer>
|
|
222
|
+
</TouchableOpacity>
|
|
223
|
+
);
|
|
224
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { Text } from 'react-native';
|
|
4
|
+
import { isEmptyObject } from '../../../utils/theme';
|
|
5
|
+
import { withTheme } from '../../../providers/ThemeProvider';
|
|
6
|
+
import { light } from '../../../utils/colors';
|
|
7
|
+
import styled, { css } from '@emotion/native';
|
|
8
|
+
// Base Styled Component Factory
|
|
9
|
+
const createBaseText = (colorResolver, fallbackColor) => styled.Text `
|
|
10
|
+
font-family: ${({ theme }) => (theme ? 'Pretendard-Bold' : 'Pretendard')};
|
|
11
|
+
color: ${({ theme }) => {
|
|
12
|
+
if (!theme || isEmptyObject(theme)) {
|
|
13
|
+
return fallbackColor;
|
|
14
|
+
}
|
|
15
|
+
return colorResolver(theme);
|
|
16
|
+
}};
|
|
17
|
+
`;
|
|
18
|
+
// Common Text Component Factory
|
|
19
|
+
const createTextComponent = (BaseText, fontSize, lineHeight) => withTheme(({ style, children, theme, ...props }) => (_jsx(BaseText, { ...props, theme: theme, style: [
|
|
20
|
+
css `
|
|
21
|
+
font-size: ${fontSize + 'px'};
|
|
22
|
+
line-height: ${lineHeight + 'px'};
|
|
23
|
+
`,
|
|
24
|
+
{ includeFontPadding: false },
|
|
25
|
+
style,
|
|
26
|
+
], children: children })));
|
|
27
|
+
// Standard and Inverted Base Components
|
|
28
|
+
const StandardBaseText = createBaseText((theme) => theme.text.basic, 'gray');
|
|
29
|
+
const InvertedBaseText = createBaseText((theme) => theme.text.contrast, light.text.contrast);
|
|
30
|
+
// Standard Typography Components
|
|
31
|
+
const Title = createTextComponent(StandardBaseText, 36, 50.4);
|
|
32
|
+
const Heading1 = createTextComponent(StandardBaseText, 28, 39.2);
|
|
33
|
+
const Heading2 = createTextComponent(StandardBaseText, 26, 36.4);
|
|
34
|
+
const Heading3 = createTextComponent(StandardBaseText, 24, 33.6);
|
|
35
|
+
const Heading4 = createTextComponent(StandardBaseText, 22, 30.8);
|
|
36
|
+
const Heading5 = createTextComponent(StandardBaseText, 20, 28);
|
|
37
|
+
const Body1 = createTextComponent(StandardBaseText, 18, 25.2);
|
|
38
|
+
const Body2 = createTextComponent(StandardBaseText, 16, 22.4);
|
|
39
|
+
const Body3 = createTextComponent(StandardBaseText, 14, 19.6);
|
|
40
|
+
const Body4 = createTextComponent(StandardBaseText, 12, 16.4);
|
|
41
|
+
// Inverted Typography Components
|
|
42
|
+
const InvertedTitle = createTextComponent(InvertedBaseText, 36, 50.4);
|
|
43
|
+
const InvertedHeading1 = createTextComponent(InvertedBaseText, 28, 39.2);
|
|
44
|
+
const InvertedHeading2 = createTextComponent(InvertedBaseText, 26, 36.4);
|
|
45
|
+
const InvertedHeading3 = createTextComponent(InvertedBaseText, 24, 33.6);
|
|
46
|
+
const InvertedHeading4 = createTextComponent(InvertedBaseText, 22, 30.8);
|
|
47
|
+
const InvertedHeading5 = createTextComponent(InvertedBaseText, 20, 28);
|
|
48
|
+
const InvertedBody1 = createTextComponent(InvertedBaseText, 18, 25.2);
|
|
49
|
+
const InvertedBody2 = createTextComponent(InvertedBaseText, 16, 22.4);
|
|
50
|
+
const InvertedBody3 = createTextComponent(InvertedBaseText, 14, 19.6);
|
|
51
|
+
const InvertedBody4 = createTextComponent(InvertedBaseText, 12, 16.4);
|
|
52
|
+
export const Typography = {
|
|
53
|
+
Title,
|
|
54
|
+
Heading1,
|
|
55
|
+
Heading2,
|
|
56
|
+
Heading3,
|
|
57
|
+
Heading4,
|
|
58
|
+
Heading5,
|
|
59
|
+
Body1,
|
|
60
|
+
Body2,
|
|
61
|
+
Body3,
|
|
62
|
+
Body4,
|
|
63
|
+
};
|
|
64
|
+
export const TypographyInverted = {
|
|
65
|
+
Title: InvertedTitle,
|
|
66
|
+
Heading1: InvertedHeading1,
|
|
67
|
+
Heading2: InvertedHeading2,
|
|
68
|
+
Heading3: InvertedHeading3,
|
|
69
|
+
Heading4: InvertedHeading4,
|
|
70
|
+
Heading5: InvertedHeading5,
|
|
71
|
+
Body1: InvertedBody1,
|
|
72
|
+
Body2: InvertedBody2,
|
|
73
|
+
Body3: InvertedBody3,
|
|
74
|
+
Body4: InvertedBody4,
|
|
75
|
+
};
|
|
76
|
+
export const setFontFamily = (fontFamily) => {
|
|
77
|
+
const style = {
|
|
78
|
+
includeFontPadding: false,
|
|
79
|
+
fontFamily,
|
|
80
|
+
};
|
|
81
|
+
// @ts-ignore
|
|
82
|
+
let oldRender = Text.render;
|
|
83
|
+
// @ts-ignore
|
|
84
|
+
Text.render = (...args) => {
|
|
85
|
+
let origin = oldRender.call(this, ...args);
|
|
86
|
+
return React.cloneElement(origin, {
|
|
87
|
+
style: [style, origin.props.style],
|
|
88
|
+
});
|
|
89
|
+
};
|
|
90
|
+
};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import {View} from 'react-native';
|
|
3
|
+
import type {RenderAPI} from '@testing-library/react-native';
|
|
4
|
+
import {render} from '@testing-library/react-native';
|
|
5
|
+
|
|
6
|
+
import {createComponent} from '../../../../test/testUtils';
|
|
7
|
+
import {Typography, TypographyInverted} from './Typography';
|
|
8
|
+
import {ThemeProvider} from '../../../providers/ThemeProvider';
|
|
9
|
+
|
|
10
|
+
let testingLib: RenderAPI;
|
|
11
|
+
|
|
12
|
+
const Component = (): JSX.Element =>
|
|
13
|
+
createComponent(
|
|
14
|
+
<View>
|
|
15
|
+
<Typography.Title />,
|
|
16
|
+
<Typography.Heading1 />,
|
|
17
|
+
<Typography.Heading2 />,
|
|
18
|
+
<Typography.Heading3 />,
|
|
19
|
+
<Typography.Body1 />,
|
|
20
|
+
<Typography.Body2 />,
|
|
21
|
+
<TypographyInverted.Title />
|
|
22
|
+
<TypographyInverted.Heading1 />
|
|
23
|
+
<TypographyInverted.Heading2 />
|
|
24
|
+
<TypographyInverted.Heading3 />
|
|
25
|
+
<TypographyInverted.Body1 />
|
|
26
|
+
<TypographyInverted.Body2 />
|
|
27
|
+
</View>,
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
describe('[Typography]', () => {
|
|
31
|
+
it('should render without crashing', () => {
|
|
32
|
+
testingLib = render(<Component />);
|
|
33
|
+
|
|
34
|
+
const json = testingLib.toJSON();
|
|
35
|
+
|
|
36
|
+
expect(json).toBeTruthy();
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('should render default theme', () => {
|
|
40
|
+
testingLib = render(Component());
|
|
41
|
+
|
|
42
|
+
const json = testingLib.toJSON();
|
|
43
|
+
|
|
44
|
+
expect(json).toBeTruthy();
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('should render [dark] mode', () => {
|
|
48
|
+
testingLib = render(
|
|
49
|
+
<ThemeProvider initialThemeType="dark">
|
|
50
|
+
<Component />
|
|
51
|
+
</ThemeProvider>,
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
const json = testingLib.toJSON();
|
|
55
|
+
|
|
56
|
+
expect(json).toBeTruthy();
|
|
57
|
+
});
|
|
58
|
+
});
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import React, {ReactNode} from 'react';
|
|
2
|
+
import {StyleProp, Text} from 'react-native';
|
|
3
|
+
|
|
4
|
+
import {CpkTheme, isEmptyObject} from '../../../utils/theme';
|
|
5
|
+
import {withTheme} from '../../../providers/ThemeProvider';
|
|
6
|
+
import {light} from '../../../utils/colors';
|
|
7
|
+
import {type TextStyle} from 'react-native';
|
|
8
|
+
import styled, {css} from '@emotion/native';
|
|
9
|
+
|
|
10
|
+
// Base Styled Component Factory
|
|
11
|
+
const createBaseText = (
|
|
12
|
+
colorResolver: (theme: CpkTheme) => string,
|
|
13
|
+
fallbackColor: string,
|
|
14
|
+
) => styled.Text<{theme?: CpkTheme}>`
|
|
15
|
+
font-family: ${({theme}) => (theme ? 'Pretendard-Bold' : 'Pretendard')};
|
|
16
|
+
color: ${({theme}) => {
|
|
17
|
+
if (!theme || isEmptyObject(theme)) {
|
|
18
|
+
return fallbackColor;
|
|
19
|
+
}
|
|
20
|
+
return colorResolver(theme);
|
|
21
|
+
}};
|
|
22
|
+
`;
|
|
23
|
+
|
|
24
|
+
// Common Text Component Factory
|
|
25
|
+
const createTextComponent = (
|
|
26
|
+
BaseText: ReturnType<typeof styled.Text>,
|
|
27
|
+
fontSize: number,
|
|
28
|
+
lineHeight: number,
|
|
29
|
+
) =>
|
|
30
|
+
withTheme(
|
|
31
|
+
({
|
|
32
|
+
style,
|
|
33
|
+
children,
|
|
34
|
+
theme,
|
|
35
|
+
...props
|
|
36
|
+
}: {
|
|
37
|
+
style?: StyleProp<TextStyle>;
|
|
38
|
+
children?: ReactNode;
|
|
39
|
+
theme?: CpkTheme;
|
|
40
|
+
}) => (
|
|
41
|
+
<BaseText
|
|
42
|
+
{...props}
|
|
43
|
+
theme={theme}
|
|
44
|
+
style={[
|
|
45
|
+
css`
|
|
46
|
+
font-size: ${fontSize + 'px'};
|
|
47
|
+
line-height: ${lineHeight + 'px'};
|
|
48
|
+
`,
|
|
49
|
+
{includeFontPadding: false},
|
|
50
|
+
style,
|
|
51
|
+
]}
|
|
52
|
+
>
|
|
53
|
+
{children}
|
|
54
|
+
</BaseText>
|
|
55
|
+
),
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
// Standard and Inverted Base Components
|
|
59
|
+
const StandardBaseText = createBaseText((theme) => theme.text.basic, 'gray');
|
|
60
|
+
const InvertedBaseText = createBaseText(
|
|
61
|
+
(theme) => theme.text.contrast,
|
|
62
|
+
light.text.contrast,
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
// Standard Typography Components
|
|
66
|
+
const Title = createTextComponent(StandardBaseText, 36, 50.4);
|
|
67
|
+
const Heading1 = createTextComponent(StandardBaseText, 28, 39.2);
|
|
68
|
+
const Heading2 = createTextComponent(StandardBaseText, 26, 36.4);
|
|
69
|
+
const Heading3 = createTextComponent(StandardBaseText, 24, 33.6);
|
|
70
|
+
const Heading4 = createTextComponent(StandardBaseText, 22, 30.8);
|
|
71
|
+
const Heading5 = createTextComponent(StandardBaseText, 20, 28);
|
|
72
|
+
const Body1 = createTextComponent(StandardBaseText, 18, 25.2);
|
|
73
|
+
const Body2 = createTextComponent(StandardBaseText, 16, 22.4);
|
|
74
|
+
const Body3 = createTextComponent(StandardBaseText, 14, 19.6);
|
|
75
|
+
const Body4 = createTextComponent(StandardBaseText, 12, 16.4);
|
|
76
|
+
|
|
77
|
+
// Inverted Typography Components
|
|
78
|
+
const InvertedTitle = createTextComponent(InvertedBaseText, 36, 50.4);
|
|
79
|
+
const InvertedHeading1 = createTextComponent(InvertedBaseText, 28, 39.2);
|
|
80
|
+
const InvertedHeading2 = createTextComponent(InvertedBaseText, 26, 36.4);
|
|
81
|
+
const InvertedHeading3 = createTextComponent(InvertedBaseText, 24, 33.6);
|
|
82
|
+
const InvertedHeading4 = createTextComponent(InvertedBaseText, 22, 30.8);
|
|
83
|
+
const InvertedHeading5 = createTextComponent(InvertedBaseText, 20, 28);
|
|
84
|
+
const InvertedBody1 = createTextComponent(InvertedBaseText, 18, 25.2);
|
|
85
|
+
const InvertedBody2 = createTextComponent(InvertedBaseText, 16, 22.4);
|
|
86
|
+
const InvertedBody3 = createTextComponent(InvertedBaseText, 14, 19.6);
|
|
87
|
+
const InvertedBody4 = createTextComponent(InvertedBaseText, 12, 16.4);
|
|
88
|
+
|
|
89
|
+
export const Typography = {
|
|
90
|
+
Title,
|
|
91
|
+
Heading1,
|
|
92
|
+
Heading2,
|
|
93
|
+
Heading3,
|
|
94
|
+
Heading4,
|
|
95
|
+
Heading5,
|
|
96
|
+
Body1,
|
|
97
|
+
Body2,
|
|
98
|
+
Body3,
|
|
99
|
+
Body4,
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
export const TypographyInverted = {
|
|
103
|
+
Title: InvertedTitle,
|
|
104
|
+
Heading1: InvertedHeading1,
|
|
105
|
+
Heading2: InvertedHeading2,
|
|
106
|
+
Heading3: InvertedHeading3,
|
|
107
|
+
Heading4: InvertedHeading4,
|
|
108
|
+
Heading5: InvertedHeading5,
|
|
109
|
+
Body1: InvertedBody1,
|
|
110
|
+
Body2: InvertedBody2,
|
|
111
|
+
Body3: InvertedBody3,
|
|
112
|
+
Body4: InvertedBody4,
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
export const setFontFamily = (fontFamily: string): void => {
|
|
116
|
+
const style = {
|
|
117
|
+
includeFontPadding: false,
|
|
118
|
+
fontFamily,
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
// @ts-ignore
|
|
122
|
+
let oldRender = Text.render;
|
|
123
|
+
|
|
124
|
+
// @ts-ignore
|
|
125
|
+
Text.render = (...args: any) => {
|
|
126
|
+
let origin = oldRender.call(this, ...args);
|
|
127
|
+
|
|
128
|
+
return React.cloneElement(origin, {
|
|
129
|
+
style: [style, origin.props.style],
|
|
130
|
+
});
|
|
131
|
+
};
|
|
132
|
+
};
|