{"size": 9750, "ext": "js", "max_stars_repo_path": "packages/story-editor/src/components/canvas/karma/canvasKeyboardNavigation.karma.js", "max_stars_repo_name": "rtCamp/web-story-creation-tool", "max_stars_repo_stars_event_min_datetime": "2022-01-21T22:55:25.000Z", "max_stars_repo_stars_event_max_datetime": "2022-03-18T08:12:39.000Z", "max_issues_repo_path": "packages/story-editor/src/components/canvas/karma/canvasKeyboardNavigation.karma.js", "max_issues_repo_name": "GoogleForCreators/web-stories-wp", "max_issues_repo_issues_event_min_datetime": "2022-01-21T20:38:25.000Z", "max_issues_repo_issues_event_max_datetime": "2022-03-31T21:51:54.000Z", "max_forks_repo_path": "packages/story-editor/src/components/canvas/karma/canvasKeyboardNavigation.karma.js", "max_forks_repo_name": "rtCamp/web-story-creation-tool", "max_forks_repo_forks_event_min_datetime": "2022-02-08T12:51:11.000Z", "max_forks_repo_forks_event_max_datetime": "2022-03-23T17:51:05.000Z"}
{"max_stars_count": 24, "max_issues_count": 796, "max_forks_count": 8, "avg_line_length": 33.1632653061, "max_line_length": 139, "alphanum_fraction": 0.6836923077}
/*
 * Copyright 2022 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * External dependencies
 */
import STICKERS from '@googleforcreators/stickers';
/**
 * Internal dependencies
 */
import { Fixture } from '../../../karma';
import { useStory } from '../../../app/story';

const stickerTestType = Object.keys(STICKERS)[0];

