Skip to content
Open
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
93 changes: 93 additions & 0 deletions blocks/content-image-teaser/_content-image-teaser.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
{
"definitions": [
{
"title": "Content/Image Teaser",
"id": "content-image-teaser",
"plugins": {
"xwalk": {
"page": {
"resourceType": "core/franklin/components/block/v1/block",
"template": {
"name": "Content/Image Teaser",
"model": "content-image-teaser",
"filter": "content-image-teaser"
}
}
}
}
}
],
"models": [
{
"id": "content-image-teaser",
"fields": [
{
"component": "text",
"name": "teaser-pretitle",
"value": "",
"label": "Pretitle"
},
{
"component": "text",
"name": "teaser-title",
"value": "",
"label": "Title"
},
{
"component": "select",
"name": "teaser-titleType",
"label": "Title Type",
"options": [
{ "name": "H2", "value": "h2" },
{ "name": "H3", "value": "h3" },
{ "name": "H4", "value": "h4" },
{ "name": "H5", "value": "h5" },
{ "name": "H6", "value": "h6" }
],
"value": "h2"
},
{
"component": "richtext",
"name": "teaser-description",
"value": "",
"label": "Text"
},
{
"component": "reference",
"name": "teaser-image",
"label": "Image",
"valueType": "string",
"multi": false
},
{
"component": "text",
"name": "teaser-imageAlt",
"label": "Image Alt Text",
"value": ""
},
{
"component": "select",
"name": "teaser-align",
"label": "Text Box Alignment",
"options": [
{ "name": "Left", "value": "left" },
{ "name": "Center", "value": "center" },
{ "name": "Right", "value": "right" }
],
"value": "left"
},
{
"component": "boolean",
"name": "teaser-coralLine",
"label": "Enable Coral Accent Line",
"value": false
},
{
"...": "../../models/partials/_button.json#/fields"
}
]
}
],
"filters": []
}

86 changes: 86 additions & 0 deletions blocks/content-image-teaser/content-image-teaser.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
.content-image-teaser {
display: flex;
flex-direction: row;
align-items: stretch;
gap: var(--spacing-l);
margin: var(--spacing-l) 0;
}

/* Alignment modifiers */
.content-image-teaser.align-right {
flex-direction: row-reverse;
}

.content-image-teaser .teaser-image,
.content-image-teaser .teaser-text {
flex: 1 1 50%;
}

.content-image-teaser .teaser-image picture,
.content-image-teaser .teaser-image img {
width: 100%;
height: auto;
display: block;
}

.content-image-teaser .teaser-text {
display: flex;
flex-direction: column;
justify-content: center;
padding: var(--spacing-m);
}

/* Coral accent line */
.content-image-teaser.coral-line .teaser-text {
border-left: 4px solid rgb(255, 127, 80);
padding-left: calc(var(--spacing-m) - 4px);
}

.content-image-teaser.align-right.coral-line .teaser-text {
border-left: none;
border-right: 4px solid rgb(255, 127, 80);
padding-left: var(--spacing-m);
padding-right: calc(var(--spacing-m) - 4px);
}

.content-image-teaser .teaser-pretitle {
font-size: var(--body-font-size-s);
font-weight: var(--font-medium);
margin: 0 0 var(--spacing-xs) 0;
text-transform: uppercase;
color: rgb(var(--text-color));
}

.content-image-teaser .teaser-title {
font-size: var(--heading-font-size-l);
font-weight: var(--font-bold);
margin: 0 0 var(--spacing-s) 0;
}

.content-image-teaser .teaser-description {
font-size: var(--body-font-size-m);
margin: 0 0 var(--spacing-m) 0;
}

.content-image-teaser .teaser-button .button {
margin-top: var(--spacing-s);
}

@media (width < 900px) {
.content-image-teaser {
flex-direction: column;
}

.content-image-teaser.align-right {
flex-direction: column;
}

.content-image-teaser.coral-line .teaser-text,
.content-image-teaser.align-right.coral-line .teaser-text {
border-left: none;
border-right: none;
border-top: 4px solid rgb(255, 127, 80);
padding: var(--spacing-m) 0 0 0;
}
}

140 changes: 140 additions & 0 deletions blocks/content-image-teaser/content-image-teaser.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import { setBlockItemOptions, moveClassToTargetedChild } from '../../scripts/utils.js';
import { renderButton } from '../../components/button/button.js';
import { createOptimizedPicture } from '../../scripts/aem.js';
import { moveInstrumentation } from '../../scripts/scripts.js';

