Skip to content

Commit f566216

Browse files
authored
Merge pull request #1272 from paypal/feature/DTCRCMERC-4152
feat: improve CLS with single-line text placeholder (v5)
2 parents f06e4f4 + e391353 commit f566216

File tree

2 files changed

+122
-0
lines changed

2 files changed

+122
-0
lines changed

src/library/zoid/message/containerTemplate.jsx

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,49 @@ import { EVENT } from '@krakenjs/zoid/src';
55
import { getOverflowObserver, createTitleGenerator } from '../../../utils';
66

77
const getTitle = createTitleGenerator();
8+
const DEFAULT_TEXT_SIZE_PX = 14;
9+
const MIN_TEXT_SIZE_PX = 10;
10+
const MAX_TEXT_SIZE_PX = 16;
11+
const TEXT_LINE_HEIGHT = 1.3;
12+
const TEXT_LINE_COUNT = 1;
13+
14+
const getPlaceholderHeightPx = style => {
15+
if (style?.layout !== 'text') {
16+
return null;
17+
}
18+
19+
const rawSize = style?.text?.size;
20+
const parsedSize = typeof rawSize === 'number' ? rawSize : Number(rawSize);
21+
const size =
22+
Number.isFinite(parsedSize) && parsedSize >= MIN_TEXT_SIZE_PX && parsedSize <= MAX_TEXT_SIZE_PX
23+
? parsedSize
24+
: DEFAULT_TEXT_SIZE_PX;
25+
26+
return Math.round(size * TEXT_LINE_HEIGHT * TEXT_LINE_COUNT * 10) / 10;
27+
};
828

929
export default ({ uid, frame, prerenderFrame, doc, event, props, container }) => {
30+
const host = container;
31+
const placeholderHeightPx = getPlaceholderHeightPx(props.style);
32+
33+
if (placeholderHeightPx && host?.style && !host.dataset?.ppPlaceholderApplied) {
34+
host.dataset.ppPlaceholderMinHeight = host.style.minHeight || '';
35+
host.dataset.ppPlaceholderApplied = 'true';
36+
host.style.minHeight = `${placeholderHeightPx}px`;
37+
}
38+
1039
event.on(EVENT.RENDERED, () => {
1140
prerenderFrame.parentNode.removeChild(prerenderFrame);
1241
});
1342

1443
const setupAutoResize = el => {
1544
event.on(EVENT.RESIZE, ({ width, height }) => {
45+
if (host?.dataset?.ppPlaceholderApplied) {
46+
host.style.minHeight = host.dataset.ppPlaceholderMinHeight || '';
47+
delete host.dataset.ppPlaceholderMinHeight;
48+
delete host.dataset.ppPlaceholderApplied;
49+
}
50+
1651
if (width !== 0 || height !== 0) {
1752
if (props.style.layout === 'flex') {
1853
// Ensure height property does not exist for flex especially when swapping from text to flex
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import { EVENT } from '@krakenjs/zoid/src';
2+
3+
jest.mock('src/utils', () => ({
4+
getOverflowObserver: jest.fn(() => Promise.resolve({ observe: jest.fn() })),
5+
createTitleGenerator: jest.fn(() => title => title)
6+
}));
7+
8+
const containerTemplate = require('src/library/zoid/message/containerTemplate').default;
9+
10+
const createEventEmitter = () => {
11+
const listeners = {};
12+
const onceListeners = {};
13+
return {
14+
on: (name, cb) => {
15+
listeners[name] = listeners[name] || [];
16+
listeners[name].push(cb);
17+
},
18+
once: (name, cb) => {
19+
onceListeners[name] = onceListeners[name] || [];
20+
onceListeners[name].push(cb);
21+
},
22+
trigger: (name, payload) => {
23+
(listeners[name] || []).forEach(cb => cb(payload));
24+
(onceListeners[name] || []).forEach(cb => cb(payload));
25+
delete onceListeners[name];
26+
}
27+
};
28+
};
29+
30+
const renderTemplate = style => {
31+
const event = createEventEmitter();
32+
const container = document.createElement('div');
33+
const frame = document.createElement('iframe');
34+
const prerenderFrame = document.createElement('iframe');
35+
36+
const rendered = containerTemplate({
37+
uid: 'uid',
38+
frame,
39+
prerenderFrame,
40+
doc: document,
41+
event,
42+
props: { style },
43+
container
44+
});
45+
46+
container.appendChild(rendered);
47+
document.body.appendChild(container);
48+
49+
return { event, container };
50+
};
51+
52+
describe('zoid/message/containerTemplate', () => {
53+
afterEach(() => {
54+
document.body.innerHTML = '';
55+
});
56+
57+
test('Sets placeholder min-height for text layout', () => {
58+
const { container } = renderTemplate({ layout: 'text', text: { size: 12 } });
59+
expect(container.style.minHeight).toBe('15.6px');
60+
});
61+
62+
test('Uses default text size when style text size is invalid', () => {
63+
const { container } = renderTemplate({ layout: 'text', text: { size: 99 } });
64+
expect(container.style.minHeight).toBe('18.2px');
65+
});
66+
67+
test('Clears placeholder min-height on first resize with rendered content dimensions', () => {
68+
const { event, container } = renderTemplate({ layout: 'text', text: { size: 12 } });
69+
expect(container.style.minHeight).toBe('15.6px');
70+
71+
event.trigger(EVENT.RESIZE, { width: 100, height: 0 });
72+
expect(container.style.minHeight).toBe('');
73+
});
74+
75+
test('Clears placeholder min-height on first resize with no-message dimensions (0x0)', () => {
76+
const { event, container } = renderTemplate({ layout: 'text', text: { size: 12 } });
77+
expect(container.style.minHeight).toBe('15.6px');
78+
79+
event.trigger(EVENT.RESIZE, { width: 0, height: 0 });
80+
expect(container.style.minHeight).toBe('');
81+
});
82+
83+
test('Does not set placeholder min-height for flex layout', () => {
84+
const { container } = renderTemplate({ layout: 'flex' });
85+
expect(container.style.minHeight).toBe('');
86+
});
87+
});

0 commit comments

Comments
 (0)