Add alert class
This commit is contained in:
parent
e57977f94b
commit
a0ef7f3854
@ -526,6 +526,43 @@ button > * {
|
|||||||
margin: 0 5px;
|
margin: 0 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.alert {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 50%;
|
||||||
|
width: 300px;
|
||||||
|
background: #666;
|
||||||
|
color: white;
|
||||||
|
font-family: "Open Sans", Verdana, sans-serif;
|
||||||
|
font-size: 0.8em;
|
||||||
|
line-height: 20px;
|
||||||
|
text-align: center;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
margin: 1em 0 1em -150px;
|
||||||
|
padding: 6px 20px;
|
||||||
|
border-radius: 5px;
|
||||||
|
border-top: 1px solid #555;
|
||||||
|
box-shadow: 10px 10px 10px -8px rgba(0, 0, 0, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert.error {
|
||||||
|
background: #d95b5b;
|
||||||
|
border-top: 1px solid #d05858;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert button {
|
||||||
|
position: absolute;
|
||||||
|
top: -1px;
|
||||||
|
right: 0;
|
||||||
|
border: none;
|
||||||
|
margin: 0;
|
||||||
|
height: 100%;
|
||||||
|
background: none;
|
||||||
|
font-weight: normal;
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
/* chart */
|
/* chart */
|
||||||
.ct-point {
|
.ct-point {
|
||||||
transition: 0.3s;
|
transition: 0.3s;
|
||||||
|
132
js/src/alert.js
Normal file
132
js/src/alert.js
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
/*
|
||||||
|
* μlogger
|
||||||
|
*
|
||||||
|
* Copyright(C) 2019 Bartek Fabiszewski (www.fabiszewski.net)
|
||||||
|
*
|
||||||
|
* This is free software; you can redistribute it and/or modify it under
|
||||||
|
* the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import uUtils from './utils.js';
|
||||||
|
|
||||||
|
export default class uAlert {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} AlertOptions
|
||||||
|
* @property {number} [autoClose=0] Optional autoclose delay time in ms, default 0 – no autoclose
|
||||||
|
* @property {string} [id] Optional box id
|
||||||
|
* @property {string} [class] Optional box class
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds alert box
|
||||||
|
* @param {string} message
|
||||||
|
* @param {AlertOptions} [options] Optional options
|
||||||
|
*/
|
||||||
|
constructor(message, options = {}) {
|
||||||
|
this.autoClose = options.autoClose || 0;
|
||||||
|
const html = `<div class="alert"><span>${message}</span></div>`;
|
||||||
|
this.box = uUtils.nodeFromHtml(html);
|
||||||
|
if (options.id) {
|
||||||
|
this.box.id = options.id;
|
||||||
|
}
|
||||||
|
if (options.class) {
|
||||||
|
this.box.classList.add(options.class);
|
||||||
|
}
|
||||||
|
if (this.autoClose === 0) {
|
||||||
|
const button = document.createElement('button');
|
||||||
|
button.setAttribute('type', 'button');
|
||||||
|
button.textContent = '×';
|
||||||
|
button.onclick = () => this.destroy();
|
||||||
|
this.box.appendChild(button);
|
||||||
|
}
|
||||||
|
this.closeHandle = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate new box top offset
|
||||||
|
* @return {number} Top offset
|
||||||
|
*/
|
||||||
|
static getPosition() {
|
||||||
|
const boxes = document.querySelectorAll('.alert');
|
||||||
|
const lastBox = boxes[boxes.length - 1];
|
||||||
|
let position = 0;
|
||||||
|
if (lastBox) {
|
||||||
|
const maxPosition = document.body.clientHeight - 100;
|
||||||
|
position = lastBox.getBoundingClientRect().bottom;
|
||||||
|
if (position > maxPosition) {
|
||||||
|
position = maxPosition;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return position;
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const top = uAlert.getPosition();
|
||||||
|
if (top) {
|
||||||
|
this.box.style.top = `${top}px`;
|
||||||
|
}
|
||||||
|
document.body.appendChild(this.box);
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy() {
|
||||||
|
if (this.closeHandle) {
|
||||||
|
clearTimeout(this.closeHandle);
|
||||||
|
this.closeHandle = null;
|
||||||
|
}
|
||||||
|
if (this.box) {
|
||||||
|
if (document.body.contains(this.box)) {
|
||||||
|
document.body.removeChild(this.box);
|
||||||
|
}
|
||||||
|
this.box = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show alert box
|
||||||
|
* @param {string} message
|
||||||
|
* @param {AlertOptions} [options] Optional options
|
||||||
|
* @return uAlert
|
||||||
|
*/
|
||||||
|
static show(message, options) {
|
||||||
|
const box = new uAlert(message, options);
|
||||||
|
box.render();
|
||||||
|
if (box.autoClose) {
|
||||||
|
box.closeHandle = setTimeout(() => box.destroy(), box.autoClose);
|
||||||
|
}
|
||||||
|
return box;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show alert error box
|
||||||
|
* @param {string} message
|
||||||
|
* @param {Error=} e Optional error to be logged to console
|
||||||
|
* @return uAlert
|
||||||
|
*/
|
||||||
|
static error(message, e) {
|
||||||
|
if (e instanceof Error) {
|
||||||
|
console.error(`${e.name}: ${e.message} (${e.stack})`);
|
||||||
|
}
|
||||||
|
return this.show(message, { class: 'error' });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show alert toast box
|
||||||
|
* @param {string} message
|
||||||
|
* @return uAlert
|
||||||
|
*/
|
||||||
|
static toast(message) {
|
||||||
|
return this.show(message, { class: 'toast', autoClose: 10000 });
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -280,22 +280,6 @@ export default class uUtils {
|
|||||||
window.location.assign(url);
|
window.location.assign(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {(Error|string)} e
|
|
||||||
* @param {string=} message
|
|
||||||
*/
|
|
||||||
static error(e, message) {
|
|
||||||
let details;
|
|
||||||
if (e instanceof Error) {
|
|
||||||
details = `${e.name}: ${e.message} (${e.stack})`;
|
|
||||||
} else {
|
|
||||||
details = e;
|
|
||||||
message = e;
|
|
||||||
}
|
|
||||||
console.error(details);
|
|
||||||
alert(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Degrees to radians
|
* Degrees to radians
|
||||||
* @param {number} degrees
|
* @param {number} degrees
|
||||||
|
143
js/test/alert.test.js
Normal file
143
js/test/alert.test.js
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
/*
|
||||||
|
* μlogger
|
||||||
|
*
|
||||||
|
* Copyright(C) 2020 Bartek Fabiszewski (www.fabiszewski.net)
|
||||||
|
*
|
||||||
|
* This is free software; you can redistribute it and/or modify it under
|
||||||
|
* the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import uAlert from '../src/alert.js';
|
||||||
|
|
||||||
|
describe('Alert tests', () => {
|
||||||
|
|
||||||
|
const message = 'test message';
|
||||||
|
let alert;
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
if (alert) {
|
||||||
|
alert.destroy();
|
||||||
|
}
|
||||||
|
document.body.innerText = '';
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create alert box with message', () => {
|
||||||
|
// when
|
||||||
|
alert = new uAlert(message);
|
||||||
|
const textEl = alert.box.firstChild;
|
||||||
|
// then
|
||||||
|
expect(textEl.innerText).toBe(message);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create alert box with autoClose option', () => {
|
||||||
|
// given
|
||||||
|
const autoClose = 1;
|
||||||
|
const options = { autoClose }
|
||||||
|
// when
|
||||||
|
alert = new uAlert(message, options);
|
||||||
|
const textEl = alert.box.firstChild;
|
||||||
|
// then
|
||||||
|
expect(textEl.innerText).toBe(message);
|
||||||
|
expect(alert.autoClose).toBe(autoClose);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create alert box with id option', () => {
|
||||||
|
// given
|
||||||
|
const id = 'testId';
|
||||||
|
const options = { id }
|
||||||
|
// when
|
||||||
|
alert = new uAlert(message, options);
|
||||||
|
const boxEl = alert.box;
|
||||||
|
const textEl = alert.box.firstChild;
|
||||||
|
// then
|
||||||
|
expect(textEl.innerText).toBe(message);
|
||||||
|
expect(boxEl.id).toBe(id);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create alert box with class option', () => {
|
||||||
|
// given
|
||||||
|
const className = 'test_class';
|
||||||
|
const options = { class: className }
|
||||||
|
// when
|
||||||
|
alert = new uAlert(message, options);
|
||||||
|
const boxEl = alert.box;
|
||||||
|
const textEl = alert.box.firstChild;
|
||||||
|
// then
|
||||||
|
expect(textEl.innerText).toBe(message);
|
||||||
|
expect(boxEl.classList).toContain(className);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render and destroy alert box', () => {
|
||||||
|
// given
|
||||||
|
const id = 'testId';
|
||||||
|
const options = { id }
|
||||||
|
alert = new uAlert(message, options);
|
||||||
|
|
||||||
|
// when
|
||||||
|
alert.render();
|
||||||
|
// then
|
||||||
|
expect(document.querySelector(`#${id}`)).not.toBeNull();
|
||||||
|
|
||||||
|
// when
|
||||||
|
alert.destroy();
|
||||||
|
// then
|
||||||
|
expect(document.querySelector(`#${id}`)).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should show and autoclose alert box', (done) => {
|
||||||
|
// given
|
||||||
|
const id = 'testId';
|
||||||
|
const options = { id: id, autoClose: 50 }
|
||||||
|
|
||||||
|
// when
|
||||||
|
alert = uAlert.show(message, options);
|
||||||
|
// then
|
||||||
|
expect(document.querySelector(`#${id}`)).not.toBeNull();
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
expect(document.querySelector(`#${id}`)).toBeNull();
|
||||||
|
done();
|
||||||
|
}, 100);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should close alert box on close button click', (done) => {
|
||||||
|
// given
|
||||||
|
const id = 'testId';
|
||||||
|
const options = { id }
|
||||||
|
alert = uAlert.show(message, options);
|
||||||
|
const closeButton = alert.box.querySelector('button');
|
||||||
|
// when
|
||||||
|
closeButton.click();
|
||||||
|
// then
|
||||||
|
setTimeout(() => {
|
||||||
|
expect(document.querySelector(`#${id}`)).toBeNull();
|
||||||
|
done();
|
||||||
|
}, 100);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should show error alert box', () => {
|
||||||
|
// when
|
||||||
|
alert = uAlert.error(message);
|
||||||
|
// then
|
||||||
|
expect(document.querySelector('.alert.error')).not.toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should show toast alert box', () => {
|
||||||
|
// when
|
||||||
|
alert = uAlert.toast(message);
|
||||||
|
// then
|
||||||
|
expect(document.querySelector('.alert.toast')).not.toBeNull();
|
||||||
|
expect(alert.autoClose).toBeGreaterThan(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
Loading…
x
Reference in New Issue
Block a user