From 648f22a6404995872557d553b944dabcc40a3312 Mon Sep 17 00:00:00 2001 From: Luca Stocchi <49404737+lstocchi@users.noreply.github.com> Date: Fri, 17 Nov 2023 13:08:38 +0100 Subject: [PATCH] feat: add purple dot when new content is available in dashboard (#4043) (#4782) * feat: add purple dot when new content is available in dashboard (#4043) Signed-off-by: lstocchi * fix: create base component Signed-off-by: lstocchi --------- Signed-off-by: lstocchi --- packages/renderer/src/AppNavigation.svelte | 38 +++--- .../NewContentOnDashboardBadge.spec.ts | 124 ++++++++++++++++++ .../NewContentOnDashboardBadge.svelte | 63 +++++++++ .../src/lib/ui/NewContentBadge.spec.ts | 55 ++++++++ .../src/lib/ui/NewContentBadge.svelte | 33 +++++ 5 files changed, 296 insertions(+), 17 deletions(-) create mode 100644 packages/renderer/src/lib/dashboard/NewContentOnDashboardBadge.spec.ts create mode 100644 packages/renderer/src/lib/dashboard/NewContentOnDashboardBadge.svelte create mode 100644 packages/renderer/src/lib/ui/NewContentBadge.spec.ts create mode 100644 packages/renderer/src/lib/ui/NewContentBadge.svelte diff --git a/packages/renderer/src/AppNavigation.svelte b/packages/renderer/src/AppNavigation.svelte index 120637c41c02d..c72507044ab50 100644 --- a/packages/renderer/src/AppNavigation.svelte +++ b/packages/renderer/src/AppNavigation.svelte @@ -15,6 +15,7 @@ import VolumeIcon from './lib/images/VolumeIcon.svelte'; import NavItem from './lib/ui/NavItem.svelte'; import type { TinroRouteMeta } from 'tinro'; import type { Unsubscriber } from 'svelte/store'; +import NewContentOnDashboardBadge from './lib/dashboard/NewContentOnDashboardBadge.svelte'; let podInfoSubscribe: Unsubscriber; let containerInfoSubscribe: Unsubscriber; @@ -95,24 +96,27 @@ export let meta: TinroRouteMeta; class="group w-leftnavbar min-w-leftnavbar flex flex-col justify-between hover:overflow-y-none" aria-label="AppNavigation"> -
- +
+ + > +
+
diff --git a/packages/renderer/src/lib/dashboard/NewContentOnDashboardBadge.spec.ts b/packages/renderer/src/lib/dashboard/NewContentOnDashboardBadge.spec.ts new file mode 100644 index 0000000000000..65d1f310b7a0d --- /dev/null +++ b/packages/renderer/src/lib/dashboard/NewContentOnDashboardBadge.spec.ts @@ -0,0 +1,124 @@ +/********************************************************************** + * Copyright (C) 2023 Red Hat, Inc. + * + * 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 + * + * http://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. + * + * SPDX-License-Identifier: Apache-2.0 + ***********************************************************************/ +import '@testing-library/jest-dom/vitest'; +import { test, expect } from 'vitest'; +import { render, screen } from '@testing-library/svelte'; +import NewContentOnDashboardBadge from './NewContentOnDashboardBadge.svelte'; +import { notificationQueue } from '/@/stores/notifications'; +import type { NotificationCard } from '../../../../main/src/plugin/api/notification'; +import type { ProviderStatus } from '@podman-desktop/api'; +import type { ProviderContainerConnectionInfo, ProviderInfo } from '../../../../main/src/plugin/api/provider-info'; +import { providerInfos } from '/@/stores/providers'; +import { router } from 'tinro'; + +async function waitRender(): Promise { + const result = render(NewContentOnDashboardBadge); + // wait that result.component.$$.ctx[3] is set + while (result.component.$$.ctx[3] === undefined) { + await new Promise(resolve => setTimeout(resolve, 100)); + } +} + +const notification1: NotificationCard = { + id: 1, + extensionId: 'extension', + title: '1', + body: '1', + type: 'info', + highlight: true, +}; + +const pStatus: ProviderStatus = 'started'; +const pInfo: ProviderContainerConnectionInfo = { + name: 'test', + status: 'started', + endpoint: { + socketPath: '', + }, + type: 'podman', +}; +const providerInfo = { + id: 'test', + internalId: 'id', + name: '', + containerConnections: [pInfo], + kubernetesConnections: undefined, + status: pStatus, + containerProviderConnectionCreation: false, + containerProviderConnectionInitialization: false, + kubernetesProviderConnectionCreation: false, + kubernetesProviderConnectionInitialization: false, + links: undefined, + detectionChecks: undefined, + warnings: undefined, + images: undefined, + installationSupport: undefined, +} as unknown as ProviderInfo; + +test('Expect to do not display any dot if active page is Dashboard', async () => { + notificationQueue.set([]); + providerInfos.set([]); + await waitRender(); + + notificationQueue.set([notification1]); + providerInfos.set([providerInfo]); + + await new Promise(resolve => setTimeout(resolve, 200)); + + const dot = screen.queryByLabelText('New content available'); + expect(dot).not.toBeInTheDocument(); +}); + +test('Expect to do not display any dot if active page is not Dashboard but there are no updates', async () => { + notificationQueue.set([]); + providerInfos.set([]); + router.goto('/pods'); + + await waitRender(); + + const dot = screen.queryByLabelText('New content available'); + expect(dot).not.toBeInTheDocument(); +}); + +test('Expect to display the dot if active page is not Dashboard and there is a new notification', async () => { + notificationQueue.set([]); + providerInfos.set([]); + router.goto('/pods'); + await waitRender(); + + notificationQueue.set([notification1]); + + await new Promise(resolve => setTimeout(resolve, 200)); + + const dot = screen.getByLabelText('New content available'); + expect(dot).toBeInTheDocument(); +}); + +test('Expect to display the dot if active page is not Dashboard and there is a new provider', async () => { + notificationQueue.set([]); + providerInfos.set([]); + router.goto('/pods'); + await waitRender(); + + providerInfos.set([providerInfo]); + + await new Promise(resolve => setTimeout(resolve, 200)); + + const dot = screen.getByLabelText('New content available'); + expect(dot).toBeInTheDocument(); +}); diff --git a/packages/renderer/src/lib/dashboard/NewContentOnDashboardBadge.svelte b/packages/renderer/src/lib/dashboard/NewContentOnDashboardBadge.svelte new file mode 100644 index 0000000000000..39abbdf27764b --- /dev/null +++ b/packages/renderer/src/lib/dashboard/NewContentOnDashboardBadge.svelte @@ -0,0 +1,63 @@ + + +
+ +
diff --git a/packages/renderer/src/lib/ui/NewContentBadge.spec.ts b/packages/renderer/src/lib/ui/NewContentBadge.spec.ts new file mode 100644 index 0000000000000..64642f97a5fc5 --- /dev/null +++ b/packages/renderer/src/lib/ui/NewContentBadge.spec.ts @@ -0,0 +1,55 @@ +/********************************************************************** + * Copyright (C) 2023 Red Hat, Inc. + * + * 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 + * + * http://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. + * + * SPDX-License-Identifier: Apache-2.0 + ***********************************************************************/ +import '@testing-library/jest-dom/vitest'; +import { test, expect } from 'vitest'; +import { render, screen } from '@testing-library/svelte'; +import NewContentBadge from './NewContentBadge.svelte'; +import { router } from 'tinro'; + +test('Expect to do not display any dot if active page is same from pagePath', async () => { + router.goto('/'); + render(NewContentBadge, { + pagePath: '/', + show: true, + }); + + const dot = screen.queryByLabelText('New content available'); + expect(dot).not.toBeInTheDocument(); +}); + +test('Expect to do not display any dot if active page is in pagePath but show prop is false', async () => { + router.goto('/'); + render(NewContentBadge, { + pagePath: '/', + show: false, + }); + + const dot = screen.queryByLabelText('New content available'); + expect(dot).not.toBeInTheDocument(); +}); + +test('Expect to display the dot if active page is not pagePath and there is new content', async () => { + router.goto('/test'); + render(NewContentBadge, { + pagePath: '/', + show: true, + }); + + const dot = screen.getByLabelText('New content available'); + expect(dot).toBeInTheDocument(); +}); diff --git a/packages/renderer/src/lib/ui/NewContentBadge.svelte b/packages/renderer/src/lib/ui/NewContentBadge.svelte new file mode 100644 index 0000000000000..690dfd07aadcb --- /dev/null +++ b/packages/renderer/src/lib/ui/NewContentBadge.svelte @@ -0,0 +1,33 @@ + + +{#if hasNew} +
+{/if}