/**
* Decorates the content/image teaser block
* Expected columns order in the authoring table:
* 0 Pretitle
* 1 Title
* 2 Title type (h2-h6)
* 3 Text (richtext)
* 4 Image path / URL
* 5 Image alt text
* 6 Alignment (left|right) – optional, defaults to left
* 7 Coral line (true|false) – optional, defaults to false
* 8 CTA link
* 9 CTA label
* 10 CTA target (e.g., _blank)
*/
export default function decorate(block) {
// Parse authoring values to an object
const blockItemsOptions = [];
const blockItemMap = [
{ name: 'pretitle' },
{ name: 'title' },
{ name: 'description' },
{ name: 'image' },
{ name: 'align' },
{ name: 'coralLine' },
{ name: 'link' },
{ name: 'label' },
{ name: 'target' },
];

setBlockItemOptions(block, blockItemMap, blockItemsOptions);
const config = blockItemsOptions[0] || {};

// Wrapper element
const wrapper = document.createElement('div');
wrapper.className = 'content-image-teaser';

// Alignment
const alignment = (config.align || 'left').toLowerCase();
wrapper.classList.add(`align-${alignment}`);

// Coral accent line
if (['true', 'yes', 'on'].includes((config.coralLine || '').toLowerCase())) {
wrapper.classList.add('coral-line');
}

/* ---------------- Image Column ---------------- */
const imageDiv = document.createElement('div');
imageDiv.className = 'teaser-image';

if (config.image) {
// Attempt to preserve Universal Editor instrumentation attributes
let originalImgEl;
const imageCell = block.children[4];
if (imageCell) {
// Look for an existing <picture> or <img> element provided by the author
originalImgEl = imageCell.querySelector('picture, img');
}

const optimizedPic = createOptimizedPicture(
config.image,
config.imageAlt || '',
false,
[{ media: '(min-width: 900px)' }],
);

if (originalImgEl) {
moveInstrumentation(originalImgEl, optimizedPic);
}

imageDiv.appendChild(optimizedPic);
} else {
const img = document.createElement('img');
img.src = 'https://placehold.co/600x400';
img.alt = config.imageAlt || 'Placeholder Image';
imageDiv.appendChild(img);
}

/* ---------------- Text Column ---------------- */
const textDiv = document.createElement('div');
textDiv.className = 'teaser-text';

// Pretitle
if (config.pretitle) {
const preEl = document.createElement('p');
preEl.className = 'teaser-pretitle';
preEl.textContent = config.pretitle;
textDiv.appendChild(preEl);
}

// Title with dynamic heading level
const headingLevel = ['h2', 'h3', 'h4', 'h5', 'h6'].includes((config.titleType || '').toLowerCase())
? config.titleType.toLowerCase()
: 'h2';

const titleEl = document.createElement(headingLevel);
titleEl.className = 'teaser-title';
titleEl.textContent = config.title || '';
textDiv.appendChild(titleEl);

// Description / rich text – basic handling (as plain text)
if (config.description) {
const descEl = document.createElement('p');
descEl.className = 'teaser-description';
descEl.textContent = config.description;
textDiv.appendChild(descEl);
}

// CTA button
if (config.label) {
const btnWrapper = document.createElement('div');
btnWrapper.className = 'teaser-button';

const button = renderButton({
link: config.link,
label: config.label,
target: config.target,
block,
});

btnWrapper.appendChild(button);
moveClassToTargetedChild(block, button);
textDiv.appendChild(btnWrapper);
}

/* ---------------- Assemble ---------------- */
// Order depends on alignment – for visual order we rely on flex-direction,
// but on mobile they stack.
wrapper.appendChild(imageDiv);
wrapper.appendChild(textDiv);

// Clean original block and inject new DOM
block.textContent = '';
block.appendChild(wrapper);
}
16 changes: 16 additions & 0 deletions component-definition.json
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,22 @@
}
}
},
{
"title": "Content/Image Teaser",
"id": "content-image-teaser",
"plugins": {
"xwalk": {
"page": {
"resourceType": "core/franklin/components/block/v1/block",
"template": {
"name": "Content/Image Teaser",
"model": "content-image-teaser",
"filter": "content-image-teaser"
}
}
}
}
},
{
"title": "Custom Button",
"id": "custom-button",
Expand Down
3 changes: 2 additions & 1 deletion component-filters.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@
"video",
"gallery",
"event-teaser",
"framed-grid"
"framed-grid",
"content-image-teaser"
]
},
{
Expand Down
Loading
Loading