Skip to content

Commit

Permalink
MWPW-161273 Standalone Gnav needs a release cycle [Bundle] (#3132)
Browse files Browse the repository at this point in the history
* bundled standalone gnav and footer

* Fixed dark mode; load css from navigation.js

* Refactored global footer to no longer use milo modal to render region-nav

* global footer unit test

* fix lint

* Added keyboard navigation to the new region nav modal

* export stuff from modal.js

* replaced the new modal implementation with a more explicit usage of the current milo modal so that it can be bundled

* code compatibility ignore pattern

* Fix keyboard navigation unit tests

* fixed footer unit test after changing the implementation of the region nav (again)

* added sourcemaps; fixed dark mode issue

* navigation unit tests

* Cover uncovered lines in global-footer.js

* prevent FOUC in region nav

* built latest gnav changes

* modified package.json to have a files field

* use evergreen css for non-bundled and built css for bundled

* Fixed region picker when there's no hash

* Added a workflow to release standalone feds and removed dist from the PR

* changed workflow_call to workflow_dispatch

* Adjusted the cd command in the workflow

* added a working directory

* missed a space in the gh release upload command

* added GITHUB_TOKEN to the upload asset step

* fixed an error with file upload in the workflow

* Removed a console.log from the build script; explicitly load fragment block in footer

* Removed an unused import

* Renamed a funciton in the build file and added a comment

* Fixed region nav breaking on certain milo consumers

* Fixed region nav breaking on some milo consumers for real this time

* Removed an unused import

* Added logic to not call the region nav code twice

* unit test

* modified a standalone footer unit test slightly

* Removed a comment
  • Loading branch information
sharmrj authored and mokimo committed Dec 6, 2024
1 parent 2a87a8a commit 38abecd
Show file tree
Hide file tree
Showing 27 changed files with 750 additions and 58 deletions.
1 change: 1 addition & 0 deletions .eslintrc-code-compatibility.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ module.exports = {
],
ignorePatterns: [
'/libs/deps/*',
'/libs/navigation/dist/*',
'/tools/loc/*',
],
};
1 change: 1 addition & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ module.exports = {
'/libs/features/mas/*',
'/tools/loc/*',
'/libs/features/spectrum-web-components/*',
'/libs/navigation/dist/*',
],
plugins: [
'chai-friendly',
Expand Down
55 changes: 55 additions & 0 deletions .github/workflows/release-standalone-feds.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
name: Create a Release for Standalone Feds GlobalNav and Footer
on:
workflow_dispatch:
inputs:
version:
description: 'Version'
required: true
type: string

permissions:
contents: write

jobs:
release-feds:
name: Release Standalone Feds
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [20.x]
defaults:
run:
working-directory: ./libs/navigation
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 2

- name: Set up Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}

- name: Install dependencies
run: npm install

- name: Build Files
run: node ./build.mjs

- name: Generate tarball
run: npm pack

