@hero-design/rn 8.29.4 → 8.30.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,47 @@
1
+ import React from 'react';
2
+ import { fireEvent } from '@testing-library/react-native';
3
+ import renderWithTheme from '../../../testHelpers/renderWithTheme';
4
+ import Rate from '..';
5
+
6
+ const ratingData = [
7
+ { value: 1 },
8
+ { value: 2 },
9
+ { value: 3 },
10
+ { value: 4 },
11
+ { value: 5 },
12
+ ];
13
+
14
+ describe('Rate', () => {
15
+ it('renders correctly', () => {
16
+ const onChange = jest.fn();
17
+ const { toJSON, getByTestId } = renderWithTheme(
18
+ <Rate value={3} onChange={onChange} options={ratingData} />
19
+ );
20
+
21
+ fireEvent.press(getByTestId('1'));
22
+ expect(onChange).toHaveBeenCalled();
23
+ expect(toJSON()).toMatchSnapshot();
24
+ });
25
+
26
+ it('renders correctly it is readonly', () => {
27
+ const onChange = jest.fn();
28
+ const { getByTestId, toJSON } = renderWithTheme(
29
+ <Rate readonly value={3} onChange={onChange} options={ratingData} />
30
+ );
31
+
32
+ fireEvent.press(getByTestId('1'));
33
+ expect(onChange).toBeCalledTimes(0);
34
+ expect(toJSON()).toMatchSnapshot();
35
+ });
36
+
37
+ it('renders correctly when disabled', () => {
38
+ const onChange = jest.fn();
39
+ const { getByTestId, toJSON } = renderWithTheme(
40
+ <Rate disabled value={3} onChange={onChange} options={ratingData} />
41
+ );
42
+
43
+ fireEvent.press(getByTestId('1'));
44
+ expect(onChange).toBeCalledTimes(0);
45
+ expect(toJSON()).toMatchSnapshot();
46
+ });
47
+ });
@@ -0,0 +1,90 @@
1
+ import React, { useEffect, useMemo, useRef } from 'react';
2
+ import { Animated, Platform, Pressable, ViewProps } from 'react-native';
3
+ import Icon from '../Icon';
4
+ import { StyledIconWrapper, StyledWrapper } from './StyledRate';
5
+
6
+ export interface RateOption<T extends string | number> {
7
+ value: T;
8
+ }
9
+
10
+ export interface RateProps<T extends string | number> extends ViewProps {
11
+ /**
12
+ * Options for rate component.
13
+ */
14
+ options: RateOption<T>[];
15
+ /**
16
+ * Value of the component.
17
+ */
18
+ value?: T;
19
+ /**
20
+ * Callback when value changes.
21
+ */
22
+ onChange?: (value: T) => void;
23
+ /**
24
+ * Whether the component is readonly.
25
+ */
26
+ readonly?: boolean;
27
+ /**
28
+ * Whether the component is disabled.
29
+ */
30
+ disabled?: boolean;
31
+ }
32
+
33
+ const Rate = <T extends string | number>({
34
+ options,
35
+ value,
36
+ onChange,
37
+ readonly = false,
38
+ disabled = false,
39
+ ...otherProps
40
+ }: RateProps<T>) => {
41
+ const valueIndex = useMemo(
42
+ () => options.findIndex((item) => item.value === value),
43
+ [value, options]
44
+ );
45
+
46
+ const animatedValue = useRef(new Animated.Value(0)).current;
47
+
48
+ const scale = animatedValue.interpolate({
49
+ inputRange: [0, 1],
50
+ outputRange: [0.8, 1],
51
+ });
52
+
53
+ useEffect(() => {
54
+ animatedValue.setValue(0);
55
+
56
+ Animated.spring(animatedValue, {
57
+ toValue: 1,
58
+ useNativeDriver: Platform.OS !== 'web',
59
+ }).start();
60
+ }, [value, animatedValue]);
61
+
62
+ return (
63
+ <StyledWrapper {...otherProps}>
64
+ {options.length > 0 &&
65
+ options.map((item, index) => (
66
+ <Pressable
67
+ key={item.value}
68
+ disabled={disabled || readonly}
69
+ onPress={() => onChange?.(item.value)}
70
+ testID={item.value.toString()}
71
+ >
72
+ <StyledIconWrapper
73
+ style={
74
+ valueIndex === index && {
75
+ transform: [{ scale }],
76
+ }
77
+ }
78
+ >
79
+ <Icon
80
+ icon={index <= valueIndex ? 'star' : 'star-outlined'}
81
+ intent={disabled ? 'disabled-text' : 'primary'}
82
+ />
83
+ </StyledIconWrapper>
84
+ </Pressable>
85
+ ))}
86
+ </StyledWrapper>
87
+ );
88
+ };
89
+
90
+ export default Rate;
package/src/index.ts CHANGED
@@ -55,6 +55,7 @@ import TimePicker from './components/TimePicker';
55
55
  import Toast from './components/Toast';
