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

chore(Hint, Tooltip): unify positioning #3370

Closed
wants to merge 20 commits into from

Conversation

SchwJ
Copy link
Member

@SchwJ SchwJ commented Mar 12, 2024

Проблема

В рамках работы над задачами #606 (Hint: Странно себя ведет у края окна браузера) и #1610 (Hint: изменять положение если не влезает на экран) стало ясно, что в Hint и Tooltip выбор позиции элемента организован по-разному (в одинаковых условиях получается разное поведение) и, в обоих случаях не очень логично.

Решение

Изменим порядок, в котором должны проверяться позиции расположения Hint'а и Tooltip'а.

Алгоритм теперь таков:

Если была указана приоритетная позиция, то пытаемся разместить в нее,
Если не удалось - проверяем её "соседей" - позиции на той же стороне в порядке center, left, right (для сторон top и bottom) и middle, top, bottom (для сторон left и right).
Если они не подходят - проверяем остальные стороны в цикле (на картинке) начиная со следующей за стороной приоритетной позиции
image
внутри каждой стороны проверяем позиции с выравниванием в порядке center, left, right (для сторон top и bottom) и middle, top, bottom (для сторон left и right).

В результате цикл проверки позиций будет таков (если разрешены все возможные позиции):
image

Ссылки

#1696 -- Hint, Tooltip: унифицировать и изменить порядок выбора положения элемента при его "непомещении" в приоритетную позицию

Чек-лист перед запросом ревью

  1. Добавлены тесты на все изменения
    ⬜ unit-тесты для логики
    ⬜ скриншоты для верстки и кросс-браузерности
    ⬜ нерелевантно

  2. Добавлена (обновлена) документация
    ⬜ styleguidist для пропов и примеров использования компонентов
    ⬜ jsdoc для утилит и хелперов
    ⬜ комментарии для неочевидных мест в коде
    ⬜ прочие инструкции (README.md, contributing.md и др.)
    ⬜ нерелевантно

  3. Изменения корректно типизированы
    ✅ без использования any (см. PR 2856)
    ⬜ нерелевантно

  4. Прочее
    ⬜ все тесты и линтеры на CI проходят
    ✅ в коде нет лишних изменений
    ✅ заголовок PR кратко и доступно отражает суть изменений (он попадет в changelog)

@SchwJ SchwJ force-pushed the hint-tooltip-unify-positioning branch from 85a8c28 to 0c3c8e8 Compare March 18, 2024 10:33
Copy link
Member

@zhzz zhzz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Обсудили в офлайне:

  1. Не хватает тестов на фиче-флаг
  2. Жестко форсить логику позиционирования не нужно
  3. Возможно, стоит объединить фиче-флаги для хинта и попапа
  4. Возможно, стоит унифицировать и проп pos для хинта и тултипа

@SchwJ SchwJ force-pushed the hint-tooltip-unify-positioning branch from 0c3c8e8 to 9b290e2 Compare March 27, 2024 13:12
# Conflicts:
#	packages/react-ui/.creevey/images/Hint/hint near screen edge/chrome.png
#	packages/react-ui/.creevey/images/Hint/hint near screen edge/firefox.png
#	packages/react-ui/components/Hint/Hint.tsx
#	packages/react-ui/components/Hint/__stories__/Hint.stories.tsx
#	packages/react-ui/lib/featureFlagsContext/FEATUREFLAGSCONTEXT.md
#	packages/react-ui/lib/featureFlagsContext/ReactUIFeatureFlagsContext.tsx
# Conflicts:
#	packages/react-ui/.creevey/images/Hint/Hints without wrapper around inline-block with 50% width/chrome2022.png
#	packages/react-ui/.creevey/images/Hint/Hints without wrapper around inline-block with 50% width/chrome2022Dark.png
#	packages/react-ui/.creevey/images/Hint/Hints without wrapper around inline-block with 50% width/firefox2022.png
#	packages/react-ui/.creevey/images/Hint/Hints without wrapper around inline-block with 50% width/firefox2022Dark.png
#	packages/react-ui/components/Hint/Hint.tsx
@@ -84,7 +81,10 @@ export interface HintState {
position: PopupPositionsType;
}

