diff --git a/src/locales/de.ts b/src/locales/de.ts index 3efc985edd..7ad1e5987a 100644 --- a/src/locales/de.ts +++ b/src/locales/de.ts @@ -25,6 +25,7 @@ export default { "common.actions.shareCopy": "Kopie teilen", "common.actions.update": "Aktualisieren", "common.ariaLabel.newTab": "öffnet in einem neuen Tab", + "common.ariaLabel.sameTab": "öffnet im selben Tab", "common.labels.admin": "Admin(s)", "common.labels.birthdate": "Geburtsdatum", "common.labels.birthday": "Geburtsdatum", diff --git a/src/locales/en.ts b/src/locales/en.ts index 49ce83b3c6..219d3b3c68 100644 --- a/src/locales/en.ts +++ b/src/locales/en.ts @@ -25,6 +25,7 @@ export default { "common.actions.shareCopy": "Share copy", "common.actions.update": "Update", "common.ariaLabel.newTab": "opens in a new tab", + "common.ariaLabel.sameTab": "opens in the same tab", "common.labels.admin": "", "common.labels.birthdate": "Date of birth", "common.labels.birthday": "Date of Birth", diff --git a/src/locales/es.ts b/src/locales/es.ts index 577a8f3423..9542bf9b30 100644 --- a/src/locales/es.ts +++ b/src/locales/es.ts @@ -25,6 +25,7 @@ export default { "common.actions.shareCopy": "Compartir copia", "common.actions.update": "Actualizar", "common.ariaLabel.newTab": "se abre en una nueva pestaña", + "common.ariaLabel.sameTab": "se abre en la misma pestaña", "common.labels.admin": "Admin(s)", "common.labels.birthdate": "Fecha de nacimiento", "common.labels.birthday": "Fecha de nacimiento", diff --git a/src/locales/uk.ts b/src/locales/uk.ts index 677427a7af..5acdea7744 100644 --- a/src/locales/uk.ts +++ b/src/locales/uk.ts @@ -25,6 +25,7 @@ export default { "common.actions.shareCopy": "Поділитися копією", "common.actions.update": "Оновити", "common.ariaLabel.newTab": "відкривається в новій вкладці", + "common.ariaLabel.sameTab": "відкривається в тій же вкладці", "common.labels.admin": "адміністратор(и)", "common.labels.birthdate": "Дата народження", "common.labels.birthday": "Дата народження", diff --git a/src/modules/feature/board-external-tool-element/ExternalToolElement.unit.ts b/src/modules/feature/board-external-tool-element/ExternalToolElement.unit.ts index 147e2d4be3..f769c66845 100644 --- a/src/modules/feature/board-external-tool-element/ExternalToolElement.unit.ts +++ b/src/modules/feature/board-external-tool-element/ExternalToolElement.unit.ts @@ -1,8 +1,4 @@ -import { - ConfigResponse, - ContentElementType, - ExternalToolElementResponse, -} from "@/serverApi/v3"; +import { ConfigResponse, ExternalToolElementResponse } from "@/serverApi/v3"; import EnvConfigModule from "@/store/env-config"; import { BusinessError } from "@/store/types/commons"; import { ENV_CONFIG_MODULE_KEY } from "@/utils/inject"; @@ -11,8 +7,8 @@ import { contextExternalToolConfigurationStatusFactory, contextExternalToolFactory, externalToolDisplayDataFactory, + externalToolElementResponseFactory, schoolToolConfigurationStatusFactory, - timestampsResponseFactory, } from "@@/tests/test-utils"; import { createTestingI18n, @@ -39,15 +35,6 @@ jest.mock("@data-board"); jest.mock("@data-external-tool"); jest.mock("@util-board"); -const EMPTY_TEST_ELEMENT: ExternalToolElementResponse = { - id: "external-tool-element-id", - content: { - contextExternalToolId: null, - }, - type: ContentElementType.ExternalTool, - timestamps: timestampsResponseFactory.build(), -}; - describe("ExternalToolElement", () => { let useContentElementStateMock: DeepMocked< ReturnType @@ -154,10 +141,9 @@ describe("ExternalToolElement", () => { it("should load the display data", async () => { getWrapper( { - element: { - ...EMPTY_TEST_ELEMENT, + element: externalToolElementResponseFactory.build({ content: { contextExternalToolId: "contextExternalToolId" }, - }, + }), isEditMode: false, }, externalToolDisplayDataFactory.build({ @@ -175,10 +161,9 @@ describe("ExternalToolElement", () => { it("should load the launch request", async () => { getWrapper( { - element: { - ...EMPTY_TEST_ELEMENT, + element: externalToolElementResponseFactory.build({ content: { contextExternalToolId: "contextExternalToolId" }, - }, + }), isEditMode: false, }, externalToolDisplayDataFactory.build({ @@ -198,10 +183,9 @@ describe("ExternalToolElement", () => { it("should not load the launch request", async () => { getWrapper( { - element: { - ...EMPTY_TEST_ELEMENT, + element: externalToolElementResponseFactory.build({ content: { contextExternalToolId: "contextExternalToolId" }, - }, + }), isEditMode: false, }, externalToolDisplayDataFactory.build({ @@ -224,10 +208,9 @@ describe("ExternalToolElement", () => { it("should not load the launch request", async () => { getWrapper( { - element: { - ...EMPTY_TEST_ELEMENT, + element: externalToolElementResponseFactory.build({ content: { contextExternalToolId: "contextExternalToolId" }, - }, + }), isEditMode: false, }, externalToolDisplayDataFactory.build({ @@ -249,10 +232,9 @@ describe("ExternalToolElement", () => { it("should not load the launch request", async () => { getWrapper( { - element: { - ...EMPTY_TEST_ELEMENT, + element: externalToolElementResponseFactory.build({ content: { contextExternalToolId: "contextExternalToolId" }, - }, + }), isEditMode: false, }, externalToolDisplayDataFactory.build({ @@ -274,10 +256,9 @@ describe("ExternalToolElement", () => { it("should not load the launch request", async () => { getWrapper( { - element: { - ...EMPTY_TEST_ELEMENT, + element: externalToolElementResponseFactory.build({ content: { contextExternalToolId: "contextExternalToolId" }, - }, + }), isEditMode: false, }, externalToolDisplayDataFactory.build({ @@ -297,11 +278,11 @@ describe("ExternalToolElement", () => { describe("when the element does not have a tool attached", () => { it("should open the configuration dialog immediately", async () => { - useSharedLastCreatedElementMock.lastCreatedElementId.value = - EMPTY_TEST_ELEMENT.id; + const element = externalToolElementResponseFactory.build(); + useSharedLastCreatedElementMock.lastCreatedElementId.value = element.id; const { wrapper } = getWrapper({ - element: EMPTY_TEST_ELEMENT, + element, isEditMode: true, }); @@ -316,7 +297,7 @@ describe("ExternalToolElement", () => { it("should not load the display data", async () => { getWrapper({ - element: EMPTY_TEST_ELEMENT, + element: externalToolElementResponseFactory.build(), isEditMode: false, }); @@ -329,7 +310,7 @@ describe("ExternalToolElement", () => { it("should not load the launch request", async () => { getWrapper({ - element: EMPTY_TEST_ELEMENT, + element: externalToolElementResponseFactory.build(), isEditMode: false, }); @@ -346,7 +327,7 @@ describe("ExternalToolElement", () => { describe("when not in edit mode", () => { const setup = () => { const { wrapper } = getWrapper({ - element: EMPTY_TEST_ELEMENT, + element: externalToolElementResponseFactory.build(), isEditMode: false, }); @@ -367,7 +348,7 @@ describe("ExternalToolElement", () => { describe("when in edit mode", () => { const setup = () => { const { wrapper } = getWrapper({ - element: EMPTY_TEST_ELEMENT, + element: externalToolElementResponseFactory.build(), isEditMode: true, }); @@ -393,10 +374,9 @@ describe("ExternalToolElement", () => { const { wrapper } = getWrapper( { - element: { - ...EMPTY_TEST_ELEMENT, + element: externalToolElementResponseFactory.build({ content: { contextExternalToolId }, - }, + }), isEditMode: false, }, externalToolDisplayDataFactory.build({ @@ -427,10 +407,9 @@ describe("ExternalToolElement", () => { const { wrapper } = getWrapper( { - element: { - ...EMPTY_TEST_ELEMENT, + element: externalToolElementResponseFactory.build({ content: { contextExternalToolId }, - }, + }), isEditMode: false, }, externalToolDisplayDataFactory.build({ @@ -459,15 +438,12 @@ describe("ExternalToolElement", () => { describe("Loading", () => { describe("when the component is loading", () => { const setup = () => { - const contextExternalToolId = "context-external-tool-id"; - useExternalToolElementDisplayStateMock.isLoading = ref(true); const { wrapper } = getWrapper({ - element: { - ...EMPTY_TEST_ELEMENT, - content: { contextExternalToolId }, - }, + element: externalToolElementResponseFactory.build({ + content: { contextExternalToolId: "contextExternalToolId" }, + }), isEditMode: false, }); @@ -493,10 +469,9 @@ describe("ExternalToolElement", () => { const { wrapper } = getWrapper( { - element: { - ...EMPTY_TEST_ELEMENT, + element: externalToolElementResponseFactory.build({ content: { contextExternalToolId }, - }, + }), isEditMode: false, }, externalToolDisplayDataFactory.build({ contextExternalToolId }) @@ -521,7 +496,7 @@ describe("ExternalToolElement", () => { describe("when clicking on a un-configured tool card in edit mode", () => { const setup = () => { const { wrapper } = getWrapper({ - element: EMPTY_TEST_ELEMENT, + element: externalToolElementResponseFactory.build(), isEditMode: true, }); @@ -557,7 +532,7 @@ describe("ExternalToolElement", () => { ); const { wrapper } = getWrapper({ - element: EMPTY_TEST_ELEMENT, + element: externalToolElementResponseFactory.build(), isEditMode: true, }); @@ -603,10 +578,9 @@ describe("ExternalToolElement", () => { describe("when clicking on a configured tool card", () => { const setup = () => { const { wrapper } = getWrapper({ - element: { - ...EMPTY_TEST_ELEMENT, + element: externalToolElementResponseFactory.build({ content: { contextExternalToolId: "contextExternalToolId" }, - }, + }), isEditMode: false, }); @@ -657,7 +631,7 @@ describe("ExternalToolElement", () => { const { wrapper } = getWrapper( { - element: EMPTY_TEST_ELEMENT, + element: externalToolElementResponseFactory.build(), isEditMode: true, }, externalToolDisplayDataFactory.build() @@ -689,7 +663,7 @@ describe("ExternalToolElement", () => { const { wrapper } = getWrapper( { - element: EMPTY_TEST_ELEMENT, + element: externalToolElementResponseFactory.build(), isEditMode: true, }, externalToolDisplayDataFactory.build() @@ -718,7 +692,7 @@ describe("ExternalToolElement", () => { const { wrapper } = getWrapper( { - element: EMPTY_TEST_ELEMENT, + element: externalToolElementResponseFactory.build(), isEditMode: true, }, externalToolDisplayDataFactory.build({ @@ -747,10 +721,9 @@ describe("ExternalToolElement", () => { jest.useFakeTimers({ legacyFakeTimers: true }); const { wrapper, refreshTime } = getWrapper( { - element: { - ...EMPTY_TEST_ELEMENT, + element: externalToolElementResponseFactory.build({ content: { contextExternalToolId: "contextExternalToolId" }, - }, + }), isEditMode: false, }, externalToolDisplayDataFactory.build({ @@ -780,4 +753,152 @@ describe("ExternalToolElement", () => { ).toHaveBeenCalledTimes(2); }); }); + + describe("when moving the element with arrow keys", () => { + const setup = () => { + const { wrapper } = getWrapper({ + element: externalToolElementResponseFactory.build({ + content: { contextExternalToolId: null }, + }), + isEditMode: true, + }); + + return { + wrapper, + }; + }; + + it("should emit an event", async () => { + const { wrapper } = setup(); + + const card = wrapper.getComponent({ ref: "externalToolElement" }); + await card.trigger("keydown.up"); + + expect(wrapper.emitted("move-keyboard:edit")).toHaveLength(1); + }); + }); + + describe("Aria label", () => { + describe("when no tool is selected", () => { + const setup = () => { + const { wrapper } = getWrapper({ + element: externalToolElementResponseFactory.build({ + content: { contextExternalToolId: null }, + }), + isEditMode: true, + }); + + return { + wrapper, + }; + }; + + it("should read that a tool needs to be selected", async () => { + const { wrapper } = setup(); + + const card = wrapper.getComponent({ ref: "externalToolElement" }); + + expect(card.attributes("aria-label")).toEqual( + "components.cardElement.externalToolElement, feature-board-external-tool-element.placeholder.selectTool" + ); + }); + }); + + describe("when a tool is displayed and will be opened in the same tab", () => { + const setup = () => { + const toolName = "testTool"; + const contextExternalToolId = "contextExternalToolId"; + + const { wrapper } = getWrapper( + { + element: externalToolElementResponseFactory.build({ + content: { contextExternalToolId }, + }), + isEditMode: true, + }, + externalToolDisplayDataFactory.build({ + name: toolName, + contextExternalToolId, + openInNewTab: false, + }) + ); + + return { + wrapper, + toolName, + }; + }; + + it("should read the tool name and the tab it is started in", async () => { + const { wrapper, toolName } = setup(); + + const card = wrapper.getComponent({ ref: "externalToolElement" }); + + expect(card.attributes("aria-label")).toEqual( + `components.cardElement.externalToolElement, ${toolName}, common.ariaLabel.sameTab` + ); + }); + }); + + describe("when a tool is displayed and will be opened in a new tab", () => { + const setup = () => { + const toolName = "testTool"; + const contextExternalToolId = "contextExternalToolId"; + + const { wrapper } = getWrapper( + { + element: externalToolElementResponseFactory.build({ + content: { contextExternalToolId }, + }), + isEditMode: true, + }, + externalToolDisplayDataFactory.build({ + name: toolName, + contextExternalToolId, + openInNewTab: true, + }) + ); + + return { + wrapper, + toolName, + }; + }; + + it("should read the tool name and the tab it is started in", async () => { + const { wrapper, toolName } = setup(); + + const card = wrapper.getComponent({ ref: "externalToolElement" }); + + expect(card.attributes("aria-label")).toEqual( + `components.cardElement.externalToolElement, ${toolName}, common.ariaLabel.newTab` + ); + }); + }); + + describe("when a tool is selected and currently loading", () => { + const setup = () => { + const { wrapper } = getWrapper({ + element: externalToolElementResponseFactory.build({ + content: { contextExternalToolId: "contextExternalToolId" }, + }), + isEditMode: true, + }); + + return { + wrapper, + }; + }; + + it("should read that the tool is loading", async () => { + const { wrapper } = setup(); + + const card = wrapper.getComponent({ ref: "externalToolElement" }); + + expect(card.attributes("aria-label")).toEqual( + `components.cardElement.externalToolElement, common.loading.text` + ); + }); + }); + }); }); diff --git a/src/modules/feature/board-external-tool-element/ExternalToolElement.vue b/src/modules/feature/board-external-tool-element/ExternalToolElement.vue index 1a2b67bb37..36912b7281 100644 --- a/src/modules/feature/board-external-tool-element/ExternalToolElement.vue +++ b/src/modules/feature/board-external-tool-element/ExternalToolElement.vue @@ -10,6 +10,7 @@ tabindex="0" role="button" :loading="isLoading" + :aria-label="ariaLabel" @keyup.enter="onClickElement" @keydown.up.down="onKeydownArrow" @keydown.stop @@ -55,7 +56,7 @@ - diff --git a/tests/test-utils/factory/externalToolElementResponseFactory.ts b/tests/test-utils/factory/externalToolElementResponseFactory.ts new file mode 100644 index 0000000000..1045bf9ad1 --- /dev/null +++ b/tests/test-utils/factory/externalToolElementResponseFactory.ts @@ -0,0 +1,16 @@ +import { + ContentElementType, + ExternalToolElementResponse, +} from "@/serverApi/v3"; +import { Factory } from "fishery"; +import { timestampsResponseFactory } from "./timestampsResponseFactory"; + +export const externalToolElementResponseFactory = + Factory.define(({ sequence }) => ({ + id: `external-tool-element-response-${sequence}`, + type: ContentElementType.ExternalTool, + content: { + contextExternalToolId: null, + }, + timestamps: timestampsResponseFactory.build(), + })); diff --git a/tests/test-utils/factory/index.ts b/tests/test-utils/factory/index.ts index 80cfb0efd4..b38763660a 100644 --- a/tests/test-utils/factory/index.ts +++ b/tests/test-utils/factory/index.ts @@ -25,6 +25,7 @@ export * from "./customParameterResponseFactory"; export * from "./drawingElementResponseFactory"; export * from "./envsFactory"; export * from "./externalToolDisplayDataFactory"; +export * from "./externalToolElementResponseFactory"; export * from "./fileElementContentFactory"; export * from "./fileElementResponseFactory"; export * from "./filerecordResponse.factory";