Release v2.18.0: template default content
This commit is contained in:
1 parent
48e8f3eb4b
commit
24f1603b43
54 files changed
+1421
-151
No files matched your search
@@ -330,7 +330,7 @@ test('slide advanced popup applies changes, cancels drafts and preserves the par
|
||||
addEventListener(type, handler) { this.handlers[type] = handler; },
|
||||
querySelectorAll() { return []; }, replaceChildren() {}, appendChild() {}, focus() {},
|
||||
setAttribute() {}, removeAttribute() {},
|
||||
style: { setProperty() {} }, classList: { add() {}, remove() {} } };
|
||||
style: { setProperty() {}, removeProperty() {} }, classList: { add() {}, remove() {}, toggle() {} } };
|
||||
}
|
||||
const ids = Object.fromEntries([
|
||||
'slide-animation-modal', 'adjust-slide-animations', 'slide-animation-region',
|
||||
@@ -344,7 +344,17 @@ test('slide advanced popup applies changes, cancels drafts and preserves the par
|
||||
const advanced = ids['animation-advanced-modal'];
|
||||
const advancedApply = element();
|
||||
const advancedReset = element();
|
||||
const animationPreviewButtons = ['intro', 'outro', 'loop'].map(mode => Object.assign(element(), { dataset: { slideAnimationPreview: mode }, querySelector: () => element() }));
|
||||
const readonlyPreviewRegion = element();
|
||||
readonlyPreviewRegion.className = 'slide-preview-region';
|
||||
readonlyPreviewRegion.dataset.regionKey = 'fixed_brand';
|
||||
readonlyPreviewRegion.classList = {
|
||||
add(...classNames) { readonlyPreviewRegion.className += ' ' + classNames.join(' '); },
|
||||
remove(...classNames) { readonlyPreviewRegion.className = readonlyPreviewRegion.className.split(/\s+/).filter(name => !classNames.includes(name)).join(' '); }
|
||||
};
|
||||
advanced.querySelectorAll = () => fields;
|
||||
ids['slide-animation-modal'].querySelectorAll = selector => selector === '[data-slide-animation-preview]' ? animationPreviewButtons : [];
|
||||
ids['slide-animation-preview'].querySelectorAll = () => [readonlyPreviewRegion];
|
||||
advanced.querySelector = selector => selector === '[data-animation-modal-apply]' ? advancedApply
|
||||
: selector === '[data-animation-modal-reset]' ? advancedReset : element();
|
||||
const hidden = { value: '{}' };
|
||||
@@ -363,7 +373,7 @@ test('slide advanced popup applies changes, cancels drafts and preserves the par
|
||||
};
|
||||
const modalInstances = new Map();
|
||||
const scrollBarCalls = [];
|
||||
const context = { window: { innerWidth: 1200, pulseModal: {
|
||||
const context = { window: { innerWidth: 1200, setTimeout() { return 1; }, clearTimeout() {}, pulseModal: {
|
||||
show(modal) { this.getOrCreate(modal)._scrollBar.hide(); modal.shown = true; },
|
||||
hide(modal) { this.getOrCreate(modal)._scrollBar.reset(); modal.shown = false; modal.handlers['hidden.bs.modal'](); },
|
||||
getOrCreate(modal) {
|
||||
@@ -387,12 +397,19 @@ test('slide advanced popup applies changes, cancels drafts and preserves the par
|
||||
vm.runInNewContext(source.replace('export function', 'function'), context);
|
||||
let applied;
|
||||
context.createSlideAnimationEditor({ stage: { parentNode: { insertBefore() {} }, nextSibling: null },
|
||||
getTemplate() { return { regions: [{ region_key: 'hero', animation_json: { intro: { preset: 'bounceIn' }, outro: { preset: 'fadeOut' } } }] }; },
|
||||
getTemplate() { return { regions: [
|
||||
{ region_key: 'hero', animation_json: { intro: { preset: 'bounceIn' }, outro: { preset: 'fadeOut' } } },
|
||||
{ region_key: 'fixed_brand', read_only: true, animation_json: { loop: { preset: 'pulse', duration_ms: 200, iterations: 1 } } }
|
||||
] }; },
|
||||
getAnimationOverrides() { return {}; }, requestPreviewRender() {}, markEdited() {},
|
||||
setAnimationOverrides(value) { applied = value; }
|
||||
});
|
||||
ids['slide-animation-region'].value = 'hero';
|
||||
ids['adjust-slide-animations'].handlers.click();
|
||||
assert.equal(animationPreviewButtons[2].disabled, false);
|
||||
animationPreviewButtons[2].handlers.click();
|
||||
assert.match(readonlyPreviewRegion.className, /animate__pulse/);
|
||||
animationPreviewButtons[2].handlers.click();
|
||||
const resetRegion = ids['slide-animation-reset-region'];
|
||||
const resetAll = ids['slide-animation-reset-all'];
|
||||
assert.equal(resetRegion.disabled, true);
|
||||
@@ -438,6 +455,7 @@ test('slide advanced popup applies changes, cancels drafts and preserves the par
|
||||
assert.equal(JSON.parse(applied.hero).intro.preset, 'none');
|
||||
assert.equal(JSON.parse(applied.hero).outro.preset, 'fadeOut');
|
||||
assert.equal(JSON.parse(applied.hero).outro.duration_ms, 1500);
|
||||
assert.equal(applied.fixed_brand, undefined);
|
||||
assert.deepEqual(scrollBarCalls, [
|
||||
['hide', ids['slide-animation-modal']],
|
||||
['reset', ids['slide-animation-modal']]
|
||||
@@ -618,4 +636,55 @@ test('shared animation route is protected and updates template animations withou
|
||||
assert.ok(calls.some(call => call[0] === 'commit'));
|
||||
assert.ok(calls.some(call => call[0] === 'notify'));
|
||||
assert.ok(calls.some(call => call[0] === 'thumbnail'));
|
||||
});
|
||||
|
||||
test('editable regions inherit template defaults until a slide supplies an override', async function () {
|
||||
const pool = {
|
||||
async query(sql) {
|
||||
if (sql.includes('FROM c_templates')) return [[{ id: 7, canvas_size_width: 1920, canvas_size_height: 1080 }]];
|
||||
if (sql.includes('FROM c_template_regions')) return [[{
|
||||
id: 11, template_id: 7, region_key: 'hero', region_type: 'text', default_content: { type: 'text', value: 'Template default' }, read_only: 0,
|
||||
x: 10, y: 20, width: 300, height: 100
|
||||
}]];
|
||||
throw new Error('Unexpected query: ' + sql);
|
||||
}
|
||||
};
|
||||
const initial = await buildSlidePayload(pool, { body: { title: 'Example', template_id: '7' } }, null);
|
||||
assert.equal(JSON.parse(initial.contentJson).hero.value, 'Template default');
|
||||
const overridden = await buildSlidePayload(pool, { body: { title: 'Example', template_id: '7', region_text_11: 'Slide override' } }, null);
|
||||
assert.equal(JSON.parse(overridden.contentJson).hero.value, 'Slide override');
|
||||
});
|
||||
|
||||
test('read-only regions are excluded from slide content and layout overrides', async function () {
|
||||
const pool = {
|
||||
async query(sql) {
|
||||
if (sql.includes('FROM c_templates')) return [[{ id: 7, canvas_size_width: 1920, canvas_size_height: 1080 }]];
|
||||
if (sql.includes('FROM c_template_regions')) return [[{
|
||||
id: 11, template_id: 7, region_key: 'brand', region_type: 'text', default_content: { type: 'text', value: 'Fixed brand' }, read_only: 1,
|
||||
x: 10, y: 20, width: 300, height: 100
|
||||
}]];
|
||||
throw new Error('Unexpected query: ' + sql);
|
||||
}
|
||||
};
|
||||
const result = await buildSlidePayload(pool, { body: {
|
||||
title: 'Example', template_id: '7', region_text_11: 'Client override',
|
||||
region_layouts_json: JSON.stringify({ brand: {
|
||||
x: 100, y: 100, width: 200, height: 80,
|
||||
animation_json: { intro: { preset: 'bounceIn' } }
|
||||
} })
|
||||
} }, null);
|
||||
assert.deepEqual(JSON.parse(result.contentJson), {});
|
||||
});
|
||||
|
||||
test('thumbnail markup uses template defaults for read-only regions', function () {
|
||||
const html = buildThumbnailPreviewMarkup({
|
||||
template: {
|
||||
regions: [{ region_key: 'brand', region_type: 'image', read_only: 1, default_content: { value: '/media/brand.png' }, x: 0, y: 0, width: 100, height: 100 }],
|
||||
canvas_size_width: 100,
|
||||
canvas_size_height: 100
|
||||
},
|
||||
content: { brand: { value: '/media/old-brand.png' } }
|
||||
}, 'http://localhost');
|
||||
assert.match(html, /\/media\/brand\.png/);
|
||||
assert.doesNotMatch(html, /old-brand/);
|
||||
});
|
||||
Reference in new issue
Block a user