Skip to content

Commit

Permalink
Merge 80684c8 into c9f221f
Browse files Browse the repository at this point in the history
  • Loading branch information
mtruj013 authored Sep 1, 2023
2 parents c9f221f + 80684c8 commit 41276d4
Show file tree
Hide file tree
Showing 43 changed files with 2,391 additions and 1,653 deletions.
1 change: 1 addition & 0 deletions .stylelintrc
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"plugins": ["stylelint-order"],
"rules": {
"order/properties-alphabetical-order": true,
"scss/at-extend-no-missing-placeholder": null,
"at-rule-no-unknown": [
true,
{
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,14 @@
},
"dependencies": {
"@canonical/cookie-policy": "3.5.0",
"@canonical/global-nav": "2.7.0",
"@canonical/global-nav": "3.5.0",
"autoprefixer": "10.4.13",
"babel-loader": "9.1.2",
"expose-loader": "4.0.0",
"postcss": "8.4.21",
"postcss-cli": "10.1.0",
"sass": "1.57.1",
"vanilla-framework": "3.15.1",
"vanilla-framework": "4.2.0",
"webpack-cli": "5.0.1"
},
"devDependencies": {
Expand Down
162 changes: 162 additions & 0 deletions static/js/side-navigation.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
/**
* Select all Side Navigation components: ".p-side-navigation" "[class*='p-side-navigation--']"
* - Collapse/Expand side navigation using button ".js-drawer-toggle"
* - Handle active state of links ".p-side-navigation__link"
* - Handle Collapse/Expand for submenus of side navigation ".p-side-navigation__expand"
*/

/**
Toggles the expanded/collapsed classed on side navigation element.
@param {HTMLElement} sideNavigation The side navigation element.
@param {Boolean} show Whether to show or hide the drawer.
@param {Boolean} ignoreTogglerFocus when we click on menu there is no redirect, the focus should jump into selected section
*/
function toggleDrawer(sideNavigation, show, ignoreTogglerFocus = false) {
const toggleButtonOutsideDrawer = sideNavigation.querySelector(
".p-side-navigation__toggle"
);
const toggleButtonInsideDrawer = sideNavigation.querySelector(
".p-side-navigation__toggle--in-drawer"
);

if (sideNavigation) {
if (show) {
sideNavigation.classList.remove("is-collapsed");
sideNavigation.classList.add("is-expanded");

if (!ignoreTogglerFocus) {
toggleButtonInsideDrawer.focus();
}
toggleButtonOutsideDrawer.setAttribute("aria-expanded", "true");
toggleButtonInsideDrawer.setAttribute("aria-expanded", "true");
} else {
sideNavigation.classList.remove("is-expanded");
sideNavigation.classList.add("is-collapsed");

if (!ignoreTogglerFocus) {
toggleButtonOutsideDrawer.focus();
}
toggleButtonOutsideDrawer.setAttribute("aria-expanded", "false");
toggleButtonInsideDrawer.setAttribute("aria-expanded", "false");
}
}
}

/**
Setup default values of aria-expanded for the toggle button, list title and list itself
@param {HTMLButtonElement} toggleMenu
*/
const setupToggleMenu = (toggleMenu) => {
const isExpanded = toggleMenu.getAttribute("aria-expanded") === "true";
if (!isExpanded) {
toggleMenu.setAttribute("aria-expanded", isExpanded);
}
const item = toggleMenu.closest(".p-side-navigation__item");
const link = item.querySelector(".p-side-navigation__link");
const nestedList = item.querySelector(".p-side-navigation__list");
if (!link?.hasAttribute("aria-expanded")) {
link.setAttribute("aria-expanded", isExpanded);
}
if (!nestedList?.hasAttribute("aria-expanded")) {
nestedList.setAttribute("aria-expanded", isExpanded);
}
};

/**
Handle toggle button to show/hide submenu
@param {Event} e
*/
const handleToggleMenu = (e) => {
const item = e.currentTarget.closest(".p-side-navigation__item");
const button = item.querySelector(".p-side-navigation__expand");
const link = item.querySelector(".p-side-navigation__link");
const nestedList = item.querySelector(".p-side-navigation__list");
[button, link, nestedList].forEach((el) =>
el.setAttribute(
"aria-expanded",
el.getAttribute("aria-expanded") === "true" ? "false" : "true"
)
);
};

/**
Attaches event listeners for the side navigation and submenu toggles
@param {HTMLElement} sideNavigation The side navigation element.
*/
function setupSideNavigation(sideNavigation) {
const toggles = [...sideNavigation.querySelectorAll(".js-drawer-toggle")];

// setup toggle buttons for sidenav
toggles.forEach(function (toggle) {
toggle.addEventListener("click", function (event) {
event.preventDefault();
toggleDrawer(
sideNavigation,
!sideNavigation.classList.contains("is-expanded")
);
});
});

window.addEventListener("keydown", (e) => {
if (e.key === "Escape") {
toggleDrawer(sideNavigation, false);
}
});

// Setup expandable submenus side navigation
const expandMenuToggles = [
...sideNavigation.querySelectorAll(".p-side-navigation__expand"),
];
expandMenuToggles.forEach((toggleMenu) => {
setupToggleMenu(toggleMenu);
toggleMenu.addEventListener("click", (e) => {
handleToggleMenu(e);
});
});

// SETUP menu links click when expand/collapse side nav is not available
if (expandMenuToggles.length === 0) {
const currentHash = window.location.hash;
const currentPath = window.location.pathname;
const currentUrl = currentPath + currentHash;
const links = [
...sideNavigation.querySelectorAll(".p-side-navigation__link"),
];
links.forEach(function (link) {
link.addEventListener("click", function () {
links.forEach(function (link) {
link.removeAttribute("aria-current");
});
this.setAttribute("aria-current", "page");
this.blur();
const isExpanded = sideNavigation.classList.contains("is-expanded");
if (isExpanded) {
toggleDrawer(sideNavigation, !isExpanded, true);
}
});

const linkUrl = link.getAttribute("href");
if (linkUrl === currentUrl || linkUrl === currentHash) {
link.setAttribute("aria-current", "page");
}
});
}
}

/**
Attaches event listeners for all the side navigations in the document.
@param {String} sideNavigationSelector The CSS selector matching side navigation elements.
*/
function setupSideNavigations(sideNavigationSelector) {
// Setup all side navigations on the page.
const sideNavigations = [
...document.querySelectorAll(sideNavigationSelector),
];
sideNavigations.forEach(setupSideNavigation);
}

setupSideNavigations(".p-side-navigation, [class*='p-side-navigation--']");
162 changes: 162 additions & 0 deletions static/js/tabbed-content.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
(function () {
const keys = {
left: "ArrowLeft",
right: "ArrowRight",
};

const direction = {
ArrowLeft: -1,
ArrowRight: 1,
};

// IE11 doesn't support event.code, but event.keyCode is
// deprecated in most modern browsers, so we should support
// both for the time being.
const IEKeys = {
left: 37,
right: 39,
};

const IEDirection = {
37: direction["ArrowLeft"],
39: direction["ArrowRight"],
};

/**
Determine which tab to show when an arrow key is pressed
@param {KeyboardEvent} event
@param {Array} tabs an array of tabs within a container
*/
const switchTabOnArrowPress = (event, tabs) => {
let compatibleKeys = IEKeys;
let compatibleDirection = IEDirection;
let pressed = event.keyCode;

if (event.code) {
compatibleKeys = keys;
compatibleDirection = direction;
pressed = event.code;
}

if (compatibleDirection[pressed]) {
const target = event.target;
if (target.index !== undefined) {
if (tabs[target.index + compatibleDirection[pressed]]) {
tabs[target.index + compatibleDirection[pressed]].focus();
} else if (pressed === compatibleKeys.left) {
tabs[tabs.length - 1].focus();
} else if (pressed === compatibleKeys.right) {
tabs[0].focus();
}
}
}
};

/**
Attaches a number of events that each trigger
the reveal of the chosen tab content
@param {Array} tabs an array of tabs within a container
*/
const attachEvents = (tabs, persistURLHash) => {
tabs.forEach(function (tab, index) {
tab.addEventListener("keyup", function (e) {
let compatibleKeys = IEKeys;
let key = e.keyCode;

if (e.code) {
compatibleKeys = keys;
key = e.code;
}

if (key === compatibleKeys.left || key === compatibleKeys.right) {
switchTabOnArrowPress(e, tabs);
}
});

tab.addEventListener("click", (e) => {
e.preventDefault();

if (persistURLHash) {
// if we're adding the ID of the tab to the URL
// this prevents the page attempting to jump to
// the section with that ID
history.pushState({}, "", tab.href);

// Update the URL again with the same hash, then go back
history.pushState({}, "", tab.href);
history.back();
}

setActiveTab(tab, tabs);
});

tab.addEventListener("focus", () => {
setActiveTab(tab, tabs);
});

tab.index = index;
});
};

/**
Cycles through an array of tab elements and ensures
only the target tab and its content are selected
@param {HTMLElement} tab the tab whose content will be shown
@param {Array} tabs an array of tabs within a container
*/
const setActiveTab = (tab, tabs) => {
tabs.forEach((tabElement) => {
var tabContent = document.querySelectorAll(
"#" + tabElement.getAttribute("aria-controls")
);
tabContent.forEach((content) => {
if (tabElement === tab) {
tabElement.setAttribute("aria-selected", true);
content.classList.remove("u-hide");
} else {
tabElement.setAttribute("aria-selected", false);
content.classList.add("u-hide");
}
});
});
};

/**
Attaches events to tab links within a given parent element,
and sets the active tab if the current hash matches the id
of an element controlled by a tab link
@param {String} selector class name of the element
containing the tabs we want to attach events to
*/
const initTabs = (selector) => {
var tabContainers = [].slice.call(document.querySelectorAll(selector));

tabContainers.forEach((tabContainer) => {
// if the tab container has this data attribute, the id of the tab
// is added to the URL, and a particular tab can be directly linked
var persistURLHash = tabContainer.getAttribute("data-maintain-hash");
var currentHash = window.location.hash;

var tabs = [].slice.call(
tabContainer.querySelectorAll("[aria-controls]")
);
attachEvents(tabs, persistURLHash);

if (persistURLHash && currentHash) {
var activeTab = document.querySelector(
".p-tabs__link[href='" + currentHash + "']"
);

if (activeTab) {
setActiveTab(activeTab, tabs);
}
} else {
setActiveTab(tabs[0], tabs);
}
});
};

document.addEventListener("DOMContentLoaded", () => {
initTabs(".js-tabbed-content");
});
})();
23 changes: 23 additions & 0 deletions static/sass/_pattern_footer.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
@mixin canonical-p-footer {
.p-footer {
@extend %vf-strip;

background: $colors--dark-theme--background-alt;
color: $colors--dark-theme--text-default;

.p-list__item--condensed {
@extend %vf-list-item;

padding-bottom: 0;
padding-top: 0;
}

a {
color: $color-link-dark;

&:visited {
color: $color-link-visited-dark;
}
}
}
}
Loading

0 comments on commit 41276d4

Please sign in to comment.