Просмотр исходного кода

feat(polls/web/native): fixed identical answers verification (#14782)

* feat(polls/web/native): fixed identical answers verification
factor2
Calinteodor 1 год назад
Родитель
Сommit
75e7f64311
Аккаунт пользователя с таким Email не найден

+ 35
- 7
react/features/base/ui/components/native/Input.tsx Просмотреть файл

@@ -1,13 +1,16 @@
1 1
 import React, { forwardRef, useCallback, useState } from 'react';
2 2
 import {
3 3
     KeyboardTypeOptions,
4
-    NativeSyntheticEvent, ReturnKeyTypeOptions,
4
+    NativeSyntheticEvent,
5
+    ReturnKeyTypeOptions,
5 6
     StyleProp,
6 7
     Text,
7 8
     TextInput,
8 9
     TextInputChangeEventData,
9
-    TextInputFocusEventData, TextInputKeyPressEventData,
10
+    TextInputFocusEventData,
11
+    TextInputKeyPressEventData,
10 12
     TextInputSubmitEditingEventData,
13
+    TextStyle,
11 14
     TouchableOpacity,
12 15
     View,
13 16
     ViewStyle
@@ -25,8 +28,16 @@ interface IProps extends IInputProps {
25 28
     autoCapitalize?: 'none' | 'sentences' | 'words' | 'characters' | undefined;
26 29
     autoFocus?: boolean;
27 30
     blurOnSubmit?: boolean | undefined;
31
+    bottomLabel?: string;
28 32
     customStyles?: ICustomStyles;
29 33
     editable?: boolean | undefined;
34
+
35
+    /**
36
+     * The id to set on the input element.
37
+     * This is required because we need it internally to tie the input to its
38
+     * info (label, error) so that screen reader users don't get lost.
39
+     */
40
+    id?: string;
30 41
     keyboardType?: KeyboardTypeOptions;
31 42
     maxLength?: number | undefined;
32 43
     minHeight?: number | string | undefined;
@@ -52,11 +63,13 @@ const Input = forwardRef<TextInput, IProps>(({
52 63
     autoCapitalize,
53 64
     autoFocus,
54 65
     blurOnSubmit,
66
+    bottomLabel,
55 67
     clearable,
56 68
     customStyles,
57 69
     disabled,
58 70
     error,
59 71
     icon,
72
+    id,
60 73
     keyboardType,
61 74
     label,
62 75
     maxLength,
@@ -106,7 +119,7 @@ const Input = forwardRef<TextInput, IProps>(({
106 119
         onSubmitEditing?.(text);
107 120
     }, [ onSubmitEditing ]);
108 121
 
109
-    return (<View style = { [ styles.inputContainer, customStyles?.container ] }>
122
+    return (<View style = { [ styles.inputContainer, customStyles?.container ] as StyleProp<ViewStyle> }>
110 123
         {label && <Text style = { styles.label }>{ label }</Text>}
111 124
         <View style = { styles.fieldContainer as StyleProp<ViewStyle> }>
112 125
             {icon && <Icon
@@ -121,6 +134,7 @@ const Input = forwardRef<TextInput, IProps>(({
121 134
                 autoFocus = { autoFocus }
122 135
                 blurOnSubmit = { blurOnSubmit }
123 136
                 editable = { !disabled }
137
+                id = { id }
124 138
                 keyboardType = { keyboardType }
125 139
                 maxLength = { maxLength }
126 140
 
@@ -145,11 +159,11 @@ const Input = forwardRef<TextInput, IProps>(({
145 159
                     clearable && styles.clearableInput,
146 160
                     customStyles?.input,
147 161
                     disabled && styles.inputDisabled,
148
-                    error && styles.inputError,
149
-                    focused && styles.inputFocused,
150 162
                     icon && styles.iconInput,
151
-                    multiline && styles.inputMultiline
152
-                ] }
163
+                    multiline && styles.inputMultiline,
164
+                    focused && styles.inputFocused,
165
+                    error && styles.inputError
166
+                ] as StyleProp<TextStyle> }
153 167
                 textContentType = { textContentType }
154 168
                 value = { typeof value === 'number' ? `${value}` : value } />
155 169
             { clearable && !disabled && value !== '' && (
@@ -163,6 +177,20 @@ const Input = forwardRef<TextInput, IProps>(({
163 177
                 </TouchableOpacity>
164 178
             )}
165 179
         </View>
180
+        {
181
+            bottomLabel && (
182
+                <View>
183
+                    <Text
184
+                        id = { `${id}-description` }
185
+                        style = { [
186
+                            styles.bottomLabel,
187
+                            error && styles.bottomLabelError
188
+                        ] }>
189
+                        { bottomLabel }
190
+                    </Text>
191
+                </View>
192
+            )
193
+        }
166 194
     </View>);
167 195
 });
168 196
 

+ 10
- 0
react/features/base/ui/components/native/inputStyles.ts Просмотреть файл

@@ -73,5 +73,15 @@ export default {
73 73
 
74 74
     clearIcon: {
75 75
         color: BaseTheme.palette.icon01
76
+    },
77
+
78
+    bottomLabel: {
79
+        ...BaseTheme.typography.labelRegular,
80
+        color: BaseTheme.palette.text02,
81
+        marginTop: BaseTheme.spacing[2]
82
+    },
83
+
84
+    bottomLabelError: {
85
+        color: BaseTheme.palette.textError
76 86
     }
77 87
 };

+ 14
- 5
react/features/polls/components/native/PollCreate.tsx Просмотреть файл

@@ -97,16 +97,26 @@ const PollCreate = (props: AbstractProps) => {
97 97
             type = { TERTIARY } />
98 98
     );
99 99
 
100
+    const pollCreateButtonsContainerStyles = Platform.OS === 'android'
101
+        ? chatStyles.pollCreateButtonsContainerAndroid : chatStyles.pollCreateButtonsContainerIos;
100 102
 
101 103
     /* eslint-disable react/jsx-no-bind */
102
-    const renderListItem = ({ index }: { index: number; }) =>
104
+    const renderListItem = ({ index }: { index: number; }) => {
105
+
106
+        const isIdenticalAnswer
107
+            = answers.slice(0, index).length === 0 ? false : answers.slice(0, index).some(prevAnswer =>
108
+                prevAnswer.name === answers[index].name
109
+                && prevAnswer.name !== '' && answers[index].name !== '');
103 110
 
104
-        // padding to take into account the two default options
105
-        (
111
+        return (
106 112
             <View
107 113
                 style = { dialogStyles.optionContainer as ViewStyle }>
108 114
                 <Input
109 115
                     blurOnSubmit = { false }
116
+                    bottomLabel = { (
117
+                        isIdenticalAnswer ? t('polls.errors.notUniqueOption', { index: index + 1 }) : '') }
118
+                    error = { isIdenticalAnswer }
119
+                    id = { `polls-answer-input-${index}` }
110 120
                     label = { t('polls.create.pollOption', { index: index + 1 }) }
111 121
                     maxLength = { CHAR_LIMIT }
112 122
                     multiline = { true }
@@ -128,8 +138,7 @@ const PollCreate = (props: AbstractProps) => {
128 138
                 }
129 139
             </View>
130 140
         );
131
-    const pollCreateButtonsContainerStyles = Platform.OS === 'android'
132
-        ? chatStyles.pollCreateButtonsContainerAndroid : chatStyles.pollCreateButtonsContainerIos;
141
+    };
133 142
 
134 143
     return (
135 144
         <View style = { chatStyles.pollCreateContainer as ViewStyle }>

+ 1
- 1
react/features/polls/functions.ts Просмотреть файл

@@ -58,7 +58,7 @@ export function hasIdenticalAnswers(currentAnswers: Array<IAnswerData>): boolean
58 58
 
59 59
     const nonEmptyCurrentAnswers = currentAnswers.filter((answer): boolean => answer.name !== '');
60 60
 
61
-    const currentAnswersSet = new Set(nonEmptyCurrentAnswers);
61
+    const currentAnswersSet = new Set(nonEmptyCurrentAnswers.map(answer => answer.name));
62 62
 
63 63
     return currentAnswersSet.size !== nonEmptyCurrentAnswers.length;
64 64
 }

Загрузка…
Отмена
Сохранить