Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MWPW-154059 - Milo templates library #2855

Merged
merged 4 commits into from
Sep 19, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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) {
rgclayton marked this conversation as resolved.
Show resolved Hide resolved
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) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above. I think it would be good to pass an object.

Suggested change
function addSearch(content, list, type) {
function addSearch({ content, list, type }) {

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

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
51 changes: 46 additions & 5 deletions libs/blocks/library-config/lists/templates.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,23 @@
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';
import { getTable, decorateImages, handleLinks } from './blocks.js';

function createSpace() {
const br = createTag('br');
return createTag('p', null, br);
}

export function getTemplateSearchTags(template, titleText) {
const templateName = titleText.textContent;

if (template.searchtags?.text) {
const terms = template.searchtags?.text.trim().toLowerCase();
return `${terms} ${templateName}`;
}
return templateName;
}

function formatDom(aemDom, path) {
// Decorate Links
handleLinks(aemDom, path);
Expand All @@ -16,11 +27,27 @@ function formatDom(aemDom, path) {

// Decorate Blocks
const divs = aemDom.querySelectorAll('main > div > div');
const template = {};

divs.forEach((div) => {
// If there is library-metadata, extract searchTags. Remove library-metadata.
if (div.classList.contains('library-metadata')) {
const libraryMetadata = getMetadata(div);
template.searchtags = libraryMetadata.searchtags;
div.remove();
return;
}
// Give table some space
div.insertAdjacentElement('afterend', createSpace());

const table = getTable(div, true);
const th = table.querySelector('th');

// Converts to a metadata block so it can be copied/pasted.
if (th.textContent === 'template-metadata') {
th.textContent = 'metadata';
}

div.parentElement.replaceChild(table, div);
});

Expand All @@ -38,7 +65,8 @@ function formatDom(aemDom, path) {
});
const flattedDom = createTag('div');
flattedDom.append(...formattedSections);
return flattedDom;
template.flattedDom = flattedDom;
return template;
}

async function formatTemplate(path) {
Expand All @@ -50,13 +78,14 @@ async function formatTemplate(path) {
return formatDom(dom, path);
}

export default async function loadTemplates(templates, list) {
export default async function loadTemplates(templates, list, query, type) {
list.textContent = '';

templates.forEach(async (template) => {
const titleText = createTag('p', { class: 'item-title' }, template.name);
const title = createTag('li', { class: 'template' }, titleText);
const previewButton = createTag('button', { class: 'preview-group' }, 'Preview');
const copy = createTag('button', { class: 'copy' });
const formatted = await formatTemplate(template.path);

list.append(title);
title.append(previewButton, copy);
Expand All @@ -66,10 +95,22 @@ export default async function loadTemplates(templates, list) {
window.open(template.path, '_templatepreview');
});

// Returns an object with flattedDom and searchtags.
const formatted = await formatTemplate(template.path);
if (query) {
if (isMatching(formatted, query, type, titleText)) {
title.classList.remove('is-hidden');
} else {
title.classList.add('is-hidden');
}
} else {
title.classList.remove('is-hidden');
}

copy.addEventListener('click', (e) => {
e.target.classList.add('copied');
setTimeout(() => { e.target.classList.remove('copied'); }, 3000);
const blob = new Blob([formatted.outerHTML], { type: 'text/html' });
const blob = new Blob([formatted.flattedDom.outerHTML], { type: 'text/html' });
createCopy(blob);
});
});
Expand Down
1 change: 1 addition & 0 deletions libs/blocks/library-metadata/library-metadata.css
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

.library-meta-row {
background-color: #EFEFEF;
color: initial;
display: grid;
grid-template-columns: 1fr 1fr;
margin-top: 4px;
Expand Down
25 changes: 13 additions & 12 deletions test/blocks/library-config/library-config.test.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { readFile } from '@web/test-runner-commands';
import { expect } from '@esm-bundle/chai';

const { getContainers, getSearchTags, isMatching, getHtml } = await import('../../../libs/blocks/library-config/lists/blocks.js');
const { getContainers, getSearchTags, getHtml } = await import('../../../libs/blocks/library-config/lists/blocks.js');
const { isMatching } = await import('../../../libs/blocks/library-config/library-utils.js');
const BLOCK_PAGE_URL = 'https://main--milo--adobecom.hlx.page/path/to/block/page';

function verifyContainer(container, elementsLength, hasLibraryMetadata) {
Expand Down Expand Up @@ -35,8 +36,8 @@ describe('Library Config: text', () => {
const searchTags = getSearchTags(containers[0]);
expect(searchTags).to.equal('tb-2up-gr10 tb-3up-gr12 text');
// verify isMatching()
expect(isMatching(containers[0], 'tb-2up-gr10')).to.be.true;
expect(isMatching(containers[0], 'non-existing')).to.be.false;
expect(isMatching(containers[0], 'tb-2up-gr10', 'blocks')).to.be.true;
expect(isMatching(containers[0], 'non-existing', 'blocks')).to.be.false;
});
});

Expand All @@ -62,8 +63,8 @@ describe('Library Config: chart', () => {
const searchTags = getSearchTags(containers[0]);
expect(searchTags).to.equal('chart-0 chart (area, green, border)');
// verify isMatching()
expect(isMatching(containers[0], 'chart-0')).to.be.true;
expect(isMatching(containers[0], 'non-existing')).to.be.false;
expect(isMatching(containers[0], 'chart-0', 'blocks')).to.be.true;
expect(isMatching(containers[0], 'non-existing', 'blocks')).to.be.false;
});
});

Expand All @@ -89,8 +90,8 @@ describe('Library Config: marquee', () => {
const searchTags = getSearchTags(containers[0]);
expect(searchTags).to.equal('mq-std-md-lt mq-std-md-rt mq-std-md-lt-vid marquee-dark marquee');
// verify isMatching()
expect(isMatching(containers[0], 'mq-std-md-lt')).to.be.true;
expect(isMatching(containers[0], 'non-existing')).to.be.false;
expect(isMatching(containers[0], 'mq-std-md-lt', 'blocks')).to.be.true;
expect(isMatching(containers[0], 'non-existing', 'blocks')).to.be.false;
});
});

Expand Down Expand Up @@ -168,10 +169,10 @@ describe('Library Config: containers', () => {
it('isMatching', async () => {
document.body.innerHTML = mixedHtml;
const containers = getContainers(document);
expect(isMatching(containers[0], 'tag1')).to.be.false;
expect(isMatching(containers[1], 'tag1')).to.be.true;
expect(isMatching(containers[2], 'tag2')).to.be.true;
expect(isMatching(containers[3], 'tag3')).to.be.true;
expect(isMatching(containers[4], 'tag4')).to.be.true;
expect(isMatching(containers[0], 'tag1', 'blocks')).to.be.false;
expect(isMatching(containers[1], 'tag1', 'blocks')).to.be.true;
expect(isMatching(containers[2], 'tag2', 'blocks')).to.be.true;
expect(isMatching(containers[3], 'tag3', 'blocks')).to.be.true;
expect(isMatching(containers[4], 'tag4', 'blocks')).to.be.true;
});
});
Loading