const Positions: PopupPositionsType[] = [
/**
* @deprecated This variable will be removed in the next version because of applying popupUnifyPositioning feature flag.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

При удалении фиче-флаг сущность с дефолтными позициями хинта станет не нужной

type DefaultProps = Required<
Pick<HintProps, 'pos' | 'allowedPositions' | 'manual' | 'opened' | 'maxWidth' | 'disableAnimations' | 'useWrapper'>
>;
type DefaultProps = Required<Pick<HintProps, 'manual' | 'opened' | 'maxWidth' | 'disableAnimations' | 'useWrapper'>>;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Дефолтные pos и allowedPositions теперь будут вычисляться в Popup'е, так как логика их определения одинакова в Hint'е и в Tooltip'е.

@@ -129,14 +125,13 @@ export class Hint extends React.PureComponent<HintProps, HintState> implements I

private timer: SafeTimer;
private theme!: Theme;
private featureFlags!: ReactUIFeatureFlags;
public featureFlags!: ReactUIFeatureFlags;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Модификатор доступа изменен на public, так к флагам нужен доступ из Hint-test.tsx для тестирования расчёта приоритетной и возможных позиций.

splitPosition.length === 2 &&
(splitPosition[1] === 'middle' || splitPosition[1] === 'center') &&
allowedPositions.indexOf(splitPosition[0] as PopupPositionsType) === -1
) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Сложная проверка, проверяет ситуации:

  • заданная позиция не находится среди возможных позиций
  • нормализованная заданная позиция не находится среди возможных позиций (нормализация преобразует 'top' в 'top center' и определит его среди набора возможных позиций ['top center', ...])
  • обрезанная центральная позиция не находится среди возможных позиций ("обрезка" значений с middle и center направлениями и нахождение их среди ['top', 'bottom', 'left', 'right'])

/**
* @deprecated This variable will be removed in the next version because of applying popupUnifyPositioning feature flag.
*/
public static oldDefaultAllowedPositions = Positions;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Дефолтная приоритетная позиция и дефолтные возможные позиции теперь будут задаваться в Popup'е, так как логика их определения одинакова в Hint'е и в Tooltip'е.

splitPosition.length === 2 &&
(splitPosition[1] === 'middle' || splitPosition[1] === 'center') &&
allowedPositions.indexOf(splitPosition[0] as PopupPositionsType) === -1
) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Сложная проверка, проверяет ситуации:

  • заданная позиция не находится среди возможных позиций
  • нормализованная заданная позиция не находится среди возможных позиций (нормализация преобразует 'top' в 'top center' и определит его среди набора возможных позиций ['top center', ...])
  • обрезанная центральная позиция не находится среди возможных позиций ("обрезка" значений с middle и center направлениями и нахождение их среди ['top', 'bottom', 'left', 'right'])

allowedPositions.indexOf(splitPosition[0] as PopupPositionsType) === -1
) {
throw new Error(
'Unexpected position ' + pos + ' passed to Tooltip. Expected one of: ' + allowedPositions.join(', '),
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Логика проверки выносится в Hint и Tooltip, так как необходимо возвращать ошибку (которая в этой строке) с указанием элемента (Hint и Tooltip), к котором она возникла. Было бы неправильно указывать ошибку в Popup'е, если пользователь его не использовал.

allowedPositions.indexOf(splitPosition[0] as PopupPositionsType) === -1
) {
throw new Error(
'Unexpected position ' + pos + ' passed to Popup. Expected one of: ' + allowedPositions.join(', '),
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Хотя в Hint'е и Tooltip'е реализована проверка, в Popup'е она тоже необходима, так как Popup может быть использован пользователем в рамках своего контрола.

@@ -217,4 +217,83 @@ describe('properly renders opened/closed states', () => {
expect(innerContainer).toHaveLength(1);
expect(innerContainer?.children()).toHaveLength(0);
});

describe('test getPosAndPositions method', () => {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Тестов без флага нет, так как при отсутствии флага popupUnifyPositioning функция getPosAndPositions не вызывается, нам нечего здесь проверять.

Copy link
Member

@zhzz zhzz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Обсудили в офлайне:

  1. Не нужно менять механизм позиционирования. Лучше остаться на существующем, который работает через массив уже отсортированных по нужному приоритету позиций. Если пользователь передал позиции в другом порядку, используем его.
  2. Валидацию на корректность переданных позиций не нужно делать в каждом компоненте. Достаточно только в том, который их реализует. И ошибку решили заменить на warning.
  3. Не нужно тестировать одинаковую логику в разных компонентах, если она реализуется в третьем компоненте. Плюс, не нужно привязываться к деталям реализации компонентов (к классовости компонентов, к именам его внутренних полей). Часто, логику можно вынести из компонента в хелпер и тестировать отдельно.

@zhzz
Copy link
Member

zhzz commented Apr 26, 2024

Зафиксирую договоренности после очередного обсуждения в офлайне:

  1. Полученный массив позиций от пользователя никак не меняем, следуем ему
  2. Если пользователь передал короткий вариант желаемой позиции, то находим первую подходящую в массиве и используем
  3. Всю общую логику уносим в Popup. В Hint и Tooltip ее не дублируем.

@zhzz
Copy link
Member

zhzz commented May 30, 2024

Закрыли в пользу #3416

@zhzz zhzz closed this May 30, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants