Skip to content

Commit

Permalink
Merge branch 'stage' into mepnestedplaceholders
Browse files Browse the repository at this point in the history
  • Loading branch information
vgoodric committed Sep 19, 2024
2 parents 79c1e90 + c3c9bcc commit 04cf9f8
Show file tree
Hide file tree
Showing 26 changed files with 838 additions and 442 deletions.
1 change: 1 addition & 0 deletions .hlxignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ LICENSE
web-test-runner.config.mjs
codecov.yaml
libs/features/mas/*
!libs/features/mas/docs
43 changes: 43 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ https://feat-branch--project--owner.hlx.page/?milolibs=local (feature code, stag
```

## Testing
### Unit Testing
```sh
npm run test
```
Expand All @@ -69,3 +70,45 @@ npm run test:watch
### Coverage
`npm run test:watch` can give misleading coverage reports. Use `npm run test` for accurate coverage reporting.

### Nala E2E UI Testing
-----

#### 1. Running Nala Tests
Nala tests are run using the `npm run nala <env> [options]` command:

```sh
npm run nala <env> [options]
```
```sh
# env: [local | libs | branch | stage | etc ] default: local

# options:
- browser=<chrome|firefox|webkit> # Browser to use (default: chrome)
- device=<desktop|mobile> # Device (default: desktop)
- test=<.test.js> # Specific test file to run (runs all tests in the file)
- -g, --g=<@tag> # Tag to filter tests by annotations ex: @test1 @accordion @marquee
- mode=<headless|ui|debug|headed> # Mode (default: headless)
- config=<config-file> # Configuration file (default: Playwright default)
- project=<project-name> # Project configuration (default: milo-live-chromium)
- milolibs=<local|prod|feature|any|> # Milolibs?=<env>

```
#### 2. Nala Help Command:
To view examples of how to use Nala commands with various options, you can run
```sh
npm run nala help
```

#### ⚠️ Important Note
- **Debug and UI Mode Caution**: When using `debug` or `ui` mode, it is recommended to run only a single test using annotations (e.g., `@test1`). Running multiple tests in these modes (e.g., `npm run nala local mode=debug` or `mode=ui`) will launch a separate browser or debugger window for each test, which can quickly become resource-intensive and challenging to manage.

- **Tip**: To effectively watch or debug, focus on one test at a time to avoid opening excessive browser instances or debugger windows.

#### 3. Nala Documentation
For detailed guides and documentation on Nala, please visit the [Nala GitHub Wiki](https://github.com/adobecom/milo/wiki/Nala#nala-introduction).






11 changes: 9 additions & 2 deletions libs/blocks/global-navigation/global-navigation.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ import {
addMepHighlightAndTargetId,
isDarkMode,
darkIcons,
setDisableAEDState,
getDisableAEDState,
} from './utilities/utilities.js';

import { replaceKey, replaceKeyArray } from '../../features/placeholders.js';
Expand Down Expand Up @@ -837,8 +839,9 @@ class Gnav {

if (!hasActiveLink()) {
const sections = this.elements.mainNav.querySelectorAll('.feds-navItem--section');
const disableAED = getDisableAEDState();

if (sections.length === 1) {
if (!disableAED && sections.length === 1) {
sections[0].classList.add(selectors.activeNavItem.slice(1));
setActiveLink(true);
}
Expand Down Expand Up @@ -1024,7 +1027,11 @@ const getSource = async () => {
export default async function init(block) {
try {
const { mep } = getConfig();
const url = await getSource();
const sourceUrl = await getSource();
const [url, hash = ''] = sourceUrl.split('#');
if (hash === '_noActiveItem') {
setDisableAEDState();
}
const content = await fetchAndProcessPlainHtml({ url });
if (!content) return null;
const gnav = new Gnav({
Expand Down
14 changes: 12 additions & 2 deletions libs/blocks/global-navigation/utilities/utilities.js
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ export function getAnalyticsValue(str, index) {

export function getExperienceName() {
const experiencePath = getMetadata('gnav-source');
const explicitExperience = experiencePath?.split('/').pop();
const explicitExperience = experiencePath?.split('#')[0]?.split('/').pop();
if (explicitExperience?.length
&& explicitExperience !== 'gnav') return explicitExperience;

Expand Down Expand Up @@ -257,14 +257,24 @@ export function setActiveDropdown(elem) {
});
}

// Disable AED(Active Element Detection)
export const [setDisableAEDState, getDisableAEDState] = (() => {
let disableAED = false;
return [
() => { disableAED = true; },
() => disableAED,
];
})();

export const [hasActiveLink, setActiveLink, getActiveLink] = (() => {
let activeLinkFound;

return [
() => activeLinkFound,
(val) => { activeLinkFound = !!val; },
(area) => {
if (hasActiveLink() || !(area instanceof HTMLElement)) return null;
const disableAED = getDisableAEDState();
if (disableAED || hasActiveLink() || !(area instanceof HTMLElement)) return null;
const { origin, pathname } = window.location;
const url = `${origin}${pathname}`;
const activeLink = [
Expand Down
1 change: 0 additions & 1 deletion libs/blocks/hero-marquee/hero-marquee.css
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
align-items: center;
}

.hero-marquee.no-min-height { min-height: unset; }
.hero-marquee.s-min-height { min-height: var(--s-min-height); }
.hero-marquee.l-min-height { min-height: var(--l-min-height); }

Expand Down
10 changes: 4 additions & 6 deletions libs/blocks/hero-marquee/hero-marquee.js
Original file line number Diff line number Diff line change
Expand Up @@ -164,16 +164,14 @@ function loadBreakpointThemes() {
export default async function init(el) {
el.classList.add('con-block');
let rows = el.querySelectorAll(':scope > div');
if (rows.length <= 1) return;
const [head, ...tail] = rows;
rows = tail;
if (head.textContent.trim() === '') {
head.remove();
} else {
if (rows.length > 1 && rows[0].textContent !== '') {
el.classList.add('has-bg');
const [head, ...tail] = rows;
handleObjectFit(head);
decorateBlockBg(el, head, { useHandleFocalpoint: true });
rows = tail;
}

// get first row that's not a keyword key/value row
const mainRowIndex = rows.findIndex((row) => {
const firstColText = row.children[0].textContent.toLowerCase().trim();
Expand Down
6 changes: 4 additions & 2 deletions libs/blocks/library-config/library-config.css
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,8 @@ input.sk-library-search-input:focus {
* Fixes block list getting cut off with search
* Margin height equal to search bar height
*/
.sk-library ul.con-blocks-list.inset {
.sk-library ul.con-blocks-list.inset,
.sk-library ul.con-templates-list.inset {
margin-bottom: 39px;
}

Expand Down Expand Up @@ -242,7 +243,8 @@ input.sk-library-search-input:focus {
font-weight: 700;
}

.sk-library .block-group.is-hidden {
.sk-library .block-group.is-hidden,
.con-templates-list .template.is-hidden {
display: none;
}

Expand Down
45 changes: 35 additions & 10 deletions libs/blocks/library-config/library-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ import { createTag } from '../../utils/utils.js';

const LIBRARY_PATH = '/docs/library/library.json';

async function loadBlocks(content, list, query) {
async function loadBlocks({ content, list, query, type }) {
const { default: blocks } = await import('./lists/blocks.js');
blocks(content, list, query);
blocks(content, list, query, type);
}

async function loadTemplates(content, list) {
async function loadTemplates({ content, list, query, type }) {
const { default: templates } = await import('./lists/templates.js');
templates(content, list);
templates(content, list, query, type);
}

async function loadPlaceholders(content, list) {
Expand All @@ -32,27 +32,47 @@ async function loadPersonalization(content, list) {
personalization(content, list);
}

function addSearch(content, list) {
function addSearch({ content, list, type }) {
const skLibrary = list.closest('.sk-library');
const header = skLibrary.querySelector('.sk-library-header');
let search = skLibrary.querySelector('.sk-library-search');
if (!search) {
search = createTag('div', { class: 'sk-library-search' });
const searchInput = createTag('input', { class: 'sk-library-search-input', placeholder: 'Search...' });
const clear = createTag('div', { class: 'sk-library-search-clear is-hidden' });

searchInput.addEventListener('input', (e) => {
const query = e.target.value;
if (query === '') {
clear.classList.add('is-hidden');
} else {
clear.classList.remove('is-hidden');
}
loadBlocks(content, list, query);

switch (type) {
case 'blocks':
loadBlocks({ content, list, query, type });
break;
case 'templates':
loadTemplates({ content, list, query, type });
break;
default:
}
});
clear.addEventListener('click', (e) => {
e.target.classList.add('is-hidden');
e.target.closest('.sk-library-search').querySelector('.sk-library-search-input').value = '';
loadBlocks(content, list);
const query = e.target.value;

switch (type) {
case 'blocks':
loadBlocks({ content, list, query, type });
break;
case 'templates':
loadTemplates({ content, list, query, type });
break;
default:
}
});
search.append(searchInput);
search.append(clear);
Expand All @@ -67,11 +87,12 @@ async function loadList(type, content, list) {
const query = list.closest('.sk-library').querySelector('.sk-library-search-input')?.value;
switch (type) {
case 'blocks':
addSearch(content, list);
loadBlocks(content, list, query);
addSearch({ content, list, type });
loadBlocks({ content, list, query, type });
break;
case 'templates':
loadTemplates(content, list);
addSearch({ content, list, type });
loadTemplates({ content, list, query, type });
break;
case 'placeholders':
loadPlaceholders(content, list);
Expand Down Expand Up @@ -198,6 +219,10 @@ function createHeader() {
el.classList.remove('inset');
});
skLibrary.classList.remove('allow-back');

// Remove library search if it's been added
const search = skLibrary.querySelector('.sk-library-search');
if (search) search.remove();
});
return header;
}
Expand Down
22 changes: 21 additions & 1 deletion libs/blocks/library-config/library-utils.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,24 @@
/* global ClipboardItem */
import { getSearchTags } from './lists/blocks.js';
import { getTemplateSearchTags } from './lists/templates.js';

/* search utility */
export function isMatching(container, query, type, titleText) {
let tagsString;

switch (type) {
case 'blocks':
tagsString = getSearchTags(container);
break;
case 'templates':
tagsString = getTemplateSearchTags(container, titleText);
break;
default:
}
if (!query || !tagsString) return false;
const searchTokens = query.split(' ');
return searchTokens.every((token) => tagsString.toLowerCase().includes(token.toLowerCase()));
}

export default function createCopy(blob) {
const data = [new ClipboardItem({ [blob.type]: blob })];
navigator.clipboard.write(data);
Expand Down
14 changes: 3 additions & 11 deletions libs/blocks/library-config/lists/blocks.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { createTag } from '../../../utils/utils.js';
import createCopy from '../library-utils.js';
import createCopy, { isMatching } from '../library-utils.js';
import { getMetadata } from '../../section-metadata/section-metadata.js';

const LIBRARY_METADATA = 'library-metadata';
Expand Down Expand Up @@ -132,13 +132,6 @@ export function getSearchTags(container) {
return containerName;
}

export function isMatching(container, query) {
const tagsString = getSearchTags(container);
if (!query || !tagsString) return false;
const searchTokens = query.split(' ');
return searchTokens.every((token) => tagsString.toLowerCase().includes(token.toLowerCase()));
}

function getBlockType(subSection, withinContainer) {
if (subSection.className === LIBRARY_CONTAINER_START) return CONTAINER_START_BLOCK;
if (subSection.className === LIBRARY_CONTAINER_END) return CONTAINER_END_BLOCK;
Expand Down Expand Up @@ -247,7 +240,7 @@ export function getContainers(doc) {
return containers;
}

export default async function loadBlocks(blocks, list, query) {
export default async function loadBlocks(blocks, list, query, type) {
list.textContent = '';
blocks.forEach(async (block) => {
const titleText = createTag('p', { class: 'item-title' }, block.name);
Expand Down Expand Up @@ -277,7 +270,6 @@ export default async function loadBlocks(blocks, list, query) {
const html = await resp.text();
const parser = new DOMParser();
const doc = parser.parseFromString(html, 'text/html');

const containers = getContainers(doc);
let matchingContainerFound = false;

Expand All @@ -298,7 +290,7 @@ export default async function loadBlocks(blocks, list, query) {
item.append(name, copy);

if (query) {
if (isMatching(container, query)) {
if (isMatching(container, query, type)) {
matchingContainerFound = true;
} else {
item.classList.add('is-hidden');
Expand Down
Loading

0 comments on commit 04cf9f8

Please sign in to comment.