diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5c3f9ba7..48f06362 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -47,7 +47,7 @@ jobs: run: | pip install -U pip wheel setuptools pip install -U -r requirements-test.txt - sudo npm install -g jshint stylelint + sudo npm install -g prettier pip install -e .[qa,rest,selenium] pip install ${{ matrix.django-version }} diff --git a/.jshintrc b/.jshintrc deleted file mode 100644 index f05beb5f..00000000 --- a/.jshintrc +++ /dev/null @@ -1,23 +0,0 @@ -{ - "unused": true, - "esversion": 6, - "curly": true, - "strict": "global", - "globals": { - "django": true, - "gettext": true, - "ReconnectingWebSocket": true, - "notificationApiHost": true, - "notificationSound": true, - "notificationSocket": true, - "owNotifyObjectId": true, - "owNotifyAppLabel": true, - "owNotifyModelName": true, - "owIsChangeForm": true, - "getAbsoluteUrl": true, - "dateTimeStampToDateTimeLocaleString": true, - "search_to_hash": true, - "hash_to_search": true - }, - "browser": true -} diff --git a/.stylelintrc.json b/.stylelintrc.json deleted file mode 100644 index 54c467e9..00000000 --- a/.stylelintrc.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "rules": { - "block-no-empty": null, - "color-no-invalid-hex": true, - "comment-empty-line-before": ["always", { - "ignore": ["stylelint-commands", "after-comment"] - }], - "rule-empty-line-before": ["never-multi-line", { - "except": ["first-nested"], - "ignore": ["after-comment", "inside-block"] - }], - "unit-allowed-list": ["em", "rem", "%", "s", "px", "vh", "deg", "dpi", "fr"], - "property-no-unknown": true - } -} diff --git a/docs/developer/installation.rst b/docs/developer/installation.rst index 4efb62fb..83bdada8 100644 --- a/docs/developer/installation.rst +++ b/docs/developer/installation.rst @@ -52,7 +52,7 @@ Install development dependencies: pip install -e .[qa,rest] pip install -r requirements-test.txt - sudo npm install -g jshint stylelint + sudo npm install -g prettier Set up the git *pre-push* hook to run tests and QA checks automatically right before the git push action, so that if anything fails the push diff --git a/docs/developer/qa-checks.rst b/docs/developer/qa-checks.rst index 7651d12f..f9851c92 100644 --- a/docs/developer/qa-checks.rst +++ b/docs/developer/qa-checks.rst @@ -20,9 +20,9 @@ It runs ``isort`` and ``black`` to format python code (these two dependencies are required and installed automatically when running ``pip install openwisp-utils[qa]``). -The ``stylelint`` and ``jshint`` programs are used to perform style checks -on CSS and JS code respectively, but they are optional: if ``stylelint`` -and/or ``jshint`` are not installed, the check(s) will be skipped. +The ``prettier`` programs is used to perform style checks on CSS and JS +code, but it is optional: if ``prettier`` is not installed, the check(s) +will be skipped. ``openwisp-qa-check`` --------------------- @@ -38,9 +38,9 @@ Shell script to run the following quality assurance checks: - ``isort`` - Sorts python imports alphabetically, and separated into sections - ``black`` - Formats python code using a common standard -- ``csslinter`` - Formats and checks CSS code using stylelint common +- ``csslinter`` - Formats and checks CSS code using prettier common standard -- ``jslinter`` - Checks Javascript code using jshint common standard +- ``jslinter`` - Checks Javascript code using prettier common standard If a check requires a flag, it can be passed forward in the same way. diff --git a/openwisp-qa-check b/openwisp-qa-check index 890bf40c..c94f9f3f 100755 --- a/openwisp-qa-check +++ b/openwisp-qa-check @@ -41,9 +41,9 @@ show_help() { printf "ReStructuredText check options:\n" printf " --skip-checkrst\t\t: Skip ReStructuredText check\n" printf "CSSlinter check options:\n" - printf " --csslinter\t\t\t: Runs stylelint check\n" + printf " --csslinter\t\t\t: Runs prettier check on css files\n" printf "Jslinter check options:\n" - printf " --jslinter\t\t\t: Runs jshint check\n" + printf " --jslinter\t\t\t: Runs prettier check on javascript files\n" } echoerr() { echo "$@" 1>&2; } @@ -67,13 +67,13 @@ runcheckendline() { fi } -runstylelintcheck() { - package='stylelint' - if which stylelint > /dev/null; then - stylelint verbose $(find . -type f -name "*.css") && - echo "SUCCESS: Stylelint check successful!" || +runcssprettiercheck() { + package='prettier' + if which prettier > /dev/null; then + prettier $(find . -type f -name "*.css") --check && + echo "SUCCESS: Prettier check successful!" || { - echoerr "ERROR: Stylelint check failed! Hint: did you forget to run openwisp-qa-format?" + echoerr "ERROR: Prettier check failed! Hint: did you forget to run openwisp-qa-format?" FAILURE=1 } else @@ -82,13 +82,13 @@ runstylelintcheck() { fi } -runjshintcheck() { - package='jshint' - if which jshint > /dev/null; then - jshint --verbose $(find . -type f -name "*.js" -a ! -path "*vendor/*.js") && - echo "SUCCESS: Jshint check successful!" || +runjsprettiercheck() { + package='prettier' + if which prettier > /dev/null; then + prettier $(find . -type f -name "*.js" -a ! -path "*vendor/*.js") --check && + echo "SUCCESS: Prettier check successful!" || { - echoerr "ERROR: Jshint check failed! Hint: please follow js code conventions. Visit: https://openwisp.io/docs/developer/contributing.html#javascript-code-conventions" + echoerr "ERROR: Prettier check failed! Hint: please follow js code conventions. Visit: https://openwisp.io/docs/developer/contributing.html#javascript-code-conventions" FAILURE=1 } else @@ -283,11 +283,11 @@ if ! $SKIP_FLAKE8; then fi if ! $SKIP_CSS_LINTER; then - runstylelintcheck + runcssprettiercheck fi if ! $SKIP_JS_LINTER; then - runjshintcheck + runjsprettiercheck fi if ! $SKIP_RSTCHECK; then diff --git a/openwisp-qa-format b/openwisp-qa-format index 66920524..ef763fba 100755 --- a/openwisp-qa-format +++ b/openwisp-qa-format @@ -5,8 +5,9 @@ isort . black -S . docstrfmt --no-docstring-trailing-line --ignore-cache --line-length 74 . -if which stylelint > /dev/null; then - stylelint $(find . -type f -name "*.css") --fix +if which prettier > /dev/null; then + prettier $(find . -type f -name "*.css") --write + prettier $(find . -type f -name "*.js" -a ! -path "*vendor/*.js") --write else - echo "SKIPPED CSS FILES: Please install stylelint" + echo "SKIPPED CSS FILES: Please install prettier" fi diff --git a/openwisp_utils/admin_theme/static/admin/css/openwisp-info.css b/openwisp_utils/admin_theme/static/admin/css/openwisp-info.css index 2f422d01..082a0c59 100644 --- a/openwisp_utils/admin_theme/static/admin/css/openwisp-info.css +++ b/openwisp_utils/admin_theme/static/admin/css/openwisp-info.css @@ -1,10 +1,10 @@ #id_user_consented { - float: left; - margin-right: 8px; - vertical-align: top; - margin-bottom: 0px; + float: left; + margin-right: 8px; + vertical-align: top; + margin-bottom: 0px; } -#id_metric_collection_consent_form > span.helptext{ - display: block; - margin-top: 8px; +#id_metric_collection_consent_form > span.helptext { + display: block; + margin-top: 8px; } diff --git a/openwisp_utils/admin_theme/static/admin/js/menu.js b/openwisp_utils/admin_theme/static/admin/js/menu.js index d939fda7..74bcd91f 100644 --- a/openwisp_utils/admin_theme/static/admin/js/menu.js +++ b/openwisp_utils/admin_theme/static/admin/js/menu.js @@ -1,15 +1,15 @@ -'use strict'; -const owContainer = document.getElementById('container'); -const owMenu = document.getElementById('menu'); -const owMainContent = document.getElementById('main-content'); -const owMenuToggle = document.querySelector('.menu-toggle'); -const owHamburger = document.querySelector('.hamburger'); -const menuBackdrop = document.querySelector('.menu-backdrop'); -const owNav = document.querySelector('#menu .nav'); -const owAccountMenu = document.querySelector('.account-menu'); -const owAccountDropdown = document.querySelector('#account-dropdown'); -const accountToggle = document.querySelector('.account-button'); -const MenuTransitionTime = '0.1s'; +"use strict"; +const owContainer = document.getElementById("container"); +const owMenu = document.getElementById("menu"); +const owMainContent = document.getElementById("main-content"); +const owMenuToggle = document.querySelector(".menu-toggle"); +const owHamburger = document.querySelector(".hamburger"); +const menuBackdrop = document.querySelector(".menu-backdrop"); +const owNav = document.querySelector("#menu .nav"); +const owAccountMenu = document.querySelector(".account-menu"); +const owAccountDropdown = document.querySelector("#account-dropdown"); +const accountToggle = document.querySelector(".account-button"); +const MenuTransitionTime = "0.1s"; var wasActiveGroupOpen = false; (function () { @@ -38,40 +38,52 @@ function Window() { function openMenuGroup(group, mgHead = null, mgDropdown = null) { // Open menu group and return first item of the group - if (!group) { return; } - if (!mgHead) { mgHead = group.querySelector('.mg-head'); } - if (!mgDropdown) { mgDropdown = group.querySelector('.mg-dropdown'); } - group.classList.add('active'); - mgHead.setAttribute('aria-expanded', 'true'); - var group_items = group.querySelectorAll('.mg-link'); + if (!group) { + return; + } + if (!mgHead) { + mgHead = group.querySelector(".mg-head"); + } + if (!mgDropdown) { + mgDropdown = group.querySelector(".mg-dropdown"); + } + group.classList.add("active"); + mgHead.setAttribute("aria-expanded", "true"); + var group_items = group.querySelectorAll(".mg-link"); group_items.forEach(function (item) { - item.setAttribute('tabindex', 0); + item.setAttribute("tabindex", 0); }); return group_items[0]; } function closeMenuGroup(group, mgHead, mgDropdown = null) { - if (!group) { return; } - if (!mgHead) { mgHead = group.querySelector('.mg-head'); } - if (!mgDropdown) { mgDropdown = group.querySelector('.mg-dropdown'); } - group.classList.remove('active'); - mgDropdown.style = ''; - mgHead.setAttribute('aria-expanded', 'false'); - var group_items = group.querySelectorAll('.mg-link'); + if (!group) { + return; + } + if (!mgHead) { + mgHead = group.querySelector(".mg-head"); + } + if (!mgDropdown) { + mgDropdown = group.querySelector(".mg-dropdown"); + } + group.classList.remove("active"); + mgDropdown.style = ""; + mgHead.setAttribute("aria-expanded", "false"); + var group_items = group.querySelectorAll(".mg-link"); group_items.forEach(function (item) { - item.setAttribute('tabindex', -1); + item.setAttribute("tabindex", -1); }); } function closeActiveGroup(group = null, mgHead = null, mgDropdown = null) { if (group === null) { - group = document.querySelector('.menu-group.active'); + group = document.querySelector(".menu-group.active"); } closeMenuGroup(group, mgHead, mgDropdown); } function isMenuOpen() { - return !owContainer.classList.contains('toggle-menu'); + return !owContainer.classList.contains("toggle-menu"); } function initResizeScreenHelpers() { @@ -81,7 +93,7 @@ function initResizeScreenHelpers() { if (owWindow.getWindowWidth() > 1024 && isMenuOpen()) { // close window closeActiveGroup(); - owContainer.classList.add('toggle-menu'); + owContainer.classList.add("toggle-menu"); owWindow.setWindowWidth(currentWidth); setMenuToggleText(); } @@ -94,7 +106,7 @@ function initResizeScreenHelpers() { } } var owWindow = new Window(); - window.addEventListener('resize', function () { + window.addEventListener("resize", function () { changeMenuState(owWindow); }); } @@ -102,13 +114,13 @@ function initResizeScreenHelpers() { function toggleGroup(e, callback = null) { e.stopPropagation(); var mgHead = e.target; - var group = mgHead.closest('.menu-group'); - var dropdown = group.querySelector('.mg-dropdown'); - var currentActiveGroup = document.querySelector('.menu-group.active'); + var group = mgHead.closest(".menu-group"); + var dropdown = group.querySelector(".mg-dropdown"); + var currentActiveGroup = document.querySelector(".menu-group.active"); var firstElement = null; if ( currentActiveGroup && - currentActiveGroup.classList.contains('active-mg') + currentActiveGroup.classList.contains("active-mg") ) { wasActiveGroupOpen = false; } @@ -116,31 +128,31 @@ function toggleGroup(e, callback = null) { closeActiveGroup(currentActiveGroup); } if ( - group.classList.contains('active-mg') && - !group.classList.contains('active') + group.classList.contains("active-mg") && + !group.classList.contains("active") ) { wasActiveGroupOpen = true; } if (window.innerWidth > 768 && !isMenuOpen()) { - if (!group.classList.contains('active')) { + if (!group.classList.contains("active")) { var groupPos = group.offsetTop; var scrolledBy = owNav.scrollTop; - var dropdownHeight = group.querySelector('.mg-dropdown').offsetHeight; + var dropdownHeight = group.querySelector(".mg-dropdown").offsetHeight; if (dropdownHeight + groupPos - scrolledBy >= window.innerHeight) { - dropdown.style.top = groupPos - scrolledBy - dropdownHeight + 87 + 'px'; + dropdown.style.top = groupPos - scrolledBy - dropdownHeight + 87 + "px"; setTimeout(function () { firstElement = openMenuGroup(group, mgHead, dropdown); dropdown.style.top = - groupPos - scrolledBy - dropdownHeight + 40 + 'px'; + groupPos - scrolledBy - dropdownHeight + 40 + "px"; if (callback) { callback(firstElement); } }, 10); } else { - dropdown.style.top = groupPos - scrolledBy + 47 + 'px'; + dropdown.style.top = groupPos - scrolledBy + 47 + "px"; setTimeout(function () { firstElement = openMenuGroup(group, mgHead, dropdown); - dropdown.style.top = groupPos - scrolledBy + 'px'; + dropdown.style.top = groupPos - scrolledBy + "px"; if (callback) { callback(firstElement); } @@ -150,7 +162,7 @@ function toggleGroup(e, callback = null) { closeMenuGroup(group, mgHead, dropdown); } } else { - if (group.classList.contains('active')) { + if (group.classList.contains("active")) { closeMenuGroup(group, mgHead, dropdown); } else { firstElement = openMenuGroup(group, mgHead, dropdown); @@ -161,43 +173,43 @@ function toggleGroup(e, callback = null) { } } -function focusGroupLink(link){ +function focusGroupLink(link) { // It is used as callback function in setTimeout of toggle_group link.focus(); } function initGroupViewHandlers() { - var mgHeads = document.querySelectorAll('.mg-head'); + var mgHeads = document.querySelectorAll(".mg-head"); mgHeads.forEach(function (mgHead) { // Handle click on menu group - mgHead.addEventListener('click', toggleGroup); - mgHead.addEventListener('keypress', function (e) { - if (e.key !== 'Enter' && e.key !== ' ') { + mgHead.addEventListener("click", toggleGroup); + mgHead.addEventListener("keypress", function (e) { + if (e.key !== "Enter" && e.key !== " ") { return; } toggleGroup(e, focusGroupLink); }); // Show menu group label on hover when menu is close - mgHead.addEventListener('mouseenter', function (e) { + mgHead.addEventListener("mouseenter", function (e) { e.stopPropagation(); if (window.innerWidth > 768 && !isMenuOpen()) { var group = e.target.parentElement; var groupPos = group.offsetTop; var scrolledBy = owNav.scrollTop; - var label = e.target.querySelector('.label'); - label.style.top = groupPos - scrolledBy + 13 + 'px'; + var label = e.target.querySelector(".label"); + label.style.top = groupPos - scrolledBy + 13 + "px"; } }); - mgHead.addEventListener('mouseleave', function (e) { + mgHead.addEventListener("mouseleave", function (e) { if (window.innerWidth > 768 && !isMenuOpen()) { - var label = e.target.querySelector('.label'); - label.style = ''; + var label = e.target.querySelector(".label"); + label.style = ""; } }); // Escape key handler for menu group - var group = mgHead.closest('.menu-group'); - group.querySelector('.mg-dropdown').addEventListener('keyup', function (e) { - if (e.key === 'Escape') { + var group = mgHead.closest(".menu-group"); + group.querySelector(".mg-dropdown").addEventListener("keyup", function (e) { + if (e.key === "Escape") { closeMenuGroup(group); mgHead.focus(); } @@ -205,26 +217,26 @@ function initGroupViewHandlers() { }); // Show menu item label on hover when menu is close - document.querySelectorAll('.menu-item').forEach(function (item) { - item.addEventListener('mouseenter', function (e) { + document.querySelectorAll(".menu-item").forEach(function (item) { + item.addEventListener("mouseenter", function (e) { e.stopPropagation(); if (window.innerWidth > 768 && !isMenuOpen()) { var itemPos = item.offsetTop; var scrolledBy = owNav.scrollTop; - var label = e.target.querySelector('.label'); - label.style.top = itemPos - scrolledBy + 13 + 'px'; + var label = e.target.querySelector(".label"); + label.style.top = itemPos - scrolledBy + 13 + "px"; } }); - item.addEventListener('mouseleave', function (e) { - var label = e.target.querySelector('.label'); - label.style = ''; + item.addEventListener("mouseleave", function (e) { + var label = e.target.querySelector(".label"); + label.style = ""; }); }); // Handle closing of menu group and account menu when focus is shifted - document.addEventListener('focusin', function (e) { - var activeGroup = document.querySelector('.menu-group.active'); + document.addEventListener("focusin", function (e) { + var activeGroup = document.querySelector(".menu-group.active"); if (activeGroup && !activeGroup.contains(e.target)) { - if (!activeGroup.classList.contains('active-mg')) { + if (!activeGroup.classList.contains("active-mg")) { closeActiveGroup(activeGroup); } } @@ -233,10 +245,10 @@ function initGroupViewHandlers() { } }); // Handle click out side the current active menu group - document.addEventListener('click', function (e) { - var currentActiveGroup = document.querySelector('.menu-group.active'); + document.addEventListener("click", function (e) { + var currentActiveGroup = document.querySelector(".menu-group.active"); if (currentActiveGroup && !currentActiveGroup.contains(e.target)) { - if (currentActiveGroup.classList.contains('active-mg') && isMenuOpen()) { + if (currentActiveGroup.classList.contains("active-mg") && isMenuOpen()) { return; } closeActiveGroup(currentActiveGroup); @@ -244,7 +256,7 @@ function initGroupViewHandlers() { }); // Close menu group on scroll when close - owNav.addEventListener('scroll', function () { + owNav.addEventListener("scroll", function () { if (!isMenuOpen()) { closeActiveGroup(); } @@ -252,26 +264,26 @@ function initGroupViewHandlers() { } function setMenuState() { - let openMenu = localStorage.getItem('ow-menu'); + let openMenu = localStorage.getItem("ow-menu"); if (window.innerWidth > 1024) { if (openMenu === null) { // User visits first time. Keep open menu - localStorage.setItem('ow-menu', true); - owContainer.classList.remove('toggle-menu'); - } else if (openMenu === 'true') { + localStorage.setItem("ow-menu", true); + owContainer.classList.remove("toggle-menu"); + } else if (openMenu === "true") { // Close the menu - owContainer.classList.remove('toggle-menu'); + owContainer.classList.remove("toggle-menu"); } } } function setMenuToggleText() { if (isMenuOpen()) { - owMenuToggle.setAttribute('title', 'Minimize menu'); - owHamburger.setAttribute('aria-label', 'Minimize menu'); + owMenuToggle.setAttribute("title", "Minimize menu"); + owHamburger.setAttribute("aria-label", "Minimize menu"); } else { - owMenuToggle.setAttribute('title', 'Maximize menu'); - owHamburger.setAttribute('aria-label', 'Maximize menu'); + owMenuToggle.setAttribute("title", "Maximize menu"); + owHamburger.setAttribute("aria-label", "Maximize menu"); } } @@ -295,37 +307,37 @@ function setMenu() { function initToggleMenuHandlers() { function toggleMenuHandler() { - var activeMenuGroup = document.querySelector('.active-mg'); + var activeMenuGroup = document.querySelector(".active-mg"); if (activeMenuGroup && isMenuOpen()) { - activeMenuGroup.classList.remove('active'); + activeMenuGroup.classList.remove("active"); } - owContainer.classList.toggle('toggle-menu'); - let isMenuOpen_ = localStorage.getItem('ow-menu'); + owContainer.classList.toggle("toggle-menu"); + let isMenuOpen_ = localStorage.getItem("ow-menu"); if (window.innerWidth > 1024) { - localStorage.setItem('ow-menu', isMenuOpen_ === 'true' ? false : true); + localStorage.setItem("ow-menu", isMenuOpen_ === "true" ? false : true); } setMenuToggleText(); if (activeMenuGroup && isMenuOpen() && wasActiveGroupOpen) { - activeMenuGroup.classList.add('active'); + activeMenuGroup.classList.add("active"); } } if (owMenuToggle && owContainer) { - owMenuToggle.addEventListener('click', toggleMenuHandler); + owMenuToggle.addEventListener("click", toggleMenuHandler); } if (owHamburger && owContainer) { - owHamburger.addEventListener('click', toggleMenuHandler); + owHamburger.addEventListener("click", toggleMenuHandler); } // Close menu when backdrop is clicked - menuBackdrop.addEventListener('click', function (e) { + menuBackdrop.addEventListener("click", function (e) { e.stopPropagation(); closeActiveGroup(); - owContainer.classList.toggle('toggle-menu'); + owContainer.classList.toggle("toggle-menu"); setMenuToggleText(); }); - owHamburger.addEventListener('keypress', function (e) { - if (e.key !== 'Enter' && e.key !== ' ') { + owHamburger.addEventListener("keypress", function (e) { + if (e.key !== "Enter" && e.key !== " ") { return; } toggleMenuHandler(); @@ -333,26 +345,26 @@ function initToggleMenuHandlers() { } function openAccountMenu() { - owAccountMenu.classList.remove('hide'); - accountToggle.setAttribute('aria-expanded', 'true'); - var links = owAccountMenu.querySelectorAll('a'); + owAccountMenu.classList.remove("hide"); + accountToggle.setAttribute("aria-expanded", "true"); + var links = owAccountMenu.querySelectorAll("a"); links.forEach(function (link) { - link.setAttribute('tabindex', '0'); + link.setAttribute("tabindex", "0"); }); return links[0]; } function closeAccountMenu() { - owAccountMenu.classList.add('hide'); - accountToggle.setAttribute('aria-expanded', 'false'); - var links = owAccountMenu.querySelectorAll('a'); + owAccountMenu.classList.add("hide"); + accountToggle.setAttribute("aria-expanded", "false"); + var links = owAccountMenu.querySelectorAll("a"); links.forEach(function (link) { - link.setAttribute('tabindex', '-1'); + link.setAttribute("tabindex", "-1"); }); return links[0]; } function toggleAccount(e) { e.stopPropagation(); - if (owAccountMenu.classList.contains('hide')) { + if (owAccountMenu.classList.contains("hide")) { return openAccountMenu(); } else { closeAccountMenu(); @@ -365,24 +377,24 @@ function initAccountViewHandler() { accountToggle.onclick = function (e) { toggleAccount(e); }; - accountToggle.addEventListener('keyup', function (e) { - if (e.key === 'Enter') { + accountToggle.addEventListener("keyup", function (e) { + if (e.key === "Enter") { var firstLink = toggleAccount(e); firstLink.focus(); } }); } // When clicked outside the account button - document.addEventListener('click', function (e) { + document.addEventListener("click", function (e) { if (accountToggle && !accountToggle.contains(e.target)) { - owAccountMenu.classList.add('hide'); - accountToggle.setAttribute('aria-expanded', 'false'); + owAccountMenu.classList.add("hide"); + accountToggle.setAttribute("aria-expanded", "false"); } }); // Escape key handler - owAccountDropdown.addEventListener('keyup', function (e) { + owAccountDropdown.addEventListener("keyup", function (e) { e.stopPropagation(); - if (e.key === 'Escape') { + if (e.key === "Escape") { toggleAccount(e); setTimeout(function () { accountToggle.focus(); @@ -393,23 +405,23 @@ function initAccountViewHandler() { function initToolTipHandlers() { // Tooltips shown only on narrow screen - var tooltips = document.querySelectorAll('.tooltip-sm'); + var tooltips = document.querySelectorAll(".tooltip-sm"); function mouseLeaveHandler(e) { - var tooltipText = e.target.getAttribute('tooltip-data'); - e.target.setAttribute('title', tooltipText); - e.target.removeAttribute('tooltip-data'); + var tooltipText = e.target.getAttribute("tooltip-data"); + e.target.setAttribute("title", tooltipText); + e.target.removeAttribute("tooltip-data"); removeMouseLeaveListner(e.target); } function removeMouseLeaveListner(tooltip) { - tooltip.removeEventListener('mouseleave', mouseLeaveHandler); + tooltip.removeEventListener("mouseleave", mouseLeaveHandler); } tooltips.forEach(function (tooltip) { - tooltip.addEventListener('mouseenter', function () { + tooltip.addEventListener("mouseenter", function () { if (window.innerWidth > 768) { - var tooltipText = tooltip.getAttribute('title'); - tooltip.removeAttribute('title'); - tooltip.setAttribute('tooltip-data', tooltipText); - tooltip.addEventListener('mouseleave', mouseLeaveHandler); + var tooltipText = tooltip.getAttribute("title"); + tooltip.removeAttribute("title"); + tooltip.setAttribute("tooltip-data", tooltipText); + tooltip.addEventListener("mouseleave", mouseLeaveHandler); } }); }); @@ -421,15 +433,15 @@ function showActiveItems() { } var pathname = window.location.pathname; const regex = new RegExp(/[\d\w-]*\/change\//); - pathname = pathname.replace(regex, ''); + pathname = pathname.replace(regex, ""); var activeLink = document.querySelector(`.nav a[href="${pathname}"]`); if (!activeLink) { return; } - activeLink.classList.add('active'); - if (activeLink.classList.contains('mg-link')) { - var group = activeLink.closest('.menu-group'); - group.classList.add('active-mg'); + activeLink.classList.add("active"); + if (activeLink.classList.contains("mg-link")) { + var group = activeLink.closest(".menu-group"); + group.classList.add("active-mg"); if (isMenuOpen()) { openMenuGroup(group); } diff --git a/openwisp_utils/admin_theme/static/admin/js/ow-auto-filter.js b/openwisp_utils/admin_theme/static/admin/js/ow-auto-filter.js index 29e9ff10..e297edd3 100644 --- a/openwisp_utils/admin_theme/static/admin/js/ow-auto-filter.js +++ b/openwisp_utils/admin_theme/static/admin/js/ow-auto-filter.js @@ -16,15 +16,17 @@ django.jQuery(document).ready(function () { // on the window.location.search (querystring). var searchHash = search_to_hash(), nullParam; - django.jQuery('.admin-autocomplete.select2-hidden-accessible').each(function (index, el) { - el = django.jQuery(el); - nullParam = el.attr("name") + "__isnull"; - if (searchHash[nullParam] !== undefined) { - el.append(new Option(el.data('empty-label'), "null", false, false)); - el.val("null"); - el.trigger("change"); - } - }); + django + .jQuery(".admin-autocomplete.select2-hidden-accessible") + .each(function (index, el) { + el = django.jQuery(el); + nullParam = el.attr("name") + "__isnull"; + if (searchHash[nullParam] !== undefined) { + el.append(new Option(el.data("empty-label"), "null", false, false)); + el.val("null"); + el.trigger("change"); + } + }); } setAllPlaceholder(".auto-filter .select2-selection__placeholder"); @@ -55,17 +57,16 @@ django.jQuery(document).ready(function () { val = filterElement.val() || "", param = filterElement.attr("name"), class_name = filterElement.attr("class"), - nullParam = param + '__isnull', + nullParam = param + "__isnull", queryString = search_to_hash(); // Use the "null" querystring if user chose to filter // for "null" values. - if (val === ""){ + if (val === "") { delete queryString[param]; delete queryString[nullParam]; - } - else if (val === "null") { + } else if (val === "null") { delete queryString[param]; - queryString[nullParam] = ['true']; + queryString[nullParam] = ["true"]; } else { delete queryString[nullParam]; queryString[param] = [val]; @@ -80,7 +81,7 @@ django.jQuery(document).ready(function () { django.jQuery(target).append( `
` + `, ); } } @@ -97,7 +98,7 @@ django.jQuery(document).ready(function () { django .jQuery(event.target) .parent() - .find(".select2-selection__placeholder") + .find(".select2-selection__placeholder"), ); }); }); diff --git a/openwisp_utils/admin_theme/static/admin/js/ow-dashboard.js b/openwisp_utils/admin_theme/static/admin/js/ow-dashboard.js index f62d6240..7a1f9424 100644 --- a/openwisp_utils/admin_theme/static/admin/js/ow-dashboard.js +++ b/openwisp_utils/admin_theme/static/admin/js/ow-dashboard.js @@ -1,45 +1,46 @@ -(function() { - 'use strict'; +(function () { + "use strict"; function slugify(str) { - str = str.replace(/^\s+|\s+$/g, ''); + str = str.replace(/^\s+|\s+$/g, ""); // Make the string lowercase str = str.toLowerCase(); // Remove invalid chars - str = str.replace(/[^a-z0-9 -]/g, '') + str = str + .replace(/[^a-z0-9 -]/g, "") // Collapse whitespace and replace by - - .replace(/\s+/g, '-') + .replace(/\s+/g, "-") // Collapse dashes - .replace(/-+/g, '-'); + .replace(/-+/g, "-"); return str; } let elementsParam = Object.values(owDashboardCharts), - container = document.getElementById('plot-container'); + container = document.getElementById("plot-container"); const layout = { - height: 450, - width: 450, - margin: { - t: 0, - b: 0 + height: 450, + width: 450, + margin: { + t: 0, + b: 0, + }, + legend: { + yanchor: "center", + xanchor: "left", + x: 0, + y: 0, + bgcolor: "transparent", + }, + title: { + yanchor: "center", + y: 0.92, + font: { size: 20 }, + }, }, - legend: { - yanchor: 'center', - xanchor: 'left', - x: 0, - y: 0, - bgcolor: 'transparent' - }, - title: { - yanchor: 'center', - y: 0.92, - font: {size: 20} - } - }, - options = { + options = { displayModeBar: false, - }; + }; for (let i = 0; i < elementsParam.length; ++i) { layout.title.text = elementsParam[i].name; @@ -48,23 +49,23 @@ // the previous chart. delete layout.annotations; let data = { - type: 'pie', - hole: 0.6, - showlegend: !elementsParam[i].hasOwnProperty('quick_link') - }, - element = document.createElement('div'), - totalValues = 0; + type: "pie", + hole: 0.6, + showlegend: !elementsParam[i].hasOwnProperty("quick_link"), + }, + element = document.createElement("div"), + totalValues = 0; // Show a graph depicting disabled graph when there is insufficient data if (elementsParam[i].query_params.values.length === 0) { data.values = [1]; - data.labels = ['Not enough data']; + data.labels = ["Not enough data"]; data.marker = { - colors: ['#80808091'] + colors: ["#80808091"], }; - data.texttemplate = ' '; + data.texttemplate = " "; data.showlegend = false; - data.hovertemplate = '%{label}'; + data.hovertemplate = "%{label}"; } else { data.values = elementsParam[i].query_params.values; data.labels = elementsParam[i].query_params.labels; @@ -73,15 +74,15 @@ data.showlegend = false; } data.rotation = 180; - data.textposition = 'inside'; - data.insidetextorientation = 'horizontal'; + data.textposition = "inside"; + data.insidetextorientation = "horizontal"; if (elementsParam[i].colors) { data.marker = { - colors: elementsParam[i].colors + colors: elementsParam[i].colors, }; } - data.texttemplate = '%{value}