Skip to content
Draft
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions streamlibs/blocks/carousel.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ export default async function mapcarousel(sectionWrapper, blockContent, figConte
selector: 'p:nth-child(2)',
value: media.text,
});
mediaDiv.querySelectorAll('.to-remove').forEach((el) => el.remove());
mediaDiv.classList.add('section');
sections.push(mediaDiv);
});
Expand Down
19 changes: 16 additions & 3 deletions streamlibs/blocks/hero-marquee.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import {
flagForRemoval,
handleActionButtons,
handleBackground, handleComponents, handleImageComponent, handleProductLockup,
} from '../components/components.js';
import { LOGOS } from '../utils/constants.js';
import { safeJsonFetch } from '../utils/error-handler.js';
import { divSwap, getFirstType, getIconSize } from '../utils/utils.js';
import {
divSwap, getFirstType, getIconSize, isEmptyValue,
} from '../utils/utils.js';

function handleSwap(blockContent, properties) {
if (getFirstType(properties?.layout) === 'image') {
Expand All @@ -17,8 +20,15 @@ function handleSwap(blockContent, properties) {
}

function handleProductLockups(value, areaEl) {
if (!value) return;
value.forEach((productLockup) => {
if (!value || !areaEl) return;
// Skip blank entries so one empty lockup can't flag the shared container
// and wipe out the valid ones at the sweep.
const lockups = value.filter((productLockup) => !isEmptyValue(productLockup));
if (!lockups.length) {
flagForRemoval(areaEl);
return;
}
lockups.forEach((productLockup) => {
handleProductLockup(productLockup, areaEl);
});
}
Expand Down Expand Up @@ -138,6 +148,9 @@ export default async function mapBlockContent(sectionWrapper, blockContent, figC
value,
actionEL,
);
// action/action1 can hold buttons even when actions is empty —
// unflag so the populated element survives the sweep.
if (actionEL.innerHTML.trim()) actionEL.classList.remove('to-remove');
break;
}
case 'checklistItems':
Expand Down
2 changes: 2 additions & 0 deletions streamlibs/blocks/logo-gallery.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {
replaceImage,
handleSpacerWithSectionMetadata,
} from '../components/components.js';
import { isEmptyValue } from '../utils/utils.js';

function handleVariants(sectionWrapper, blockContent, properties) {
if (properties?.topSpacer) handleSpacerWithSectionMetadata(sectionWrapper, blockContent, properties.topSpacer.name, 'top');
Expand All @@ -26,6 +27,7 @@ export default async function mapBlockContent(sectionWrapper, blockContent, figC
try {
addActionScroller(sectionWrapper);
properties['logo-items'].forEach((logo) => {
if (isEmptyValue(logo?.image)) return;
const actionItem = blockContent.cloneNode(true);
replaceImage(actionItem.querySelector('picture'), logo.image);
sectionWrapper.appendChild(actionItem);
Expand Down
4 changes: 2 additions & 2 deletions streamlibs/blocks/table.js
Original file line number Diff line number Diff line change
Expand Up @@ -106,13 +106,13 @@ function createDataRow(row, rowTemplate, cellTemplate) {
// For section title rows, create only ONE cell with the heading
const titleCell = document.createElement('div');
titleCell.setAttribute('data-valign', 'middle');
titleCell.innerHTML = `<strong>${row.heading || ''}</strong>`;
if (row.heading) titleCell.innerHTML = `<strong>${row.heading}</strong>`;
rowEl.appendChild(titleCell);
} else {
// Regular data row - first cell is row heading
const headingCell = document.createElement('div');
headingCell.setAttribute('data-valign', 'middle');
headingCell.innerHTML = `<strong>${row.heading || ''}</strong>`;
if (row.heading) headingCell.innerHTML = `<strong>${row.heading}</strong>`;
rowEl.appendChild(headingCell);

// Data cells
Expand Down
35 changes: 28 additions & 7 deletions streamlibs/components/components.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,24 @@ import {
LOGOS,
ICON_CLASS,
} from '../utils/constants.js';
import { isEmptyValue } from '../utils/utils.js';

export function flagForRemoval(el, selector) {
const target = selector ? el.querySelector(selector) : el;
if (!target) return null;
target.classList.add('to-remove');
// Flag parent wrappers left with no real content, but never structural
// row/cell divs — Milo parses blocks positionally.
const isRemovable = (node) => (node.nodeType === Node.TEXT_NODE && !node.textContent.trim())
|| (node.nodeType === Node.ELEMENT_NODE && node.classList.contains('to-remove'));
let parent = target.parentElement;
while (parent && parent !== el && parent.tagName !== 'DIV'
&& [...parent.childNodes].every(isRemovable)) {
parent.classList.add('to-remove');
parent = parent.parentElement;
}
return null;
}

export function resolveImageValue(value) {
if (!value) return { url: '', altText: '' };
Expand All @@ -14,8 +32,8 @@ export function resolveImageValue(value) {
}

export function handleTextComponent({ el, value, selector }) {
if (isEmptyValue(value)) return flagForRemoval(el, selector);
const textEl = el.querySelector(selector);
if (!value) return textEl.classList.add('to-remove');
textEl.innerHTML = '';
const lines = value.split('\n');
if (lines.length === 1) {
Expand All @@ -29,8 +47,8 @@ export function handleTextComponent({ el, value, selector }) {
}

export function handleImageComponent({ el, value, selector }) {
if (isEmptyValue(value)) return flagForRemoval(el, selector);
const picEl = el.querySelector(selector);
if (!value) return picEl.classList.add('to-remove');
const { url, altText } = resolveImageValue(value);
picEl.querySelectorAll('source').forEach((source) => { source.srcset = url; });
const imgEl = picEl.querySelector('img');
Expand All @@ -40,16 +58,15 @@ export function handleImageComponent({ el, value, selector }) {
}

function handleContainerComponent({ el, value, selector }) {
if (isEmptyValue(value)) return flagForRemoval(el, selector);
const containerEl = el.querySelector(selector);
if (!value || (Array.isArray(value) && value.length < 1)) return containerEl.classList.add('to-remove');
containerEl.innerHTML = '';
return containerEl;
}

function handleLogoContainerComponent({ el, value, selector }) {
const containerEl = el.querySelector(selector);
if (!value) return containerEl.classList.add('to-remove');
return containerEl;
if (isEmptyValue(value)) return flagForRemoval(el, selector);
return el.querySelector(selector);
}

function addSVGInButton(icon) {
Expand All @@ -75,6 +92,7 @@ export function handleButtonComponent({
hasLeadingIcon,
hasTrailingIcon,
}) {
if (!actionArea) return;
const btnType = buttonType ? buttonType.toLowerCase() : 'm button / text';
// Button type
// eslint-disable-next-line no-restricted-syntax
Expand All @@ -96,6 +114,9 @@ export function handleButtonComponent({
}

export function handleComponents(el, value, mappingConfig) {
if (isEmptyValue(value)) {
return mappingConfig.selector ? flagForRemoval(el, mappingConfig.selector) : null;
}
switch (mappingConfig.type) {
case 'text':
return handleTextComponent({ el, selector: mappingConfig.selector, value });
Expand Down Expand Up @@ -322,7 +343,7 @@ export function replaceImage(pic, src) {
export function handleProductLockup(value, areaEl) {
if (!areaEl) return;
// eslint-disable-next-line consistent-return
if (!value) return areaEl.classList.add('to-remove');
if (isEmptyValue(value)) return flagForRemoval(areaEl);
// eslint-disable-next-line prefer-destructuring, no-param-reassign
if (Array.isArray(value)) value = value[0];
const tileName = value?.productTile?.name || 'placeholder';
Expand Down
9 changes: 2 additions & 7 deletions streamlibs/sources/figma.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { handleError, safeFetch } from '../utils/error-handler.js';
import { createFigmaLoaderReporter } from '../utils/loader.js';
import { appendBlockActionButton } from '../utils/block-action-button.js';
import { isEmptyValue } from '../utils/utils.js';

const PLACEHOLDER_URL = 'https://main--stream-mapper--adobecom.aem.live/fragments/stream-block-placeholder';
const METADATA_KEYS = new Set(['colorTheme', 'miloTag', 'layout']);
Expand All @@ -10,13 +11,7 @@ function isEmptyBlockContent(properties) {
if (!properties || typeof properties !== 'object') return true;
const entries = Object.entries(properties);
if (entries.length === 0) return true;
return entries.every(([key, value]) => {
if (METADATA_KEYS.has(key)) return true;
if (value === false || value === '' || value == null) return true;
if (Array.isArray(value)) return value.length === 0;
if (typeof value === 'object') return Object.keys(value).length === 0;
return false;
});
return entries.every(([key, value]) => METADATA_KEYS.has(key) || isEmptyValue(value));
}

function createPlaceholder() {
Expand Down
7 changes: 7 additions & 0 deletions streamlibs/utils/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,13 @@ export function fixRelativeLinks(html) {
return html.replaceAll('./media', 'https://main--milo--adobecom.aem.page/media');
}

export function isEmptyValue(value) {
if (value === false || value == null || value === '') return true;
if (Array.isArray(value)) return value.length === 0;
if (typeof value === 'object') return Object.keys(value).length === 0;
return false;
}

export async function getConfig() {
const { getConfig: miloGetConfig } = await import(`${getLibs()}/utils/utils.js`);
return miloGetConfig();
Expand Down