@eohjsc/react-native-smart-city 0.3.80 → 0.3.82

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 (31) hide show
  1. package/package.json +2 -2
  2. package/src/commons/ActionGroup/OnOffTemplate/SwitchButtonTemplate.js +63 -0
  3. package/src/commons/ActionGroup/__test__/OnOffButtonTemplate.test.js +54 -12
  4. package/src/commons/ActionGroup/__test__/SwitchButtonTemplate.test.js +93 -0
  5. package/src/commons/ActionGroup/index.js +3 -0
  6. package/src/commons/Device/ProgressBar/__test__/ProgressBar.test.js +49 -0
  7. package/src/commons/Device/ProgressBar/index.js +45 -0
  8. package/src/commons/Device/ProgressBar/styles.js +33 -0
  9. package/src/commons/Popover/__test__/index.test.js +31 -0
  10. package/src/configs/Colors.js +1 -0
  11. package/src/configs/IOPinConstants.js +92 -165
  12. package/src/screens/AddNewDevice/__test__/AddNewDevice.test.js +17 -8
  13. package/src/screens/AddNewDevice/index.js +5 -4
  14. package/src/screens/AllGateway/DetailConfigActionInternal/__test__/index.test.js +1 -1
  15. package/src/screens/AllGateway/DetailConfigActionInternal/index.js +21 -23
  16. package/src/screens/AllGateway/DeviceInternalDetail/__test__/index.test.js +8 -6
  17. package/src/screens/AllGateway/DeviceInternalDetail/index.js +2 -0
  18. package/src/screens/AllGateway/hooks/useGateway.js +30 -2
  19. package/src/screens/Device/__test__/DetailHistoryChart.test.js +34 -21
  20. package/src/screens/Device/__test__/EmergencyCountdown.test.js +34 -0
  21. package/src/screens/Device/components/SensorDisplayItem.js +3 -0
  22. package/src/screens/EmergencySetting/__test__/index.test.js +22 -0
  23. package/src/screens/SyncLGDevice/AddLGDevice.js +5 -4
  24. package/src/screens/Unit/Detail.js +8 -7
  25. package/src/screens/Unit/hook/useFavorites.js +1 -1
  26. package/src/utils/Converter/__test__/timer.test.js +7 -0
  27. package/src/utils/Functions/preloadImages.js +1 -1
  28. package/src/utils/I18n/translations/en.json +1 -0
  29. package/src/utils/I18n/translations/vi.json +1 -0
  30. package/src/utils/Utils.js +23 -12
  31. package/src/utils/__test__/Utils.test.js +4 -4
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@eohjsc/react-native-smart-city",
3
3
  "title": "React Native Smart Home",
4
- "version": "0.3.80",
4
+ "version": "0.3.82",
5
5
  "description": "TODO",
6
6
  "main": "index.js",
