Skip to content

Commit

Permalink
experiment: add LitElement based version of vaadin-notification (#8084)
Browse files Browse the repository at this point in the history
  • Loading branch information
tomivirkki authored Nov 4, 2024
1 parent 27b95d6 commit f7b2a1e
Show file tree
Hide file tree
Showing 27 changed files with 215 additions and 41 deletions.
129 changes: 129 additions & 0 deletions packages/notification/src/vaadin-lit-notification.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
/**
* @license
* Copyright (c) 2017 - 2024 Vaadin Ltd.
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
*/
import { css, html, LitElement } from 'lit';
import { defineCustomElement } from '@vaadin/component-base/src/define.js';
import { ElementMixin } from '@vaadin/component-base/src/element-mixin.js';
import { PolylitMixin } from '@vaadin/component-base/src/polylit-mixin.js';
import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
import { NotificationContainerMixin, NotificationMixin } from './vaadin-notification-mixin.js';
import { notificationCardStyles, notificationContainerStyles } from './vaadin-notification-styles.js';

/**
* An element used internally by `<vaadin-notification>`. Not intended to be used separately.
*
* @customElement
* @extends HTMLElement
* @mixes NotificationContainerMixin
* @mixes ElementMixin
* @mixes ThemableMixin
* @private
*/
class NotificationContainer extends NotificationContainerMixin(ElementMixin(ThemableMixin(PolylitMixin(LitElement)))) {
static get styles() {
return notificationContainerStyles;
}

render() {
return html`
<div region="top-stretch"><slot name="top-stretch"></slot></div>
<div region-group="top">
<div region="top-start"><slot name="top-start"></slot></div>
<div region="top-center"><slot name="top-center"></slot></div>
<div region="top-end"><slot name="top-end"></slot></div>
</div>
<div region="middle"><slot name="middle"></slot></div>
<div region-group="bottom">
<div region="bottom-start"><slot name="bottom-start"></slot></div>
<div region="bottom-center"><slot name="bottom-center"></slot></div>
<div region="bottom-end"><slot name="bottom-end"></slot></div>
</div>
<div region="bottom-stretch"><slot name="bottom-stretch"></slot></div>
`;
}

static get is() {
return 'vaadin-notification-container';
}
}

/**
* An element used internally by `<vaadin-notification>`. Not intended to be used separately.
*
* @customElement
* @extends HTMLElement
* @mixes ThemableMixin
* @mixes ElementMixin
* @private
*/
class NotificationCard extends ElementMixin(ThemableMixin(PolylitMixin(LitElement))) {
static get styles() {
return notificationCardStyles;
}

render() {
return html`
<div part="overlay">
<div part="content">
<slot></slot>
</div>
</div>
`;
}

static get is() {
return 'vaadin-notification-card';
}

/** @protected */
ready() {
super.ready();
this.setAttribute('role', 'alert');
}
}

/**
* LitElement based version of `<vaadin-notification>` web component.
*
* ## Disclaimer
*
* This component is an experiment and not yet a part of Vaadin platform.
* There is no ETA regarding specific Vaadin version where it'll land.
* Feel free to try this code in your apps as per Apache 2.0 license.
*/
class Notification extends NotificationMixin(ElementMixin(ThemableMixin(PolylitMixin(LitElement)))) {
static get styles() {
return css`
:host {
display: none !important;
}
`;
}

render() {
return html`
<vaadin-notification-card
theme="${this._theme || ''}"
aria-live="${this.__computeAriaLive(this.assertive)}"
></vaadin-notification-card>
`;
}

static get is() {
return 'vaadin-notification';
}

/**
* Fired when the notification is closed.
*
* @event closed
*/
}

defineCustomElement(NotificationContainer);
defineCustomElement(NotificationCard);
defineCustomElement(Notification);

export { Notification };
14 changes: 13 additions & 1 deletion packages/notification/src/vaadin-notification-mixin.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export const NotificationContainerMixin = (superClass) =>
opened: {
type: Boolean,
value: false,
sync: true,
observer: '_openedChanged',
},
};
Expand Down Expand Up @@ -102,6 +103,7 @@ export const NotificationMixin = (superClass) =>
assertive: {
type: Boolean,
value: false,
sync: true,
},

