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.
Files changed (69) hide show
  1. package/README.md +27 -0
  2. package/components/modals/AlertDialog/AlertDialog.js +80 -0
  3. package/components/modals/AlertDialog/AlertDialog.stories.tsx +80 -0
  4. package/components/modals/AlertDialog/AlertDialog.tsx +194 -0
  5. package/components/modals/Snackbar/Snackbar.js +94 -0
  6. package/components/modals/Snackbar/Snackbar.stories.tsx +98 -0
  7. package/components/modals/Snackbar/Snackbar.tsx +174 -0
  8. package/components/modals/Snackbar/const.js +5 -0
  9. package/components/modals/Snackbar/const.ts +4 -0
  10. package/components/uis/Accordion/Accordion.js +11 -0
  11. package/components/uis/Accordion/Accordion.stories.tsx +38 -0
  12. package/components/uis/Accordion/Accordion.test.tsx +128 -0
  13. package/components/uis/Accordion/Accordion.tsx +58 -0
  14. package/components/uis/Accordion/AccordionItem.js +102 -0
  15. package/components/uis/Accordion/AccordionItem.tsx +213 -0
  16. package/components/uis/Button/Button.js +161 -0
  17. package/components/uis/Button/Button.stories.tsx +81 -0
  18. package/components/uis/Button/Button.test.tsx +560 -0
  19. package/components/uis/Button/Button.tsx +335 -0
  20. package/components/uis/Checkbox/Checkbox.js +70 -0
  21. package/components/uis/Checkbox/Checkbox.stories.tsx +46 -0
  22. package/components/uis/Checkbox/Checkbox.test.tsx +139 -0
  23. package/components/uis/Checkbox/Checkbox.tsx +150 -0
  24. package/components/uis/Icon/Icon.js +3780 -0
  25. package/components/uis/Icon/Icon.stories.tsx +45 -0
  26. package/components/uis/Icon/Icon.test.tsx +19 -0
  27. package/components/uis/Icon/Icon.tsx +3800 -0
  28. package/components/uis/Icon/Pretendard-Bold.otf +0 -0
  29. package/components/uis/Icon/Pretendard-Regular.otf +0 -0
  30. package/components/uis/Icon/Pretendard-Thin.otf +0 -0
  31. package/components/uis/Icon/cpk.ttf +0 -0
  32. package/components/uis/Icon/selection.json +1 -0
  33. package/components/uis/IconButton/IconButton.js +120 -0
  34. package/components/uis/IconButton/IconButton.test.tsx +165 -0
  35. package/components/uis/IconButton/IconButton.tsx +252 -0
  36. package/components/uis/LoadingIndicator/LoadingIndicator.js +24 -0
  37. package/components/uis/LoadingIndicator/LoadingIndicator.tsx +79 -0
  38. package/components/uis/Rating/Rating.js +53 -0
  39. package/components/uis/Rating/Rating.test.tsx +72 -0
  40. package/components/uis/Rating/Rating.tsx +155 -0
  41. package/components/uis/StatusbarBrightness/StatusBarBrightness.js +13 -0
  42. package/components/uis/StatusbarBrightness/StatusBarBrightness.test.tsx +41 -0
  43. package/components/uis/StatusbarBrightness/StatusBarBrightness.tsx +17 -0
  44. package/components/uis/Styled/StyledComponents.js +130 -0
  45. package/components/uis/Styled/StyledComponents.tsx +200 -0
  46. package/components/uis/SwitchToggle/SwitchToggle.js +126 -0
  47. package/components/uis/SwitchToggle/SwitchToggle.test.tsx +70 -0
  48. package/components/uis/SwitchToggle/SwitchToggle.tsx +224 -0
  49. package/components/uis/Typography/Typography.js +90 -0
  50. package/components/uis/Typography/Typography.test.tsx +58 -0
  51. package/components/uis/Typography/Typography.tsx +132 -0
  52. package/hooks/useDebouncedColorScheme.js +21 -0
  53. package/hooks/useDebouncedColorScheme.tsx +30 -0
  54. package/index.js +16 -0
  55. package/index.tsx +18 -0
  56. package/package.json +35 -0
  57. package/providers/ThemeProvider.js +106 -0
  58. package/providers/ThemeProvider.tsx +180 -0
  59. package/providers/index.js +62 -0
  60. package/providers/index.tsx +125 -0
  61. package/react-native.config.cjs +5 -0
  62. package/utils/colors.js +124 -0
  63. package/utils/colors.ts +127 -0
  64. package/utils/createCtx.js +15 -0
  65. package/utils/createCtx.tsx +26 -0
  66. package/utils/guards.js +44 -0
  67. package/utils/guards.ts +93 -0
  68. package/utils/theme.js +5 -0
  69. 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
+ };