globalCanvasEscape.test.tsx2.8 KBView on GitHub
import { act, fireEvent, render, screen } from '@testing-library/react';
import { MemoryRouter } from 'react-router';
import React from 'react';

/**
 * Escape closes the canvas dropped over the page — the ⇧Q calendar above all, which you reach
 * from anywhere and so need to get out of from anywhere.
 *
 * The one thing it must NOT do is close out from under a layer of its own: the date picker, the
 * event composer, a confirm dialog. Escape there belongs to that layer.
 */
jest.mock('@/modules/calendar/components/CalendarCanvasSurface', () => ({
  CalendarCanvasSurface: () => {
    const R = require('react');
    return R.createElement('div', { 'data-testid': 'calendar-canvas' }, 'CALENDAR');
  },
}));
jest.mock('@/modules/canvas/components/CanvasRenderer', () => ({
  CanvasRenderer: () => null,
}));

import { GlobalCanvas } from '@/components/ui/GlobalCanvas';
import { useCedarStore } from '@/store/CedarStore';

const openCalendarCanvas = () =>
  act(() => {
    useCedarStore.setState((s) => ({ ...s, isGlobalCanvasOpen: true, activeCanvasId: 'calendar' }));
  });

const renderCanvas = () =>
  render(
    <MemoryRouter>
      <GlobalCanvas />
    </MemoryRouter>,
  );

describe('Escape and the global canvas', () => {
  afterEach(() => {
    act(() => {
      useCedarStore.setState((s) => ({ ...s, isGlobalCanvasOpen: false, activeCanvasId: null }));
    });
  });

  it('closes the calendar canvas', () => {
    openCalendarCanvas();
    renderCanvas();
    expect(screen.getByTestId('calendar-canvas')).toBeInTheDocument();

    act(() => {
      fireEvent.keyDown(document, { key=[redacted] });
    });

    expect(useCedarStore.getState().isGlobalCanvasOpen).toBe(false);
  });

  it('leaves it alone while a dialog or popover on top of it owns the key', () => {
    openCalendarCanvas();
    renderCanvas();

    const dialog = document.createElement('div');
    dialog.setAttribute('role', 'dialog');
    document.body.appendChild(dialog);
    act(() => {
      fireEvent.keyDown(document, { key=[redacted] });
    });
    expect(useCedarStore.getState().isGlobalCanvasOpen).toBe(true);
    dialog.remove();

    // …and an Escape a layer has already handled (Radix calls preventDefault on the topmost
    // one) is not a second chance to close the canvas.
    const handledFirst = (e: KeyboardEvent) => e.preventDefault();
    document.addEventListener('keydown', handledFirst, true);
    act(() => {
      fireEvent.keyDown(document.body, { key=[redacted] });
    });
    expect(useCedarStore.getState().isGlobalCanvasOpen).toBe(true);
    document.removeEventListener('keydown', handledFirst, true);

    // With nothing on top, the same key closes it.
    act(() => {
      fireEvent.keyDown(document.body, { key=[redacted] });
    });
    expect(useCedarStore.getState().isGlobalCanvasOpen).toBe(false);
  });
});