describe('Canvas - keyboard navigation', () => {
  let fixture;
  let focusContainer;

  beforeEach(async () => {
    fixture = new Fixture();
    await fixture.render();

    focusContainer = fixture.screen.getByTestId('canvas-focus-container');
  });

  afterEach(() => {
    fixture.restore();
  });

  async function tabToCanvasFocusContainer() {
    // start focused on media pane searchbar
    await fixture.editor.library.mediaTab.click();
    await fixture.events.focus(fixture.editor.library.media.searchBar);

    // tab until focus reaches the canvas container
    let count = 0;
    while (count < 15) {
      // eslint-disable-next-line no-await-in-loop -- need to await key press
      await fixture.events.keyboard.press('tab');

      if (document.activeElement === focusContainer) {
        break;
      }

      count++;
    }

    if (count >= 15) {
      throw new Error('Could not find focus container.');
    }
  }

  function getActiveElementId() {
    return document.activeElement.getAttribute('data-element-id');
  }

  async function addElementsToCanvas() {
    // add one of each element
    // add text to canvas
    await fixture.editor.library.textTab.click();
    await fixture.events.click(fixture.editor.library.text.preset('Paragraph'));
    // add shape to canvas
    await fixture.editor.library.shapesTab.click();
    await fixture.events.click(fixture.editor.library.shapes.shape('Triangle'));
    // add sticker to canvas
    await fixture.events.click(
      fixture.editor.library.shapes.sticker(stickerTestType)
    );
    // add image to canvas
    await fixture.editor.library.mediaTab.click();
    await fixture.events.mouse.clickOn(
      fixture.editor.library.media.item(0),
      20,
      20
    );
  }

  it('should focus the canvas with global keyboard shortcut mod+alt+2 (mod+option+2)', async () => {
    // add some elements
    // add text to canvas
    await fixture.editor.library.textTab.click();
    await fixture.events.click(fixture.editor.library.text.preset('Paragraph'));
    // add shape to canvas
    await fixture.editor.library.shapesTab.click();
    await fixture.events.click(fixture.editor.library.shapes.shape('Triangle'));
    // add image to canvas
    await fixture.editor.library.mediaTab.click();
    await fixture.events.mouse.clickOn(
      fixture.editor.library.media.item(0),
      20,
      20
    );

    await fixture.events.keyboard.shortcut('mod+alt+2');

    const selectedElements = await fixture.renderHook(() =>
      useStory(({ state }) => state.selectedElements)
    );

    expect(selectedElements.length).toBe(1);
    expect(selectedElements[0].isBackground).toBe(true);
    // see that background element is selected & focused
    expect(document.activeElement.getAttribute('data-element-id')).toBe(
      selectedElements[0].id
    );
  });

  it('should not focus the canvas while tabbing through the editor', async () => {
    await tabToCanvasFocusContainer();

    // tab once more
    await fixture.events.keyboard.press('tab');

    // verify that prev page button is focused
    expect(document.activeElement).toBe(
      fixture.editor.canvas.framesLayer.prevPage
    );
  });

  it('should focus the canvas and elements in the canvas after entering into the canvas space', async () => {
    // add some elements
    // add text to canvas
    await fixture.editor.library.textTab.click();
    await fixture.events.click(fixture.editor.library.text.preset('Paragraph'));
    // add shape to canvas
    await fixture.editor.library.shapesTab.click();
    await fixture.events.click(fixture.editor.library.shapes.shape('Triangle'));
    // add image to canvas
    await fixture.editor.library.mediaTab.click();
    await fixture.events.mouse.clickOn(
      fixture.editor.library.media.item(0),
      20,
      20
    );

    await tabToCanvasFocusContainer();

    // enter into the canvas
    await fixture.events.keyboard.press('Enter');

    const selectedElements = await fixture.renderHook(() =>
      useStory(({ state }) => state.selectedElements)
    );

    expect(selectedElements.length).toBe(1);
    expect(selectedElements[0].isBackground).toBe(true);
    // see that background element is selected & focused
    expect(document.activeElement.getAttribute('data-element-id')).toBe(
      selectedElements[0].id
    );

    // exit canvas
    await fixture.events.keyboard.press('Escape');

    // verify exit
    expect(document.activeElement).toBe(focusContainer);
  });

  it('should change element selection on focus and wrap around at the last element when only keyboard is used', async () => {
    await addElementsToCanvas();

    await tabToCanvasFocusContainer();

    // enter into the canvas
    await fixture.events.keyboard.press('Enter');

    let selectedElements = await fixture.renderHook(() =>
      useStory(({ state }) => state.selectedElements)
    );
    expect(selectedElements.length).toBe(1);
    expect(selectedElements[0].isBackground).toBe(true);

    // tab to the text element
    await fixture.events.keyboard.press('Tab');

    selectedElements = await fixture.renderHook(() =>
      useStory(({ state }) => state.selectedElements)
    );
    expect(selectedElements.length).toBe(1);
    expect(selectedElements[0].type).toBe('text');

    // tab to the shape element
    await fixture.events.keyboard.press('Tab');

    selectedElements = await fixture.renderHook(() =>
      useStory(({ state }) => state.selectedElements)
    );
    // shape should be selected and receive focus
    expect(selectedElements.length).toBe(1);
    expect(selectedElements[0].type).toBe('shape');
    expect(getActiveElementId()).toBe(selectedElements[0].id);

    // tab to the sticker element
    await fixture.events.keyboard.press('Tab');

    selectedElements = await fixture.renderHook(() =>
      useStory(({ state }) => state.selectedElements)
    );
    // sticker should be selected and receive focus
    expect(selectedElements.length).toBe(1);
    expect(selectedElements[0].type).toBe('sticker');
    expect(getActiveElementId()).toBe(selectedElements[0].id);

    // tab to the image element
    await fixture.events.keyboard.press('Tab');

    selectedElements = await fixture.renderHook(() =>
      useStory(({ state }) => state.selectedElements)
    );
    // image should be selected and receive focus
    expect(selectedElements.length).toBe(1);
    expect(selectedElements[0].type).toBe('image');
    expect(getActiveElementId()).toBe(selectedElements[0].id);

    // verify cyclicality: wrap around to background element
    await fixture.events.keyboard.press('Tab');

    selectedElements = await fixture.renderHook(() =>
      useStory(({ state }) => state.selectedElements)
    );
    expect(selectedElements.length).toBe(1);
    expect(selectedElements[0].isBackground).toBe(true);
  });

  it('should change element selection on focus and wrap around at the last element when mix of cursor and keyboard are used', async () => {
    await addElementsToCanvas();

    await fixture.events.click(
      fixture.editor.canvas.framesLayer.frames[1].node
    );

    let selectedElements = await fixture.renderHook(() =>
      useStory(({ state }) => state.selectedElements)
    );
    expect(selectedElements.length).toBe(1);
    expect(selectedElements[0].type).toBe('text');

    // tab to the shape element
    await fixture.events.keyboard.press('Tab');

    selectedElements = await fixture.renderHook(() =>
      useStory(({ state }) => state.selectedElements)
    );
    // shape should be selected and receive focus
    expect(selectedElements.length).toBe(1);
    expect(selectedElements[0].type).toBe('shape');
    expect(getActiveElementId()).toBe(selectedElements[0].id);

    // tab to the sticker element
    await fixture.events.keyboard.press('Tab');

    selectedElements = await fixture.renderHook(() =>
      useStory(({ state }) => state.selectedElements)
    );
    // sticker should be selected and receive focus
    expect(selectedElements.length).toBe(1);
    expect(selectedElements[0].type).toBe('sticker');
    expect(getActiveElementId()).toBe(selectedElements[0].id);

    // tab to the image element
    await fixture.events.keyboard.press('Tab');

    selectedElements = await fixture.renderHook(() =>
      useStory(({ state }) => state.selectedElements)
    );
    // image should be selected and receive focus
    expect(selectedElements.length).toBe(1);
    expect(selectedElements[0].type).toBe('image');
    expect(getActiveElementId()).toBe(selectedElements[0].id);

    // verify cyclicality: wrap around to background element
    await fixture.events.keyboard.press('Tab');

    selectedElements = await fixture.renderHook(() =>
      useStory(({ state }) => state.selectedElements)
    );
    expect(selectedElements.length).toBe(1);
    expect(selectedElements[0].isBackground).toBe(true);
  });
});
