From b31514d9c69cd6603ef225acd6cf8a2412c42a01 Mon Sep 17 00:00:00 2001 From: Felix Hamann Date: Tue, 28 May 2019 00:19:03 +0200 Subject: [PATCH 01/13] allow js decorator functions --- .babelrc | 12 +++++------- .eslintrc.json | 5 ++++- package-lock.json | 43 +++++++++++++++++++++++++++++++++++++++++++ package.json | 3 +++ 4 files changed, 55 insertions(+), 8 deletions(-) diff --git a/.babelrc b/.babelrc index 352d48a80..857e7ba32 100644 --- a/.babelrc +++ b/.babelrc @@ -1,11 +1,9 @@ { "presets": [ - [ - "@babel/preset-env", - { - "useBuiltIns": "usage" - } - ] + ["@babel/preset-env", { "useBuiltIns": "usage" }] ], - "plugins": ["@babel/plugin-proposal-class-properties"] + "plugins": [ + ["@babel/plugin-proposal-decorators", { "legacy": true }], + ["@babel/plugin-proposal-class-properties", { "loose": true }] + ] } diff --git a/.eslintrc.json b/.eslintrc.json index 8c0382cfa..06d95155c 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -13,7 +13,10 @@ }, "parser": "babel-eslint", "parserOptions": { - "ecmaVersion": 2018 + "ecmaVersion": 2018, + "ecmaFeatures": { + "legacyDecorators": true + } }, "rules": { "no-console": "off", diff --git a/package-lock.json b/package-lock.json index b757dbbcd..ef0a855d9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -338,6 +338,17 @@ "@babel/helper-plugin-utils": "^7.0.0" } }, + "@babel/plugin-proposal-decorators": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.4.4.tgz", + "integrity": "sha512-z7MpQz3XC/iQJWXH9y+MaWcLPNSMY9RQSthrLzak8R8hCj0fuyNk+Dzi9kfNe/JxxlWQ2g7wkABbgWjW36MTcw==", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.4.4", + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-syntax-decorators": "^7.2.0" + } + }, "@babel/plugin-proposal-json-strings": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.2.0.tgz", @@ -388,6 +399,15 @@ "@babel/helper-plugin-utils": "^7.0.0" } }, + "@babel/plugin-syntax-decorators": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.2.0.tgz", + "integrity": "sha512-38QdqVoXdHUQfTpZo3rQwqQdWtCn5tMv4uV6r2RMfTqNBuv4ZBhz79SfaQWKTVmxHjeFv/DnXVC/+agHCklYWA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, "@babel/plugin-syntax-json-strings": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.2.0.tgz", @@ -1655,12 +1675,29 @@ "babel-runtime": "^6.22.0" } }, + "babel-plugin-syntax-decorators": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-decorators/-/babel-plugin-syntax-decorators-6.13.0.tgz", + "integrity": "sha1-MSVjtNvePMgGzuPkFszurd0RrAs=", + "dev": true + }, "babel-plugin-syntax-dynamic-import": { "version": "6.18.0", "resolved": "https://registry.npmjs.org/babel-plugin-syntax-dynamic-import/-/babel-plugin-syntax-dynamic-import-6.18.0.tgz", "integrity": "sha1-jWomIpyDdFqZgqRBBRVyyqF5sdo=", "dev": true }, + "babel-plugin-transform-decorators-legacy": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-decorators-legacy/-/babel-plugin-transform-decorators-legacy-1.3.5.tgz", + "integrity": "sha512-jYHwjzRXRelYQ1uGm353zNzf3QmtdCfvJbuYTZ4gKveK7M9H1fs3a5AKdY1JUDl0z97E30ukORW1dzhWvsabtA==", + "dev": true, + "requires": { + "babel-plugin-syntax-decorators": "^6.1.18", + "babel-runtime": "^6.2.0", + "babel-template": "^6.3.0" + } + }, "babel-plugin-transform-es2015-arrow-functions": { "version": "6.22.0", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz", @@ -6295,6 +6332,12 @@ "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", "dev": true }, + "lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=", + "dev": true + }, "lodash.tail": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.tail/-/lodash.tail-4.1.1.tgz", diff --git a/package.json b/package.json index 10ab7582a..a8744aec4 100644 --- a/package.json +++ b/package.json @@ -37,12 +37,14 @@ "@babel/cli": "^7.4.4", "@babel/core": "^7.4.5", "@babel/plugin-proposal-class-properties": "^7.4.4", + "@babel/plugin-proposal-decorators": "^7.4.4", "@babel/preset-env": "^7.4.5", "autoprefixer": "^9.5.1", "babel-core": "^6.26.3", "babel-eslint": "^10.0.1", "babel-loader": "^8.0.6", "babel-plugin-syntax-dynamic-import": "^6.18.0", + "babel-plugin-transform-decorators-legacy": "^1.3.5", "babel-preset-es2015": "^6.24.1", "css-loader": "^2.1.1", "eslint": "^5.16.0", @@ -57,6 +59,7 @@ "karma-mocha-reporter": "^2.2.5", "karma-webpack": "^3.0.5", "lint-staged": "^8.1.7", + "lodash.debounce": "^4.0.8", "node-sass": "^4.12.0", "npm-run-all": "^4.1.5", "null-loader": "^2.0.0", From befa9f394129336110a60ed59a74f5223fc8c2ea Mon Sep 17 00:00:00 2001 From: Felix Hamann Date: Tue, 28 May 2019 00:19:39 +0200 Subject: [PATCH 02/13] refactor js utilities to use class syntax and Utility decorator --- frontend/src/app.js | 5 +- frontend/src/app.spec.js | 6 + frontend/src/core/utility.js | 22 + .../services/util-registry/util-registry.js | 8 +- .../util-registry/util-registry.spec.js | 6 - frontend/src/utils/alerts/alerts.js | 207 +++--- frontend/src/utils/alerts/alerts.md | 35 + frontend/src/utils/alerts/alerts.spec.js | 19 +- frontend/src/utils/asidenav/asidenav.js | 98 +-- frontend/src/utils/asidenav/asidenav.md | 21 + frontend/src/utils/async-form/async-form.js | 126 ++-- frontend/src/utils/async-form/async-form.md | 17 + frontend/src/utils/async-table/async-table.js | 344 +++++----- frontend/src/utils/async-table/async-table.md | 7 + frontend/src/utils/check-all/check-all.js | 132 ++-- frontend/src/utils/check-all/check-all.md | 9 + frontend/src/utils/form/auto-submit-button.js | 29 + frontend/src/utils/form/auto-submit-button.md | 11 + frontend/src/utils/form/auto-submit-input.js | 47 ++ frontend/src/utils/form/auto-submit-input.md | 9 + frontend/src/utils/form/datepicker.js | 62 ++ frontend/src/utils/form/datepicker.md | 8 + frontend/src/utils/form/form-error-remover.js | 46 ++ frontend/src/utils/form/form-error-remover.md | 8 + frontend/src/utils/form/form.js | 615 +----------------- .../src/utils/form/interactive-fieldset.js | 97 +++ .../src/utils/form/interactive-fieldset.md | 35 + .../src/utils/form/navigate-away-prompt.js | 64 ++ .../src/utils/form/navigate-away-prompt.md | 11 + .../src/utils/form/reactive-submit-button.js | 85 +++ .../src/utils/form/reactive-submit-button.md | 17 + frontend/src/utils/inputs/checkbox.js | 43 ++ frontend/src/utils/inputs/checkbox.md | 10 + frontend/src/utils/inputs/file-input.js | 99 +++ frontend/src/utils/inputs/file-input.md | 23 + frontend/src/utils/inputs/inputs.js | 200 +----- frontend/src/utils/mass-input/mass-input.js | 179 +++-- frontend/src/utils/mass-input/mass-input.md | 17 + frontend/src/utils/modal/modal.js | 207 +++--- frontend/src/utils/modal/modal.md | 16 + frontend/src/utils/show-hide/show-hide.js | 120 ++-- frontend/src/utils/show-hide/show-hide.md | 21 + frontend/src/utils/tooltips/tooltips.js | 9 +- frontend/src/utils/utils.js | 58 +- 44 files changed, 1564 insertions(+), 1644 deletions(-) create mode 100644 frontend/src/core/utility.js create mode 100644 frontend/src/utils/alerts/alerts.md create mode 100644 frontend/src/utils/asidenav/asidenav.md create mode 100644 frontend/src/utils/async-form/async-form.md create mode 100644 frontend/src/utils/async-table/async-table.md create mode 100644 frontend/src/utils/check-all/check-all.md create mode 100644 frontend/src/utils/form/auto-submit-button.js create mode 100644 frontend/src/utils/form/auto-submit-button.md create mode 100644 frontend/src/utils/form/auto-submit-input.js create mode 100644 frontend/src/utils/form/auto-submit-input.md create mode 100644 frontend/src/utils/form/datepicker.js create mode 100644 frontend/src/utils/form/datepicker.md create mode 100644 frontend/src/utils/form/form-error-remover.js create mode 100644 frontend/src/utils/form/form-error-remover.md create mode 100644 frontend/src/utils/form/interactive-fieldset.js create mode 100644 frontend/src/utils/form/interactive-fieldset.md create mode 100644 frontend/src/utils/form/navigate-away-prompt.js create mode 100644 frontend/src/utils/form/navigate-away-prompt.md create mode 100644 frontend/src/utils/form/reactive-submit-button.js create mode 100644 frontend/src/utils/form/reactive-submit-button.md create mode 100644 frontend/src/utils/inputs/checkbox.js create mode 100644 frontend/src/utils/inputs/checkbox.md create mode 100644 frontend/src/utils/inputs/file-input.js create mode 100644 frontend/src/utils/inputs/file-input.md create mode 100644 frontend/src/utils/mass-input/mass-input.md create mode 100644 frontend/src/utils/modal/modal.md create mode 100644 frontend/src/utils/show-hide/show-hide.md diff --git a/frontend/src/app.js b/frontend/src/app.js index 6c7fa215f..b2db86c31 100644 --- a/frontend/src/app.js +++ b/frontend/src/app.js @@ -2,6 +2,7 @@ import { HttpClient } from './services/http-client/http-client'; import { HtmlHelpers } from './services/html-helpers/html-helpers'; import { I18n } from './services/i18n/i18n'; import { UtilRegistry } from './services/util-registry/util-registry'; +import { isValidUtility } from './core/utility'; export class App { httpClient = new HttpClient(); @@ -11,6 +12,8 @@ export class App { constructor() { this.utilRegistry.setApp(this); + + document.addEventListener('DOMContentLoaded', () => this.utilRegistry.setupAll()); } registerUtilities(utils) { @@ -18,7 +21,7 @@ export class App { throw new Error('Utils are expected to be passed as array!'); } - utils.forEach((util) => { + utils.filter(isValidUtility).forEach((util) => { this.utilRegistry.register(util); }); } diff --git a/frontend/src/app.spec.js b/frontend/src/app.spec.js index 8ce962b2d..21a381a68 100644 --- a/frontend/src/app.spec.js +++ b/frontend/src/app.spec.js @@ -16,6 +16,12 @@ describe('App', () => { expect(app).toBeTruthy(); }); + it('should setup all utlites when page is done loading', () => { + spyOn(app.utilRegistry, 'setupAll'); + document.dispatchEvent(new Event('DOMContentLoaded')); + expect(app.utilRegistry.setupAll).toHaveBeenCalled(); + }); + describe('provides services', () => { it('HttpClient as httpClient', () => { expect(app.httpClient).toBeTruthy(); diff --git a/frontend/src/core/utility.js b/frontend/src/core/utility.js new file mode 100644 index 000000000..6b74920dd --- /dev/null +++ b/frontend/src/core/utility.js @@ -0,0 +1,22 @@ +export function isValidUtility(utility) { + if (!utility) { + return false; + } + + if (Utility.selector) { + return false; + } + + return true; +}; + +export function Utility(metadata) { + if (!metadata.selector) { + throw new Error('Utility needs to have a selector!'); + } + + return function (target) { + target.selector = metadata.selector; + target.isUtility = true; + }; +}; diff --git a/frontend/src/services/util-registry/util-registry.js b/frontend/src/services/util-registry/util-registry.js index 18b656b8c..5e71554bc 100644 --- a/frontend/src/services/util-registry/util-registry.js +++ b/frontend/src/services/util-registry/util-registry.js @@ -6,10 +6,6 @@ export class UtilRegistry { _activeUtilInstances = []; _appInstance; - constructor() { - document.addEventListener('DOMContentLoaded', () => this.setupAll()); - } - /** * function registerUtil * @@ -63,14 +59,14 @@ export class UtilRegistry { console.log('setting up util', { util }); } - if (util && typeof util.setup === 'function') { + if (util) { const elements = this._findUtilElements(util, scope); elements.forEach((element) => { let utilInstance = null; try { - utilInstance = util.setup(element, this._appInstance); + utilInstance = new util(element, this._appInstance); } catch(err) { if (DEBUG_MODE > 0) { console.warn('Error while trying to initialize a utility!', { util , element, err }); diff --git a/frontend/src/services/util-registry/util-registry.spec.js b/frontend/src/services/util-registry/util-registry.spec.js index e3f69f63c..2e4afedf2 100644 --- a/frontend/src/services/util-registry/util-registry.spec.js +++ b/frontend/src/services/util-registry/util-registry.spec.js @@ -21,12 +21,6 @@ describe('UtilRegistry', () => { expect(utilRegistry).toBeTruthy(); }); - it('should setup all utlites when page is done loading', () => { - spyOn(utilRegistry, 'setupAll'); - document.dispatchEvent(new Event('DOMContentLoaded')); - expect(utilRegistry.setupAll).toHaveBeenCalled(); - }); - describe('register()', () => { it('should allow to add utilities', () => { utilRegistry.register(TEST_UTILS[0]); diff --git a/frontend/src/utils/alerts/alerts.js b/frontend/src/utils/alerts/alerts.js index 3e49080bc..e7e04ddbb 100644 --- a/frontend/src/utils/alerts/alerts.js +++ b/frontend/src/utils/alerts/alerts.js @@ -1,191 +1,158 @@ +import { Utility } from '../../core/utility'; import './alerts.scss'; -/** - * - * Alerts Utility - * makes alerts interactive - * - * Attribute: uw-alerts - * - * Types of alerts: - * [default] - * Regular Info Alert - * Disappears automatically after 30 seconds - * Disappears after x seconds if explicitly specified via data-decay='x' - * Can be told not to disappear with data-decay='0' - * - * [success] - * Currently no special visual appearance - * Disappears automatically after 30 seconds - * - * [warning] - * Will be coloured warning-orange regardless of user's selected theme - * Does not disappear - * - * [error] - * Will be coloured error-red regardless of user's selected theme - * Does not disappear - * - * Example usage: - *
- *
- *
- *
- *
- *
- * This is some information - * - */ +const ALERTS_INITIALIZED_CLASS = 'alerts--initialized'; +const ALERTS_ELEVATED_CLASS = 'alerts--elevated'; +const ALERTS_TOGGLER_CLASS = 'alerts__toggler'; +const ALERTS_TOGGLER_VISIBLE_CLASS = 'alerts__toggler--visible'; +const ALERTS_TOGGLER_APPEAR_DELAY = 120; -var ALERTS_UTIL_NAME = 'alerts'; -var ALERTS_UTIL_SELECTOR = '[uw-alerts]'; +const ALERT_CLASS = 'alert'; +const ALERT_INITIALIZED_CLASS = 'alert--initialized'; +const ALERT_CLOSER_CLASS = 'alert__closer'; +const ALERT_ICON_CLASS = 'alert__icon'; +const ALERT_CONTENT_CLASS = 'alert__content'; +const ALERT_INVISIBLE_CLASS = 'alert--invisible'; +const ALERT_AUTO_HIDE_DELAY = 10; +const ALERT_AUTOCLOSING_MATCHER = '.alert-info, .alert-success'; -var ALERTS_INITIALIZED_CLASS = 'alerts--initialized'; -var ALERTS_ELEVATED_CLASS = 'alerts--elevated'; -var ALERTS_TOGGLER_CLASS = 'alerts__toggler'; -var ALERTS_TOGGLER_VISIBLE_CLASS = 'alerts__toggler--visible'; -var ALERTS_TOGGLER_APPEAR_DELAY = 120; +@Utility({ + selector: '[uw-alerts]', +}) +export class Alerts { + _togglerCheckRequested = false; + _togglerElement; + _alertElements; -var ALERT_CLASS = 'alert'; -var ALERT_INITIALIZED_CLASS = 'alert--initialized'; -var ALERT_CLOSER_CLASS = 'alert__closer'; -var ALERT_ICON_CLASS = 'alert__icon'; -var ALERT_CONTENT_CLASS = 'alert__content'; -var ALERT_INVISIBLE_CLASS = 'alert--invisible'; -var ALERT_AUTO_HIDE_DELAY = 10; -var ALERT_AUTOCLOSING_MATCHER = '.alert-info, .alert-success'; + _element; + _app; -var alertsUtil = function(element, app) { - var togglerCheckRequested = false; - var togglerElement; - var alertElements; - - function init() { + constructor(element, app) { if (!element) { throw new Error('Alerts util has to be called with an element!'); } - if (element.classList.contains(ALERTS_INITIALIZED_CLASS)) { + this._element = element; + this._app = app; + + if (this._element.classList.contains(ALERTS_INITIALIZED_CLASS)) { return false; } - togglerElement = element.querySelector('.' + ALERTS_TOGGLER_CLASS); - alertElements = gatherAlertElements(); + this._togglerElement = this._element.querySelector('.' + ALERTS_TOGGLER_CLASS); + this._alertElements = this._gatherAlertElements(); - initToggler(); - initAlerts(); + if (this._togglerElement) { + this._initToggler(); + } + + this._initAlerts(); // register http client interceptor to filter out Alerts Header - setupHttpInterceptor(); + this._setupHttpInterceptor(); // mark initialized - element.classList.add(ALERTS_INITIALIZED_CLASS); - - return { - name: ALERTS_UTIL_NAME, - element: element, - destroy: function() {}, - }; + this._element.classList.add(ALERTS_INITIALIZED_CLASS); } - function gatherAlertElements() { - return Array.from(element.querySelectorAll('.' + ALERT_CLASS)).filter(function(alert) { + destroy() { + console.log('TBD: Destroy Alert'); + } + + _gatherAlertElements() { + return Array.from(this._element.querySelectorAll('.' + ALERT_CLASS)).filter(function(alert) { return !alert.classList.contains(ALERT_INITIALIZED_CLASS); }); } - function initToggler() { - togglerElement.addEventListener('click', function() { - alertElements.forEach(function(alertEl) { - toggleAlert(alertEl, true); - }); - togglerElement.classList.remove(ALERTS_TOGGLER_VISIBLE_CLASS); + _initToggler() { + this._togglerElement.addEventListener('click', () => { + this._alertElements.forEach((alertEl) => this._toggleAlert(alertEl, true)); + this._togglerElement.classList.remove(ALERTS_TOGGLER_VISIBLE_CLASS); }); } - function initAlerts() { - alertElements.forEach(initAlert); + _initAlerts() { + this._alertElements.forEach((alert) => this._initAlert(alert)); } - function initAlert(alertElement) { - var autoHideDelay = ALERT_AUTO_HIDE_DELAY; + _initAlert(alertElement) { + let autoHideDelay = ALERT_AUTO_HIDE_DELAY; if (alertElement.dataset.decay) { autoHideDelay = parseInt(alertElement.dataset.decay, 10); } - var closeEl = alertElement.querySelector('.' + ALERT_CLOSER_CLASS); - closeEl.addEventListener('click', function() { - toggleAlert(alertElement); + const closeEl = alertElement.querySelector('.' + ALERT_CLOSER_CLASS); + closeEl.addEventListener('click', () => { + this._toggleAlert(alertElement); }); if (autoHideDelay > 0 && alertElement.matches(ALERT_AUTOCLOSING_MATCHER)) { - window.setTimeout(function() { - toggleAlert(alertElement); - }, autoHideDelay * 1000); + window.setTimeout(() => this._toggleAlert(alertElement), autoHideDelay * 1000); } } - function toggleAlert(alertEl, visible) { + _toggleAlert(alertEl, visible) { alertEl.classList.toggle(ALERT_INVISIBLE_CLASS, !visible); - checkToggler(); + this._checkToggler(); } - function checkToggler() { - if (togglerCheckRequested) { + _checkToggler() { + if (this._togglerCheckRequested) { return; } - var alertsHidden = alertElements.reduce(function(acc, alert) { + const alertsHidden = this._alertElements.reduce(function(acc, alert) { return acc && alert.classList.contains(ALERT_INVISIBLE_CLASS); }, true); - window.setTimeout(function() { - togglerElement.classList.toggle(ALERTS_TOGGLER_VISIBLE_CLASS, alertsHidden); - togglerCheckRequested = false; + window.setTimeout(() => { + this._togglerElement.classList.toggle(ALERTS_TOGGLER_VISIBLE_CLASS, alertsHidden); + this._togglerCheckRequested = false; }, ALERTS_TOGGLER_APPEAR_DELAY); } - function setupHttpInterceptor() { - app.httpClient.addResponseInterceptor(responseInterceptor.bind(this)); + _setupHttpInterceptor() { + this._app.httpClient.addResponseInterceptor(this._responseInterceptor.bind(this)); } - function elevateAlerts() { - element.classList.add(ALERTS_ELEVATED_CLASS); + _elevateAlerts() { + this._element.classList.add(ALERTS_ELEVATED_CLASS); } - function responseInterceptor(response) { - var alerts; - for (var header of response.headers) { + _responseInterceptor = (response) => { + let alerts; + for (const header of response.headers) { if (header[0] === 'alerts') { - var decodedHeader = decodeURIComponent(header[1]); + const decodedHeader = decodeURIComponent(header[1]); alerts = JSON.parse(decodedHeader); break; } } if (alerts) { - alerts.forEach(function(alert) { - var alertElement = createAlertElement(alert.status, alert.content); - element.appendChild(alertElement); - alertElements.push(alertElement); - initAlert(alertElement); + alerts.forEach((alert) => { + const alertElement = this._createAlertElement(alert.status, alert.content); + this._element.appendChild(alertElement); + this._alertElements.push(alertElement); + this._initAlert(alertElement); }); - elevateAlerts(); + this._elevateAlerts(); } } - function createAlertElement(type, content) { - var alertElement = document.createElement('div'); + _createAlertElement(type, content) { + const alertElement = document.createElement('div'); alertElement.classList.add(ALERT_CLASS, 'alert-' + type); - var alertCloser = document.createElement('div'); + const alertCloser = document.createElement('div'); alertCloser.classList.add(ALERT_CLOSER_CLASS); - var alertIcon = document.createElement('div'); + const alertIcon = document.createElement('div'); alertIcon.classList.add(ALERT_ICON_CLASS); - var alertContent = document.createElement('div'); + const alertContent = document.createElement('div'); alertContent.classList.add(ALERT_CONTENT_CLASS); alertContent.innerHTML = content; @@ -195,12 +162,4 @@ var alertsUtil = function(element, app) { return alertElement; } - - return init(); -}; - -export default { - name: ALERTS_UTIL_NAME, - selector: ALERTS_UTIL_SELECTOR, - setup: alertsUtil, -}; +} diff --git a/frontend/src/utils/alerts/alerts.md b/frontend/src/utils/alerts/alerts.md new file mode 100644 index 000000000..e3b36feed --- /dev/null +++ b/frontend/src/utils/alerts/alerts.md @@ -0,0 +1,35 @@ +# Alerts + +Makes alerts interactive. + +## Attribute: `uw-alerts` + +## Types of alerts: +- `default`\ + Regular Info Alert + Disappears automatically after 30 seconds + Disappears after x seconds if explicitly specified via data-decay='x' + Can be told not to disappear with data-decay='0' + +- `success`\ + Currently no special visual appearance + Disappears automatically after 30 seconds + +- `warning`\ + Will be coloured warning-orange regardless of user's selected theme + Does not disappear + +- `error`\ + Will be coloured error-red regardless of user's selected theme + Does not disappear + +## Example usage: +```html +
+
+
+
+
+
+ This is some information +``` diff --git a/frontend/src/utils/alerts/alerts.spec.js b/frontend/src/utils/alerts/alerts.spec.js index 65a4c4a72..6d0440391 100644 --- a/frontend/src/utils/alerts/alerts.spec.js +++ b/frontend/src/utils/alerts/alerts.spec.js @@ -1,8 +1,21 @@ -import alerts from "./alerts"; +import { Alerts } from "./alerts"; + +const MOCK_APP = { + httpClient: { + addResponseInterceptor: () => {}, + }, +}; describe('Alerts', () => { - it('should be called alerts', () => { - expect(alerts.name).toMatch('alerts'); + let alerts; + + beforeEach(() => { + const element = document.createElement('div'); + alerts = new Alerts(element, MOCK_APP); + }); + + it('should create', () => { + expect(alerts).toBeTruthy(); }); }); diff --git a/frontend/src/utils/asidenav/asidenav.js b/frontend/src/utils/asidenav/asidenav.js index c5ba552c9..09d295063 100644 --- a/frontend/src/utils/asidenav/asidenav.js +++ b/frontend/src/utils/asidenav/asidenav.js @@ -1,89 +1,69 @@ +import { Utility } from '../../core/utility'; import './asidenav.scss'; -/** - * - * Asidenav Utility - * Correctly positions hovered asidenav submenus and handles the favorites button on mobile - * - * Attribute: uw-asidenav - * - * Example usage: - *
- *
- *
- *