- name: Create Release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh release create "feds-standalone-v${{ inputs.version }}" \
--repo="$GITHUB_REPOSITORY" \
--title="@adobecom/standalone-feds v${{ inputs.version }} Release" \
--generate-notes
- name: Upload Files to Release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: gh release upload "feds-standalone-v${{ inputs.version }}" "adobecom-standalone-feds-${{ inputs.version }}.tgz"
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ logs/*
test-html-results/
test-results/
test-a11y-results/
libs/navigation/dist/
14 changes: 14 additions & 0 deletions libs/blocks/global-footer/global-footer.css
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,20 @@
height: 12px;
}

@media (min-width: 600px) {
dialog.feds-dialog {
max-width: 80vw;
width: fit-content;
}
}

@media (min-width: 1200px) {
dialog.feds-dialog {
width: 1200px;
max-width: calc((100% - 6px) - 2em);
}
}

@media (min-width: 900px) {
/* If there is too much content, float it on multiple rows */
.feds-footer-wrapper .feds-menu-content {
Expand Down
40 changes: 34 additions & 6 deletions libs/blocks/global-footer/global-footer.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import {
decorateLinks,
getMetadata,
getConfig,
loadBlock,
localizeLink,
loadStyle,
} from '../../utils/utils.js';

import {
Expand Down Expand Up @@ -217,6 +217,8 @@ class Footer {
</svg>
${regionPickerTextElem}
</a>`;
regionPickerElem.dataset.modalPath = `${url.pathname}#_inline`;
regionPickerElem.dataset.modalHash = url.hash;
const regionPickerWrapperClass = 'feds-regionPicker-wrapper';
this.elements.regionPicker = toFragment`<div class="${regionPickerWrapperClass}">
${regionPickerElem}
Expand All @@ -230,24 +232,48 @@ class Footer {
// Hash -> region selector opens a modal
decorateAutoBlock(regionPickerElem); // add modal-specific attributes
// TODO remove logs after finding the root cause for the region picker 404s -> MWPW-143627
regionPickerElem.href = url.hash;
if (regionPickerElem.classList[0] !== 'modal') {
lanaLog({
message: `Modal block class missing from region picker pre loading the block; locale: ${locale}; regionPickerElem: ${regionPickerElem.outerHTML}`,
tags: 'errorType=warn,module=global-footer',
});
}
await loadBlock(regionPickerElem); // load modal logic and styles
loadStyle(`${base}/blocks/modal/modal.css`);
const { default: initModal } = await import('../modal/modal.js');
const modal = await initModal(regionPickerElem);

const loadRegionNav = async () => {
const block = document.querySelector('.region-nav');
if (block && getConfig().standaloneGnav) {
// on standalone the region-nav will fail to load automatically through
// the modal calling fragment.js. In that case we will have data-failed=true
// and we should manually load region nav
// If that's not the case then we're not a standalone gnav
// and we mustn't load region-nav twice.
if (block.getAttribute('data-failed') !== 'true') return;
block.classList.add('hide');
loadStyle(`${base}/blocks/region-nav/region-nav.css`);
const { default: initRegionNav } = await import('../region-nav/region-nav.js');
initRegionNav(block);
// decoratePlaceholders(block, getConfig());
block.classList.remove('hide');
}
};

if (modal) await loadRegionNav(); // just in case the modal is already open

if (regionPickerElem.classList[0] !== 'modal') {
lanaLog({
message: `Modal block class missing from region picker post loading the block; locale: ${locale}; regionPickerElem: ${regionPickerElem.outerHTML}`,
tags: 'errorType=warn,module=global-footer',
});
}
// 'decorateAutoBlock' logic replaces class name entirely, need to add it back
regionPickerElem.classList.add(regionPickerClass);
regionPickerElem.addEventListener('click', () => {
if (!isRegionPickerExpanded()) {
regionPickerElem.setAttribute('aria-expanded', 'true');
// wait for the modal to load before we load the region nav
window.addEventListener('milo:modal:loaded', loadRegionNav, { once: true });
}
});
// Set aria-expanded to false when region modal is closed
Expand All @@ -262,7 +288,8 @@ class Footer {
regionSelector.href = localizeLink(regionSelector.href);
decorateAutoBlock(regionSelector); // add fragment-specific class(es)
this.elements.regionPicker.append(regionSelector); // add fragment after regionPickerElem
await loadBlock(regionSelector); // load fragment and replace original link
const { default: initFragment } = await import('../fragment/fragment.js');
await initFragment(regionSelector); // load fragment and replace original link
// Update aria-expanded on click
regionPickerElem.addEventListener('click', (e) => {
e.preventDefault();
Expand All @@ -271,14 +298,15 @@ class Footer {
});
// Close region picker dropdown on outside click
document.addEventListener('click', (e) => {
e.preventDefault();
if (isRegionPickerExpanded()
&& !e.target.closest(`.${regionPickerWrapperClass}`)) {
regionPickerElem.setAttribute('aria-expanded', false);
}
});
}

return this.regionPicker;
return this.elements.regionPicker;
};

decorateSocial = () => {
Expand Down
4 changes: 2 additions & 2 deletions libs/blocks/global-navigation/base.css
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,8 @@
align-items: center;
}

header.global-navigation {
visibility: visible;
header.global-navigation.ready {
visibility: visible !important;
}

/* Desktop styles */
Expand Down
19 changes: 10 additions & 9 deletions libs/blocks/global-navigation/global-navigation.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint import/no-relative-packages: 0 */
/* eslint-disable no-async-promise-executor */
import {
getConfig,
Expand All @@ -20,7 +21,6 @@ import {
isTangentToViewport,
lanaLog,
loadBaseStyles,
loadBlock,
loadDecorateMenu,
rootPath,
loadStyles,
Expand Down Expand Up @@ -222,7 +222,7 @@ const decorateProfileTrigger = async ({ avatar }) => {
let keyboardNav;
const setupKeyboardNav = async () => {
keyboardNav = keyboardNav || new Promise(async (resolve) => {
const KeyboardNavigation = await loadBlock('./keyboard/index.js');
const { default: KeyboardNavigation } = await import('./utilities/keyboard/index.js');
const instance = new KeyboardNavigation();
resolve(instance);
});
Expand Down Expand Up @@ -428,17 +428,17 @@ class Gnav {
this.block.removeEventListener('keydown', this.loadDelayed);
if (this.searchPresent()) {
const [
Search,
{ default: Search },
] = await Promise.all([
loadBlock('../features/search/gnav-search.js'),
import('./features/search/gnav-search.js'),
loadStyles(rootPath('features/search/gnav-search.css')),
]);
this.Search = Search;
}

if (!this.useUniversalNav) {
const [ProfileDropdown] = await Promise.all([
loadBlock('../features/profile/dropdown.js'),
const [{ default: ProfileDropdown }] = await Promise.all([
import('./features/profile/dropdown.js'),
loadStyles(rootPath('features/profile/dropdown.css')),
]);
this.ProfileDropdown = ProfileDropdown;
Expand Down Expand Up @@ -543,7 +543,7 @@ class Gnav {
const unavVersion = new URLSearchParams(window.location.search).get('unavVersion') || '1.3';
await Promise.all([
loadScript(`https://${environment}.adobeccstatic.com/unav/${unavVersion}/UniversalNav.js`),
loadStyles(`https://${environment}.adobeccstatic.com/unav/${unavVersion}/UniversalNav.css`),
loadStyles(`https://${environment}.adobeccstatic.com/unav/${unavVersion}/UniversalNav.css`, true),
]);

const getChildren = () => {
Expand Down Expand Up @@ -913,7 +913,7 @@ class Gnav {

const menuLogic = await loadDecorateMenu();

menuLogic.decorateMenu({
await menuLogic.decorateMenu({
item,
template,
type: itemType,
Expand Down Expand Up @@ -1024,7 +1024,7 @@ class Gnav {
const breadcrumbsElem = this.block.querySelector('.breadcrumbs');
// Breadcrumbs are not initially part of the nav, need to decorate the links
if (breadcrumbsElem) decorateLinks(breadcrumbsElem);
const createBreadcrumbs = await loadBlock('../features/breadcrumbs/breadcrumbs.js');
const { default: createBreadcrumbs } = await import('./features/breadcrumbs/breadcrumbs.js');
this.elements.breadcrumbsWrapper = await createBreadcrumbs(breadcrumbsElem);
return this.elements.breadcrumbsWrapper;
};
Expand Down Expand Up @@ -1094,5 +1094,6 @@ export default async function init(block) {
const mepMartech = mep?.martech || '';
block.setAttribute('daa-lh', `gnav|${getExperienceName()}${mepMartech}`);
if (isDarkMode()) block.classList.add('feds--dark');
block.classList.add('ready');
return gnav;
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint import/no-relative-packages: 0 */
/* eslint-disable camelcase */
import { getConfig } from '../../../utils/utils.js';

Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint import/no-relative-packages: 0 */
/* eslint-disable no-promise-executor-return, no-async-promise-executor */
import { getConfig } from '../../../utils/utils.js';

Expand Down
20 changes: 9 additions & 11 deletions libs/blocks/global-navigation/utilities/utilities.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint import/no-relative-packages: 0 */
import {
getConfig, getMetadata, loadStyle, loadLana, decorateLinks, localizeLink,
} from '../../../utils/utils.js';
Expand Down Expand Up @@ -134,7 +135,9 @@ export function rootPath(path) {
return url;
}

export function loadStyles(url) {
export function loadStyles(url, override = false) {
const { standaloneGnav } = getConfig();
if (standaloneGnav && !override) return;
loadStyle(url, (e) => {
if (e === 'error') {
lanaLog({
Expand All @@ -155,6 +158,8 @@ export function isDarkMode() {
// since they can be independent of each other.
// CSS imports were not used due to duplication of file include
export async function loadBaseStyles() {
const { standaloneGnav } = getConfig();
if (standaloneGnav) return;
if (isDarkMode()) {
new Promise((resolve) => { loadStyle(rootPath('base.css'), resolve); })
.then(() => loadStyles(rootPath('dark-nav.css')));
Expand All @@ -164,10 +169,6 @@ export async function loadBaseStyles() {
}
}

export function loadBlock(path) {
return import(path).then((module) => module.default);
}

let cachedDecorateMenu;
export async function loadDecorateMenu() {
if (cachedDecorateMenu) return cachedDecorateMenu;
Expand All @@ -177,15 +178,12 @@ export async function loadDecorateMenu() {
resolve = _resolve;
});

const [{ decorateMenu, decorateLinkGroup }] = await Promise.all([
loadBlock('./menu/menu.js'),
const [menu] = await Promise.all([
import('./menu/menu.js'),
loadStyles(rootPath('utilities/menu/menu.css')),
]);

resolve({
decorateMenu,
decorateLinkGroup,
});
resolve(menu.default);
return cachedDecorateMenu;
}

Expand Down
4 changes: 4 additions & 0 deletions libs/blocks/region-nav/region-nav.css
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@
column-count: 1;
}

.region-nav.hide {
display: none;
}

@media (min-width: 600px) {
.region-nav > div:nth-of-type(2) {
column-count: 3;
Expand Down
1 change: 1 addition & 0 deletions libs/navigation/base.css
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@import '../blocks/global-navigation/base.css';
11 changes: 4 additions & 7 deletions libs/navigation/bootstrapper.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
export default async function bootstrapBlock(miloLibs, blockConfig) {
/* eslint import/no-relative-packages: 0 */
export default async function bootstrapBlock(initBlock, blockConfig) {
const { name, targetEl, layout, noBorder, jarvis } = blockConfig;
const { getConfig, createTag, loadLink, loadScript } = await import(`${miloLibs}/utils/utils.js`);
const { default: initBlock } = await import(`${miloLibs}/blocks/${name}/${name}.js`);

const styles = [`${miloLibs}/blocks/${name}/${name}.css`, `${miloLibs}/navigation/navigation.css`];
styles.forEach((url) => loadLink(url, { rel: 'stylesheet' }));
const { getConfig, createTag, loadScript } = await import('../utils/utils.js');

const setNavLayout = () => {
const element = document.querySelector(targetEl);
Expand Down Expand Up @@ -41,7 +38,7 @@ export default async function bootstrapBlock(miloLibs, blockConfig) {

await initBlock(document.querySelector(targetEl));
if (blockConfig.targetEl === 'footer') {
const { loadPrivacy } = await import(`${miloLibs}/scripts/delayed.js`);
const { loadPrivacy } = await import('../scripts/delayed.js');
setTimeout(() => {
loadPrivacy(getConfig, loadScript);
}, blockConfig.delay);
Expand Down
Loading

0 comments on commit 38abecd

Please sign in to comment.