z-index и наслоение элементов

Важной частью интерфейса веб-приложений является управление слоями элементов. Использование свойства CSS z-index позволяет контролировать, какой элемент будет отображаться поверх других, когда они пересекаются. В процессе тестирования компонентов с использованием React Testing Library важно учитывать поведение этих слоёв и корректность их наложения. Рассмотрим, как можно тестировать элементы, которые используют z-index для их позиционирования.

Как работает z-index

Свойство z-index определяет порядок отображения элементов вдоль оси Z в пространстве DOM. Элементы с более высоким значением z-index отображаются поверх тех, у которых оно меньше. Например, если два элемента имеют абсолютное или фиксированное позиционирование, тот, у которого больше значение z-index, будет отображаться выше.

При этом стоит учитывать несколько моментов:

  • Относительное позиционирование (position: relative) не влияет на поведение z-index, если элемент не имеет явно заданного стиля.
  • Элементы с position: static игнорируют z-index — это их дефолтное состояние.
  • z-index работает только в пределах их родительских контейнеров, если родитель не имеет явного position.

Тестирование видимости элементов с использованием z-index

Когда в приложении используется несколько наложенных слоёв, важно проверить, что элементы отображаются или скрыты в зависимости от их порядка. Например, можно протестировать, что модальное окно всегда отображается поверх основного контента, или что элементы, перекрывающие друг друга, правильно видны.

В React Testing Library для таких проверок полезны методы screen.queryBy, screen.getBy, а также различные ассерторы, такие как toBeInTheDocument, которые проверяют наличие элементов в DOM. Однако для корректного тестирования наложения элементов нужно также учитывать их стили.

Пример тестирования наложенных элементов

Предположим, есть компонент, который отображает две кнопки: одну поверх другой, с использованием z-index. Задача — убедиться, что верхняя кнопка видна, а нижняя скрыта за ней.

import React from 'react';

function ButtonStack() {
  return (
    <div style={{ position: 'relative', width: '200px', height: '100px' }}>
      <button style={{ position: 'absolute', zIndex: 1 }}>Top Button</button>
      <button style={{ position: 'absolute', zIndex: 0 }}>Bottom Button</button>
    </div>
  );
}

export default ButtonStack;

Для того чтобы протестировать этот компонент с использованием React Testing Library, можно написать следующий тест:

import { render, screen } from '@testing-library/react';
import ButtonStack from './ButtonStack';

test('верхняя кнопка отображается выше нижней', () => {
  render(<ButtonStack />);
  
  const topButton = screen.getByText('Top Button');
  const bottomButton = screen.getByText('Bottom Button');
  
  // Проверка того, что верхняя кнопка видна
  expect(topButton).toBeInTheDocument();
  
  // Проверка того, что нижняя кнопка скрыта
  expect(bottomButton).toBeInTheDocument();
  
  // Проверка позиций кнопок по z-index
  const topButtonStyle = window.getComputedStyle(topButton);
  const bottomButtonStyle = window.getComputedStyle(bottomButton);
  
  expect(parseInt(topButtonStyle.zIndex)).toBeGreaterThan(parseInt(bottomButtonStyle.zIndex));
});

Этот тест проверяет, что обе кнопки присутствуют в DOM, но верхняя кнопка отображается выше, используя свойство z-index.

Использование z-index при тестировании всплывающих элементов

Модальные окна, всплывающие подсказки или контекстные меню часто используют z-index для наложения на другие элементы страницы. При тестировании таких компонентов важно учитывать, что они могут быть скрыты за другими элементами, даже если они правильно добавлены в DOM. Например, можно проверять, что модальное окно отображается поверх контента после его открытия.

Пример теста для модального окна, которое должно открыться поверх содержимого:

import { render, fireEvent, screen } from '@testing-library/react';
import Modal from './Modal';

test('модальное окно появляется поверх контента', () => {
  render(<Modal />);
  
  // Модальное окно скрыто по умолчанию
  const modal = screen.queryByText('Модальное окно');
  expect(modal).not.toBeInTheDocument();

  // Кнопка открытия модального окна
  const openButton = screen.getByText('Открыть модальное окно');
  
  fireEvent.click(openButton);
  
  // Модальное окно должно появиться
  expect(screen.getByText('Модальное окно')).toBeInTheDocument();
  
  // Проверка, что модальное окно находится сверху
  const modalStyle = window.getComputedStyle(screen.getByText('Модальное окно'));
  expect(parseInt(modalStyle.zIndex)).toBeGreaterThan(10);  // 10 - это z-index основного контента
});

В этом примере сначала проверяется, что модальное окно скрыто, а затем после нажатия на кнопку оно появляется и отображается поверх других элементов.

Тестирование скрытых элементов и наложений

В некоторых случаях необходимо тестировать, что элементы скрыты из-за наложения. Например, если элемент не виден на экране, потому что его перекрывает другой элемент с более высоким z-index, то тестирование этого поведения может быть более сложным.

Для этого можно использовать методы getBy или queryBy, чтобы убедиться, что элемент не виден или не доступен для взаимодействия:

test('элемент скрыт, если перекрыт другим элементом', () => {
  render(<ComponentWithOverlay />);
  
  const overlay = screen.getByTestId('overlay');
  const hiddenElement = screen.getByTestId('hidden-element');
  
  // Проверяем, что элемент скрыт за перекрытием
  expect(hiddenElement).toBeInTheDocument();
  
  // Проверка, что overlay накрывает скрытый элемент
  const hiddenElementRect = hiddenElement.getBoundingClientRect();
  const overlayRect = overlay.getBoundingClientRect();
  
  expect(overlayRect.top).toBeLessThanOrEqual(hiddenElementRect.top);
});

Здесь проверяется, что скрытый элемент на самом деле находится под наложенным слоем, который его перекрывает.

Заключение

Тестирование взаимодействий между элементами с использованием z-index в React Testing Library помогает гарантировать правильное отображение и поведение слоёв на странице. Это важно для приложений, где пользовательский интерфейс зависит от правильного наложения элементов, например, при работе с модальными окнами, всплывающими подсказками и другими интерактивными компонентами.