@hero-design/rn 8.12.5 → 8.14.0

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.
@@ -0,0 +1,349 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`Error renders error screen with image correctly 1`] = `
4
+ <View
5
+ style={
6
+ Array [
7
+ Object {
8
+ "backgroundColor": "#f6f6f7",
9
+ "display": "flex",
10
+ "flex": 1,
11
+ "flexDirection": "column",
12
+ },
13
+ undefined,
14
+ ]
15
+ }
16
+ themeVariant="in-page"
17
+ >
18
+ <View
19
+ style={
20
+ Array [
21
+ Object {
22
+ "alignItems": "center",
23
+ "display": "flex",
24
+ "flex": 1,
25
+ "flexDirection": "column",
26
+ "justifyContent": "center",
27
+ "padding": 24,
28
+ },
29
+ undefined,
30
+ ]
31
+ }
32
+ >
33
+ <Image
34
+ source={
35
+ Object {
36
+ "uri": "path_to_image",
37
+ }
38
+ }
39
+ style={
40
+ Array [
41
+ Object {
42
+ "borderRadius": 0,
43
+ "height": 72,
44
+ "width": 72,
45
+ },
46
+ Array [
47
+ Object {
48
+ "height": 176,
49
+ "marginBottom": 32,
50
+ "resizeMode": "contain",
51
+ "width": 176,
52
+ },
53
+ undefined,
54
+ ],
55
+ ]
56
+ }
57
+ testID="error-image"
58
+ />
59
+ <Text
60
+ allowFontScaling={false}
61
+ style={
62
+ Array [
63
+ Object {
64
+ "color": "#001f23",
65
+ "fontFamily": "BeVietnamPro-Regular",
66
+ "fontSize": 14,
67
+ "letterSpacing": 0.42,
68
+ "lineHeight": 22,
69
+ },
70
+ Array [
71
+ Object {
72
+ "color": "#001f23",
73
+ "fontFamily": "RebondGrotesque-SemiBold",
74
+ "fontSize": 24,
75
+ "marginBottom": 8,
76
+ "textAlign": "center",
77
+ },
78
+ undefined,
79
+ ],
80
+ ]
81
+ }
82
+ themeFontSize="medium"
83
+ themeFontWeight="regular"
84
+ themeIntent="body"
85
+ themeTypeface="neutral"
86
+ >
87
+ We’re sorry, something went wrong
88
+ </Text>
89
+ <Text
90
+ allowFontScaling={false}
91
+ style={
92
+ Array [
93
+ Object {
94
+ "color": "#001f23",
95
+ "fontFamily": "BeVietnamPro-Regular",
96
+ "fontSize": 14,
97
+ "letterSpacing": 0.42,
98
+ "lineHeight": 22,
99
+ },
100
+ Array [
101
+ Object {
102
+ "color": "#4d6265",
103
+ "fontFamily": "RebondGrotesque",
104
+ "fontSize": 18,
105
+ "textAlign": "center",
106
+ },
107
+ undefined,
108
+ ],
109
+ ]
110
+ }
111
+ themeFontSize="medium"
112
+ themeFontWeight="regular"
113
+ themeIntent="body"
114
+ themeTypeface="neutral"
115
+ >
116
+ Please try again later
117
+ </Text>
118
+ </View>
119
+ </View>
120
+ `;
121
+
122
+ exports[`Error renders full screen error page correctly 1`] = `
123
+ <Modal
124
+ animationType="slide"
125
+ hardwareAccelerated={false}
126
+ onDismiss={[Function]}
127
+ onRequestClose={[Function]}
128
+ style={
129
+ Array [
130
+ Object {
131
+ "height": "100%",
132
+ "width": "100%",
133
+ },
134
+ undefined,
135
+ ]
136
+ }
137
+ visible={true}
138
+ >
139
+ <View
140
+ style={
141
+ Array [
142
+ Object {
143
+ "backgroundColor": "#ccd2d3",
144
+ "display": "flex",
145
+ "flex": 1,
146
+ "flexDirection": "column",
147
+ },
148
+ undefined,
149
+ ]
150
+ }
151
+ themeVariant="full-screen"
152
+ >
153
+ <View
154
+ style={
155
+ Array [
156
+ Object {
157
+ "alignItems": "center",
158
+ "display": "flex",
159
+ "flex": 1,
160
+ "flexDirection": "column",
161
+ "justifyContent": "center",
162
+ "padding": 24,
163
+ },
164
+ undefined,
165
+ ]
166
+ }
167
+ >
168
+ <Image
169
+ source={
170
+ Object {
171
+ "uri": "path_to_image",
172
+ }
173
+ }
174
+ style={
175
+ Array [
176
+ Object {
177
+ "borderRadius": 0,
178
+ "height": 72,
179
+ "width": 72,
180
+ },
181
+ Array [
182
+ Object {
183
+ "height": 176,
184
+ "marginBottom": 32,
185
+ "resizeMode": "contain",
186
+ "width": 176,
187
+ },
188
+ undefined,
189
+ ],
190
+ ]
191
+ }
192
+ testID="error-image"
193
+ />
194
+ <Text
195
+ allowFontScaling={false}
196
+ style={
197
+ Array [
198
+ Object {
199
+ "color": "#001f23",
200
+ "fontFamily": "BeVietnamPro-Regular",
201
+ "fontSize": 14,
202
+ "letterSpacing": 0.42,
203
+ "lineHeight": 22,
204
+ },
205
+ Array [
206
+ Object {
207
+ "color": "#001f23",
208
+ "fontFamily": "RebondGrotesque-SemiBold",
209
+ "fontSize": 24,
210
+ "marginBottom": 8,
211
+ "textAlign": "center",
212
+ },
213
+ undefined,
214
+ ],
215
+ ]
216
+ }
217
+ themeFontSize="medium"
218
+ themeFontWeight="regular"
219
+ themeIntent="body"
220
+ themeTypeface="neutral"
221
+ >
222
+ We’re sorry, something went wrong
223
+ </Text>
224
+ <Text
225
+ allowFontScaling={false}
226
+ style={
227
+ Array [
228
+ Object {
229
+ "color": "#001f23",
230
+ "fontFamily": "BeVietnamPro-Regular",
231
+ "fontSize": 14,
232
+ "letterSpacing": 0.42,
233
+ "lineHeight": 22,
234
+ },
235
+ Array [
236
+ Object {
237
+ "color": "#4d6265",
238
+ "fontFamily": "RebondGrotesque",
239
+ "fontSize": 18,
240
+ "textAlign": "center",
241
+ },
242
+ undefined,
243
+ ],
244
+ ]
245
+ }
246
+ themeFontSize="medium"
247
+ themeFontWeight="regular"
248
+ themeIntent="body"
249
+ themeTypeface="neutral"
250
+ >
251
+ Please try again later
252
+ </Text>
253
+ </View>
254
+ </View>
255
+ </Modal>
256
+ `;
257
+
258
+ exports[`Error renders title only correctly 1`] = `
259
+ <View
260
+ style={
261
+ Array [
262
+ Object {
263
+ "backgroundColor": "#f6f6f7",
264
+ "display": "flex",
265
+ "flex": 1,
266
+ "flexDirection": "column",
267
+ },
268
+ undefined,
269
+ ]
270
+ }
271
+ themeVariant="in-page"
272
+ >
273
+ <View
274
+ style={
275
+ Array [
276
+ Object {
277
+ "alignItems": "center",
278
+ "display": "flex",
279
+ "flex": 1,
280
+ "flexDirection": "column",
281
+ "justifyContent": "center",
282
+ "padding": 24,
283
+ },
284
+ undefined,
285
+ ]
286
+ }
287
+ >
288
+ <Text
289
+ allowFontScaling={false}
290
+ style={
291
+ Array [
292
+ Object {
293
+ "color": "#001f23",
294
+ "fontFamily": "BeVietnamPro-Regular",
295
+ "fontSize": 14,
296
+ "letterSpacing": 0.42,
297
+ "lineHeight": 22,
298
+ },
299
+ Array [
300
+ Object {
301
+ "color": "#001f23",
302
+ "fontFamily": "RebondGrotesque-SemiBold",
303
+ "fontSize": 24,
304
+ "marginBottom": 8,
305
+ "textAlign": "center",
306
+ },
307
+ undefined,
308
+ ],
309
+ ]
310
+ }
311
+ themeFontSize="medium"
312
+ themeFontWeight="regular"
313
+ themeIntent="body"
314
+ themeTypeface="neutral"
315
+ >
316
+ We’re sorry, something went wrong
317
+ </Text>
318
+ <Text
319
+ allowFontScaling={false}
320
+ style={
321
+ Array [
322
+ Object {
323
+ "color": "#001f23",
324
+ "fontFamily": "BeVietnamPro-Regular",
325
+ "fontSize": 14,
326
+ "letterSpacing": 0.42,
327
+ "lineHeight": 22,
328
+ },
329
+ Array [
330
+ Object {
331
+ "color": "#4d6265",
332
+ "fontFamily": "RebondGrotesque",
333
+ "fontSize": 18,
334
+ "textAlign": "center",
335
+ },
336
+ undefined,
337
+ ],
338
+ ]
339
+ }
340
+ themeFontSize="medium"
341
+ themeFontWeight="regular"
342
+ themeIntent="body"
343
+ themeTypeface="neutral"
344
+ >
345
+ Please try again later
346
+ </Text>
347
+ </View>
348
+ </View>
349
+ `;
@@ -0,0 +1,67 @@
1
+ import React from 'react';
2
+ import { fireEvent } from '@testing-library/react-native';
3
+ import renderWithTheme from '../../../testHelpers/renderWithTheme';
4
+ import Error from '..';
5
+
6
+ const title = `We’re sorry, something went wrong`;
7
+ const description = 'Please try again later';
8
+
9
+ describe('Error', () => {
10
+ it('renders title only correctly', () => {
11
+ const { toJSON, getByText } = renderWithTheme(
12
+ <Error title={title} description={description} />
13
+ );
14
+
15
+ expect(getByText(title)).toBeTruthy();
16
+ expect(getByText(description)).toBeTruthy();
17
+ expect(toJSON()).toMatchSnapshot();
18
+ });
19
+ it('renders error screen with image correctly', () => {
20
+ const { toJSON, getByText, getByTestId } = renderWithTheme(
21
+ <Error title={title} description={description} image="path_to_image" />
22
+ );
23
+
24
+ expect(getByText(title)).toBeTruthy();
25
+ expect(getByText(description)).toBeTruthy();
26
+ expect(getByTestId('error-image')).toBeTruthy();
27
+ expect(toJSON()).toMatchSnapshot();
28
+ });
29
+ it('renders full screen error page correctly', () => {
30
+ const { toJSON, getByText, getByTestId } = renderWithTheme(
31
+ <Error
32
+ variant="full-screen"
33
+ title={title}
34
+ description={description}
35
+ image="path_to_image"
36
+ />
37
+ );
38
+
39
+ expect(getByText(title)).toBeTruthy();
40
+ expect(getByText(description)).toBeTruthy();
41
+ expect(getByTestId('error-image')).toBeTruthy();
42
+ expect(toJSON()).toMatchSnapshot();
43
+ });
44
+ it('handles CTA press correctly', () => {
45
+ const onCtaPress = jest.fn();
46
+ const onSecondaryCtaPress = jest.fn();
47
+ const { getByText } = renderWithTheme(
48
+ <Error
49
+ title={title}
50
+ description={description}
51
+ image="path_to_image"
52
+ ctaText="CTA Text"
53
+ onCtaPress={onCtaPress}
54
+ secondaryCtaText="Secondary CTA Text"
55
+ onSecondaryCtaPress={onSecondaryCtaPress}
56
+ />
57
+ );
58
+
59
+ const CTA = getByText('CTA Text');
60
+ fireEvent.press(CTA);
61
+ expect(onCtaPress).toBeCalledTimes(1);
62
+
63
+ const secondaryCTA = getByText('Secondary CTA Text');
64
+ fireEvent.press(secondaryCTA);
65
+ expect(onSecondaryCtaPress).toBeCalledTimes(1);
66
+ });
67
+ });
@@ -0,0 +1,223 @@
1
+ import React, { useCallback, useEffect, useState } from 'react';
2
+ import type { ReactElement } from 'react';
3
+ import type { ImageSourcePropType, ViewProps } from 'react-native';
4
+ import {
5
+ ErrorVariant,
6
+ StyledErrorButtonContainer,
7
+ StyledErrorButtonPrimary,
8
+ StyledErrorButtonSecondary,
9
+ StyledErrorContainer,
10
+ StyledErrorContent,
11
+ StyledErrorDescription,
12
+ StyledErrorImage,
13
+ StyledErrorModal,
14
+ StyledErrorTitle,
15
+ } from './StyledError';
16
+ import { useDeprecation } from '../../utils/hooks';
17
+
18
+ interface ErrorProps extends ViewProps {
19
+ /**
20
+ * @deprecated use conditional rendering instead
21
+ *
22
+ * Visibility of the error
23
+ */
24
+ visible?: boolean;
25
+ /**
26
+ * Variant of the error
27
+ */
28
+ variant?: ErrorVariant;
29
+ /**
30
+ * Title of the error
31
+ */
32
+ title: string;
33
+ /**
34
+ * Description of the error
35
+ */
36
+ description?: string;
37
+ /**
38
+ * Image of the error
39
+ */
40
+ image?: ImageSourcePropType | string;
41
+ /**
42
+ * Testing id of the component.
43
+ */
44
+ testID?: string;
45
+ /**
46
+ * Action button text
47
+ */
48
+ ctaText?: string;
49
+ /**
50
+ * Callback when the action button is pressed.
51
+ */
52
+ onCtaPress?: () => void;
53
+ /**
54
+ * Secondary button text
55
+ */
56
+ secondaryCtaText?: string;
57
+ /**
58
+ * Callback when the secondary button is pressed.
59
+ */
60
+ onSecondaryCtaPress?: () => void;
61
+ }
62
+
63
+ type onCloseCallbackType = 'cta' | 'secondaryCta' | null;
64
+
65
+ const ErrorPage = ({
66
+ variant = 'in-page',
67
+ title,
68
+ description,
69
+ image,
70
+ testID,
71
+ ctaText,
72
+ onCtaPress,
73
+ secondaryCtaText,
74
+ onSecondaryCtaPress,
75
+ ...nativeProps
76
+ }: ErrorProps): ReactElement => {
77
+ const showCta = ctaText && onCtaPress !== undefined;
78
+ const showSecondaryCta =
79
+ secondaryCtaText && onSecondaryCtaPress !== undefined;
80
+ const showButtonContainer = showCta || showSecondaryCta;
81
+ return (
82
+ <StyledErrorContainer testID={testID} themeVariant={variant}>
83
+ <StyledErrorContent {...nativeProps}>
84
+ {image && (
85
+ <StyledErrorImage
86
+ source={typeof image === 'string' ? { uri: image } : image}
87
+ testID="error-image"
88
+ />
89
+ )}
90
+
91
+ <StyledErrorTitle>{title}</StyledErrorTitle>
92
+
93
+ {description && (
94
+ <StyledErrorDescription>{description}</StyledErrorDescription>
95
+ )}
96
+ </StyledErrorContent>
97
+
98
+ {showButtonContainer && (
99
+ <StyledErrorButtonContainer>
100
+ {showCta && (
101
+ <StyledErrorButtonPrimary
102
+ variant="filled"
103
+ text={ctaText}
104
+ onPress={onCtaPress}
105
+ />
106
+ )}
107
+ {showSecondaryCta && (
108
+ <StyledErrorButtonSecondary
109
+ variant="text"
110
+ text={secondaryCtaText}
111
+ onPress={onSecondaryCtaPress}
112
+ />
113
+ )}
114
+ </StyledErrorButtonContainer>
115
+ )}
116
+ </StyledErrorContainer>
117
+ );
118
+ };
119
+ /**
120
+ * Renders error page
121
+ *
122
+ * @param {ErrorProps}
123
+ * @return {*} {ReactElement}
124
+ */
125
+ const Error = ({
126
+ visible = true,
127
+ variant = 'in-page',
128
+ title,
129
+ description,
130
+ image,
131
+ testID,
132
+ ctaText,
133
+ onCtaPress,
134
+ secondaryCtaText,
135
+ onSecondaryCtaPress,
136
+ ...nativeProps
137
+ }: ErrorProps): ReactElement => {
138
+ useDeprecation(
139
+ `Visible prop is deprecated. Use conditional rendering instead`,
140
+ visible
141
+ );
142
+
143
+ const [isVisible, setIsVisible] = useState(visible);
144
+ const [ctaPressed, setCtaPressed] = useState<onCloseCallbackType>(null);
145
+
146
+ const onCloseModal = useCallback(() => {
147
+ if (ctaPressed === 'cta' && onCtaPress) {
148
+ onCtaPress();
149
+ }
150
+ if (ctaPressed === 'secondaryCta' && onSecondaryCtaPress) {
151
+ onSecondaryCtaPress();
152
+ }
153
+ setCtaPressed(null);
154
+ }, [ctaPressed, onCtaPress, onSecondaryCtaPress]);
155
+
156
+ useEffect(() => {
157
+ setIsVisible(visible);
158
+ }, [visible]);
159
+
160
+ // These useEffect lines prevents race condition error when callback contains navigation logic
161
+ useEffect(() => {
162
+ if (ctaPressed) {
163
+ setIsVisible(false);
164
+ }
165
+ }, [ctaPressed]);
166
+
167
+ useEffect(() => {
168
+ if (!isVisible) {
169
+ onCloseModal();
170
+ }
171
+ }, [isVisible, onCloseModal]);
172
+
173
+ if (variant === 'full-screen') {
174
+ return (
175
+ <StyledErrorModal
176
+ visible={isVisible}
177
+ onRequestClose={() => setIsVisible(false)}
178
+ onDismiss={() => setIsVisible(false)}
179
+ animationType="slide"
180
+ >
181
+ <ErrorPage
182
+ variant={variant}
183
+ title={title}
184
+ description={description}
185
+ image={image}
186
+ testID={testID}
187
+ ctaText={ctaText}
188
+ onCtaPress={
189
+ onCtaPress &&
190
+ (() => {
191
+ setCtaPressed('cta');
192
+ })
193
+ }
194
+ secondaryCtaText={secondaryCtaText}
195
+ onSecondaryCtaPress={
196
+ onSecondaryCtaPress &&
197
+ (() => {
198
+ setCtaPressed('secondaryCta');
199
+ })
200
+ }
201
+ {...nativeProps}
202
+ />
203
+ </StyledErrorModal>
204
+ );
205
+ }
206
+
207
+ return (
208
+ <ErrorPage
209
+ variant={variant}
210
+ title={title}
211
+ description={description}
212
+ image={image}
213
+ testID={testID}
214
+ ctaText={ctaText}
215
+ onCtaPress={onCtaPress}
216
+ secondaryCtaText={secondaryCtaText}
217
+ onSecondaryCtaPress={onSecondaryCtaPress}
218
+ {...nativeProps}
219
+ />
220
+ );
221
+ };
222
+
223
+ export default Error;
package/src/index.ts CHANGED
@@ -32,6 +32,7 @@ import DatePicker from './components/DatePicker';
32
32
  import Divider from './components/Divider';
