-
Notifications
You must be signed in to change notification settings - Fork 131
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(Input, Combobox, Autocomplete): add showCleanCross prop #3594
base: next
Are you sure you want to change the base?
Conversation
8432121
to
4cf6e49
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Пример в доку Autocomplete, так как он наследует свойство инпута showCleanCross
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Тест, проверяющий что в Autocomplete работает свойство showCleanCross. Достаточно selenium теста, так как визуально всё аналогично инпуту
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Creevey-тест, проверяющий Combobox:
- появление крестика при вводе в инпут
- очистка при клике на крестик
- фокусировка по табу на крестике
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Пример в доку ComboBox
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
История для теста Combobox
@@ -446,6 +453,20 @@ export class Input extends React.Component<InputProps, InputState> { | |||
|
|||
return ( | |||
<InputLayout | |||
clearInput={() => { | |||
if (this.props.onValueChange) { | |||
this.props.onValueChange(''); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
для управляемого инпута и для оповещения при неуправляемом
if (this.props.onValueChange) { | ||
this.props.onValueChange(''); | ||
} | ||
if (this.input) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
для неуправляемого инпута
<Input showCleanCross size="small" value={valueSmall} onValueChange={setValueSmall} data-tid="small" /> | ||
<Input showCleanCross size="medium" value={valueMedium} onValueChange={setValueMedium} data-tid="medium" /> | ||
<Input showCleanCross size="large" value={valueLarge} onValueChange={setValueLarge} data-tid="large" /> | ||
</Gapped> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Кажется, можно оставить скриншоты только для размеров, а функциональность можно проверить через RTL.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Функциональность я тоже решила проверить скриншотами, так как:
а) нужно проверить обводку крестика по табу (и в этом же тесте удобно проверить что при последующем нажатии enter происходит очистка поля)
б) мне в целом не очень хочется плодить тесты, связанные с одним и тем же пропом.
Но твое предложение и мой пункт б) натолкнули меня на мысль, что стоит сделать один скриншотный тест, который в целом будет проверять работу пропа с крестиком: размеры, работу на по-разному управляемых контролах и фокус по табу.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Сейчас получается, что скриншот обводки по фокусу есть только для среднего размера, если поедет маленький или большой, то не увидим.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
В целом всё хорошо, на мой взгляд. Единственное, кажется что много избыточных комментариев. Практически везде, где они есть, и так понятно, что происходит, исходя из контекста и названий переменных.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
У меня замечаний, кроме тех двух нет. Можно Мишу звать.
После Роминого ревью:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
еще общее замечание:
в заголовке есть Input и Combobox, но фича добавить еще и в Autocomplete
и странная работа с фокусировкой крестика:
- он фокусируется мышкой, рисуется обводка -- зачем?
обычно достаточно показывать рамку при фокусировке с клавиатуры
browser_SKA5YzKGo3.mp4
@@ -24,7 +24,7 @@ export interface CurrencyInputProps | |||
extends Pick<AriaAttributes, 'aria-label'>, | |||
CommonProps, | |||
Override< | |||
InputProps, | |||
Omit<InputProps, 'showCleanCross'>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
хочется понять, почему в CurrencyInput не добавили крестик?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Решили с Андреем что он не нужен
@@ -444,8 +451,23 @@ export class Input extends React.Component<InputProps, InputState> { | |||
<FocusControlWrapper onBlurWhenDisabled={this.resetFocus}>{this.getInput(inputProps)}</FocusControlWrapper> | |||
); | |||
|
|||
const clearInputAndFocusInput = () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
давай перенесем этот метод в методы класса и сдеаем его private handleClearInput
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Перенесла
return ( | ||
<InputLayout | ||
clearInput={clearInputAndFocusInput} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
clearInput -- это callback, давай его назовем onClearInput
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Переименовала
return ( | ||
<InputLayout | ||
clearInput={clearInputAndFocusInput} | ||
showCleanCross={showCleanCross && this.state.needsShowCleanCross} | ||
onCrossBlur={() => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
onCrossBlur лучше назвать по аналогии onCleanCrossBlur
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Переименовала
@@ -532,6 +558,7 @@ export class Input extends React.Component<InputProps, InputState> { | |||
private handleFocus = (event: React.FocusEvent<HTMLInputElement>) => { | |||
this.setState({ | |||
focused: true, | |||
needsShowCleanCross: !!this.input?.value, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Решила заменить на такое: !!this.input?.value || this.input?.value === '0'
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
а может ли случиться ситуация что в input.value будет число 0 ?
со строкой то все будет работать нормально
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
} | ||
}; | ||
|
||
const [focusedByTab, setFocusedByTab] = React.useState(false); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
давай явно затипизируем useState -- это вообще лучше всегда делать, чтобы более сильно типизировать и ускорять компиляцию ts
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Добавила
size?: SizeProp; | ||
} | ||
|
||
export const CleanCrossIcon: React.FunctionComponent<CleanCrossIconProps> = ({ size = 'small', style, ...rest }) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
не вижу автотестов на CleanCrossIcon
лучше добавить, на react-testing-library
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Добавила
rest.disabled && styles.rootDisabled(theme), | ||
getSizeClassName(size), | ||
)} | ||
style={{ ...style }} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
зачем тут { ...style } ?
можно просто style={style}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Убрала
}; | ||
const handleBlur = () => setFocusedByTab(false); | ||
|
||
const tabIndex = rest.disabled ? -1 : 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
tabIndex можно заинлайнить, нет практического смысла в этой переменной
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Согласна
@@ -0,0 +1,13 @@ | |||
import React from 'react'; | |||
import { XIcon16Light, XIcon20Light, XIcon24Regular } from '@skbkontur/icons/icons/XIcon'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Сделала
return ( | ||
<CommonWrapper {...rest}> | ||
<button | ||
tabIndex={tabIndex} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Для button
нам не нужен tabindex, поскольку он интерактивный
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Предлагаю указать tabindex="-1"
, чтобы кнпока фокусилась и не попадалась скринридеру.
Иначе переживаю, что мы очень усложним ввод с клавиатуры лишним табом — с учетом тех привычек, которые есть у пользователей
Screen.Recording.2025-02-18.at.15.09.23.mov
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Я сделал небольшую демку с нативным input[type=search] с крестиком, где видно, что крестик не фокусится с клавиатуры.
И переадресовал вопрос ребятам из кружка доступности!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Кружок доступности предложил, а мы с Мишей и Егором заапрувили: от таба избавляемся и оставляем tabIndex={-1}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
И type="button"
:)
@@ -53,6 +53,9 @@ export interface InputProps | |||
Override< | |||
React.InputHTMLAttributes<HTMLInputElement>, | |||
{ | |||
/** Устанавливает иконку крестика, при нажатии на который инпут очищается. */ | |||
showCleanCross?: boolean; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
А давай уберем слово show
по аналогии с другими пропами. Например, у Input
есть leftIcon
и rightIcon
без этого слова
@@ -149,10 +152,12 @@ export interface InputState { | |||
blinking: boolean; | |||
focused: boolean; | |||
needsPolyfillPlaceholder: boolean; | |||
needsShowCleanCross: boolean; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Кажется, эта штука отвечает за показ, а не за needs
. Я думаю, что правильней назвать это cleanCrossShowed
Проблема
Необходимо отрисовывать в инпуте крестик и очищать значение по клику на него.
Решение
Добавила пропы в Input и Combobox, отнаследовала проп в Autocomplete, написала скриншотные тесты и добавила примеры в документацию.
Ссылки
Задача
Гайд для Input с крестикой очистки
Тред в маттермосте с обсуждением дизайна
Чек-лист перед запросом ревью
Добавлены тесты на все изменения
✅ unit-тесты для логики
✅ скриншоты для верстки и кросс-браузерности
⬜ нерелевантно
Добавлена (обновлена) документация
✅ styleguidist для пропов и примеров использования компонентов
⬜ jsdoc для утилит и хелперов
⬜ комментарии для неочевидных мест в коде
⬜ прочие инструкции (
README.md
,contributing.md
и др.)⬜ нерелевантно
Изменения корректно типизированы
✅ без использования
any
(см. PR2856
)⬜ нерелевантно
Прочее
✅ все тесты и линтеры на CI проходят
✅ в коде нет лишних изменений
✅ заголовок PR кратко и доступно отражает суть изменений (он попадет в changelog)