Skip to content

Commit

Permalink
Adding a dynamic nav status button into the global nav to aid content…
Browse files Browse the repository at this point in the history
… QA in understanding which nav is active
  • Loading branch information
JasonHowellSlavin committed Sep 19, 2024
1 parent c3c9bcc commit 2b4175d
Show file tree
Hide file tree
Showing 8 changed files with 571 additions and 4 deletions.
2 changes: 1 addition & 1 deletion libs/blocks/global-navigation/global-navigation.js
Original file line number Diff line number Diff line change
Expand Up @@ -1018,7 +1018,7 @@ const getSource = async () => {
const { locale, dynamicNavKey } = getConfig();
let url = getMetadata('gnav-source') || `${locale.contentRoot}/gnav`;
if (dynamicNavKey) {
const { default: dynamicNav } = await import('../../features/dynamic-navigation.js');
const { default: dynamicNav } = await import('../../features/dynamic-navigation/dynamic-navigation.js');
url = dynamicNav(url, dynamicNavKey);
}
return url;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { getMetadata } from '../utils/utils.js';
import { getMetadata } from '../../utils/utils.js';

function isDynamicNavDisabled() {
export function isDynamicNavDisabled() {
const dynamicNavDisableValues = getMetadata('dynamic-nav-disable');
if (!dynamicNavDisableValues) return false;

Expand Down
173 changes: 173 additions & 0 deletions libs/features/dynamic-navigation/status.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
.dynamic-nav-status {
border: 2px solid white;
border-radius: 32px;
color: #eee;
font-size: 16px;
padding: 12px 24px;
cursor: pointer;
display: flex;
align-items: center;
margin: 12px;
position: relative;
}

.dynamic-nav-status .title {
display: flex;
}

.dynamic-nav-status.active {
background-color: #280;
}

.dynamic-nav-status.enabled {
background-color: #ec4;
}

.dynamic-nav-status.inactive {
background-color: #e20;
}

.dns-badge {
border: 2px solid white;
border-radius: 32px;
background-color: transparent;
box-sizing: border-box;
color: #eee;
padding: 8px;
height: 12px;
width: 12px;
margin: 4px 8px 4px 0;
cursor: pointer;
display: flex;
align-items: center;
position: relative;
}

.dns-badge::after {
content: '';
display: block;
box-sizing: border-box;
position: absolute;
width: 6px;
height: 6px;
border-top: 2px solid;
border-right: 2px solid;
transform: rotate(45deg);
left: 5px;
bottom: 5px;
transition-duration: 0.2s;
}

.dns-badge.dns-open::after{
transform: rotate(135deg);
transition-duration: 0.2s;
}

.dynamic-nav-status .hidden {
display: none;
}

.dynamic-nav-status.enabled .title,
.dynamic-nav-status.enabled .dns-badge {
color: var(--feds-color-hamburger);
border-color: var(--feds-color-hamburger);
}

.dynamic-nav-status .dns-close-container {
display: flex;
justify-content: flex-end;
width: 100%;
height: 10px;
padding: 2px;
}

.dynamic-nav-status .dns-close {
cursor: pointer;
display: block;
position: absolute;
border: 2px solid white;
border-radius: 32px;
background-color: transparent;
color: #eee;
height: 20px;
width: 20px;
top: 6px;
right: 6px;
box-sizing: border-box;
}

.dynamic-nav-status .dns-close::after {
content: 'x';
display: block;
box-sizing: border-box;
position: absolute;
width: 6px;
height: 6px;
left: 4px;
top: -8px;
font-size: 18px;
font-weight: 600;
}

.dynamic-nav-status .details {
position: absolute;
top: 60px;
right: 0;
background-color: #444;
min-width: 300px;
border-radius: 16px;
box-shadow: 0 0 10px #000;
font-size: 12px;
padding: 20px;
z-index: 1;
}

.dynamic-nav-status .details::before {
content: '';
width: 0;
height: 0;
position: absolute;
border-left: 15px solid transparent;
border-right: 15px solid transparent;
border-top: 15px solid #444;
top: -15px;
right: 75px;
rotate: 180deg;
}

.dynamic-nav-status p{
margin: 2px;
}

.dynamic-nav-status .details p {
font-weight: 600;
}

.dynamic-nav-status .details span {
font-weight: 300;
}

.dynamic-nav-status .details .additional-info {
border-bottom: 1px solid white;
}

.dynamic-nav-status .disable-values{
min-width: 100%;
}

.dynamic-nav-status .disable-values table {
border-collapse: collapse;
width: 100%;
}

.dynamic-nav-status .disable-values caption{
min-width: 100%;
text-align: left;
font-weight: 600;
}

.dynamic-nav-status .disable-values th,
.dynamic-nav-status .disable-values td {
border: 1px solid rgb(160 160 160);
padding: 8px 10px;
}
133 changes: 133 additions & 0 deletions libs/features/dynamic-navigation/status.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
import { createTag, getConfig, getMetadata, loadStyle } from '../../utils/utils.js';
import { isDynamicNavDisabled } from './dynamic-navigation.js';

export const ACTIVE = 'active';
export const ENABLED = 'enabled';
export const INACTIVE = 'inactive';
export const tooltipInfo = {
active: 'Displayed in green, this status appears when a user is on an entry page or a page with the Dynamic Nav enabled, indicating that the nav is fully functioning.',
enabled: 'Displayed in yellow, this status indicates that the Dynamic Nav is set to "on," but the user has not yet visited an entry page.',
inactive: 'Displayed in red, this status indicates that the Dynamic Nav is either not configured or has been disabled.',
};

const getCurrentSource = (status, storageSource, authoredSource) => {
if (status === 'on') {
return storageSource || authoredSource;
}
return authoredSource;
};

const getStatus = (status, disabled, storageSource) => {
if (status === 'entry') return ACTIVE;

if (disabled) return INACTIVE;

if (status === 'on' && storageSource) return ACTIVE;

if (status === 'on' && !storageSource) return ENABLED;

return INACTIVE;
};

const processDisableValues = (arr, elem) => {
if (arr === null || arr === undefined || arr.length === 0) return;

const diableValueList = arr.split(',');
const table = createTag('table');

const tableDOM = `
<caption>Disable Values</caption>
<thead>
<tr>
<th>Key</th>
<th>Value</th>
</tr>
</thead>
<tbody>
</tbody>`;

table.innerHTML = tableDOM;
const tBody = table.querySelector('tbody');

diableValueList.forEach((pair) => {
const itemRow = createTag('tr');
const [key, value] = pair.split(';');
const keyElem = createTag('td');
const valElem = createTag('td');
keyElem.innerText = key;
valElem.innerText = value;

itemRow.append(keyElem, valElem);
tBody.append(itemRow);
});

elem.append(table);
};

const returnPath = (url) => {
if (!url.includes('https://')) return '';
const sourceUrl = new URL(url);
return sourceUrl.pathname;
};

const createStatusWidget = (dynamicNavKey) => {
const storedSource = window.sessionStorage.getItem('gnavSource');
const authoredSource = getMetadata('gnav-source') || 'Metadata not found: site gnav source';
const dynamicNavSetting = getMetadata('dynamic-nav');
const currentSource = getCurrentSource(dynamicNavSetting, storedSource, authoredSource);
const dynamicNavDisableValues = getMetadata('dynamic-nav-disable');
const status = getStatus(dynamicNavSetting, isDynamicNavDisabled(), storedSource);
const statusWidget = createTag('div', { class: 'dynamic-nav-status' });

statusWidget.innerHTML = `
<span class="title"><span class="dns-badge"></span>Dynamic Nav</span>
<section class="details hidden">
<span class="dns-close"></span>
<div class="message additional-info">
<p>Additional Info:
<span>${tooltipInfo[status]}</span>
</p>
</div>
<p class="status">Status: <span>${status}</span></p>
<p class="setting">Setting: <span>${dynamicNavSetting}</span></p>
<p class="consumer-key">Consumer key: <span>${dynamicNavKey}</span></p>
<div class="nav-source-info">
<p>Authored and stored source match: <span>${authoredSource === currentSource}</span></p>
<p>Authored Nav Source:
<span>${returnPath(authoredSource)}</span></p>
<p>Stored Nav Source:
<span>${returnPath(currentSource)}</span></p>
</div>
<div class="disable-values">
</div>
</section>
`;

processDisableValues(dynamicNavDisableValues, statusWidget.querySelector('.disable-values'));
statusWidget.classList.add(status);

statusWidget.addEventListener('click', () => {
statusWidget.querySelector('.details').classList.toggle('hidden');
statusWidget.querySelector('.dns-badge').classList.toggle('dns-open');
});

return statusWidget;
};

export default async function main() {
const { dynamicNavKey, miloLibs, codeRoot, env } = getConfig();
if (env?.name === 'prod') return;

loadStyle(`${miloLibs || `${codeRoot}/libs`}/features/dynamic-navigation/status.css`);

const statusWidget = createStatusWidget(dynamicNavKey);
const topNav = document.querySelector('.feds-topnav');
const fedsWrapper = document.querySelector('.feds-nav-wrapper');
const dnsClose = statusWidget.querySelector('.dns-close');

dnsClose.addEventListener('click', () => {
topNav.removeChild(statusWidget);
});

fedsWrapper.after(statusWidget);
}
4 changes: 4 additions & 0 deletions libs/utils/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -1126,6 +1126,10 @@ export async function loadDeferred(area, blocks, config) {
import('../features/personalization/preview.js')
.then(({ default: decoratePreviewMode }) => decoratePreviewMode());
}
if (config?.dynamicNavKey) {
const { default: loadDNStatus } = await import('../features/dynamic-navigation/status.js');
loadDNStatus();
}

Check warning on line 1132 in libs/utils/utils.js

View check run for this annotation

Codecov / codecov/patch

libs/utils/utils.js#L1130-L1132

Added lines #L1130 - L1132 were not covered by tests
}

function initSidekick() {
Expand Down
2 changes: 1 addition & 1 deletion test/features/dynamic-nav/dynamicNav.test.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { readFile } from '@web/test-runner-commands';
import { expect } from '@esm-bundle/chai';
import { setConfig } from '../../../libs/utils/utils.js';
import dynamicNav from '../../../libs/features/dynamic-navigation.js';
import dynamicNav from '../../../libs/features/dynamic-navigation/dynamic-navigation.js';

describe('Dynamic nav', () => {
beforeEach(() => {
Expand Down
9 changes: 9 additions & 0 deletions test/features/dynamic-nav/mocks/status.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<header>
<nav class="feds-topnav" aria-label="Main">
<div class="feds-brand-container">
</div>
<div class="feds-nav-wrapper" id="feds-nav-wrapper">
<div class="feds-nav"></div>
</div>
</nav>
</header>
Loading

0 comments on commit 2b4175d

Please sign in to comment.