56
56
  import Toolbar from './components/Toolbar';
57
57
  import Typography from './components/Typography';
58
+ import Rate from './components/Rate';
58
59
  import RefreshControl from './components/RefreshControl';
59
60
  import RichTextEditor from './components/RichTextEditor';
60
61
  import PageControl from './components/PageControl';
@@ -117,6 +118,7 @@ export {
117
118
  Toast,
118
119
  Toolbar,
119
120
  Typography,
121
+ Rate,
120
122
  RefreshControl,
121
123
  RichTextEditor,
122
124
  };
@@ -690,6 +690,15 @@ Object {
690
690
  "groupTopMargin": 4,
691
691
  },
692
692
  },
693
+ "rate": Object {
694
+ "colors": Object {
695
+ "iconStroke": "#401960",
696
+ },
697
+ "spaces": Object {
698
+ "iconWrapperMarginRight": 4,
699
+ "iconWrapperPadding": 8,
700
+ },
701
+ },
693
702
  "refreshControl": Object {
694
703
  "colors": Object {
695
704
  "indicator": "#401960",
@@ -0,0 +1,16 @@
1
+ import type { GlobalTheme } from '../global';
2
+
3
+ const getRateTheme = (theme: GlobalTheme) => {
4
+ const colors = {
5
+ iconStroke: theme.colors.primary,
6
+ };
7
+
8
+ const spaces = {
9
+ iconWrapperPadding: theme.space.small,
10
+ iconWrapperMarginRight: theme.space.xsmall,
11
+ };
12
+
13
+ return { colors, spaces };
14
+ };
15
+
16
+ export default getRateTheme;
@@ -27,6 +27,7 @@ import getPageControlTheme from './components/pageControl';
27
27
  import getPinInputTheme from './components/pinInput';
28
28
  import getProgressTheme from './components/progress';
29
29
  import getRadioTheme from './components/radio';
30
+ import getRateTheme from './components/rate';
30
31
  import getRefreshControlTheme from './components/refreshControl';
31
32
  import getRichTextEditorTheme from './components/richTextEditor';
32
33
  import getSectionHeadingTheme from './components/sectionHeading';
@@ -76,6 +77,7 @@ type Theme = GlobalTheme & {
76
77
  pinInput: ReturnType<typeof getPinInputTheme>;
77
78
  progress: ReturnType<typeof getProgressTheme>;
78
79
  radio: ReturnType<typeof getRadioTheme>;
80
+ rate: ReturnType<typeof getRateTheme>;
79
81
  refreshControl: ReturnType<typeof getRefreshControlTheme>;
80
82
  richTextEditor: ReturnType<typeof getRichTextEditorTheme>;
81
83
  sectionHeading: ReturnType<typeof getSectionHeadingTheme>;
@@ -131,6 +133,7 @@ const getTheme = (
131
133
  pinInput: getPinInputTheme(globalTheme),
132
134
  progress: getProgressTheme(globalTheme),
133
135
  radio: getRadioTheme(globalTheme),
136
+ rate: getRateTheme(globalTheme),
134
137
  refreshControl: getRefreshControlTheme(globalTheme),
135
138
  richTextEditor: getRichTextEditorTheme(globalTheme),
136
139
  sectionHeading: getSectionHeadingTheme(globalTheme),
@@ -10,12 +10,15 @@ jest.mock('react-native-vector-icons', () => ({
10
10
  jest.mock('react-native', () => {
11
11
  const RN = jest.requireActual('react-native');
12
12
 
13
- RN.Animated.timing = () => ({
13
+ const mockedAnimatedFunctions = {
14
14
  start: () => jest.fn(),
15
15
  stop: () => jest.fn(),
16
16
  _isUsingNativeDriver: () => jest.fn(),
17
17
  _startNativeLoop: () => jest.fn(),
18
- });
18
+ };
19
+
20
+ RN.Animated.timing = () => mockedAnimatedFunctions;
21
+ RN.Animated.spring = () => mockedAnimatedFunctions;
19
22
 
20
23
  return RN;
21
24
  });
@@ -0,0 +1,13 @@
1
+ /// <reference types="react" />
2
+ import { Animated, View } from 'react-native';
3
+ declare const StyledWrapper: import("@emotion/native").StyledComponent<import("../Box").BoxProps & {
4
+ theme?: import("@emotion/react").Theme | undefined;
5
+ as?: import("react").ElementType<any> | undefined;
6
+ }, {}, {}>;
7
+ declare const StyledIconWrapper: import("@emotion/native").StyledComponent<Animated.AnimatedProps<import("react-native").ViewProps & import("react").RefAttributes<View>> & {
8
+ children?: import("react").ReactNode;
9
+ } & {
10
+ theme?: import("@emotion/react").Theme | undefined;
11
+ as?: import("react").ElementType<any> | undefined;
12
+ }, {}, {}>;
13
+ export { StyledWrapper, StyledIconWrapper };
@@ -0,0 +1,28 @@
1
+ import { ViewProps } from 'react-native';
2
+ export interface RateOption<T extends string | number> {
3
+ value: T;
4
+ }
5
+ export interface RateProps<T extends string | number> extends ViewProps {
6
+ /**
7
+ * Options for rate component.
8
+ */
9
+ options: RateOption<T>[];
10
+ /**
11
+ * Value of the component.
12
+ */
13
+ value?: T;
14
+ /**
15
+ * Callback when value changes.
16
+ */
17
+ onChange?: (value: T) => void;
18
+ /**
19
+ * Whether the component is readonly.
20
+ */
21
+ readonly?: boolean;
22
+ /**
23
+ * Whether the component is disabled.
24
+ */
25
+ disabled?: boolean;
26
+ }
27
+ declare const Rate: <T extends string | number>({ options, value, onChange, readonly, disabled, ...otherProps }: RateProps<T>) => JSX.Element;
28
+ export default Rate;
package/types/index.d.ts CHANGED
@@ -42,8 +42,9 @@ import TimePicker from './components/TimePicker';
42
42
  import Toast from './components/Toast';
43
43
  import Toolbar from './components/Toolbar';
44
44
  import Typography from './components/Typography';
45
+ import Rate from './components/Rate';
45
46
  import RefreshControl from './components/RefreshControl';
46
47
  import RichTextEditor from './components/RichTextEditor';
47
48
  import PageControl from './components/PageControl';
48
- export { theme, getTheme, useTheme, scale, ThemeProvider, ThemeSwitcher, withTheme, swagSystemPalette, swagDarkSystemPalette, workSystemPalette, jobsSystemPalette, walletSystemPalette, eBensSystemPalette, Accordion, Alert, Attachment, Avatar, useAvatarColors, Badge, BottomNavigation, BottomSheet, Box, Button, Calendar, Card, Carousel, Collapse, Checkbox, ContentNavigator, DatePicker, Divider, Drawer, Empty, Error, FAB, Icon, Image, List, PinInput, Progress, PageControl, Skeleton, Slider, Spinner, Swipeable, Radio, SectionHeading, Select, Success, Switch, Tabs, Tag, TextInput, TimePicker, Toast, Toolbar, Typography, RefreshControl, RichTextEditor, };
49
+ export { theme, getTheme, useTheme, scale, ThemeProvider, ThemeSwitcher, withTheme, swagSystemPalette, swagDarkSystemPalette, workSystemPalette, jobsSystemPalette, walletSystemPalette, eBensSystemPalette, Accordion, Alert, Attachment, Avatar, useAvatarColors, Badge, BottomNavigation, BottomSheet, Box, Button, Calendar, Card, Carousel, Collapse, Checkbox, ContentNavigator, DatePicker, Divider, Drawer, Empty, Error, FAB, Icon, Image, List, PinInput, Progress, PageControl, Skeleton, Slider, Spinner, Swipeable, Radio, SectionHeading, Select, Success, Switch, Tabs, Tag, TextInput, TimePicker, Toast, Toolbar, Typography, Rate, RefreshControl, RichTextEditor, };
49
50
  export * from './types';
@@ -0,0 +1,11 @@
1
+ import type { GlobalTheme } from '../global';
2
+ declare const getRateTheme: (theme: GlobalTheme) => {
3
+ colors: {
4
+ iconStroke: string;
5
+ };
6
+ spaces: {
7
+ iconWrapperPadding: number;
8
+ iconWrapperMarginRight: number;
9
+ };
10
+ };
11
+ export default getRateTheme;
@@ -25,6 +25,7 @@ import getPageControlTheme from './components/pageControl';
25
25
  import getPinInputTheme from './components/pinInput';
26
26
  import getProgressTheme from './components/progress';
27
27
  import getRadioTheme from './components/radio';
28
+ import getRateTheme from './components/rate';
28
29
  import getRefreshControlTheme from './components/refreshControl';
29
30
  import getRichTextEditorTheme from './components/richTextEditor';
30
31
  import getSectionHeadingTheme from './components/sectionHeading';
@@ -72,6 +73,7 @@ declare type Theme = GlobalTheme & {
72
73
  pinInput: ReturnType<typeof getPinInputTheme>;
73
74
  progress: ReturnType<typeof getProgressTheme>;
74
75
  radio: ReturnType<typeof getRadioTheme>;
76
+ rate: ReturnType<typeof getRateTheme>;
75
77
  refreshControl: ReturnType<typeof getRefreshControlTheme>;
76
78
  richTextEditor: ReturnType<typeof getRichTextEditorTheme>;
77
79
  sectionHeading: ReturnType<typeof getSectionHeadingTheme>;