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

N21-2167 preferred tools for boards #3419

Merged
merged 32 commits into from
Oct 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
a0349f2
add dynamic element to board poc WIP
IgorCapCoder Sep 12, 2024
c722389
fix icons, fix loop, mock tools
IgorCapCoder Sep 17, 2024
25178f0
create element with tool directly
IgorCapCoder Sep 18, 2024
b9401eb
page reload and config dialog when custom parameters
IgorCapCoder Sep 19, 2024
ec2d095
open tool edit dialog for tools with custom params
IgorCapCoder Sep 20, 2024
1e2ba47
clean up
IgorCapCoder Sep 23, 2024
5efdd18
mock data change
IgorCapCoder Sep 23, 2024
b96bb44
Merge branch 'main' into N21-2172-add-dynamic-board-element
IgorCapCoder Oct 8, 2024
e0d6737
mergte main into POC of N21-2172
IgorCapCoder Oct 8, 2024
7f9d1fb
generate Api
MBergCap Oct 9, 2024
ba94f53
generate Api
MBergCap Oct 9, 2024
c6ca765
cleanups and refactorings
IgorCapCoder Oct 9, 2024
d80bd8e
add feature flag and call to server (todo: add props)
IgorCapCoder Oct 10, 2024
d0a6c3a
server call long name solution
IgorCapCoder Oct 11, 2024
3c3f17c
testId changes, tooltipText WIP
IgorCapCoder Oct 14, 2024
2645507
testId fix, tooltipText WIP, toDo: remove console.logs
IgorCapCoder Oct 14, 2024
91b0d9f
fix translations of label, use lineclamp
IgorCapCoder Oct 15, 2024
625ce0c
add tooltip
MBergCap Oct 16, 2024
60e21bc
unit tests WIP
IgorCapCoder Oct 16, 2024
ade866b
Merge remote-tracking branch 'origin/N21-2167-preferred-tools-for-boa…
IgorCapCoder Oct 16, 2024
170c39a
add datatestId
MBergCap Oct 17, 2024
e75e71a
unit tests WIP 2
IgorCapCoder Oct 18, 2024
90e727f
unit tests WIP 3
IgorCapCoder Oct 21, 2024
fc19b26
Merge branch 'main' into N21-2167-preferred-tools-for-boards
IgorCapCoder Oct 21, 2024
9969296
generate client, lint fix
IgorCapCoder Oct 21, 2024
97c831d
Merge branch 'main' into N21-2167-preferred-tools-for-boards
IgorCapCoder Oct 23, 2024
50fd08e
fix tests and env config-defaults
IgorCapCoder Oct 23, 2024
bbf84c0
Merge branch 'main' into N21-2167-preferred-tools-for-boards
IgorCapCoder Oct 25, 2024
cbffc7a
remove comments, fix type and spelling
IgorCapCoder Oct 25, 2024
7bf104d
fix group mapping from generate api client
IgorCapCoder Oct 25, 2024
9ad61f3
fix reliability concerns of sonar cloud
IgorCapCoder Oct 25, 2024
4c3fe90
fix test
IgorCapCoder Oct 25, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/components/administration/ExternalToolSection.unit.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { ConfigResponse } from "@/serverApi/v3";
import AuthModule from "@/store/auth";
import EnvConfigModule from "@/store/env-config";
import { SchoolExternalToolMetadata } from "@/store/external-tool";
import NotifierModule from "@/store/notifier";
import SchoolExternalToolsModule from "@/store/school-external-tools";
import {
Expand Down Expand Up @@ -28,8 +30,6 @@ import { nextTick, ref } from "vue";
import vueDompurifyHTMLPlugin from "vue-dompurify-html";
import { Router, useRouter } from "vue-router";
import ExternalToolSection from "./ExternalToolSection.vue";
import { SchoolExternalToolMetadata } from "@/store/external-tool";
import { ConfigResponse } from "@/serverApi/v3";

jest.mock("@data-external-tool");

Expand Down Expand Up @@ -402,6 +402,7 @@ describe("ExternalToolSection", () => {
statusText: "statusText",
isOutdated: false,
isDeactivated: false,
restrictToContexts: "",
};

const itemName: string = wrapper.vm.getItemName;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import ExternalToolConfigurator from "@/components/external-tools/configuration/ExternalToolConfigurator.vue";
import { ToolContextType } from "@/serverApi/v3";
import NotifierModule from "@/store/notifier";
import { NOTIFIER_MODULE_KEY } from "@/utils/inject";
import SchoolExternalToolsModule from "@/store/school-external-tools";
import {
NOTIFIER_MODULE_KEY,
SCHOOL_EXTERNAL_TOOLS_MODULE_KEY,
} from "@/utils/inject";
import { createModuleMocks } from "@/utils/mock-store-module";
import {
businessErrorFactory,
Expand Down Expand Up @@ -40,15 +44,25 @@ describe("CourseContextExternalToolConfigurator", () => {
>;

const getWrapper = (
props: ComponentProps<typeof ContextExternalToolConfigurator>
props: ComponentProps<typeof ContextExternalToolConfigurator>,
getters: Partial<SchoolExternalToolsModule> = {}
) => {
const notifierModule = createModuleMocks(NotifierModule);
const schoolExternalToolsModule = createModuleMocks(
SchoolExternalToolsModule,
{
getContextExternalToolConfigurationTemplate: undefined,
...getters,
}
);

const wrapper = mount(ContextExternalToolConfigurator, {
global: {
plugins: [createTestingVuetify(), createTestingI18n()],
provide: {
[NOTIFIER_MODULE_KEY.valueOf()]: notifierModule,
[SCHOOL_EXTERNAL_TOOLS_MODULE_KEY.valueOf()]:
schoolExternalToolsModule,
},
},
props,
Expand Down Expand Up @@ -144,6 +158,49 @@ describe("CourseContextExternalToolConfigurator", () => {
).toHaveBeenCalledWith("configId");
});
});

describe("when a preferred tool with a custom parameter is loaded", () => {
const setup = async () => {
const contextExternalToolConfigurationTemplate =
contextExternalToolConfigurationTemplateFactory.build();
const { wrapper } = getWrapper(
{
contextId: "contextId",
contextType: ToolContextType.BoardElement,
},
{
getContextExternalToolConfigurationTemplate:
contextExternalToolConfigurationTemplate,
}
);

return {
wrapper,
contextExternalToolConfigurationTemplate,
};
};

it("should not fetch available tools", async () => {
const { wrapper } = await setup();

await wrapper.vm.fetchData();

expect(
useContextExternalToolConfigurationStateMock.fetchAvailableToolConfigurationsForContext
).not.toHaveBeenCalledWith("contextId", ToolContextType.BoardElement);
});

it("should set the preferred tool as an available tool", async () => {
const { wrapper, contextExternalToolConfigurationTemplate } =
await setup();

await wrapper.vm.fetchData();

expect(
useContextExternalToolConfigurationStateMock.availableTools.value
).toEqual([contextExternalToolConfigurationTemplate]);
});
});
});

describe("onCancel", () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
<external-tool-configurator
:templates="availableTools"
:configuration="configuration"
:isPreferredTool="isPreferredTool"
:error="apiError"
:loading="loading"
:display-settings-title="displaySettingsTitle"
Expand All @@ -27,7 +28,9 @@
import ExternalToolConfigurator from "@/components/external-tools/configuration/ExternalToolConfigurator.vue";
import { ToolContextType } from "@/serverApi/v3";
import { ToolParameterEntry } from "@/store/external-tool";
import SchoolExternalToolsModule from "@/store/school-external-tools";
import { BusinessError } from "@/store/types/commons";
import { injectStrict, SCHOOL_EXTERNAL_TOOLS_MODULE_KEY } from "@/utils/inject";
import {
ContextExternalTool,
ContextExternalToolConfigurationTemplate,
Expand Down Expand Up @@ -86,6 +89,15 @@ const apiError: ComputedRef<BusinessError | undefined> = computed(
() => configError.value || templateError.value
);

const schoolExternalToolsModule: SchoolExternalToolsModule = injectStrict(
SCHOOL_EXTERNAL_TOOLS_MODULE_KEY
);

const preferredTool =
schoolExternalToolsModule.getContextExternalToolConfigurationTemplate;

const isPreferredTool: Ref<boolean> = ref(false);

const onCancel = async () => {
emit("cancel");
};
Expand Down Expand Up @@ -129,6 +141,12 @@ const fetchData = async () => {

await fetchContextExternalTool(props.configId);
displayName.value = configuration.value?.displayName;
} else if (preferredTool) {
availableTools.value = [preferredTool];
isPreferredTool.value = true;
schoolExternalToolsModule.setContextExternalToolConfigurationTemplate(
undefined
);
} else {
await fetchAvailableToolConfigurationsForContext(
props.contextId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import { NOTIFIER_MODULE_KEY } from "@/utils/inject";
import { createModuleMocks } from "@/utils/mock-store-module";
import {
contextExternalToolConfigurationTemplateFactory,
contextExternalToolFactory,
schoolExternalToolConfigurationTemplateFactory,
schoolExternalToolFactory,
toolParameterFactory,
Expand Down Expand Up @@ -86,12 +88,40 @@
});
});

describe("when a preferred tool is loaded", () => {
const setup = () => {
const template1 =
contextExternalToolConfigurationTemplateFactory.build();
const template2 =
contextExternalToolConfigurationTemplateFactory.build();

const { wrapper } = getWrapper({
templates: [template1, template2],
configuration: contextExternalToolFactory.build(),
isPreferredTool: true,
});

return {
wrapper,
template1,
};
};

it("should display the preferred tool in the selection", async () => {
const { wrapper, template1 } = setup();

const selectionRow = wrapper.find(".v-autocomplete .v-list-item-title");

expect(selectionRow.text()).toEqual(template1.name);
});
});

describe("when clicking on the 'paste' icon", () => {
const setup = () => {
const clipboardText = "https://google.de";
const template = schoolExternalToolConfigurationTemplateFactory.build();

const clipboardMock = createMock<Clipboard>();

Check warning on line 124 in src/components/external-tools/configuration/ExternalToolConfigurator.unit.ts

View workflow job for this annotation

GitHub Actions / lint

'Clipboard' is not defined
Object.assign(navigator, { clipboard: clipboardMock });

const { wrapper } = getWrapper({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,6 @@

<script setup lang="ts">
import ExternalToolConfigSettings from "@/components/external-tools/configuration/ExternalToolConfigSettings.vue";
import { mdiAlertCircle, mdiContentPaste } from "@icons/material";
import { useExternalToolMappings } from "@/composables/external-tool-mappings.composable";
import {
SchoolExternalTool,
Expand All @@ -114,6 +113,7 @@ import {
ContextExternalTool,
ExternalToolConfigurationTemplate,
} from "@data-external-tool";
import { mdiAlertCircle, mdiContentPaste } from "@icons/material";
import {
computed,
ComputedRef,
Expand Down Expand Up @@ -145,6 +145,9 @@ const props = defineProps({
configuration: {
type: Object as PropType<ConfigurationTypes>,
},
isPreferredTool: {
type: Boolean,
},
error: {
type: Object as PropType<BusinessError>,
},
Expand Down Expand Up @@ -364,4 +367,10 @@ const filterToolNameOrUrl = (

return isMatchItemName || isMatchItemUrl;
};

watch(configurationTemplates, () => {
if (props.isPreferredTool) {
selectedTemplate.value = props.templates[0];
}
});
</script>
8 changes: 4 additions & 4 deletions src/components/share/ShareModalResult.vue
Original file line number Diff line number Diff line change
Expand Up @@ -15,31 +15,31 @@
class="d-sm-none d-flex"
data-testid="mobilePlatformAction"
:icon="mdiShareVariantOutline"
label="common.actions.share"
:label="$t('common.actions.share')"
@click.stop="onShareMobilePlatflorm(shareUrl)"
/>

<ExtendedIconBtn
class="d-sm-flex d-none"
data-testid="shareMailAction"
:icon="mdiEmailOutline"
label="components.molecules.share.result.mailShare"
:label="$t('components.molecules.share.result.mailShare')"
@click.stop="onMailShareUrl(shareUrl, type)"
/>

<ExtendedIconBtn
class="d-sm-flex d-none"
data-testid="copyAction"
:icon="mdiContentCopy"
label="components.molecules.share.result.copyClipboard"
:label="$t('components.molecules.share.result.copyClipboard')"
@click.stop="onCopy(shareUrl)"
/>

<ExtendedIconBtn
class="d-flex"
data-testid="qrCodeAction"
:icon="mdiQrcode"
label="components.molecules.share.result.qrCodeScan"
:label="$t('components.molecules.share.result.qrCodeScan')"
@click.stop="onShowQrCode"
/>
</div>
Expand Down
2 changes: 1 addition & 1 deletion src/modules/data/board/BoardApi.composable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ import {
CardResponse,
ColumnResponse,
ContentElementType,
CourseRoomsApiFactory,
CreateCardBodyParamsRequiredEmptyElementsEnum,
CreateContentElementBodyParams,
DrawingElementContent,
ExternalToolElementContentBody,
FileElementContentBody,
LinkElementContentBody,
RichTextElementContentBody,
CourseRoomsApiFactory,
SubmissionContainerElementContentBody,
} from "@/serverApi/v3";
import { BoardContextType } from "@/types/board/BoardContext";
Expand Down
29 changes: 28 additions & 1 deletion src/modules/data/board/Card.store.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { CardResponse, ContentElementType } from "@/serverApi/v3";
import {
CardResponse,
ContentElementType,
PreferredToolResponse,
ToolContextType,
} from "@/serverApi/v3";
import { envConfigModule } from "@/store";
import { useSharedEditMode, useSharedLastCreatedElement } from "@util-board";
import { defineStore } from "pinia";
Expand All @@ -7,6 +12,7 @@ import { CreateCardSuccessPayload } from "./boardActions/boardActionPayload";

import { useBoardFocusHandler } from "./BoardFocusHandler.composable";
import {
CreateElementRequestPayload,
CreateElementSuccessPayload,
DeleteCardSuccessPayload,
DeleteElementSuccessPayload,
Expand All @@ -21,6 +27,7 @@ import { useCardSocketApi } from "./cardActions/cardSocketApi.composable";

export const useCardStore = defineStore("cardStore", () => {
const cards = ref<Record<string, CardResponse>>({});
const preferredTools = ref<PreferredToolResponse[]>();
const { lastCreatedElementId } = useSharedLastCreatedElement();

const restApi = useCardRestApi();
Expand Down Expand Up @@ -90,6 +97,13 @@ export const useCardStore = defineStore("cardStore", () => {

const createElementRequest = socketOrRest.createElementRequest;

const createPreferredElement = async (
payload: CreateElementRequestPayload,
tool: PreferredToolResponse
) => {
restApi.createPreferredElement(payload, tool);
};

const createElementSuccess = async (payload: CreateElementSuccessPayload) => {
const card = cards.value[payload.cardId];
if (card === undefined) return;
Expand All @@ -109,6 +123,7 @@ export const useCardStore = defineStore("cardStore", () => {
lastCreatedElementId.value = payload.newElement.id;
setFocus(payload.newElement.id);
}

return payload.newElement;
};

Expand Down Expand Up @@ -220,7 +235,16 @@ export const useCardStore = defineStore("cardStore", () => {
return previousElement.id;
};

const loadPreferredTools = async (contextType: ToolContextType) => {
preferredTools.value = await restApi.getPreferredTools(contextType);
};

const getPreferredTools = (): PreferredToolResponse[] | undefined => {
return preferredTools.value;
};

return {
createPreferredElement,
createCardSuccess,
IgorCapCoder marked this conversation as resolved.
Show resolved Hide resolved
createElementRequest,
createElementSuccess,
Expand All @@ -242,5 +266,8 @@ export const useCardStore = defineStore("cardStore", () => {
updateCardHeightSuccess,
updateCardTitleRequest,
updateCardTitleSuccess,
loadPreferredTools,
preferredTools,
getPreferredTools,
};
});
Loading
Loading