cpk-ui 0.2.2 → 0.2.3

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.
@@ -124,7 +124,17 @@ export function Button({ testID, type = 'solid', color = 'primary', size = 'medi
124
124
  borderRadius,
125
125
  hovered,
126
126
  styles,
127
- }), [theme, type, color, size, loading, innerDisabled, borderRadius, hovered, styles]);
127
+ }), [
128
+ theme,
129
+ type,
130
+ color,
131
+ size,
132
+ loading,
133
+ innerDisabled,
134
+ borderRadius,
135
+ hovered,
136
+ styles,
137
+ ]);
128
138
  // Memoize loading view
129
139
  const LoadingView = useMemo(() => loadingElement ?? (_jsx(LoadingIndicator, { color: loadingColor
130
140
  ? loadingColor
@@ -149,7 +159,14 @@ export function Button({ testID, type = 'solid', color = 'primary', size = 'medi
149
159
  element: endElement,
150
160
  backgroundColor: viewStyle?.backgroundColor,
151
161
  color: textStyle?.color,
152
- })] })), [startElement, endElement, text, compositeStyles.text, viewStyle?.backgroundColor, textStyle?.color]);
162
+ })] })), [
163
+ startElement,
164
+ endElement,
165
+ text,
166
+ compositeStyles.text,
167
+ viewStyle?.backgroundColor,
168
+ textStyle?.color,
169
+ ]);
153
170
  // Memoize container renderer