/**
Expand All @@ -112,6 +114,7 @@ export const NotificationMixin = (superClass) =>
duration: {
type: Number,
value: 5000,
sync: true,
},

/**
Expand All @@ -122,6 +125,7 @@ export const NotificationMixin = (superClass) =>
type: Boolean,
value: false,
notify: true,
sync: true,
observer: '_openedChanged',
},

Expand All @@ -134,6 +138,7 @@ export const NotificationMixin = (superClass) =>
type: String,
value: 'bottom-start',
observer: '_positionChanged',
sync: true,
},

/**
Expand All @@ -145,7 +150,10 @@ export const NotificationMixin = (superClass) =>
* - `notification` The reference to the `<vaadin-notification>` element.
* @type {!NotificationRenderer | undefined}
*/
renderer: Function,
renderer: {
type: Function,
sync: true,
},
};
}

Expand Down Expand Up @@ -345,6 +353,10 @@ export const NotificationMixin = (superClass) =>
return;
}

if (this._container.performUpdate) {
this._container.performUpdate();
}

if (!this._container.shadowRoot.querySelector(`slot[name="${this.position}"]`)) {
console.warn(`Invalid alignment parameter provided: position=${this.position}`);
return;
Expand Down
2 changes: 2 additions & 0 deletions packages/notification/test/animation-lit.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import '../vaadin-lit-notification.js';
import './animation.common.js';
2 changes: 2 additions & 0 deletions packages/notification/test/animation-polymer.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import '../vaadin-notification.js';
import './animation.common.js';
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { expect } from '@vaadin/chai-plugins';
import { aTimeout, fixtureSync, oneEvent } from '@vaadin/testing-helpers';
import '../vaadin-notification.js';
import { aTimeout, fixtureSync, nextFrame, oneEvent } from '@vaadin/testing-helpers';

describe('animated notifications', () => {
let wrapper, notifications, container;
Expand All @@ -14,6 +13,7 @@ describe('animated notifications', () => {
<vaadin-notification position="middle"></vaadin-notification>
</div>
`);
await nextFrame();
notifications = Array.from(wrapper.children);
container = notifications[0]._container;

Expand Down
2 changes: 2 additions & 0 deletions packages/notification/test/lit-lit.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import '../vaadin-lit-notification.js';
import './lit.common.js';
2 changes: 2 additions & 0 deletions packages/notification/test/lit-polymer.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import '../vaadin-notification.js';
import './lit.common.js';
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import '../vaadin-lit-notification.js';
import './lit-renderer-directives.common.js';
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import '../vaadin-notification.js';
import './lit-renderer-directives.common.js';
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { expect } from '@vaadin/chai-plugins';
import { fixtureSync, nextFrame } from '@vaadin/testing-helpers';
import sinon from 'sinon';
import '../vaadin-notification.js';
import { html, render } from 'lit';
import { notificationRenderer } from '../lit.js';

Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { expect } from '@vaadin/chai-plugins';
import { fixtureSync } from '@vaadin/testing-helpers';
import '../vaadin-notification.js';
import { fixtureSync, nextFrame } from '@vaadin/testing-helpers';
import { html, render } from 'lit';

describe('lit', () => {
describe('renderer', () => {
let notification;

beforeEach(() => {
beforeEach(async () => {
notification = fixtureSync(`<vaadin-notification></vaadin-notification>`);
await nextFrame();
notification.open();
notification.renderer = (root) => {
render(html`Initial Content`, root);
Expand Down
3 changes: 3 additions & 0 deletions packages/notification/test/multiple-lit.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import './not-animated-styles.js';
import '../vaadin-lit-notification.js';
import './multiple.common.js';
3 changes: 3 additions & 0 deletions packages/notification/test/multiple-polymer.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import './not-animated-styles.js';
import '../vaadin-notification.js';
import './multiple.common.js';
Original file line number Diff line number Diff line change
@@ -1,20 +1,6 @@
import { expect } from '@vaadin/chai-plugins';
import { aTimeout, fixtureSync } from '@vaadin/testing-helpers';
import sinon from 'sinon';
import '../vaadin-notification.js';
import { css, registerStyles } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';

registerStyles(
'vaadin-notification-card',
css`
:host {
width: 200px;
background: lightgrey;
animation: none !important;
}
`,
{ moduleId: 'vaadin-notification-card-multiple-theme' },
);

describe('multiple notification', () => {
let wrapper, notifications, container, regions;
Expand Down
2 changes: 2 additions & 0 deletions packages/notification/test/notification-lit.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import '../vaadin-lit-notification.js';
import './notification.common.js';
2 changes: 2 additions & 0 deletions packages/notification/test/notification-polymer.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import '../vaadin-notification.js';
import './notification.common.js';
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import { expect } from '@vaadin/chai-plugins';
import { aTimeout, fixtureSync, isIOS, listenOnce } from '@vaadin/testing-helpers';
import { aTimeout, fixtureSync, isIOS, listenOnce, nextFrame } from '@vaadin/testing-helpers';
import sinon from 'sinon';
import '../vaadin-notification.js';

describe('vaadin-notification', () => {
let notification;

beforeEach(() => {
beforeEach(async () => {
notification = fixtureSync(`
<vaadin-notification duration="20"></vaadin-notification>
`);
await nextFrame();

notification.renderer = (root) => {
root.innerHTML = `Your work has been <strong>saved</strong>`;
Expand Down Expand Up @@ -85,7 +85,7 @@ describe('vaadin-notification', () => {
expect(document.body.querySelectorAll('vaadin-notification-container').length).to.be.equal(0);
});

it('should not be in the body when notification reopens', () => {
it('should be in the body when notification reopens', () => {
notification.close();
notification._removeNotificationCard();
notification.open();
Expand Down Expand Up @@ -223,8 +223,9 @@ describe('vaadin-notification', () => {
});

describe('theme', () => {
it('should propagate theme attribute to card', () => {
it('should propagate theme attribute to card', async () => {
notification.setAttribute('theme', 'foo');
await nextFrame();

expect(notification._card.getAttribute('theme')).to.equal('foo');
});
Expand Down
2 changes: 2 additions & 0 deletions packages/notification/test/renderer-lit.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import '../vaadin-lit-notification.js';
import './renderer.common.js';
2 changes: 2 additions & 0 deletions packages/notification/test/renderer-polymer.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import '../vaadin-notification.js';
import './renderer.common.js';
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import { expect } from '@vaadin/chai-plugins';
import { fixtureSync } from '@vaadin/testing-helpers';
import { fixtureSync, nextFrame } from '@vaadin/testing-helpers';
import sinon from 'sinon';
import '../vaadin-notification.js';

describe('renderer', () => {
describe('basic', () => {
let notification;
let rendererContent;

beforeEach(() => {
beforeEach(async () => {
rendererContent = document.createElement('p');
rendererContent.textContent = 'renderer-content';

notification = fixtureSync('<vaadin-notification></vaadin-notification>');
await nextFrame();

// Force sync card attaching and removal instead of waiting for the animation
sinon
Expand Down
2 changes: 2 additions & 0 deletions packages/notification/test/statichelper-lit.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import '../vaadin-lit-notification.js';
import './statichelper.common.js';
2 changes: 2 additions & 0 deletions packages/notification/test/statichelper-polymer.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import '../vaadin-notification.js';
import './statichelper.common.js';
Loading

0 comments on commit f7b2a1e

Please sign in to comment.