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

feat: add when property to extensions menus #3959

Merged
merged 17 commits into from Sep 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions packages/main/src/plugin/menu-registry.ts
Expand Up @@ -21,11 +21,14 @@ import { Disposable } from './types/disposable.js';
export interface Menu {
command: string;
title: string;
when?: string;
}

export enum MenuContext {
DASHBOARD_IMAGE = 'dashboard/image',
DASHBOARD_CONTAINER = 'dashboard/container',
DASHBOARD_POD = 'dashboard/pod',
DASHBOARD_COMPOSE = 'dashboard/compose',
}

export class MenuRegistry {
Expand Down
13 changes: 3 additions & 10 deletions packages/renderer/src/lib/ContainerList.svelte
Expand Up @@ -38,7 +38,6 @@ import { CONTAINER_LIST_VIEW } from './view/views';
import type { ViewInfoUI } from '../../../main/src/plugin/api/view-info';
import type { ContextUI } from './context/context';
import Button from './ui/Button.svelte';
import { type Menu, MenuContext } from '../../../main/src/plugin/menu-registry';

const containerUtils = new ContainerUtils();
let openChoiceModal = false;
Expand Down Expand Up @@ -223,7 +222,6 @@ let contextsUnsubscribe: Unsubscriber;
let podUnsubscribe: Unsubscriber;
let viewsUnsubscribe: Unsubscriber;
let pods: PodInfo[];
let contributedMenus: Menu[];

onMount(async () => {
// grab previous groups
Expand All @@ -250,8 +248,6 @@ onMount(async () => {
podUnsubscribe = podsInfos.subscribe(podInfos => {
pods = podInfos;
});

contributedMenus = await window.getContributedMenus(MenuContext.DASHBOARD_CONTAINER);
});

function updateContainers(containers: ContainerInfo[], globalContext: ContextUI, viewContributions: ViewInfoUI[]) {
Expand Down Expand Up @@ -536,8 +532,7 @@ function errorCallback(container: ContainerInfoUI, errorMessage: string): void {
})),
kind: 'podman',
}}"
dropdownMenu="{true}"
contributions="{contributedMenus}" />
dropdownMenu="{true}" />
{/if}
{#if containerGroup.type === ContainerGroupInfoTypeUI.COMPOSE && containerGroup.status && containerGroup.engineId && containerGroup.engineType}
<ComposeActions
Expand All @@ -550,8 +545,7 @@ function errorCallback(container: ContainerInfoUI, errorMessage: string): void {
}}"
dropdownMenu="{true}"
inProgressCallback="{(containers, flag, state) =>
composeGroupInProgressCallback(containerGroup.containers, flag, state)}"
contributions="{contributedMenus}" />
composeGroupInProgressCallback(containerGroup.containers, flag, state)}" />
{/if}
</td>
</tr>
Expand Down Expand Up @@ -631,8 +625,7 @@ function errorCallback(container: ContainerInfoUI, errorMessage: string): void {
errorCallback="{error => errorCallback(container, error)}"
inProgressCallback="{(flag, state) => inProgressCallback(container, flag, state)}"
container="{container}"
dropdownMenu="{true}"
contributions="{contributedMenus}" />
dropdownMenu="{true}" />
</div>
</div>
</td>
Expand Down
8 changes: 1 addition & 7 deletions packages/renderer/src/lib/ImagesList.svelte
Expand Up @@ -22,8 +22,6 @@ import type { ContainerInfo } from '../../../main/src/plugin/api/container-info'
import moment from 'moment';
import Prune from './engine/Prune.svelte';
import type { EngineInfoUI } from './engine/EngineInfoUI';
import type { Menu } from '../../../main/src/plugin/menu-registry';
import { MenuContext } from '../../../main/src/plugin/menu-registry';
import Checkbox from './ui/Checkbox.svelte';
import Button from './ui/Button.svelte';
import { faArrowCircleDown, faCube, faTrash } from '@fortawesome/free-solid-svg-icons';
Expand Down Expand Up @@ -108,7 +106,6 @@ let imagesUnsubscribe: Unsubscriber;
let containersUnsubscribe: Unsubscriber;
let storeContainers: ContainerInfo[] = [];
let storeImages: ImageInfo[] = [];
let contributedMenus: Menu[];

onMount(async () => {
containersUnsubscribe = containersInfos.subscribe(value => {
Expand All @@ -120,8 +117,6 @@ onMount(async () => {
storeImages = value;
updateImages();
});

contributedMenus = await window.getContributedMenus(MenuContext.DASHBOARD_IMAGE);
});

onDestroy(() => {
Expand Down Expand Up @@ -317,8 +312,7 @@ function computeInterval(): number {
image="{image}"
onPushImage="{handlePushImageModal}"
onRenameImage="{handleRenameImageModal}"
dropdownMenu="{true}"
contributions="{contributedMenus}" />
dropdownMenu="{true}" />
</td>
</tr>
<tr><td class="leading-[8px]">&nbsp;</td></tr>
Expand Down
66 changes: 66 additions & 0 deletions packages/renderer/src/lib/actions/ContributionActions.spec.ts
Expand Up @@ -108,3 +108,69 @@ test('Expect executeCommand to be called with sanitize object nested', async ()
await fireEvent.click(item);
expect(executeCommand).toBeCalledWith('dummy.command', { parent: { serializable: 'hello' } });
});

test('Expect when property to be true', async () => {
render(ContributionActions, {
args: [
{
property: 'hello',
},
],
contributions: [
{
command: 'dummy.command',
title: 'dummy-title',
when: 'property === hello',
},
],
onError: () => {},
dropdownMenu: true,
});
const item = screen.getByText('dummy-title');
expect(item).toBeInTheDocument();
});

test('Expect when property to be false', async () => {
render(ContributionActions, {
args: [
{
property: 'hello',
},
],
contributions: [
{
command: 'dummy.command',
title: 'dummy-title',
when: 'property === world',
},
],
onError: () => {},
dropdownMenu: true,
});
const item = screen.queryByText('dummy-title');
expect(item).toBeNull();
});

test('Expect when property to be true multiple args', async () => {
render(ContributionActions, {
args: [
{
property: 'hello',
},
{
property: 'world',
},
],
contributions: [
{
command: 'dummy.command',
title: 'dummy-title',
when: 'property === world',
},
],
onError: () => {},
dropdownMenu: true,
});
const item = screen.getByText('dummy-title');
expect(item).toBeInTheDocument();
});
52 changes: 51 additions & 1 deletion packages/renderer/src/lib/actions/ContributionActions.svelte
Expand Up @@ -3,12 +3,62 @@ import type { Menu } from '../../../../main/src/plugin/menu-registry';
import { faEllipsisVertical } from '@fortawesome/free-solid-svg-icons';
import ListItemButtonIcon from '../ui/ListItemButtonIcon.svelte';
import { removeNonSerializableProperties } from '/@/lib/actions/ActionUtils';
import type { ContextUI } from '/@/lib/context/context';
import { onDestroy, onMount } from 'svelte';
import { context } from '/@/stores/context';
import type { Unsubscriber } from 'svelte/store';
import { ContextKeyExpr } from '/@/lib/context/contextKey';
import { transformObjectToContext } from '/@/lib/context/ContextUtils';

export let args: unknown[];

export let contextPrefix: string | undefined = undefined;

export let dropdownMenu = false;
export let contributions: Menu[] = [];

let filteredContributions: Menu[] = [];
$: {
filteredContributions = contributions.reduce((previousValue, currentValue) => {
// If no when property is set, we keep all additional menus
if (currentValue.when === undefined) return [...previousValue, currentValue];

// Deserialize the `when` property
const whenDeserialized = ContextKeyExpr.deserialize(currentValue.when);

// Evaluate with global context first
if (globalContext && whenDeserialized?.evaluate(globalContext)) {
return [...previousValue, currentValue];
}

// Transform the unknown[] args objects as contexts
const argsContexts = args.map(arg => transformObjectToContext(arg, contextPrefix));

// Evaluate the arguments as context
for (let argsContext of argsContexts) {
if (whenDeserialized?.evaluate(argsContext)) {
return [...previousValue, currentValue];
}
}
return previousValue;
}, [] as Menu[]);
}

let globalContext: ContextUI;
let contextsUnsubscribe: Unsubscriber;

onMount(async () => {
contextsUnsubscribe = context.subscribe(value => {
globalContext = value;
});
});

onDestroy(() => {
// unsubscribe from the store
if (contextsUnsubscribe) {
contextsUnsubscribe();
}
});
export let onError: (errorMessage: string) => void;

async function executeContribution(menu: Menu): Promise<void> {
Expand All @@ -20,7 +70,7 @@ async function executeContribution(menu: Menu): Promise<void> {
}
</script>

{#each contributions as menu}
{#each filteredContributions as menu}
<ListItemButtonIcon
title="{menu.title}"
onClick="{() => executeContribution(menu)}"
Expand Down
9 changes: 8 additions & 1 deletion packages/renderer/src/lib/compose/ComposeActions.svelte
Expand Up @@ -8,17 +8,23 @@ import FlatMenu from '../ui/FlatMenu.svelte';
import type { ContainerInfoUI } from '../container/ContainerInfoUI';
import type { Menu } from '../../../../main/src/plugin/menu-registry';
import ContributionActions from '/@/lib/actions/ContributionActions.svelte';
import { onMount } from 'svelte';
import { MenuContext } from '../../../../main/src/plugin/menu-registry';

export let compose: ComposeInfoUI;
export let dropdownMenu = false;
export let detailed = false;
export let contributions: Menu[] = [];

export let inProgressCallback: (containers: ContainerInfoUI[], inProgress: boolean, state?: string) => void = () => {};
export let errorCallback: (erroMessage: string) => void = () => {};

const composeLabel = 'com.docker.compose.project';

let contributions: Menu[] = [];
onMount(async () => {
contributions = await window.getContributedMenus(MenuContext.DASHBOARD_COMPOSE);
});

async function startCompose(composeInfoUI: ComposeInfoUI) {
inProgressCallback(composeInfoUI.containers, true, 'STARTING');
try {
Expand Down Expand Up @@ -129,6 +135,7 @@ if (dropdownMenu) {
icon="{faArrowsRotate}" />
<ContributionActions
args="{[compose]}"
contextPrefix="composeItem"
dropdownMenu="{dropdownMenu}"
contributions="{contributions}"
onError="{errorCallback}" />
Expand Down
3 changes: 3 additions & 0 deletions packages/renderer/src/lib/compose/ComposeDetails.spec.ts
Expand Up @@ -10,6 +10,7 @@ import { get } from 'svelte/store';

const listContainersMock = vi.fn();
const getProviderInfosMock = vi.fn();
const getContributedMenusMock = vi.fn();

vi.mock('xterm', () => {
return {
Expand Down Expand Up @@ -41,6 +42,8 @@ beforeAll(() => {
(window as any).logsContainer = vi.fn();
(window as any).listViewsContributions = vi.fn();
(window as any).generatePodmanKube = vi.fn();
(window as any).getContributedMenus = getContributedMenusMock;
getContributedMenusMock.mockImplementation(() => Promise.resolve([]));
mockBreadcrumb();
});

Expand Down
@@ -1,5 +1,5 @@
import '@testing-library/jest-dom/vitest';
import { test, expect, vi, beforeAll } from 'vitest';
import { beforeAll, expect, test, vi } from 'vitest';
import { render, screen } from '@testing-library/svelte';
import { mockBreadcrumb } from '../../stores/breadcrumb';
import ComposeDetailsLogs from './ComposeDetailsLogs.svelte';
Expand Down
Expand Up @@ -18,14 +18,20 @@ import DropdownMenu from '../ui/DropdownMenu.svelte';
import FlatMenu from '../ui/FlatMenu.svelte';
import type { Menu } from '../../../../main/src/plugin/menu-registry';
import ContributionActions from '/@/lib/actions/ContributionActions.svelte';
import { MenuContext } from '../../../../main/src/plugin/menu-registry';
import { onMount } from 'svelte';
export let container: ContainerInfoUI;
export let dropdownMenu = false;
export let detailed = false;
export let contributions: Menu[] = [];

export let inProgressCallback: (inProgress: boolean, state?: string) => void = () => {};
export let errorCallback: (erroMessage: string) => void = () => {};

let contributions: Menu[] = [];
onMount(async () => {
contributions = await window.getContributedMenus(MenuContext.DASHBOARD_CONTAINER);
});

async function startContainer() {
inProgressCallback(true, 'STARTING');
try {
Expand Down Expand Up @@ -178,6 +184,7 @@ if (dropdownMenu) {
icon="{faArrowsRotate}" />
<ContributionActions
args="{[container]}"
contextPrefix="containerItem"
dropdownMenu="{dropdownMenu}"
contributions="{contributions}"
onError="{errorCallback}" />
Expand Down
Expand Up @@ -50,6 +50,7 @@ const myContainer: ContainerInfo = {
};

const deleteContainerMock = vi.fn();
const getContributedMenusMock = vi.fn();

vi.mock('xterm', () => {
return {
Expand All @@ -69,6 +70,9 @@ beforeAll(() => {
addListener: vi.fn(),
});
(window as any).ResizeObserver = vi.fn().mockReturnValue({ observe: vi.fn(), unobserve: vi.fn() });

(window as any).getContributedMenus = getContributedMenusMock;
getContributedMenusMock.mockImplementation(() => Promise.resolve([]));
});

beforeEach(() => {});
Expand Down