@eohjsc/react-native-smart-city 0.4.95 → 0.4.97

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.
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.4.95",
4
+ "version": "0.4.97",
5
5
  "description": "TODO",
6
6
  "main": "index.js",
7
7
  "files": [
@@ -196,13 +196,13 @@
196
196
  "react-native-super-grid": "^4.0.3",
197
197
  "react-native-svg": "^12.1.0",
198
198
  "react-native-toast-message": "^2.1.1",
199
- "react-native-udp": "^4.1.7",
199
+ "react-native-udp": "4.1.3",
200
200
  "react-native-version-check": "^3.4.2",
201
201
  "react-native-vlc-media-player": "^1.0.41",
202
202
  "react-native-webview": "11.22.7",
203
203
  "react-native-wheel-color-picker": "^1.2.0",
204
204
  "react-native-wheel-scrollview-picker": "^1.2.2",
205
- "react-native-wifi-reborn": "^4.11.0",
205
+ "react-native-wifi-reborn": "4.5.0",
206
206
  "react-navigation": "^2.2.0",
207
207
  "string-format": "^2.0.0",
208
208
  "sync-directory": "^5.1.7",
@@ -76,10 +76,7 @@ const OnOffTemplate = memo(({ item = {}, doAction, sensor = {} }) => {
76
76
  config_value: isOn ? 0 : 1,
77
77
  };
78
78
  }
79
-
80
- if (device_type === DEVICE_TYPE.LG_THINQ) {
81
- setTempIsOn((prev) => !prev);
82
- }
79
+ setTempIsOn((prev) => !prev);
83
80
  await doAction(action_data, data);
84
81
  updateStatusFromPusher(); // todo Bang read about this magic
85
82
 
@@ -91,10 +91,7 @@ const SliderRangeTemplate = memo(
91
91
  <View
92
92
  style={(isWidgetOrder && styles.wrapOrderItem) || styles.viewBrightness}
93
93
  >
94
- <Text type="H4" style={styles.LeftTextBrightness}>
95
- {item?.title || item?.label || t('brightness')}
96
- </Text>
97
-
94
+ <Text type="H4">{item?.title || item?.label || t('brightness')}</Text>
98
95
  <View style={styles.wrap}>
99
96
  <Slider
100
97
  step={1}
@@ -0,0 +1,68 @@
1
+ import React, { useCallback, useState } from 'react';
2
+ import { View, TouchableOpacity } from 'react-native';
3
+ import { IconOutline } from '@ant-design/icons-react-native';
4
+ import Text from '../../commons/Text';
5
+ import { AlertAction } from '../../commons';
6
+ import { useDropdownAction } from './hooks/useDropdownAction';
7
+ import { useConfigGlobalState } from '../../iot/states';
8
+ import styles from './TextBoxTemplateStyle';
9
+
10
+ import { useTranslations } from '../../hooks/Common/useTranslations';
11
+ import _TextInput from '../Form/TextInput';
12
+ import AccessibilityLabel from '../../configs/AccessibilityLabel';
13
+
14
+ const TextBoxTemplate = ({ item, doAction, isWidgetOrder }) => {
15
+ const t = useTranslations();
16
+ const { label, configuration } = item;
17
+ const { action_data, config } = configuration;
18
+ const [configValues] = useConfigGlobalState('configValues');
19
+ const [value, setValue] = useState();
20
+
21
+ const { stateAlert, hideAlertAction, onShowAlert } = useDropdownAction();
22
+
23
+ const onDone = useCallback(() => {
24
+ doAction(action_data, JSON.stringify({ value: value }));
25
+ hideAlertAction();
26
+ }, [doAction, action_data, value, hideAlertAction]);
27
+
28
+ const onInputChange = (value) => {
29
+ setValue(value);
30
+ };
31
+ return (
32
+ <View style={(isWidgetOrder && styles.wrapOrderItem) || styles.wrap}>
33
+ <View>
34
+ <Text type="H4">{label}</Text>
35
+ </View>
36
+
37
+ <View style={styles.iconAndText}>
38
+ <Text type="H4">{configValues[config.id]?.value}</Text>
39
+ <TouchableOpacity
40
+ style={styles.iconAndTextOption}
41
+ onPress={onShowAlert}
42
+ accessibilityLabel={AccessibilityLabel.TEXT_BOX_BUTTON_EDIT}
43
+ >
44
+ <IconOutline name="edit" size={20} />
45
+ </TouchableOpacity>
46
+ <AlertAction
47
+ visible={stateAlert.visible}
48
+ hideModal={hideAlertAction}
49
+ title={t('enter_parameters')}
50
+ message={stateAlert.message}
51
+ leftButtonTitle={stateAlert.leftButton}
52
+ leftButtonClick={hideAlertAction}
53
+ rightButtonTitle={stateAlert.rightButton}
54
+ rightButtonClick={onDone}
55
+ rightDisabled={!value}
56
+ >
57
+ <_TextInput
58
+ wrapStyle={styles.wrapInputStyle}
59
+ value={value}
60
+ onChange={onInputChange}
61
+ />
62
+ </AlertAction>
63
+ </View>
64
+ </View>
65
+ );
66
+ };
67
+
68
+ export default TextBoxTemplate;
@@ -0,0 +1,37 @@
1
+ import { StyleSheet } from 'react-native';
2
+ import { Colors } from '../../configs';
3
+
4
+ export default StyleSheet.create({
5
+ wrapOrderItem: {
6
+ flex: 1,
7
+ flexDirection: 'row',
8
+ justifyContent: 'space-between',
9
+ padding: 16,
10
+ },
11
+ wrap: {
12
+ padding: 16,
13
+ marginHorizontal: 16,
14
+ marginBottom: 16,
15
+ borderWidth: 1,
16
+ borderColor: Colors.Gray4,
17
+ borderRadius: 10,
18
+ flex: 1,
19
+ flexDirection: 'column',
20
+ justifyContent: 'space-between',
21
+ },
22
+ iconAndText: {
23
+ flex: 1,
24
+ flexDirection: 'row',
25
+ alignItems: 'center',
26
+ },
27
+ iconAndTextOption: {
28
+ flex: 1,
29
+ flexDirection: 'row',
30
+ alignItems: 'center',
31
+ justifyContent: 'flex-end',
32
+ },
33
+ wrapInputStyle: {
34
+ marginRight: 14,
35
+ paddingLeft: 14,
36
+ },
37
+ });
@@ -0,0 +1,130 @@
1
+ import React from 'react';
2
+ import { TouchableOpacity, View } from 'react-native';
3
+ import { act, create } from 'react-test-renderer';
4
+ import { watchMultiConfigs } from '../../../iot/Monitor';
5
+ import { AlertAction } from '../..';
6
+ import { AccessibilityLabel } from '../../../configs/Constants';
7
+ import { SCProvider } from '../../../context';
8
+ import { mockSCStore } from '../../../context/mockStore';
9
+
10
+ import TextBoxTemplate from '../TextBoxTemplate';
11
+ import _TextInput from '../../Form/TextInput';
12
+ import { Colors } from '../../../configs';
13
+
14
+ const wrapComponent = (item, mockDoAction, isWidgetOrder = false) => (
15
+ <SCProvider initState={mockSCStore({})}>
16
+ <TextBoxTemplate
17
+ item={item}
18
+ doAction={mockDoAction}
19
+ isWidgetOrder={isWidgetOrder}
20
+ />
21
+ </SCProvider>
22
+ );
23
+
24
+ jest.mock('../../../iot/Monitor');
25
+
26
+ jest.mock('../../../iot/states', () => ({
27
+ useConfigGlobalState: () => [{ 5: { value: 2 } }, null],
28
+ }));
29
+
30
+ jest.mock('@react-navigation/native', () => {
31
+ return {
32
+ ...jest.requireActual('@react-navigation/native'),
33
+ useFocusEffect: jest.fn(),
34
+ };
35
+ });
36
+
37
+ describe('Test OptionsDropdownActionTemplate', () => {
38
+ const actionData = {
39
+ color: '#00979D',
40
+ command_prefer_over_bluetooth: true,
41
+ command_prefer_over_googlehome: false,
42
+ command_prefer_over_internet: false,
43
+ googlehome_actions: [],
44
+ icon: 'caret-up',
45
+ id: 20,
46
+ key: '5ed1d4dc-a905-47cd-b0c9-f979644bd21a',
47
+ };
48
+
49
+ let displayItem;
50
+ let sensor;
51
+ let wrapper;
52
+
53
+ beforeEach(() => {
54
+ watchMultiConfigs.mockClear();
55
+ displayItem = {
56
+ label: 'label',
57
+ configuration: {
58
+ title: 'Fan Speed',
59
+ action_data: actionData,
60
+ config: { id: 5 },
61
+ icon: 'slack',
62
+ },
63
+ };
64
+ sensor = {
65
+ name: 'Sensor name',
66
+ is_managed_by_backend: false,
67
+ };
68
+ });
69
+
70
+ it('render template', async () => {
71
+ const mockDoAction = jest.fn();
72
+ await act(async () => {
73
+ wrapper = await create(wrapComponent(displayItem, mockDoAction));
74
+ });
75
+ const instance = wrapper.root;
76
+ const views = instance.findAllByType(View);
77
+
78
+ expect(views[0].props.style).toEqual({
79
+ padding: 16,
80
+ marginHorizontal: 16,
81
+ marginBottom: 16,
82
+ borderWidth: 1,
83
+ borderColor: Colors.Gray4,
84
+ borderRadius: 10,
85
+ flex: 1,
86
+ flexDirection: 'column',
87
+ justifyContent: 'space-between',
88
+ });
89
+ const buttonEdit = instance.find(
90
+ (el) =>
91
+ el.props.accessibilityLabel ===
92
+ AccessibilityLabel.TEXT_BOX_BUTTON_EDIT &&
93
+ el.type === TouchableOpacity
94
+ );
95
+ await act(async () => {
96
+ await buttonEdit.props.onPress();
97
+ });
98
+ const alertAction = instance.findByType(AlertAction);
99
+ expect(alertAction.props.visible).toBeTruthy();
100
+ expect(alertAction.props.rightDisabled).toBeTruthy();
101
+ const textInput = instance.findByType(_TextInput);
102
+ await act(async () => {
103
+ await textInput.props.onChange('123');
104
+ });
105
+ expect(alertAction.props.rightDisabled).toBeFalsy();
106
+ await act(async () => {
107
+ await alertAction.props.rightButtonClick();
108
+ });
109
+ expect(mockDoAction).toHaveBeenCalledWith(
110
+ actionData,
111
+ JSON.stringify({ value: '123' })
112
+ );
113
+ });
114
+ it('render template isWidgetOrder change position', async () => {
115
+ const mockDoAction = jest.fn();
116
+ sensor.is_managed_by_backend = true;
117
+ await act(async () => {
118
+ wrapper = await create(wrapComponent(displayItem, mockDoAction, true));
119
+ });
120
+ const instance = wrapper.root;
121
+ const views = instance.findAllByType(View);
122
+
123
+ expect(views[0].props.style).toEqual({
124
+ flex: 1,
125
+ flexDirection: 'row',
126
+ justifyContent: 'space-between',
127
+ padding: 16,
128
+ });
129
+ });
130
+ });
@@ -13,6 +13,7 @@ import SliderRangeTemplate from './SliderRangeTemplate';
13
13
  import OnOffSmartLock from './OnOffSmartLock/OnOffSmartLock';
14
14
  import TwoButtonTemplate from './TwoButtonTemplate';
15
15
  import SwitchButtonTemplate from './OnOffTemplate/SwitchButtonTemplate';
16
+ import TextBoxTemplate from './TextBoxTemplate';
16
17
 
17
18
  export const getActionComponent = (template) => {
18
19
  switch (template) {
@@ -46,6 +47,8 @@ export const getActionComponent = (template) => {
46
47
  return TwoButtonTemplate;
47
48
  case 'switch_button_action_template':
48
49
  return SwitchButtonTemplate;
50
+ case 'TextBoxTemplate':
51
+ return TextBoxTemplate;
49
52
  default:
50
53
  return null;
51
54
  }
@@ -23,6 +23,7 @@ const AlertAction = ({
23
23
  boxLeftButtonStyle,
24
24
  boxRightButtonStyle,
25
25
  transY = 0,
26
+ rightDisabled = false,
26
27
  }) => {
27
28
  const [keyboardAnim] = useState(new Animated.Value(0));
28
29
 
@@ -82,6 +83,7 @@ const AlertAction = ({
82
83
  accessibilityLabelPrefix={accessibilityLabelPrefix}
83
84
  wrapStyle={styles.wrapViewButtonStyle}
84
85
  disableKeyBoardAnimated
86
+ rightDisabled={rightDisabled}
85
87
  />
86
88
  </View>
87
89
  </Animated.View>
@@ -44,7 +44,4 @@ export default StyleSheet.create({
44
44
  marginBottom: 24,
45
45
  marginTop: 16,
46
46
  },
47
- flexContent: {
48
- // flexDirection: 'row',
49
- },
50
47
  });
@@ -0,0 +1,97 @@
1
+ import React, { memo, useState, useCallback, useEffect } from 'react';
2
+ import { View, TouchableOpacity } from 'react-native';
3
+ import { useTranslations } from '../../hooks/Common/useTranslations';
4
+ import styles from './TextBoxActionTemplateStyles';
5
+ import { Colors } from '../../configs';
6
+ import SelectActionCard from '../SelectActionCard';
7
+ import Text from '../Text';
8
+ import { useConfigGlobalState } from '../../iot/states';
9
+ import { AccessibilityLabel } from '../../configs/Constants';
10
+ import { ModalCustom } from '../Modal';
11
+ import _TextInput from '../Form/TextInput';
12
+
13
+ const SliderRangeActionTemplate = ({ device, item, onSelectAction }) => {
14
+ const t = useTranslations();
15
+ const [visible, setVisible] = useState(false);
16
+ const onClose = () => setVisible(false);
17
+ const onPress = () => setVisible(true);
18
+ const { configuration, template, title } = item;
19
+ const { config, action } = configuration;
20
+ const [configValues] = useConfigGlobalState('configValues');
21
+ const [value, setValue] = useState();
22
+
23
+ const onInputChange = (value) => {
24
+ setValue(value);
25
+ };
26
+
27
+ useEffect(() => {
28
+ const configValue = configValues[config.id]?.value;
29
+ setValue(configValue);
30
+ }, [configValues, config]);
31
+
32
+ const onPressDone = useCallback(() => {
33
+ let actionData = { value: value };
34
+ setValue(value);
35
+ onSelectAction &&
36
+ onSelectAction({
37
+ index: item.index,
38
+ action,
39
+ data: actionData,
40
+ template,
41
+ });
42
+ onClose();
43
+ }, [value, onSelectAction, item.index, action, template]);
44
+
45
+ return (
46
+ <>
47
+ <SelectActionCard onPress={onPress} action={value} title={title} />
48
+
49
+ <ModalCustom
50
+ isVisible={visible}
51
+ onBackButtonPress={onClose}
52
+ onBackdropPress={onClose}
53
+ >
54
+ <View style={styles.popoverStyle}>
55
+ <View>
56
+ <Text type="H4" bold style={styles.textWithLine}>
57
+ {t('enter_value')}
58
+ </Text>
59
+ <View style={styles.modalContent}>
60
+ <_TextInput
61
+ wrapStyle={styles.wrapInputStyle}
62
+ value={value}
63
+ onChange={onInputChange}
64
+ keyboardType={'numeric'}
65
+ />
66
+ </View>
67
+ <View style={styles.wrapButton}>
68
+ <TouchableOpacity
69
+ onPress={onClose}
70
+ accessibilityLabel={AccessibilityLabel.BUTTON_CANCEL}
71
+ >
72
+ <Text type="H4" bold color={Colors.Primary}>
73
+ {t('cancel')}
74
+ </Text>
75
+ </TouchableOpacity>
76
+ <TouchableOpacity
77
+ onPress={onPressDone}
78
+ accessibilityLabel={AccessibilityLabel.BUTTON_DONE}
79
+ disabled={!value}
80
+ >
81
+ <Text
82
+ type="H4"
83
+ bold
84
+ color={!value ? Colors.Gray : Colors.Primary}
85
+ >
86
+ {t('done')}
87
+ </Text>
88
+ </TouchableOpacity>
89
+ </View>
90
+ </View>
91
+ </View>
92
+ </ModalCustom>
93
+ </>
94
+ );
95
+ };
96
+
97
+ export default memo(SliderRangeActionTemplate);
@@ -0,0 +1,96 @@
1
+ import React, { memo, useState, useCallback, useEffect } from 'react';
2
+ import { View, TouchableOpacity } from 'react-native';
3
+ import { useTranslations } from '../../hooks/Common/useTranslations';
4
+ import styles from './TextBoxActionTemplateStyles';
5
+ import { Colors } from '../../configs';
6
+ import SelectActionCard from '../SelectActionCard';
7
+ import Text from '../Text';
8
+ import { useConfigGlobalState } from '../../iot/states';
9
+ import { AccessibilityLabel } from '../../configs/Constants';
10
+ import { ModalCustom } from '../Modal';
11
+ import _TextInput from '../Form/TextInput';
12
+
13
+ const TextBoxActionTemplate = ({ device, item, onSelectAction }) => {
14
+ const t = useTranslations();
15
+ const [visible, setVisible] = useState(false);
16
+ const onClose = () => setVisible(false);
17
+ const onPress = () => setVisible(true);
18
+ const { configuration, template, title } = item;
19
+ const { config, action } = configuration;
20
+ const [configValues] = useConfigGlobalState('configValues');
21
+ const [value, setValue] = useState();
22
+
23
+ const onInputChange = (value) => {
24
+ setValue(value);
25
+ };
26
+
27
+ useEffect(() => {
28
+ const configValue = configValues[config.id]?.value;
29
+
30
+ setValue(configValue);
31
+ }, [configValues, config]);
32
+
33
+ const onPressDone = useCallback(() => {
34
+ let actionData = { value: value };
35
+ setValue(value);
36
+ onSelectAction &&
37
+ onSelectAction({
38
+ index: item.index,
39
+ action,
40
+ data: actionData,
41
+ template,
42
+ });
43
+ onClose();
44
+ }, [value, onSelectAction, item.index, action, template]);
45
+
46
+ return (
47
+ <>
48
+ <SelectActionCard onPress={onPress} action={value} title={title} />
49
+ <ModalCustom
50
+ isVisible={visible}
51
+ onBackButtonPress={onClose}
52
+ onBackdropPress={onClose}
53
+ >
54
+ <View style={styles.popoverStyle}>
55
+ <View>
56
+ <Text type="H4" bold style={styles.textWithLine}>
57
+ {t('enter_parameters')}
58
+ </Text>
59
+ <View style={styles.modalContent}>
60
+ <_TextInput
61
+ wrapStyle={styles.wrapInputStyle}
62
+ value={value}
63
+ onChange={onInputChange}
64
+ />
65
+ </View>
66
+ <View style={styles.wrapButton}>
67
+ <TouchableOpacity
68
+ onPress={onClose}
69
+ accessibilityLabel={AccessibilityLabel.TEXT_BOX_BUTTON_CANCEL}
70
+ >
71
+ <Text type="H4" bold color={Colors.Primary}>
72
+ {t('cancel')}
73
+ </Text>
74
+ </TouchableOpacity>
75
+ <TouchableOpacity
76
+ onPress={onPressDone}
77
+ accessibilityLabel={AccessibilityLabel.TEXT_BOX_BUTTON_DONE}
78
+ disabled={!value}
79
+ >
80
+ <Text
81
+ type="H4"
82
+ bold
83
+ color={!!value ? Colors.Primary : Colors.Gray}
84
+ >
85
+ {t('done')}
86
+ </Text>
87
+ </TouchableOpacity>
88
+ </View>
89
+ </View>
90
+ </View>
91
+ </ModalCustom>
92
+ </>
93
+ );
94
+ };
95
+
96
+ export default memo(TextBoxActionTemplate);
@@ -0,0 +1,28 @@
1
+ import { StyleSheet } from 'react-native';
2
+ import { Colors } from '../../configs';
3
+
4
+ export default StyleSheet.create({
5
+ popoverStyle: {
6
+ width: '100%',
7
+ backgroundColor: Colors.White,
8
+ borderRadius: 10,
9
+ },
10
+ textWithLine: {
11
+ padding: 16,
12
+ },
13
+ modalContent: {
14
+ paddingBottom: 26,
15
+ flexDirection: 'row',
16
+ justifyContent: 'center',
17
+ alignItems: 'center',
18
+ },
19
+ wrapInputStyle: {
20
+ width: '90%',
21
+ },
22
+ wrapButton: {
23
+ flexDirection: 'row',
24
+ justifyContent: 'space-around',
25
+ alignItems: 'center',
26
+ marginBottom: 24,
27
+ },
28
+ });
@@ -0,0 +1,96 @@
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 { TouchableOpacity } from 'react-native';
6
+ import { AccessibilityLabel } from '../../../configs/Constants';
7
+ import SelectActionCard from '../../SelectActionCard';
8
+ import _TextInput from '../../Form/TextInput';
9
+ import SliderRangeActionTemplate from '../SliderRangeActionTemplate';
10
+
11
+ const mockOnSelectAction = jest.fn();
12
+
13
+ const wrapComponent = (item, params = {}) => (
14
+ <SCProvider initState={mockSCStore({})}>
15
+ <SliderRangeActionTemplate
16
+ item={item}
17
+ onSelectAction={mockOnSelectAction}
18
+ {...params}
19
+ />
20
+ </SCProvider>
21
+ );
22
+
23
+ describe('Test SliderRangeActionTemplate', () => {
24
+ let tree;
25
+ let data = {
26
+ title: 'Slider range',
27
+ template: 'slider_range_template',
28
+ configuration: {
29
+ keep_track_config: true,
30
+ config: { id: 1023 },
31
+ action: 'b498234c-6c1a-452d-a1d1-87a314c20528',
32
+ },
33
+ };
34
+
35
+ beforeEach(() => {
36
+ mockOnSelectAction.mockClear();
37
+ });
38
+
39
+ const renderOptions = async (params = {}) => {
40
+ await act(async () => {
41
+ tree = await create(wrapComponent(data, params));
42
+ });
43
+ const instance = tree.root;
44
+ const card = instance.findByType(SelectActionCard);
45
+ await act(async () => {
46
+ card.props.onPress();
47
+ });
48
+ return instance;
49
+ };
50
+
51
+ it('Test onPress Done', async () => {
52
+ const instance = await renderOptions();
53
+ const textInput = instance.findByType(_TextInput);
54
+ await act(async () => {
55
+ await textInput.props.onChange('123');
56
+ });
57
+ const touchableOpacity = instance.find(
58
+ (item) =>
59
+ item.props.accessibilityLabel === AccessibilityLabel.BUTTON_DONE &&
60
+ item.type === TouchableOpacity
61
+ );
62
+ await act(async () => {
63
+ touchableOpacity.props.onPress();
64
+ });
65
+ expect(mockOnSelectAction).toHaveBeenCalledWith({
66
+ action: 'b498234c-6c1a-452d-a1d1-87a314c20528',
67
+ data: { value: '123' },
68
+ index: undefined,
69
+ template: 'slider_range_template',
70
+ });
71
+ });
72
+
73
+ it('Test onPress Cancel', async () => {
74
+ const instance = await renderOptions();
75
+ const textInput = instance.findByType(_TextInput);
76
+ await act(async () => {
77
+ await textInput.props.onChange('');
78
+ });
79
+ const touchableDone = instance.find(
80
+ (item) =>
81
+ item.props.accessibilityLabel === AccessibilityLabel.BUTTON_DONE &&
82
+ item.type === TouchableOpacity
83
+ );
84
+ expect(touchableDone.props.disabled).toBeTruthy();
85
+
86
+ const touchableOpacity = instance.find(
87
+ (item) =>
88
+ item.props.accessibilityLabel === AccessibilityLabel.BUTTON_CANCEL &&
89
+ item.type === TouchableOpacity
90
+ );
91
+ await act(async () => {
92
+ touchableOpacity.props.onPress();
93
+ });
94
+ expect(mockOnSelectAction).not.toHaveBeenCalled();
95
+ });
96
+ });
@@ -0,0 +1,99 @@
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 { TouchableOpacity } from 'react-native';
6
+ import { AccessibilityLabel } from '../../../configs/Constants';
7
+ import SelectActionCard from '../../SelectActionCard';
8
+ import TextBoxActionTemplate from '../TextBoxActionTemplate';
9
+ import _TextInput from '../../Form/TextInput';
10
+
11
+ const mockOnSelectAction = jest.fn();
12
+
13
+ const wrapComponent = (item, params = {}) => (
14
+ <SCProvider initState={mockSCStore({})}>
15
+ <TextBoxActionTemplate
16
+ item={item}
17
+ onSelectAction={mockOnSelectAction}
18
+ {...params}
19
+ />
20
+ </SCProvider>
21
+ );
22
+
23
+ describe('Test TextBoxActionTemplate', () => {
24
+ let tree;
25
+ let data = {
26
+ title: '',
27
+ template: 'TextBoxTemplate',
28
+ configuration: {
29
+ keep_track_config: true,
30
+ config: { id: 1023 },
31
+ action: 'b498234c-6c1a-452d-a1d1-87a314c20528',
32
+ },
33
+ };
34
+
35
+ beforeEach(() => {
36
+ mockOnSelectAction.mockClear();
37
+ });
38
+
39
+ const renderOptions = async (params = {}) => {
40
+ await act(async () => {
41
+ tree = await create(wrapComponent(data, params));
42
+ });
43
+ const instance = tree.root;
44
+ const card = instance.findByType(SelectActionCard);
45
+ await act(async () => {
46
+ card.props.onPress();
47
+ });
48
+ return instance;
49
+ };
50
+
51
+ it('Test onPress Done', async () => {
52
+ const instance = await renderOptions();
53
+ const textInput = instance.findByType(_TextInput);
54
+ await act(async () => {
55
+ await textInput.props.onChange('123');
56
+ });
57
+ const touchableOpacity = instance.find(
58
+ (item) =>
59
+ item.props.accessibilityLabel ===
60
+ AccessibilityLabel.TEXT_BOX_BUTTON_DONE &&
61
+ item.type === TouchableOpacity
62
+ );
63
+ await act(async () => {
64
+ touchableOpacity.props.onPress();
65
+ });
66
+ expect(mockOnSelectAction).toHaveBeenCalledWith({
67
+ action: 'b498234c-6c1a-452d-a1d1-87a314c20528',
68
+ data: { value: '123' },
69
+ index: undefined,
70
+ template: 'TextBoxTemplate',
71
+ });
72
+ });
73
+
74
+ it('Test onPress Cancel', async () => {
75
+ const instance = await renderOptions();
76
+ const textInput = instance.findByType(_TextInput);
77
+ await act(async () => {
78
+ await textInput.props.onChange('');
79
+ });
80
+ const touchableDone = instance.find(
81
+ (item) =>
82
+ item.props.accessibilityLabel ===
83
+ AccessibilityLabel.TEXT_BOX_BUTTON_DONE &&
84
+ item.type === TouchableOpacity
85
+ );
86
+ expect(touchableDone.props.disabled).toBeTruthy();
87
+
88
+ const touchableOpacity = instance.find(
89
+ (item) =>
90
+ item.props.accessibilityLabel ===
91
+ AccessibilityLabel.TEXT_BOX_BUTTON_CANCEL &&
92
+ item.type === TouchableOpacity
93
+ );
94
+ await act(async () => {
95
+ touchableOpacity.props.onPress();
96
+ });
97
+ expect(mockOnSelectAction).not.toHaveBeenCalled();
98
+ });
99
+ });
@@ -497,6 +497,7 @@ export default {
497
497
  CONNECTED_GATEWAY_UNIT_NAME: 'CONNECTED_GATEWAY_UNIT_NAME',
498
498
  CONNECTED_GATEWAY_CHIP_NAME: 'CONNECTED_GATEWAY_CHIP_NAME',
499
499
  BUTTON_DONE: 'BUTTON_DONE',
500
+ BUTTON_CANCEL: 'BUTTON_CANCEL',
500
501
 
501
502
  // Add New Device
502
503
  ADD_NEW_DEVICE_ADD: 'ADD_NEW_DEVICE_ADD',
@@ -559,7 +560,10 @@ export default {
559
560
  NUMBER_UP_DOWN_ACTION_DONE: 'NUMBER_UP_DOWN_ACTION_DONE',
560
561
  NUMBER_ACTION_UP: 'NUMBER_ACTION_UP',
561
562
  NUMBER_ACTION_DOWN: 'NUMBER_ACTION_DOWN',
562
-
563
+ //TestBoxTemplate
564
+ TEXT_BOX_BUTTON_EDIT: 'TEXT_BOX_BUTTON_EDIT',
565
+ TEXT_BOX_BUTTON_CANCEL: 'TEXT_BOX_BUTTON_CANCEL',
566
+ TEXT_BOX_BUTTON_DONE: 'TEXT_BOX_BUTTON_DONE',
563
567
  // OnOffButtonAction
564
568
  ON_OFF_BUTTON_ACTION_TITLE: 'ON_OFF_BUTTON_ACTION_TITLE',
565
569
 
@@ -13,6 +13,8 @@ import Routes from '../../../utils/Route';
13
13
  import NewActionWrapper from './NewActionWrapper';
14
14
  import moment from 'moment';
15
15
  import { ToastBottomHelper } from '../../../utils/Utils';
16
+ import TextBoxActionTemplate from '../../../commons/OneTapTemplate/TextBoxActionTemplate';
17
+ import SliderRangeActionTemplate from '../../../commons/OneTapTemplate/SliderRangeActionTemplate';
16
18
 
17
19
  const RenderActionItem = ({ device, item, handleOnSelectAction, index, t }) => {
18
20
  item.index = index;
@@ -58,6 +60,24 @@ const RenderActionItem = ({ device, item, handleOnSelectAction, index, t }) => {
58
60
  onSelectAction={handleOnSelectAction}
59
61
  />
60
62
  );
63
+ case 'slider_range_template':
64
+ return (
65
+ <SliderRangeActionTemplate
66
+ key={item.id}
67
+ device={device}
68
+ item={item}
69
+ onSelectAction={handleOnSelectAction}
70
+ />
71
+ );
72
+ case 'TextBoxTemplate':
73
+ return (
74
+ <TextBoxActionTemplate
75
+ key={item.id}
76
+ device={device}
77
+ item={item}
78
+ onSelectAction={handleOnSelectAction}
79
+ />
80
+ );
61
81
  default:
62
82
  ToastBottomHelper.error(
63
83
  t('template_not_supported', { template: item.template }),
@@ -43,7 +43,9 @@ const NewActionWrapper = ({ name, children, canNext, onNext, nextTitle }) => {
43
43
  isShowSeparator
44
44
  wrapTitleStyle={styles.wrapTitleStyle}
45
45
  />
46
- <KeyboardAwareScrollView>{children}</KeyboardAwareScrollView>
46
+ <KeyboardAwareScrollView contentContainerStyle={styles.scroll}>
47
+ {children}
48
+ </KeyboardAwareScrollView>
47
49
  {hasNext && (
48
50
  <BottomButtonView
49
51
  style={styles.bottomButtonView}
@@ -81,4 +81,7 @@ export default StyleSheet.create({
81
81
  paddingLeft: 16,
82
82
  paddingRight: 16,
83
83
  },
84
+ scroll: {
85
+ paddingBottom: 200,
86
+ },
84
87
  });
@@ -61,7 +61,7 @@ const ChangePosition = () => {
61
61
  <View style={styles.disableSensor} />
62
62
  <View style={styles.widgetOrder}>
63
63
  <SensorDisplayItem
64
- isWidgetOrder
64
+ isWidgetOrder={true}
65
65
  item={item}
66
66
  sensor={sensor}
67
67
  offsetTitle={offsetTitle}
@@ -170,6 +170,7 @@ export const SensorDisplayItem = ({
170
170
  case 'slider_range_template':
171
171
  case 'two_button_action_template':
172
172
  case 'switch_button_action_template':
173
+ case 'TextBoxTemplate':
173
174
  return (
174
175
  <ActionGroup
175
176
  accessibilityLabel={AccessibilityLabel.DEVICE_DETAIL_ACTION_GROUP}
@@ -1469,4 +1469,6 @@ export default {
1469
1469
  when_value_is: 'When value is',
1470
1470
  template_not_supported: '"{template}" not yet supported',
1471
1471
  invited_user: 'Invited user {user}',
1472
+ enter_value: 'Enter value',
1473
+ enter_parameters: 'Enter parameters',
1472
1474
  };
@@ -1479,4 +1479,6 @@ export default {
1479
1479
  when_value_is: 'Khi giá trị là',
1480
1480
  template_not_supported: '"{template}" chưa được hỗ trợ',
1481
1481
  invited_user: 'Đã mời người dùng {user}',
1482
+ enter_value: 'Nhập giá trị',
1483
+ enter_parameters: 'Nhập thông số',
1482
1484
  };