154
171
  const renderContainer = useCallback(({ children, loadingView, }) => (_jsxs(ButtonContainer, { disabled: innerDisabled, size: size, style: [
155
172
  hovered && !innerDisabled && compositeStyles.hovered,
@@ -159,6 +176,10 @@ export function Button({ testID, type = 'solid', color = 'primary', size = 'medi
159
176
  background-color: transparent;
160
177
  `,
161
178
  innerDisabled && compositeStyles.disabled,
179
+ !!borderRadius &&
180
+ css `
181
+ border-radius: ${borderRadius + 'px'};
182
+ `,
162
183
  ], testID: loading ? 'loading-view' : 'button-container', type: type, children: [_jsx(View, { style: compositeStyles.content, children: children }), _jsx(View, { style: compositeStyles.loading, children: loadingView })] })), [
163
184
  innerDisabled,
164
185
  size,
@@ -182,8 +203,8 @@ export function Button({ testID, type = 'solid', color = 'primary', size = 'medi
182
203
  const buttonStyles = useMemo(() => [
183
204
  style,
184
205
  css `
185
- border-radius: ${borderRadius + 'px'};
186
- `,
206
+ border-radius: ${borderRadius + 'px'};
207
+ `,
187
208
  ], [style, borderRadius]);
188
209
  return (_jsx(TouchableHighlight, { activeOpacity: activeOpacity, delayPressIn: 30, disabled: innerDisabled || loading || !onPress, hitSlop: hitSlop, onPress: handlePress, ref: Platform.select({
189
210
  web: ref,
@@ -206,10 +206,13 @@ const useButtonState = ({
206
206
  innerDisabled: boolean;
207
207
  isLoading: boolean;
208
208
  } => {
209
- return useMemo(() => ({
210
- innerDisabled: disabled || !onPress,
211
- isLoading: loading,
212
- }), [disabled, onPress, loading]);
209
+ return useMemo(
210
+ () => ({
211
+ innerDisabled: disabled || !onPress,
212
+ isLoading: loading,
213
+ }),
214
+ [disabled, onPress, loading],
215
+ );
213
216
  };
214
217
 
215
218
  export function Button({
@@ -238,33 +241,51 @@ export function Button({
238
241
  const {theme} = useTheme();
239
242
 
240
243
  const {innerDisabled} = useButtonState({disabled, onPress, loading});
241
-
244
+
242
245
  // Memoize styles calculation
243
- const compositeStyles: Styles = useMemo(() => calculateStyles({
244
- theme,
245
- type,
246
- color,
247
- size,
248
- loading,
249
- disabled: innerDisabled,
250
- borderRadius,
251
- hovered,
252
- styles,
253
- }), [theme, type, color, size, loading, innerDisabled, borderRadius, hovered, styles]);
246
+ const compositeStyles: Styles = useMemo(
247
+ () =>
248
+ calculateStyles({
249
+ theme,
250
+ type,
251
+ color,
252
+ size,
253
+ loading,
254
+ disabled: innerDisabled,
255
+ borderRadius,
256
+ hovered,
257
+ styles,
258
+ }),
259
+ [
260
+ theme,
261
+ type,
262
+ color,
263
+ size,
264
+ loading,
265
+ innerDisabled,
266
+ borderRadius,
267
+ hovered,
268
+ styles,
269
+ ],
270
+ );
254
271
 
255
272
  // Memoize loading view
256
- const LoadingView = useMemo(() => loadingElement ?? (
257
- <LoadingIndicator
258
- color={
259
- loadingColor
260
- ? loadingColor
261
- : type === 'solid'
262
- ? theme.text.contrast
263
- : theme.text.basic
264
- }
265
- size="small"
266
- />
267
- ), [loadingElement, loadingColor, type, theme.text.contrast, theme.text.basic]);
273
+ const LoadingView = useMemo(
274
+ () =>
275
+ loadingElement ?? (
276
+ <LoadingIndicator
277
+ color={
278
+ loadingColor
279
+ ? loadingColor
280
+ : type === 'solid'
281
+ ? theme.text.contrast
282
+ : theme.text.basic
283
+ }
284
+ size="small"
285
+ />
286
+ ),
287
+ [loadingElement, loadingColor, type, theme.text.contrast, theme.text.basic],
288
+ );
268
289
 
269
290
  // Memoize style resolvers
270
291
  const resolveStyle = useCallback(<T,>(style: StyleProp<T>): T | undefined => {
@@ -274,29 +295,47 @@ export function Button({
274
295
  return (style as T) || undefined;
275
296
  }, []);
276
297
 
277
- const viewStyle = useMemo(() => resolveStyle<ViewStyle>(compositeStyles.container), [resolveStyle, compositeStyles.container]);
278
- const textStyle = useMemo(() => resolveStyle<TextStyle>(compositeStyles.text), [resolveStyle, compositeStyles.text]);
298
+ const viewStyle = useMemo(
299
+ () => resolveStyle<ViewStyle>(compositeStyles.container),
300
+ [resolveStyle, compositeStyles.container],
301
+ );
302
+ const textStyle = useMemo(
303
+ () => resolveStyle<TextStyle>(compositeStyles.text),
304
+ [resolveStyle, compositeStyles.text],
305
+ );
279
306
 
280
307
  // Memoize child view
281
- const ChildView = useMemo(() => (
282
- <>
283
- {cloneElemWithDefaultColors({
284
- element: startElement,
285
- backgroundColor: viewStyle?.backgroundColor,
286
- color: textStyle?.color,
287
- })}
288
- {!text || typeof text === 'string' ? (
289
- <Typography.Body2 style={compositeStyles.text}>{text}</Typography.Body2>
290
- ) : (
291
- text
292
- )}
293
- {cloneElemWithDefaultColors({
294
- element: endElement,
295
- backgroundColor: viewStyle?.backgroundColor,
296
- color: textStyle?.color,
297
- })}
298
- </>
299
- ), [startElement, endElement, text, compositeStyles.text, viewStyle?.backgroundColor, textStyle?.color]);
308
+ const ChildView = useMemo(
309
+ () => (
310
+ <>
311
+ {cloneElemWithDefaultColors({
312
+ element: startElement,
313
+ backgroundColor: viewStyle?.backgroundColor,
314
+ color: textStyle?.color,
315
+ })}
316
+ {!text || typeof text === 'string' ? (
317
+ <Typography.Body2 style={compositeStyles.text}>
318
+ {text}
319
+ </Typography.Body2>
320
+ ) : (
321
+ text
322
+ )}
323
+ {cloneElemWithDefaultColors({
324
+ element: endElement,
325
+ backgroundColor: viewStyle?.backgroundColor,
326
+ color: textStyle?.color,
327
+ })}
328
+ </>
329
+ ),
330
+ [
331
+ startElement,
332
+ endElement,
333
+ text,
334
+ compositeStyles.text,
335
+ viewStyle?.backgroundColor,
336
+ textStyle?.color,
337
+ ],
338
+ );
300
339
 
301
340
  // Memoize container renderer
302
341
  const renderContainer = useCallback(
@@ -318,6 +357,10 @@ export function Button({
318
357
  background-color: transparent;
319
358
  `,
320
359
  innerDisabled && compositeStyles.disabled,
360
+ !!borderRadius &&
361
+ css`
362
+ border-radius: ${borderRadius + 'px'};
363
+ `,
321
364
  ]}
322
365
  testID={loading ? 'loading-view' : 'button-container'}
323
366
  type={type}
@@ -341,20 +384,26 @@ export function Button({
341
384
  );
342
385
 
343
386
  // Memoize press handler
344
- const handlePress = useCallback((e: any) => {
345
- onPress?.(e);
346
- if (hapticFeedback) {
347
- Haptics.impactAsync(hapticFeedback);
348
- }
349
- }, [onPress, hapticFeedback]);
387
+ const handlePress = useCallback(
388
+ (e: any) => {
389
+ onPress?.(e);
390
+ if (hapticFeedback) {
391
+ Haptics.impactAsync(hapticFeedback);
392
+ }
393
+ },
394
+ [onPress, hapticFeedback],
395
+ );
350
396
 
351
397
  // Memoize button styles
352
- const buttonStyles = useMemo(() => [
353
- style,
354
- css`
355
- border-radius: ${borderRadius + 'px'};
356
- `,
357
- ], [style, borderRadius]);
398
+ const buttonStyles = useMemo(
399
+ () => [
400
+ style,
401
+ css`
402
+ border-radius: ${borderRadius + 'px'};
403
+ `,
404
+ ],
405
+ [style, borderRadius],
406
+ );
358
407
 
359
408
  return (
360
409
  <TouchableHighlight
@@ -0,0 +1,47 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ /**
3
+ * @name Card
4
+ *
5
+ * See the style guide: https://github.com/hyochan/style-guide/blob/main/docs/REACT.md
6
+ */
7
+ import React from 'react';
8
+ import { View } from 'react-native';
9
+ import styled from '@emotion/native';
10
+ import { useTheme } from '../../../providers/ThemeProvider';
11
+ export default function Card({ testID = 'card-container', children, elevation = 2, borderRadius = 8, padding = 16, margin = 0, backgroundColor, style, styles, onPress, }) {
12
+ const { theme } = useTheme();
13
+ const shadowStyle = React.useMemo(() => ({
14
+ elevation: elevation,
15
+ shadowOffset: {
16
+ width: 0,
17
+ height: elevation / 2,
18
+ },
19
+ shadowOpacity: 0.1,
20
+ shadowRadius: elevation,
21
+ shadowColor: theme.text.basic,
22
+ }), [elevation, theme.text.basic]);
23
+ const containerStyle = React.useMemo(() => [
24
+ {
25
+ backgroundColor: backgroundColor || theme.bg.paper,
26
+ borderRadius: borderRadius,
27
+ padding: padding,
28
+ margin: margin,
29
+ },
30
+ shadowStyle,
31
+ style,
32
+ styles?.container,
33
+ ], [backgroundColor, theme.bg.paper, borderRadius, padding, margin, shadowStyle, style, styles?.container]);
34
+ if (onPress) {
35
+ return (_jsx(TouchableContainer, { testID: testID, style: containerStyle, onPress: onPress, activeOpacity: 0.8, children: _jsx(View, { style: styles?.content, children: children }) }));
36
+ }
37
+ return (_jsx(Container, { testID: testID, style: containerStyle, children: _jsx(View, { style: styles?.content, children: children }) }));
38
+ }
39
+ const Container = styled.View `
40
+ background-color: ${({ theme }) => theme.bg.paper};
41
+ border-radius: 8px;
42
+ `;
43
+ const TouchableContainer = styled.TouchableOpacity `
44
+ background-color: ${({ theme }) => theme.bg.paper};
45
+ border-radius: 8px;
46
+ `;
47
+ export { Card };
@@ -0,0 +1,146 @@
1
+ import '@testing-library/jest-native/extend-expect';
2
+
3
+ import React from 'react';
4
+ import {Text} from 'react-native';
5
+ import type {RenderAPI} from '@testing-library/react-native';
6
+ import {fireEvent, render} from '@testing-library/react-native';
7
+
8
+ import {createComponent} from '../../../../test/testUtils';
9
+ import {Card} from './Card';
10
+ import type {CardProps} from './Card';
11
+ import type {ThemeType} from '../../../providers/ThemeProvider';
12
+
13
+ let testingLib: RenderAPI;
14
+
15
+ const Component = ({
16
+ props,
17
+ themeType,
18
+ }: {
19
+ props?: CardProps;
20
+ themeType?: ThemeType;
21
+ }): React.JSX.Element =>
22
+ createComponent(
23
+ <Card {...props}>
24
+ <Text>Test Content</Text>
25
+ </Card>,
26
+ themeType,
27
+ );
28
+
29
+ describe('[Card]', () => {
30
+ it('should render without crashing', () => {
31
+ testingLib = render(Component({}));
32
+
33
+ const json = testingLib.toJSON();
34
+ expect(json).toBeTruthy();
35
+ });
36
+
37
+ it('should render with default props', () => {
38
+ testingLib = render(Component({}));
39
+
40
+ const cardContainer = testingLib.getByTestId('card-container');
41
+ expect(cardContainer).toBeTruthy();
42
+ });
43
+
44
+ it('should render children content', () => {
45
+ testingLib = render(Component({}));
46
+
47
+ expect(testingLib.getByText('Test Content')).toBeTruthy();
48
+ });
49
+
50
+ it('should handle onPress when provided', () => {
51
+ const mockOnPress = jest.fn();
52
+ testingLib = render(Component({props: {onPress: mockOnPress}}));
53
+
54
+ const cardContainer = testingLib.getByTestId('card-container');
55
+ fireEvent.press(cardContainer);
56
+
57
+ expect(mockOnPress).toHaveBeenCalledTimes(1);
58
+ });
59
+
60
+ it('should apply custom testID', () => {
61
+ const customTestID = 'custom-card';
62
+ testingLib = render(Component({props: {testID: customTestID}}));
63
+
64
+ expect(testingLib.getByTestId(customTestID)).toBeTruthy();
65
+ });
66
+
67
+ it('should apply custom elevation', () => {
68
+ testingLib = render(Component({props: {elevation: 5}}));
69
+
70
+ const cardContainer = testingLib.getByTestId('card-container');
71
+ expect(cardContainer).toBeTruthy();
72
+ });
73
+
74
+ it('should apply custom border radius', () => {
75
+ testingLib = render(Component({props: {borderRadius: 12}}));
76
+
77
+ const cardContainer = testingLib.getByTestId('card-container');
78
+ expect(cardContainer).toBeTruthy();
79
+ });
80
+
81
+ it('should apply custom padding', () => {
82
+ testingLib = render(Component({props: {padding: 24}}));
83
+
84
+ const cardContainer = testingLib.getByTestId('card-container');
85
+ expect(cardContainer).toBeTruthy();
86
+ });
87
+
88
+ it('should apply custom margin', () => {
89
+ testingLib = render(Component({props: {margin: 16}}));
90
+
91
+ const cardContainer = testingLib.getByTestId('card-container');
92
+ expect(cardContainer).toBeTruthy();
93
+ });
94
+
95
+ it('should apply custom background color', () => {
96
+ testingLib = render(Component({props: {backgroundColor: '#ff0000'}}));
97
+
98
+ const cardContainer = testingLib.getByTestId('card-container');
99
+ expect(cardContainer).toBeTruthy();
100
+ });
101
+
102
+ describe('Themes', () => {
103
+ it('should render correctly with light theme', () => {
104
+ testingLib = render(Component({themeType: 'light'}));
105
+
106
+ const cardContainer = testingLib.getByTestId('card-container');
107
+ expect(cardContainer).toBeTruthy();
108
+ });
109
+
110
+ it('should render correctly with dark theme', () => {
111
+ testingLib = render(Component({themeType: 'dark'}));
112
+
113
+ const cardContainer = testingLib.getByTestId('card-container');
114
+ expect(cardContainer).toBeTruthy();
115
+ });
116
+ });
117
+
118
+ describe('Custom Styles', () => {
119
+ it('should apply custom container styles', () => {
120
+ const customStyles = {
121
+ container: {
122
+ borderWidth: 2,
123
+ borderColor: '#000',
124
+ },
125
+ };
126
+
127
+ testingLib = render(Component({props: {styles: customStyles}}));
128
+
129
+ const cardContainer = testingLib.getByTestId('card-container');
130
+ expect(cardContainer).toBeTruthy();
131
+ });
132
+
133
+ it('should apply custom content styles', () => {
134
+ const customStyles = {
135
+ content: {
136
+ alignItems: 'center' as const,
137
+ },
138
+ };
139
+
140
+ testingLib = render(Component({props: {styles: customStyles}}));
141
+
142
+ const cardContainer = testingLib.getByTestId('card-container');
143
+ expect(cardContainer).toBeTruthy();
144
+ });
145
+ });
146
+ });
@@ -0,0 +1,103 @@
1
+ /**
2
+ * @name Card
3
+ *
4
+ * See the style guide: https://github.com/hyochan/style-guide/blob/main/docs/REACT.md
5
+ */
6
+ import React from 'react';
7
+ import type {StyleProp, ViewStyle} from 'react-native';
8
+ import {View} from 'react-native';
9
+ import styled, {css} from '@emotion/native';
10
+
11
+ import {useTheme} from '../../../providers/ThemeProvider';
12
+ import type {CpkTheme} from '../../../utils/theme';
13
+
14
+ type Styles = {
15
+ container?: StyleProp<ViewStyle>;
16
+ content?: StyleProp<ViewStyle>;
17
+ };
18
+
19
+ export type CardProps = {
20
+ testID?: string;
21
+ children?: React.ReactNode;
22
+ elevation?: number;
23
+ borderRadius?: number;
24
+ padding?: number;
25
+ margin?: number;
26
+ backgroundColor?: string;
27
+ style?: StyleProp<ViewStyle>;
28
+ styles?: Styles;
29
+ onPress?: () => void;
30
+ };
31
+
32
+ export default function Card({
33
+ testID = 'card-container',
34
+ children,
35
+ elevation = 2,
36
+ borderRadius = 8,
37
+ padding = 16,
38
+ margin = 0,
39
+ backgroundColor,
40
+ style,
41
+ styles,
42
+ onPress,
43
+ }: CardProps): JSX.Element {
44
+ const {theme} = useTheme();
45
+
46
+ const shadowStyle = React.useMemo(() => ({
47
+ elevation: elevation,
48
+ shadowOffset: {
49
+ width: 0,
50
+ height: elevation / 2,
51
+ },
52
+ shadowOpacity: 0.1,
53
+ shadowRadius: elevation,
54
+ shadowColor: theme.text.basic,
55
+ }), [elevation, theme.text.basic]);
56
+
57
+ const containerStyle = React.useMemo(() => [
58
+ {
59
+ backgroundColor: backgroundColor || theme.bg.paper,
60
+ borderRadius: borderRadius,
61
+ padding: padding,
62
+ margin: margin,
63
+ },
64
+ shadowStyle,
65
+ style,
66
+ styles?.container,
67
+ ], [backgroundColor, theme.bg.paper, borderRadius, padding, margin, shadowStyle, style, styles?.container]);
68
+
69
+ if (onPress) {
70
+ return (
71
+ <TouchableContainer
72
+ testID={testID}
73
+ style={containerStyle}
74
+ onPress={onPress}
75
+ activeOpacity={0.8}
76
+ >
77
+ <View style={styles?.content}>
78
+ {children}
79
+ </View>
80
+ </TouchableContainer>
81
+ );
82
+ }
83
+
84
+ return (
85
+ <Container testID={testID} style={containerStyle}>
86
+ <View style={styles?.content}>
87
+ {children}
88
+ </View>
89
+ </Container>
90
+ );
91
+ }
92
+
93
+ const Container = styled.View`
94
+ background-color: ${({theme}: {theme: CpkTheme}) => theme.bg.paper};
95
+ border-radius: 8px;
96
+ `;
97
+
98
+ const TouchableContainer = styled.TouchableOpacity`
99
+ background-color: ${({theme}: {theme: CpkTheme}) => theme.bg.paper};
100
+ border-radius: 8px;
101
+ `;
102
+
103
+ export {Card};
package/index.js CHANGED
@@ -8,6 +8,7 @@ export * from './providers';
8
8
  // UIs
9
9
  export * from './components/uis/Accordion/Accordion';
10
10
  export * from './components/uis/Button/Button';
11
+ export * from './components/uis/Card/Card';
11
12
  export * from './components/uis/Checkbox/Checkbox';
12
13
  export * from './components/uis/CustomPressable/CustomPressable';
13
14
  export * from './components/uis/EditText/EditText';
package/index.tsx CHANGED
@@ -11,6 +11,7 @@ export * from './providers';
11
11
  // UIs
12
12
  export * from './components/uis/Accordion/Accordion';
13
13
  export * from './components/uis/Button/Button';
14
+ export * from './components/uis/Card/Card';
14
15
  export * from './components/uis/Checkbox/Checkbox';
15
16
  export * from './components/uis/CustomPressable/CustomPressable';
16
17
  export * from './components/uis/EditText/EditText';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cpk-ui",
3
- "version": "0.2.2",
3
+ "version": "0.2.3",
4
4
  "main": "index",
5
5
  "react-native": "index",
6
6
  "module": "index",