Skip to content

Commit

Permalink
fix[CustomSelect]: Disable autocomplete tooltip on iOS in readonly mo…
Browse files Browse the repository at this point in the history
…de (#6274) (#6402)

Согласно #6205 в мобильном iOS при клике на селект, в обычном режиме (не `searchable`), в верхней части страницы появляется тултип `Автозаполнение`. Появляется иммено в этом режиме, потому что в обычном режиме мы тоже показываем `input`, но он `readonly`.
Ни одно из свойств, типа `autoComplete` и пр., не помогают, iOS их, в некотором смысле игнорирует.

В других библиотеках обычный селект просто не рендерит `input`, а довольствуется `div`.

Не хочется менять input на div, потому что это может привезти к тому, что при клике на связанный лэйбл `div` фокус не попадёт на селект.

Изменения
- решил, что лучшим решением будет спрятать инпут в режиме `readonly`. Это позволит избавится от назойливого тултипа. В то же время, это не изменит ничего во взаимодействии с селектом. Это не помешает фокусу на инпуте при клике, не помешает навигации с клавиатуры, фокус при клике на label продолжит работать. Всё потому, что у нас есть обёртка, которая всегда ловит клик и передаёт фокус инпуту.
Альтернативный вариант решения это отключение `pointer-events`. Но хотелось этого избежать, потому что Playwright тогда не может по инпуту попасть. Пока искал решенеие пришел к `VisuallyHidden`. В целом можно и c `pointer-events: none`, результат будет тот же.

В режиме `searchable` input остаётся как есть, там тултип появляется в нормальном месте.

- в тоже время добавил свойства, которые по идее тоже должны были бы помочь, такие же свойства мы используем в ChipsSelect.
- переместил эти дополнительные свойства выше, до `{...restProps}`, чтобы их мог бы задать/поменять пользователь.
  • Loading branch information
mendrew authored and actions-user committed Jan 18, 2024
1 parent fa716aa commit 9e79974
Show file tree
Hide file tree
Showing 5 changed files with 50 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,13 @@ test.describe('CustomSelect', () => {
}) => {
await mount(<CustomSelectNoMaxHeightPlayground {...componentPlaygroundProps} />);

await page.getByTestId('target-select').click();
await page
.getByTestId('target-select')
/*
* Используем force, потому что на платформе ios у селекта в обычном режиме (не searchable)
* спрятан инпут, чтобы не появлялся тултип autosuggestion на iOS при клике на инпут.
**/
.click({ force: componentPlaygroundProps.platform === 'ios' });

await expectScreenshotClippedToContent();
});
Expand Down
3 changes: 3 additions & 0 deletions packages/vkui/src/components/CustomSelect/CustomSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -857,6 +857,9 @@ export function CustomSelect<OptionInterfaceT extends CustomSelectOptionInterfac
)}
<CustomSelectInput
autoComplete="off"
autoCapitalize="none"
autoCorrect="off"
spellCheck="false"
{...restProps}
{...selectInputAriaProps}
getRef={selectInputRef}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ export const CustomSelectClearButton = ({
}: CustomSelectClearButtonProps) => {
return (
<IconButton
Component="div"
Component="button"
aria-label="Очистить поле"
onKeyDown={stopPropagation}
role="button"
type="button"
activeMode="opacity"
hoverMode="opacity"
{...restProps}
Expand Down
50 changes: 35 additions & 15 deletions packages/vkui/src/components/CustomSelect/CustomSelectInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@ import { classNames } from '@vkontakte/vkjs';
import { useAdaptivity } from '../../hooks/useAdaptivity';
import { useExternRef } from '../../hooks/useExternRef';
import { useFocusWithin } from '../../hooks/useFocusWithin';
import { usePlatform } from '../../hooks/usePlatform';
import { SizeType } from '../../lib/adaptivity';
import { getFormFieldModeFromSelectType } from '../../lib/select';
import { HasAlign, HasRef, HasRootRef } from '../../types';
import { FormField, FormFieldProps } from '../FormField/FormField';
import type { SelectType } from '../Select/Select';
import { SelectTypography } from '../SelectTypography/SelectTypography';
import { Text } from '../Typography/Text/Text';
import { VisuallyHidden } from '../VisuallyHidden/VisuallyHidden';
import styles from './CustomSelectInput.module.css';

const sizeYClassNames = {
Expand Down Expand Up @@ -59,6 +61,25 @@ export const CustomSelectInput = ({
const handleRootRef = useExternRef(getRootRef);
const focusWithin = useFocusWithin(handleRootRef);

const input = (
<Text
type="text"
{...restProps}
disabled={disabled && !fetching}
readOnly={restProps.readOnly || (disabled && fetching)}
Component="input"
normalize={false}
className={classNames(
styles['CustomSelectInput__el'],
(restProps.readOnly || (showLabelOrPlaceholder && !focusWithin)) &&
styles['CustomSelectInput__el--cursor-pointer'],
)}
getRootRef={getRef}
placeholder={children ? '' : placeholder}
/>
);

const platform = usePlatform();
return (
<FormField
Component="div"
Expand Down Expand Up @@ -92,21 +113,20 @@ export const CustomSelectInput = ({
{showLabelOrPlaceholder && title}
</SelectTypography>
</div>
<Text
{...restProps}
disabled={disabled && !fetching}
readOnly={restProps.readOnly || (disabled && fetching)}
Component="input"
normalize={false}
type="text"
className={classNames(
styles['CustomSelectInput__el'],
(restProps.readOnly || (showLabelOrPlaceholder && !focusWithin)) &&
styles['CustomSelectInput__el--cursor-pointer'],
)}
getRootRef={getRef}
placeholder={children ? '' : placeholder}
/>
{/* Чтобы отключить autosuggestion в iOS, тултипы которого начинают всплывать даже когда input
* в режиме readonly, мы оборачиваем инпут в VisuallyHidden.
* Тултипы появляются при каждом клике на input.
* смотри: https://github.com/VKCOM/VKUI/issues/6205
*
* Достаточно не дать пользователю кликнуть по инпуту.
* Делаем это только для режима read-only. Потому что проблема именно в режиме read-only.
* Обертка вокруг инпута обрабатывает клики и передаёт фокус, так что на взаимодействии с инпутом это никак не скажется.
**/}
{restProps.readOnly && platform === 'ios' ? (
<VisuallyHidden>{input}</VisuallyHidden>
) : (
input
)}
</div>
</FormField>
);
Expand Down
4 changes: 3 additions & 1 deletion packages/vkui/src/components/CustomSelect/Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@
- получения текста, введённого пользователем при поиске опций, в режиме `searchable`;
- получения информации о наличии фокуса на компоненте;
- симуляции работы с компонентом с клавиатуры.
- для получения текста, выбранной в данный момент опции используйте свойство `labelTextTestId`.
- используйте `labelTextTestId`:
- для симуляции работы с компонентом с помощью мыши, тач-устройства;
- для получения текста выбранной в данный момент опции.
- для взаимодействия с кнопкой очистки состояния компонента, которая появляется, если `CustomSelect` имеет свойство `searchable` и пользователь выбрал опцию, используйте свойство `clearButtonTestId`.
- `CustomSelect` внутри себя хранит невидимый `<select>`, для того, чтобы `CustomSelect` можно было использовать внутри формы. Для получения доступа к `<select>` используйте свойство `nativeSelectTestId`. Полезно для доступа к значению `value`, выбранной в данный момент опции.

Expand Down

0 comments on commit 9e79974

Please sign in to comment.