mirror of
https://github.com/withastro/astro.git
synced 2025-02-17 22:44:24 -05:00
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 <natemoo-re@users.noreply.github.com>
This commit is contained in:
parent
a31bbd7ff8
commit
3307cb34f1
5 changed files with 106 additions and 26 deletions
17
.changeset/hungry-needles-cough.md
Normal file
17
.changeset/hungry-needles-cough.md
Normal file
|
@ -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",
|
||||
},
|
||||
})
|
||||
);
|
||||
```
|
|
@ -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('/'));
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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:
|
||||
'<svg viewBox="0 0 10 10"><rect width="9" height="9" x=".5" y=".5" fill="#B33E66" stroke="#13151A" rx="4.5"/></svg>',
|
||||
warning:
|
||||
'<svg xmlns="http://www.w3.org/2000/svg" width="12" height="10" fill="none"><path fill="#B58A2D" stroke="#13151A" d="M7.29904 1.25c-.57735-1-2.02073-1-2.59808 0l-3.4641 6C.65951 8.25 1.3812 9.5 2.5359 9.5h6.9282c1.1547 0 1.8764-1.25 1.299-2.25l-3.46406-6Z"/></svg>',
|
||||
info: '<svg viewBox="0 0 10 10"><rect width="9" height="9" x=".5" y=".5" fill="#3645D9" stroke="#13151A" rx="1.5"/></svg>',
|
||||
} 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) => {
|
||||
const target = overlay.shadowRoot?.querySelector(`[data-app-id="${app.id}"]`);
|
||||
if (!target) return;
|
||||
if (!(evt instanceof CustomEvent)) return;
|
||||
|
||||
let newState = true;
|
||||
if (evt instanceof CustomEvent) {
|
||||
newState = evt.detail.state ?? true;
|
||||
}
|
||||
const target = overlay.shadowRoot?.querySelector(`[data-app-id="${app.id}"]`);
|
||||
const notificationElement = target?.querySelector('.notification');
|
||||
if (!target || !notificationElement) return;
|
||||
|
||||
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',
|
||||
},
|
||||
})
|
||||
);
|
||||
|
|
|
@ -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] {
|
||||
|
|
Loading…
Add table
Reference in a new issue