From 3307cb34f17159dfd3f03144697040fcaa10e903 Mon Sep 17 00:00:00 2001 From: Erika <3019731+Princesseuh@users.noreply.github.com> Date: Fri, 8 Mar 2024 11:53:48 +0100 Subject: [PATCH] Add new levels of notification for dev toolbar apps (#10252) * Add new levels of notification * feat: proper support * chore: changeset * fix: remove unrelated change * test: add test * feat: implement new icons * fix: go back to previous layout * fix: custom app number --------- Co-authored-by: Nate Moore --- .changeset/hungry-needles-cough.md | 17 +++++ packages/astro/e2e/dev-toolbar.test.js | 14 ++++ .../e2e/fixtures/dev-toolbar/custom-plugin.js | 10 ++- .../runtime/client/dev-toolbar/entrypoint.ts | 71 ++++++++++++++----- .../src/runtime/client/dev-toolbar/toolbar.ts | 18 ++--- 5 files changed, 105 insertions(+), 25 deletions(-) create mode 100644 .changeset/hungry-needles-cough.md diff --git a/.changeset/hungry-needles-cough.md b/.changeset/hungry-needles-cough.md new file mode 100644 index 000000000000..e7b6282d072a --- /dev/null +++ b/.changeset/hungry-needles-cough.md @@ -0,0 +1,17 @@ +--- +"astro": minor +--- + +Adds support for emitting warning and info notifications from dev toolbar apps. + +When using the `toggle-notification` event, the severity can be specified through `detail.level`: + +```ts +eventTarget.dispatchEvent( + new CustomEvent("toggle-notification", { + detail: { + level: "warning", + }, + }) +); +``` diff --git a/packages/astro/e2e/dev-toolbar.test.js b/packages/astro/e2e/dev-toolbar.test.js index c3e5528fb10a..b2e6242b47cf 100644 --- a/packages/astro/e2e/dev-toolbar.test.js +++ b/packages/astro/e2e/dev-toolbar.test.js @@ -295,6 +295,20 @@ test.describe('Dev Toolbar', () => { expect(clientRenderTime).not.toBe(null); }); + test('apps can show notifications', async ({ page, astro }) => { + await page.goto(astro.resolveUrl('/')); + + const toolbar = page.locator('astro-dev-toolbar'); + const appButton = toolbar.locator('button[data-app-id="my-plugin"]'); + await appButton.click(); + + const customAppNotification = appButton.locator('.icon .notification'); + await expect(customAppNotification).toHaveAttribute('data-active'); + await expect(customAppNotification).toHaveAttribute('data-level', 'warning'); + + await expect(customAppNotification).toBeVisible(); + }); + test('can quit apps by clicking outside the window', async ({ page, astro }) => { await page.goto(astro.resolveUrl('/')); diff --git a/packages/astro/e2e/fixtures/dev-toolbar/custom-plugin.js b/packages/astro/e2e/fixtures/dev-toolbar/custom-plugin.js index 42340b1ffa25..10bb7faff93a 100644 --- a/packages/astro/e2e/fixtures/dev-toolbar/custom-plugin.js +++ b/packages/astro/e2e/fixtures/dev-toolbar/custom-plugin.js @@ -2,7 +2,7 @@ export default { id: 'my-plugin', name: 'My Plugin', icon: 'astro:logo', - init(canvas) { + init(canvas, eventTarget) { const astroWindow = document.createElement('astro-dev-toolbar-window'); const myButton = document.createElement('astro-dev-toolbar-button'); myButton.size = 'medium'; @@ -13,6 +13,14 @@ export default { console.log('Clicked!'); }); + eventTarget.dispatchEvent( + new CustomEvent("toggle-notification", { + detail: { + level: "warning", + }, + }) + ); + astroWindow.appendChild(myButton); canvas.appendChild(astroWindow); diff --git a/packages/astro/src/runtime/client/dev-toolbar/entrypoint.ts b/packages/astro/src/runtime/client/dev-toolbar/entrypoint.ts index 6a62b8416612..f115decf0676 100644 --- a/packages/astro/src/runtime/client/dev-toolbar/entrypoint.ts +++ b/packages/astro/src/runtime/client/dev-toolbar/entrypoint.ts @@ -62,30 +62,47 @@ document.addEventListener('DOMContentLoaded', async () => { overlay = document.createElement('astro-dev-toolbar'); + const notificationLevels = ['error', 'warning', 'info'] as const; + const notificationSVGs: Record<(typeof notificationLevels)[number], string> = { + error: + '', + warning: + '', + info: '', + } as const; + const prepareApp = (appDefinition: DevToolbarAppDefinition, builtIn: boolean): DevToolbarApp => { const eventTarget = new EventTarget(); - const app = { + const app: DevToolbarApp = { ...appDefinition, builtIn: builtIn, active: false, - status: 'loading' as const, - notification: { state: false }, + status: 'loading', + notification: { state: false, level: undefined }, eventTarget: eventTarget, }; // Events apps can send to the overlay to update their status eventTarget.addEventListener('toggle-notification', (evt) => { + if (!(evt instanceof CustomEvent)) return; + const target = overlay.shadowRoot?.querySelector(`[data-app-id="${app.id}"]`); - if (!target) return; + const notificationElement = target?.querySelector('.notification'); + if (!target || !notificationElement) return; - let newState = true; - if (evt instanceof CustomEvent) { - newState = evt.detail.state ?? true; - } + let newState = evt.detail.state ?? true; + let level = notificationLevels.includes(evt?.detail?.level) + ? (evt.detail.level as (typeof notificationLevels)[number]) + : 'error'; app.notification.state = newState; + if (newState) app.notification.level = level; - target.querySelector('.notification')?.toggleAttribute('data-active', newState); + notificationElement.toggleAttribute('data-active', newState); + if (newState) { + notificationElement.setAttribute('data-level', level); + notificationElement.innerHTML = notificationSVGs[level]; + } }); const onToggleApp = async (evt: Event) => { @@ -137,12 +154,13 @@ document.addEventListener('DOMContentLoaded', async () => { display: none; position: absolute; top: -4px; - right: -6px; - width: 8px; - height: 8px; - border-radius: 9999px; - border: 1px solid rgba(19, 21, 26, 1); - background: #B33E66; + right: -5px; + width: 12px; + height: 10px; + } + + .notification svg { + display: block; } #dropdown:not([data-no-notification]) .notification[data-active] { @@ -222,12 +240,33 @@ document.addEventListener('DOMContentLoaded', async () => { app.eventTarget.addEventListener('toggle-notification', (evt) => { if (!(evt instanceof CustomEvent)) return; - notification.toggleAttribute('data-active', evt.detail.state ?? true); + let newState = evt.detail.state ?? true; + let level = notificationLevels.includes(evt?.detail?.level) + ? (evt.detail.level as (typeof notificationLevels)[number]) + : 'error'; + + notification.toggleAttribute('data-active', newState); + + if (newState) { + notification.setAttribute('data-level', level); + notification.innerHTML = notificationSVGs[level]; + } + + app.notification.state = newState; + if (newState) app.notification.level = level; eventTarget.dispatchEvent( new CustomEvent('toggle-notification', { detail: { state: hiddenApps.some((p) => p.notification.state === true), + level: + ['error', 'warning', 'info'].find((notificationLevel) => + hiddenApps.some( + (p) => + p.notification.state === true && + p.notification.level === notificationLevel + ) + ) ?? 'error', }, }) ); diff --git a/packages/astro/src/runtime/client/dev-toolbar/toolbar.ts b/packages/astro/src/runtime/client/dev-toolbar/toolbar.ts index 6ce2978c0b97..144f414ee3e5 100644 --- a/packages/astro/src/runtime/client/dev-toolbar/toolbar.ts +++ b/packages/astro/src/runtime/client/dev-toolbar/toolbar.ts @@ -9,6 +9,7 @@ export type DevToolbarApp = DevToolbarAppDefinition & { status: 'ready' | 'loading' | 'error'; notification: { state: boolean; + level?: 'error' | 'warning' | 'info'; }; eventTarget: EventTarget; }; @@ -187,8 +188,8 @@ export class AstroDevToolbar extends HTMLElement { } } - #dev-bar #bar-container .item.active .notification { - border-color: rgba(71, 78, 94, 1); + #dev-bar #bar-container .item.active .notification rect, #dev-bar #bar-container .item.active .notification path { + stroke: rgba(71, 78, 94, 1); } #dev-bar .item .icon { @@ -198,7 +199,7 @@ export class AstroDevToolbar extends HTMLElement { user-select: none; } - #dev-bar .item svg { + #dev-bar .item .icon>svg { width: 20px; height: 20px; display: block; @@ -216,11 +217,12 @@ export class AstroDevToolbar extends HTMLElement { position: absolute; top: -4px; right: -6px; - width: 8px; - height: 8px; - border-radius: 9999px; - border: 1px solid rgba(19, 21, 26, 1); - background: #B33E66; + width: 10px; + height: 10px; + } + + #dev-bar .item .notification svg { + display: block; } #dev-toolbar-root:not([data-no-notification]) #dev-bar .item .notification[data-active] {