Skip to content

Commit

Permalink
Add a static "show" method to NotificationElement (#2322)
Browse files Browse the repository at this point in the history
* Add a static "show" method to NotificationElement

Fixes #914

* Remove unnecessary defaults

* Use only one options parameter

* add API docs for options object

Co-authored-by: Sascha Ißbrücker <[email protected]>
  • Loading branch information
Artur- and sissbruecker authored Aug 31, 2021
1 parent ab43882 commit abf0f69
Show file tree
Hide file tree
Showing 4 changed files with 154 additions and 0 deletions.
5 changes: 5 additions & 0 deletions packages/vaadin-notification/src/interfaces.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,8 @@ export interface NotificationElementEventMap {
}

export interface NotificationEventMap extends HTMLElementEventMap, NotificationElementEventMap {}

export interface ShowOptions {
duration?: number;
position?: NotificationPosition;
}
22 changes: 22 additions & 0 deletions packages/vaadin-notification/src/vaadin-notification.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,28 @@ declare class NotificationElement extends ThemePropertyMixin(ElementMixin(HTMLEl
listener: (this: NotificationElement, ev: NotificationEventMap[K]) => void,
options?: boolean | EventListenerOptions
): void;

/**
* Shows a notification with the given content.
* By default, positions the notification at `bottom-start` and uses a 5 second duration.
* An options object can be passed to configure the notification.
* The options object has the following structure:
*
* ```
* {
* position?: string
* duration?: number
* }
* ```
*
* See the individual documentation for:
* - [`position`](#/elements/vaadin-notification#property-position)
* - [`duration`](#/elements/vaadin-notification#property-duration)
*
* @param contents the contents to show, either as a string or a Lit template.
* @param options optional options for customizing the notification.
*/
static show(contents: string | TemplateResult, options?: ShowOptions): NotificationElement;
}

declare global {
Expand Down
56 changes: 56 additions & 0 deletions packages/vaadin-notification/src/vaadin-notification.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import { ElementMixin } from '@vaadin/vaadin-element-mixin/vaadin-element-mixin.
import { processTemplates } from '@vaadin/vaadin-element-mixin/templates.js';
import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
import { ThemePropertyMixin } from '@vaadin/vaadin-themable-mixin/vaadin-theme-property-mixin.js';
import { isTemplateResult } from 'lit/directive-helpers.js';
import { render } from 'lit';

/**
* An element used internally by `<vaadin-notification>`. Not intended to be used separately.
Expand Down Expand Up @@ -471,6 +473,60 @@ class NotificationElement extends ThemePropertyMixin(ElementMixin(PolymerElement
}
}
}

/**
* Shows a notification with the given content.
* By default, positions the notification at `bottom-start` and uses a 5 second duration.
* An options object can be passed to configure the notification.
* The options object has the following structure:
*
* ```
* {
* position?: string
* duration?: number
* }
* ```
*
* See the individual documentation for:
* - [`position`](#/elements/vaadin-notification#property-position)
* - [`duration`](#/elements/vaadin-notification#property-duration)
*
* @param contents the contents to show, either as a string or a Lit template.
* @param options optional options for customizing the notification.
*/
static show(contents, options) {
if (isTemplateResult(contents)) {
return NotificationElement._createAndShowNotification((root) => {
render(contents, root);
}, options);
} else {
return NotificationElement._createAndShowNotification((root) => {
root.innerText = contents;
}, options);
}
}

/** @private */
static _createAndShowNotification(renderer, options) {
const notification = document.createElement(NotificationElement.is);
if (options && options.duration) {
notification.duration = options.duration;
}
if (options && options.position) {
notification.position = options.position;
}
notification.renderer = renderer;
document.body.appendChild(notification);
notification.opened = true;

notification.addEventListener('opened-changed', (e) => {
if (!e.detail.value) {
notification.remove();
}
});

return notification;
}
}

customElements.define(NotificationContainer.is, NotificationContainer);
Expand Down
71 changes: 71 additions & 0 deletions packages/vaadin-notification/test/statichelper.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { expect } from '@esm-bundle/chai';
import { NotificationElement } from '../src/vaadin-notification.js';
import '../vaadin-notification.js';
import { html } from 'lit';
import { aTimeout } from '@vaadin/testing-helpers';

describe('static helpers', () => {
it('show should show a text notification', () => {
const notification = NotificationElement.show('Hello world');
const notificationDom = document.body.querySelector('vaadin-notification');
expect(notification).to.equal(notificationDom);

expect(notification._card.innerText.trim()).to.equal('Hello world');
});

it('show should show a Lit template notificatipn', () => {
const notification = NotificationElement.show(html`Hello world`);

//const notificationDom = document.body.querySelector('vaadin-notification');
//FIXME This causes 'TypeError: Converting circular structure to JSON'
// expect(notification).to.equal(notificationDom);

expect(notification._card.innerText.trim()).to.equal('Hello world');
});

it('show should use a default duration of 5s and bottom-start', () => {
const notification = NotificationElement.show('Hello world');
expect(notification.duration).to.equal(5000);
expect(notification.position).to.equal('bottom-start');
});

it('show should use the given duration and position', () => {
const notification = NotificationElement.show('Hello world', { duration: 123, position: 'top-center' });
expect(notification.duration).to.equal(123);
expect(notification.position).to.equal('top-center');
});

it('show remove the element from the document after closing', async () => {
const notification = NotificationElement.show('Hello world', { duration: 1 });
expect(notification.parentElement).to.equal(document.body);
await aTimeout(10);
expect(notification.parentElement).to.be.null;
});

it('show should support Lit event handlers', () => {
let clicked = 0;
const doClose = () => {
clicked++;
};
const notification = NotificationElement.show(html`Click <button @click=${doClose}>this</button> to count`);
notification._card.querySelector('button').click();

expect(clicked).to.equal(1);
});

it('show should support closing through an event handler', () => {
const notification = NotificationElement.show(
html`Click
<button
@click=${() => {
notification.opened = false;
}}
>this</button
>
to close`
);
notification._card.querySelector('button').click();

expect(notification.opened).to.equal(false);
});
});

0 comments on commit abf0f69

Please sign in to comment.