7
7
  "files": [
@@ -162,7 +162,7 @@
162
162
  "react-native-credit-card-input": "^0.4.1",
163
163
  "react-native-dash": "^0.0.11",
164
164
  "react-native-deep-linking": "^2.2.0",
165
- "react-native-device-info": "^5.6.3",
165
+ "react-native-device-info": "^10.3.0",
166
166
  "react-native-draggable-flatlist": "^2.6.2",
167
167
  "react-native-fast-image": "^8.3.2",
168
168
  "react-native-geocoder": "^0.5.0",
@@ -0,0 +1,63 @@
1
+ import React, { memo, useCallback, useState } from 'react';
2
+ import { Switch, View } from 'react-native';
3
+
4
+ import Text from '../../Text';
5
+ import { Colors } from '../../../configs';
6
+ import styles from './OnOffButtonTemplateStyle';
7
+ import AccessibilityLabel from '../../../configs/AccessibilityLabel';
8
+
9
+ const SwitchButtonTemplate = memo(
10
+ ({ isOn, triggerAction, actionGroup = {}, isLight = false, doAction }) => {
11
+ const [_isOn, setIsOn] = useState(isOn);
12
+
13
+ const { configuration = {}, id } = actionGroup;
14
+ const {
15
+ action_off_data,
16
+ action_on_data,
17
+ text_on,
18
+ text_off,
19
+ color_on,
20
+ color_off,
21
+ } = configuration;
22
+
23
+ const onChangeSwitch = useCallback(() => {
24
+ if (_isOn) {
25
+ setIsOn(false);
26
+ doAction(action_off_data, null);
27
+ return;
28
+ }
29
+ doAction(action_on_data, null);
30
+ setIsOn(true);
31
+ }, [action_off_data, action_on_data, doAction, _isOn]);
32
+
33
+ return (
34
+ <>
35
+ <View style={styles.barrierControlContainer}>
36
+ <Switch
37
+ trackColor={{ false: color_off, true: color_on }}
38
+ ios_backgroundColor={_isOn ? color_on : color_off}
39
+ onValueChange={onChangeSwitch}
40
+ value={_isOn}
41
+ />
42
+ <Text
43
+ style={[
44
+ styles.textBig,
45
+ { color: _isOn ? Colors.Gray8 : Colors.Gray6 },
46
+ ]}
47
+ accessibilityLabel={`${AccessibilityLabel.SENSOR_STATUS}-${id}`}
48
+ >
49
+ {_isOn ? text_on : text_off}
50
+ </Text>
51
+ </View>
52
+
53
+ {!!actionGroup.title && !isLight && (
54
+ <Text type="H3" semibold center>
55
+ {actionGroup.title}
56
+ </Text>
57
+ )}
58
+ </>
59
+ );
60
+ }
61
+ );
62
+
63
+ export default SwitchButtonTemplate;
@@ -1,8 +1,16 @@
1
1
  import React from 'react';
2
+ import { TouchableOpacity } from 'react-native';
2
3
  import { act, create } from 'react-test-renderer';
4
+
3
5
  import OnOffTemplate from '../OnOffTemplate';
4
6
  import { mockSCStore } from '../../../context/mockStore';
5
7
  import { SCProvider } from '../../../context';
8
+ import Text from '../../Text';
9
+ import { useConfigGlobalState } from '../../../iot/states';
10
+
11
+ jest.mock('../../../iot/states', () => ({
12
+ useConfigGlobalState: jest.fn(),
13
+ }));
6
14
 
7
15
  const wrapComponent = (actionGroup, mockDoAction) => (
8
16
  <SCProvider initState={mockSCStore({})}>
@@ -49,45 +57,79 @@ describe('Test OneBigButtonTemplate', () => {
49
57
  action_off_data: action_off_data,
50
58
  icon_off: 'caret-up',
51
59
  text_off: 'OFF',
60
+ config: 1,
52
61
  },
62
+ template: 'on_off_button_action_template',
63
+ id: 1,
53
64
  };
54
65
  let wrapper;
55
66
 
56
- const assertRender = async () => {
67
+ beforeEach(() => {
68
+ useConfigGlobalState.mockClear();
69
+ });
70
+
71
+ const assertRender = async (textValue) => {
57
72
  const mockDoAction = jest.fn();
58
73
 
59
74
  await act(async () => {
60
75
  wrapper = create(wrapComponent(actionGroup, mockDoAction));
61
76
  });
62
77
 
63
- const renderJson = wrapper.toJSON();
64
-
65
- expect(renderJson[1].props?.visible).toEqual(false);
66
- expect(renderJson[1]?.type).toEqual('Modal');
78
+ const instance = wrapper.root;
79
+ const texts = instance.findAllByType(Text);
80
+ expect(texts[0].props.children).toEqual(textValue);
67
81
  };
68
82
 
69
83
  it('render state on', async () => {
70
- await assertRender(true, 'ON');
84
+ useConfigGlobalState.mockImplementation(() => [
85
+ {
86
+ 1: {
87
+ value: 1,
88
+ },
89
+ },
90
+ jest.fn(),
91
+ ]);
92
+ await assertRender('ON');
71
93
  });
72
94
 
73
95
  it('render state off', async () => {
74
- await assertRender(false, 'OFF');
96
+ useConfigGlobalState.mockImplementation(() => [
97
+ {
98
+ 1: {
99
+ value: 0,
100
+ },
101
+ },
102
+ jest.fn(),
103
+ ]);
104
+ await assertRender('OFF');
75
105
  });
76
106
 
77
- const assertActionCall = async (state, action_data) => {
107
+ const assertActionCall = async (state, action_data, data2) => {
108
+ useConfigGlobalState.mockImplementation(() => [
109
+ {
110
+ 1: {
111
+ value: state,
112
+ },
113
+ },
114
+ jest.fn(),
115
+ ]);
78
116
  const mockDoAction = jest.fn();
79
117
  await act(async () => {
80
118
  wrapper = create(wrapComponent(actionGroup, mockDoAction));
81
119
  });
82
-
83
- expect(mockDoAction).not.toHaveBeenCalled();
120
+ const instance = wrapper.root;
121
+ const button = instance.findByType(TouchableOpacity);
122
+ await act(async () => {
123
+ await button.props.onPress();
124
+ });
125
+ expect(mockDoAction).toHaveBeenCalledWith(action_data, data2);
84
126
  };
85
127
 
86
128
  it('action state on', async () => {
87
- await assertActionCall(true, action_off_data);
129
+ await assertActionCall(true, action_on_data, { state: 0, value: 0 });
88
130
  });
89
131
 
90
132
  it('action state off', async () => {
91
- await assertActionCall(false, action_on_data);
133
+ await assertActionCall(false, action_off_data, { state: 1, value: 1 });
92
134
  });
93
135
  });
@@ -0,0 +1,93 @@
1
+ import React from 'react';
2
+ import { act, create } from 'react-test-renderer';
3
+
4
+ import { mockSCStore } from '../../../context/mockStore';
5
+ import { SCProvider } from '../../../context';
6
+ import SwitchButtonTemplate from '../OnOffTemplate/SwitchButtonTemplate';
7
+
8
+ const wrapComponent = (actionGroup, mockDoAction) => (
9
+ <SCProvider initState={mockSCStore({})}>
10
+ <SwitchButtonTemplate actionGroup={actionGroup} doAction={mockDoAction} />
11
+ </SCProvider>
12
+ );
13
+
14
+ jest.mock('@react-navigation/native', () => {
15
+ return {
16
+ ...jest.requireActual('@react-navigation/native'),
17
+ useFocusEffect: jest.fn(),
18
+ };
19
+ });
20
+
21
+ describe('Test SwitchButtonTemplate', () => {
22
+ const action_on_data = {
23
+ color: '#00979D',
24
+ command_prefer_over_bluetooth: true,
25
+ command_prefer_over_googlehome: false,
26
+ command_prefer_over_internet: false,
27
+ googlehome_actions: [],
28
+ icon: 'caret-up',
29
+ id: 20,
30
+ key: '5ed1d4dc-a905-47cd-b0c9-f979644bd21a',
31
+ };
32
+
33
+ const action_off_data = {
34
+ color: '#00979D',
35
+ command_prefer_over_bluetooth: true,
36
+ command_prefer_over_googlehome: false,
37
+ command_prefer_over_internet: false,
38
+ googlehome_actions: [],
39
+ icon: 'caret-up',
40
+ id: 20,
41
+ key: '5ed1d4dc-a905-47cd-b0c9-f979644bd21a',
42
+ };
43
+ const actionGroup = {
44
+ configuration: {
45
+ action_on: '5ed1d4dc-a905-47cd-b0c9-f979644bd21a',
46
+ action_on_data: action_on_data,
47
+ icon_on: 'caret-up',
48
+ text_on: 'ON',
49
+ action_off: '5ed1d4dc-a905-47cd-b0c9-f979644bd21b',
50
+ action_off_data: action_off_data,
51
+ icon_off: 'caret-up',
52
+ text_off: 'OFF',
53
+ },
54
+ };
55
+ let wrapper;
56
+
57
+ const assertRender = async () => {
58
+ const mockDoAction = jest.fn();
59
+
60
+ await act(async () => {
61
+ wrapper = create(wrapComponent(actionGroup, mockDoAction));
62
+ });
63
+
64
+ const renderJson = wrapper.toJSON();
65
+ expect(renderJson[1].props?.visible).toEqual(false);
66
+ expect(renderJson[1]?.type).toEqual('Modal');
67
+ };
68
+
69
+ it('render state on', async () => {
70
+ await assertRender(true, 'ON');
71
+ });
72
+
73
+ it('render state off', async () => {
74
+ await assertRender(false, 'OFF');
75
+ });
76
+
77
+ const assertActionCall = async (state, action_data) => {
78
+ const mockDoAction = jest.fn();
79
+ await act(async () => {
80
+ wrapper = create(wrapComponent(actionGroup, mockDoAction));
81
+ });
82
+
83
+ expect(mockDoAction).not.toHaveBeenCalled();
84
+ };
85
+
86
+ it('action state on', async () => {
87
+ await assertActionCall(true, action_off_data);
88
+ });
89
+
90
+ it('action state off', async () => {
91
+ await assertActionCall(false, action_on_data);
92
+ });
93
+ });
@@ -13,6 +13,7 @@ import ColorPickerTemplate from './ColorPickerTemplate';
13
13
  import SliderRangeTemplate from './SliderRangeTemplate';
14
14
  import OnOffSmartLock from './OnOffSmartLock/OnOffSmartLock';
15
15
  import TwoButtonTemplate from './TwoButtonTemplate';
16
+ import SwitchButtonTemplate from './OnOffTemplate/SwitchButtonTemplate';
16
17
 
17
18
  export const getActionComponent = (template) => {
18
19
  switch (template) {
@@ -46,6 +47,8 @@ export const getActionComponent = (template) => {
46
47
  return SliderRangeTemplate;
47
48
  case 'two_button_action_template':
48
49
  return TwoButtonTemplate;
50
+ case 'switch_button_action_template':
51
+ return SwitchButtonTemplate;
49
52
  default:
50
53
  return null;
51
54
  }
@@ -0,0 +1,49 @@
1
+ /* eslint-disable promise/prefer-await-to-callbacks */
2
+ import React from 'react';
3
+ import { useSelector } from 'react-redux';
4
+ import renderer, { act } from 'react-test-renderer';
5
+
6
+ import ProgressBar from '..';
7
+ import Text from '../../../Text';
8
+ import { SCProvider } from '../../../../context';
9
+ import { mockSCStore } from '../../../../context/mockStore';
10
+
11
+ jest.mock('react-redux', () => ({
12
+ ...jest.requireActual('react-redux'),
13
+ useSelector: jest.fn(),
14
+ }));
15
+
16
+ const wrapComponent = (item, data) => (
17
+ <SCProvider initState={mockSCStore({})}>
18
+ <ProgressBar item={item} data={data} />
19
+ </SCProvider>
20
+ );
21
+
22
+ describe('Test ProgressBar', () => {
23
+ let tree;
24
+ beforeEach(() => {
25
+ const localState = {
26
+ language: 'en',
27
+ };
28
+ useSelector.mockImplementation((cb) => {
29
+ return cb(localState);
30
+ });
31
+ });
32
+
33
+ it('render ProgressBar', async () => {
34
+ const item = {
35
+ label: 'Value bar',
36
+ configuration: { max_value: 100 },
37
+ value: 8,
38
+ };
39
+ const data = [{ value: 10 }];
40
+ await act(async () => {
41
+ tree = renderer.create(wrapComponent(item, data));
42
+ });
43
+ const instance = tree.root;
44
+ const texts = instance.findAllByType(Text);
45
+ expect(texts[0].props.children).toBe('Value bar');
46
+ expect(texts[1].props.children).toEqual(['Max value', ': ', 100]);
47
+ expect(texts[2].props.children).toEqual([10, '%']);
48
+ });
49
+ });
@@ -0,0 +1,45 @@
1
+ import React, { memo, useMemo } from 'react';
2
+ import { View } from 'react-native';
3
+ import * as Progress from 'react-native-progress';
4
+
5
+ import { Colors } from '../../../configs';
6
+ import { useTranslations } from '../../../hooks/Common/useTranslations';
7
+ import Text from '../../Text';
8
+ import styles from './styles';
9
+
10
+ const ProgressBar = memo(({ data = [], item }) => {
11
+ const t = useTranslations();
12
+
13
+ const maxValue = useMemo(() => {
14
+ return Number(item?.configuration?.max_value) || 60;
15
+ }, [item?.configuration?.max_value]);
16
+ const value = data.length ? data[0].value : 0;
17
+ const percent = value / maxValue; // a number between 0 and 1
18
+
19
+ return (
20
+ <View style={styles.container}>
21
+ <Text size={16} style={styles.textLabel}>
22
+ {item?.label || 'Label'}
23
+ </Text>
24
+
25
+ <Text size={16} style={styles.textMaxValue}>
26
+ {t('max_value')}: {maxValue}
27
+ </Text>
28
+
29
+ <View style={styles.wrapProgressBar}>
30
+ <Progress.Bar
31
+ progress={percent}
32
+ height={40}
33
+ width={200}
34
+ color={Colors.Blue16}
35
+ unfilledColor={Colors.Blue15}
36
+ borderWidth={0}
37
+ borderRadius={10}
38
+ />
39
+ <Text style={styles.textValue}>{value}%</Text>
40
+ </View>
41
+ </View>
42
+ );
43
+ });
44
+
45
+ export default ProgressBar;
@@ -0,0 +1,33 @@
1
+ import { StyleSheet } from 'react-native';
2
+
3
+ import { Colors } from '../../../configs';
4
+
5
+ export default StyleSheet.create({
6
+ container: {
7
+ flex: 1,
8
+ flexDirection: 'column', //column direction
9
+ marginRight: 60,
10
+ marginLeft: 60,
11
+ borderWidth: 1,
12
+ padding: 12,
13
+ borderRadius: 8,
14
+ borderColor: Colors.Gray4,
15
+ },
16
+ textLabel: {
17
+ paddingHorizontal: 16,
18
+ paddingTop: 8,
19
+ },
20
+ textMaxValue: {
21
+ textAlign: 'right',
22
+ },
23
+ wrapProgressBar: {
24
+ flex: 1,
25
+ flexDirection: 'row',
26
+ justifyContent: 'center',
27
+ alignItems: 'center',
28
+ padding: 8,
29
+ },
30
+ textValue: {
31
+ marginLeft: 8,
32
+ },
33
+ });
@@ -0,0 +1,31 @@
1
+ import React from 'react';
2
+ import { create, act } from 'react-test-renderer';
3
+ import { SCProvider } from '../../../context';
4
+ import { mockSCStore } from '../../../context/mockStore';
5
+ import PopoverComponent from '../index';
6
+ import Popover from 'react-native-popover-view';
7
+
8
+ const wrapComponent = (data) => (
9
+ <SCProvider initState={mockSCStore({})}>
10
+ <PopoverComponent {...data} />
11
+ </SCProvider>
12
+ );
13
+ const mockOnCloseComplete = jest.fn();
14
+ describe('Test PopoverComponent', () => {
15
+ let tree;
16
+
17
+ it('Test Popover', async () => {
18
+ await act(async () => {
19
+ tree = await create(
20
+ wrapComponent({ isVisible: true, onCloseComplete: mockOnCloseComplete })
21
+ );
22
+ });
23
+ const instance = tree.root;
24
+ const popover = instance.findByType(Popover);
25
+ await act(async () => {
26
+ popover.props.onCloseComplete();
27
+ });
28
+ expect(popover.props.isVisible).toEqual(true);
29
+ expect(mockOnCloseComplete).toBeCalled();
30
+ });
31
+ });
@@ -120,6 +120,7 @@ export const Colors = {
120
120
  Blue13: '#E2F0F8',
121
121
  Blue14: '#D9ECFF',
122
122
  Blue15: '#99CCFF',
123
+ Blue16: '#0059B2',
123
124
 
124
125
  //Range Volcano:
125
126
  Volcano3: '#FFBB96',