Skip to content
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

[Tooltip] В тултипе с триггером hover не отрабатывает handleMouseLeave, когда выбираешь айтем в боксе или дату в datepicker #898

Closed
paatrofimov opened this issue Nov 9, 2018 · 7 comments · Fixed by #1177
Assignees

Comments

@paatrofimov
Copy link
Contributor

paatrofimov commented Nov 9, 2018

Если при наведении на бокс, триггерится тултип, и после выбора айтема тултип тоже должен триггериться (например, сменилась валидация), то после выбора этого айтема, тултип не исчезает, хотя hover off по факту произошел.

ezgif-1-33e1729ff2de

Код, с которым можно воспроизвести.

let getItems = q =>
  Promise.resolve(
    [
      { value: 1, label: 'First' },
    ].filter(
      x =>
        x.label.toLowerCase().includes(q.toLowerCase()) ||
        x.value.toString(10) === q
    )
  );

let initialState = {
  selected: null,
  error: true
};

let handleChange = (_, item) => setState({ selected: item, error: item.value === 1 ? true : false });

let handleFocus = () => setState({ error: false });

<Tooltip
  closeButton={false}
  render={() => !state.selected ? 'Item must be selected!' : state.selected.value === 1 ? 'Validation on FirstItem' : null}
  trigger={state.error ? 'hover' : 'closed'}
>
  <ComboBox
    error={state.error}
    getItems={getItems}
    onChange={handleChange}
    onFocus={handleFocus}
    placeholder="Enter number"
    value={state.selected}
  />
</Tooltip>;

Пример того, как проявляется в форме.
https://www.screencast.com/t/mfK8BljrNjq

@21alexander21
Copy link
Contributor

Потому что mouseleave никогда не сработает при таком сценарии

@paatrofimov
Copy link
Contributor Author

А делать что? Мы тултип как-то неправильно настраиваем? На гифке из скринкаста же, наверное, не все хорошо выглядит?
Я не ковырял, внутренности попапа, чтоб выяснить, когда срабатывает mouseleave, но мне кажется, что очевидно, что hover off логически тут должен произойти, потому что это то, что я делаю курсором.

@paatrofimov
Copy link
Contributor Author

@wKich такое написал

Там кажется проблема в том что выпадашка это ещё один портал, который находится "внутри" контейнера тултипа, но при выборе элемента, этот портал пропадает и handleMouseLeave не срабатывает

@21alexander21
Copy link
Contributor

Согласен, что выглядит не очень хорошо. Закрывать тултип надо руками на onchange или как-то еще. mouseleave - это частный случай mousemove, которого не происходит на выпадашке, т.к. она закрывается

@paatrofimov
Copy link
Contributor Author

Ну то есть вы не будете пробовать это лечить внутри коробки? )

@21alexander21
Copy link
Contributor

Это известная проблема #646 , решения пока нет, но мы будем его искать. А я лишь помогаю тебе разобраться в ситуации

@k-g-a
Copy link
Contributor

k-g-a commented Feb 14, 2019

Провел небольшое исследование. Ситуация следующая:

  • если использовать в Combobox/Select disablePortal=true: mouseleave не сработает, т.к. выпавшее меню фактически является дочерним элементом кнопки; в такой ситуации это не баг, а ожидаемое поведение;

Во всех последующих сценариях будем считать, что disablePortal=false. В этом случае портал не является потомком кнопки (combobox'а/select'а) и вообще-то mouseleave должен отрабатывать.

  • если используется 15-ый react: портал не настоящий, и баг не воспроизводится; когда пользователь ведет курсор мыши к пунктам меню - честно срабатывает mouseleave, toolip закрывается в этот момент и после выбора не открывается, пока не наведешь обратно на кнопку;
  • если используется 16-ый react: портал настоящий, они имитируют поведение детей портала, как будто бы это настоящие дети (пруф); вообще, я бы не назвал это expected behaviour, но видимо они сознательно решили это сделать, чтобы упростить жизнь тем, кто не в теме (очевидно, что воспроизвести такое поведение самим тяжелее, чем наоборот от него отказаться).

Чтобы отказаться от такого поведения, нужно вместо событий react'а получить ref на span tooltip'а, оборачивающий цель, и использовать обычные события через addEventListener.
Но, как обычно, есть нюансы: по факту всплывающие подсказки реализованы через компонент Popup, который принимает anchorElement и рендериться вокруг него, либо создает свою обертку, если anchorElement - не instanceof HTMLElement или не React.isValidElement. Соответственно, есть три варианта решения проблемы:

  • починить во всех местах использования (Hint, Tooltip, TokeInput): верстка не поменяется, пользователи не заметят, но править одну проблему в 3 местах - путь к дьяволу; стоит обратить внимание, что tooltip еще имеет варианты: useWrapper=true/false.
  • добавить обертку в Popup и починить на ней: легко сделать и конкретно эта логика соберется в одном месте, но верстка поменяется (+1 "лишний" элемент), тесты могут поломаться (а snapshot'ные - точно сломаются);
  • порефакторить Popup: там какие-то непрозрачные ветки вокруг anchorElement/ref'ов/обработчиков, и в каких-то случаях уже вешаются события на anchorElement; есть ощущение, что если логику сделать чуть более линейной, можно будет реализовать второй пункт без недостатков.

Учитывая, что popup - внутренний компонент, и изменения в нем не должны коснуться пользователей, я пока попробую 3ий вариант. @wKich @zhzz - может есть какие-то нюансы, которые стоит учесть?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

Successfully merging a pull request may close this issue.

5 participants