33
33
  import Drawer from './components/Drawer';
34
34
  import Empty from './components/Empty';
35
+ import Error from './components/Error';
35
36
  import FAB from './components/FAB';
36
37
  import Icon from './components/Icon';
37
38
  import Image from './components/Image';
@@ -90,6 +91,7 @@ export {
90
91
  Divider,
91
92
  Drawer,
92
93
  Empty,
94
+ Error,
93
95
  FAB,
94
96
  Icon,
95
97
  Image,
@@ -420,6 +420,36 @@ Object {
420
420
  "wrapperPadding": 16,
421
421
  },
422
422
  },
423
+ "error": Object {
424
+ "colors": Object {
425
+ "description": "#4d6265",
426
+ "fullScreenBackground": "#ccd2d3",
427
+ "inPageBackground": "#f6f6f7",
428
+ "title": "#001f23",
429
+ },
430
+ "fontSizes": Object {
431
+ "description": 18,
432
+ "title": 24,
433
+ },
434
+ "fonts": Object {
435
+ "description": "RebondGrotesque",
436
+ "title": "RebondGrotesque-SemiBold",
437
+ },
438
+ "sizes": Object {
439
+ "image": 176,
440
+ },
441
+ "space": Object {
442
+ "button": Object {
443
+ "margin": 16,
444
+ "padding": 16,
445
+ "wrapperHorizontalPadding": 16,
446
+ "wrapperVerticalPadding": 48,
447
+ },
448
+ "imageMarginBottom": 32,
449
+ "titleMarginBottom": 8,
450
+ "wrapperPadding": 24,
451
+ },
452
+ },
423
453
  "fab": Object {
424
454
  "colors": Object {
425
455
  "actionItemBackground": "#401960",