feat: parameterless photo_archive for recent photos
This commit is contained in:
@@ -279,7 +279,8 @@ const hydratePhotoArchive = async (
|
|||||||
postId: string,
|
postId: string,
|
||||||
onImageClick: (index: number, images: { src: string; alt: string }[]) => void
|
onImageClick: (index: number, images: { src: string; alt: string }[]) => void
|
||||||
) => {
|
) => {
|
||||||
const archives = container.querySelectorAll('.macro-photo-archive[data-year]');
|
// Match both year-based and recent-based archives
|
||||||
|
const archives = container.querySelectorAll('.macro-photo-archive[data-year], .macro-photo-archive[data-recent]');
|
||||||
|
|
||||||
if (archives.length === 0) {
|
if (archives.length === 0) {
|
||||||
// No photo_archive macros - unlink any previously linked and clear state
|
// No photo_archive macros - unlink any previously linked and clear state
|
||||||
@@ -323,19 +324,75 @@ const doHydratePhotoArchive = async (
|
|||||||
|
|
||||||
// Phase 1: Collect all media IDs that should be linked based on current macros
|
// Phase 1: Collect all media IDs that should be linked based on current macros
|
||||||
const shouldBeLinkedIds = new Set<string>();
|
const shouldBeLinkedIds = new Set<string>();
|
||||||
|
type ImageData = { id: string; originalName: string; alt?: string; mimeType: string; createdAt?: Date };
|
||||||
const archiveData: Array<{
|
const archiveData: Array<{
|
||||||
element: Element;
|
element: Element;
|
||||||
year: number;
|
mode: 'single-month' | 'full-year' | 'recent';
|
||||||
|
year?: number;
|
||||||
month?: number;
|
month?: number;
|
||||||
images?: Array<{ id: string; originalName: string; alt?: string; mimeType: string }>;
|
images?: ImageData[];
|
||||||
monthlyImages?: Map<number, Array<{ id: string; originalName: string; alt?: string; mimeType: string }>>;
|
// Map key is "YYYY-MM" for recent mode, or month number (1-12) for year mode
|
||||||
|
monthlyImages?: Map<string | number, ImageData[]>;
|
||||||
|
showYearInLabel?: boolean;
|
||||||
}> = [];
|
}> = [];
|
||||||
|
|
||||||
console.log(`[photo_archive] Processing ${archives.length} archive macro(s), previously linked: ${previouslyLinkedIds.size} IDs`);
|
console.log(`[photo_archive] Processing ${archives.length} archive macro(s), previously linked: ${previouslyLinkedIds.size} IDs`);
|
||||||
|
|
||||||
for (const archive of archives) {
|
for (const archive of archives) {
|
||||||
const year = parseInt(archive.getAttribute('data-year') || '0', 10);
|
const recentStr = archive.getAttribute('data-recent');
|
||||||
|
const yearStr = archive.getAttribute('data-year');
|
||||||
const monthStr = archive.getAttribute('data-month');
|
const monthStr = archive.getAttribute('data-month');
|
||||||
|
|
||||||
|
if (recentStr) {
|
||||||
|
// Recent mode: get last N months with images
|
||||||
|
const recentCount = parseInt(recentStr, 10) || 10;
|
||||||
|
console.log(`[photo_archive] Recent mode: fetching last ${recentCount} months with images`);
|
||||||
|
|
||||||
|
// Fetch all images (no filter)
|
||||||
|
const allMedia = await window.electronAPI?.media.filter({});
|
||||||
|
const allImages = (allMedia || []).filter(m => m.mimeType?.startsWith('image/'));
|
||||||
|
|
||||||
|
// Group by year-month and sort by most recent
|
||||||
|
const monthlyMap = new Map<string, ImageData[]>();
|
||||||
|
for (const img of allImages) {
|
||||||
|
if (!img.createdAt) continue;
|
||||||
|
const date = new Date(img.createdAt);
|
||||||
|
const year = date.getFullYear();
|
||||||
|
const month = date.getMonth() + 1; // 1-based
|
||||||
|
const key = `${year}-${String(month).padStart(2, '0')}`; // e.g. "2024-06"
|
||||||
|
|
||||||
|
if (!monthlyMap.has(key)) {
|
||||||
|
monthlyMap.set(key, []);
|
||||||
|
}
|
||||||
|
monthlyMap.get(key)!.push(img);
|
||||||
|
shouldBeLinkedIds.add(img.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort by key descending (newest first) and take top N
|
||||||
|
const sortedKeys = Array.from(monthlyMap.keys()).sort().reverse().slice(0, recentCount);
|
||||||
|
const recentMonthlyImages = new Map<string, ImageData[]>();
|
||||||
|
|
||||||
|
// Clear shouldBeLinkedIds and only add ones that are in top N months
|
||||||
|
shouldBeLinkedIds.clear();
|
||||||
|
for (const key of sortedKeys) {
|
||||||
|
const images = monthlyMap.get(key)!;
|
||||||
|
recentMonthlyImages.set(key, images);
|
||||||
|
for (const img of images) {
|
||||||
|
shouldBeLinkedIds.add(img.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const totalImages = Array.from(recentMonthlyImages.values()).reduce((sum, imgs) => sum + imgs.length, 0);
|
||||||
|
archiveData.push({
|
||||||
|
element: archive,
|
||||||
|
mode: 'recent',
|
||||||
|
monthlyImages: recentMonthlyImages,
|
||||||
|
showYearInLabel: true
|
||||||
|
});
|
||||||
|
console.log(`[photo_archive] Recent: ${totalImages} images across ${recentMonthlyImages.size} months`);
|
||||||
|
|
||||||
|
} else if (yearStr) {
|
||||||
|
const year = parseInt(yearStr, 10);
|
||||||
const month = monthStr ? parseInt(monthStr, 10) : undefined;
|
const month = monthStr ? parseInt(monthStr, 10) : undefined;
|
||||||
|
|
||||||
if (!year) continue;
|
if (!year) continue;
|
||||||
@@ -353,11 +410,11 @@ const doHydratePhotoArchive = async (
|
|||||||
shouldBeLinkedIds.add(img.id);
|
shouldBeLinkedIds.add(img.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
archiveData.push({ element: archive, year, month, images });
|
archiveData.push({ element: archive, mode: 'single-month', year, month, images });
|
||||||
console.log(`[photo_archive] Year ${year} month ${month}: ${images.length} images`);
|
console.log(`[photo_archive] Year ${year} month ${month}: ${images.length} images`);
|
||||||
} else {
|
} else {
|
||||||
// Full year view - collect all months, tracking which month each image belongs to
|
// Full year view - collect all months, tracking which month each image belongs to
|
||||||
const monthlyImages = new Map<number, Array<{ id: string; originalName: string; alt?: string; mimeType: string }>>();
|
const monthlyImages = new Map<number, ImageData[]>();
|
||||||
|
|
||||||
for (let m = 0; m < 12; m++) {
|
for (let m = 0; m < 12; m++) {
|
||||||
const mediaItems = await window.electronAPI?.media.filter({
|
const mediaItems = await window.electronAPI?.media.filter({
|
||||||
@@ -377,10 +434,11 @@ const doHydratePhotoArchive = async (
|
|||||||
}
|
}
|
||||||
|
|
||||||
const totalImages = Array.from(monthlyImages.values()).reduce((sum, imgs) => sum + imgs.length, 0);
|
const totalImages = Array.from(monthlyImages.values()).reduce((sum, imgs) => sum + imgs.length, 0);
|
||||||
archiveData.push({ element: archive, year, month: undefined, monthlyImages });
|
archiveData.push({ element: archive, mode: 'full-year', year, month: undefined, monthlyImages });
|
||||||
console.log(`[photo_archive] Year ${year}: ${totalImages} images across ${monthlyImages.size} months`);
|
console.log(`[photo_archive] Year ${year}: ${totalImages} images across ${monthlyImages.size} months`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
console.log(`[photo_archive] Should link ${shouldBeLinkedIds.size} media IDs`);
|
console.log(`[photo_archive] Should link ${shouldBeLinkedIds.size} media IDs`);
|
||||||
|
|
||||||
@@ -397,7 +455,7 @@ const doHydratePhotoArchive = async (
|
|||||||
saveLinkedIds(postId, shouldBeLinkedIds);
|
saveLinkedIds(postId, shouldBeLinkedIds);
|
||||||
|
|
||||||
// Phase 3: Link new media and render
|
// Phase 3: Link new media and render
|
||||||
for (const { element, year, month, images, monthlyImages } of archiveData) {
|
for (const { element, mode, year, month, images, monthlyImages, showYearInLabel } of archiveData) {
|
||||||
const archiveContainer = element.querySelector('.photo-archive-container');
|
const archiveContainer = element.querySelector('.photo-archive-container');
|
||||||
if (!archiveContainer) continue;
|
if (!archiveContainer) continue;
|
||||||
|
|
||||||
@@ -405,7 +463,7 @@ const doHydratePhotoArchive = async (
|
|||||||
// Render the gallery
|
// Render the gallery
|
||||||
let html = '';
|
let html = '';
|
||||||
|
|
||||||
if (month !== undefined && images) {
|
if (mode === 'single-month' && month !== undefined && images && year) {
|
||||||
// Single month view
|
// Single month view
|
||||||
// Link images to the post
|
// Link images to the post
|
||||||
for (const img of images) {
|
for (const img of images) {
|
||||||
@@ -419,9 +477,37 @@ const doHydratePhotoArchive = async (
|
|||||||
archiveContainer.innerHTML = `<div class="photo-archive-empty">No photos found for ${FULL_MONTH_NAMES[month - 1]} ${year}</div>`;
|
archiveContainer.innerHTML = `<div class="photo-archive-empty">No photos found for ${FULL_MONTH_NAMES[month - 1]} ${year}</div>`;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
html = buildMonthGallery(month, year, images, onImageClick);
|
html = buildMonthGallery(month, year, images, onImageClick, false);
|
||||||
} else if (monthlyImages) {
|
} else if (mode === 'recent' && monthlyImages) {
|
||||||
// Full year view - already grouped by month
|
// Recent mode - keys are "YYYY-MM" strings
|
||||||
|
// Link all images to the post
|
||||||
|
for (const imgs of monthlyImages.values()) {
|
||||||
|
for (const img of imgs) {
|
||||||
|
const isLinked = await window.electronAPI?.postMedia.isLinked(postId, img.id);
|
||||||
|
if (!isLinked) {
|
||||||
|
await window.electronAPI?.postMedia.link(postId, img.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (monthlyImages.size === 0) {
|
||||||
|
archiveContainer.innerHTML = `<div class="photo-archive-empty">No recent photos found</div>`;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort by key descending (newest first) - keys are "YYYY-MM" strings
|
||||||
|
const sortedEntries = Array.from(monthlyImages.entries())
|
||||||
|
.sort((a, b) => (b[0] as string).localeCompare(a[0] as string));
|
||||||
|
|
||||||
|
html = sortedEntries.map(([key, imgs]) => {
|
||||||
|
// Parse "YYYY-MM" to get year and month
|
||||||
|
const [yearStr, monthStr] = (key as string).split('-');
|
||||||
|
const entryYear = parseInt(yearStr, 10);
|
||||||
|
const entryMonth = parseInt(monthStr, 10);
|
||||||
|
return `<div class="photo-archive-month-wrapper">${buildMonthGallery(entryMonth, entryYear, imgs, onImageClick, true)}</div>`;
|
||||||
|
}).join('');
|
||||||
|
} else if (mode === 'full-year' && monthlyImages && year) {
|
||||||
|
// Full year view - keys are month numbers
|
||||||
// Link all images to the post
|
// Link all images to the post
|
||||||
for (const imgs of monthlyImages.values()) {
|
for (const imgs of monthlyImages.values()) {
|
||||||
for (const img of imgs) {
|
for (const img of imgs) {
|
||||||
@@ -437,10 +523,11 @@ const doHydratePhotoArchive = async (
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort months and build gallery
|
// Sort months ascending (January first)
|
||||||
const sortedMonths = Array.from(monthlyImages.entries()).sort((a, b) => a[0] - b[0]);
|
const sortedMonths = Array.from(monthlyImages.entries())
|
||||||
|
.sort((a, b) => (a[0] as number) - (b[0] as number));
|
||||||
html = sortedMonths.map(([m, imgs]) =>
|
html = sortedMonths.map(([m, imgs]) =>
|
||||||
`<div class="photo-archive-month-wrapper">${buildMonthGallery(m, year, imgs, onImageClick)}</div>`
|
`<div class="photo-archive-month-wrapper">${buildMonthGallery(m as number, year, imgs, onImageClick, showYearInLabel || false)}</div>`
|
||||||
).join('');
|
).join('');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -460,19 +547,26 @@ const doHydratePhotoArchive = async (
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Build HTML for a single month's gallery with rotated month label
|
* Build HTML for a single month's gallery with rotated month label
|
||||||
|
* @param month - 1-based month number (1 = January)
|
||||||
|
* @param year - The year
|
||||||
|
* @param images - Array of image data
|
||||||
|
* @param _onImageClick - Click handler (unused in template, set up separately)
|
||||||
|
* @param showYear - Whether to include the year in the label (e.g., "January 2024")
|
||||||
*/
|
*/
|
||||||
function buildMonthGallery(
|
function buildMonthGallery(
|
||||||
month: number,
|
month: number,
|
||||||
year: number,
|
year: number,
|
||||||
images: { id: string; originalName: string; alt?: string }[],
|
images: { id: string; originalName: string; alt?: string }[],
|
||||||
_onImageClick: (index: number, images: { src: string; alt: string }[]) => void
|
_onImageClick: (index: number, images: { src: string; alt: string }[]) => void,
|
||||||
|
showYear: boolean = false
|
||||||
): string {
|
): string {
|
||||||
const monthName = FULL_MONTH_NAMES[month - 1];
|
const monthName = FULL_MONTH_NAMES[month - 1];
|
||||||
|
const labelText = showYear ? `${monthName} ${year}` : monthName;
|
||||||
|
|
||||||
return `
|
return `
|
||||||
<div class="photo-archive-month" data-month="${month}" data-year="${year}">
|
<div class="photo-archive-month" data-month="${month}" data-year="${year}">
|
||||||
<div class="photo-archive-month-label">
|
<div class="photo-archive-month-label">
|
||||||
<span>${monthName}</span>
|
<span>${labelText}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="photo-archive-gallery gallery-lightbox">
|
<div class="photo-archive-gallery gallery-lightbox">
|
||||||
${images.map((img, index) => `
|
${images.map((img, index) => `
|
||||||
|
|||||||
@@ -6,15 +6,17 @@
|
|||||||
* When rendered, images are automatically linked to the post.
|
* When rendered, images are automatically linked to the post.
|
||||||
*
|
*
|
||||||
* Usage:
|
* Usage:
|
||||||
|
* [[photo_archive]] - Newest 10 months with images (month + year label)
|
||||||
* [[photo_archive year="2024"]] - All months of 2024, each in its own lightbox
|
* [[photo_archive year="2024"]] - All months of 2024, each in its own lightbox
|
||||||
* [[photo_archive year="2024" month="6"]] - Only June 2024 photos
|
* [[photo_archive year="2024" month="6"]] - Only June 2024 photos
|
||||||
*
|
*
|
||||||
* Parameters:
|
* Parameters:
|
||||||
* - year (required): The year to display photos from (e.g., 2024)
|
* - year (optional): The year to display photos from (e.g., 2024)
|
||||||
* - month (optional): Specific month (1-12). If omitted, shows all months with photos.
|
* - month (optional): Specific month (1-12). Requires year. If omitted with year, shows all months.
|
||||||
*
|
*
|
||||||
* Gallery Layout:
|
* Gallery Layout:
|
||||||
* - Each month is in its own lightbox with the month name rotated 90° on the side
|
* - Each month is in its own lightbox with the month name rotated 90° on the side
|
||||||
|
* - When no year specified, shows "Month Year" label (e.g., "January 2024")
|
||||||
* - Images are displayed in a grid and are clickable for lightbox viewing
|
* - Images are displayed in a grid and are clickable for lightbox viewing
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -38,18 +40,19 @@ const photoArchiveMacro: MacroDefinition = {
|
|||||||
description: 'Creates a photo archive gallery organized by year and month, automatically linking discovered images to the post',
|
description: 'Creates a photo archive gallery organized by year and month, automatically linking discovered images to the post',
|
||||||
|
|
||||||
validate(params: MacroParams): string | undefined {
|
validate(params: MacroParams): string | undefined {
|
||||||
// Year is required
|
// Year is optional - if not provided, shows recent 10 months
|
||||||
if (!params.year) {
|
if (params.year) {
|
||||||
return 'photo_archive macro requires a "year" parameter';
|
|
||||||
}
|
|
||||||
|
|
||||||
const year = parseInt(params.year, 10);
|
const year = parseInt(params.year, 10);
|
||||||
if (isNaN(year) || year < 1000 || year > 9999) {
|
if (isNaN(year) || year < 1000 || year > 9999) {
|
||||||
return 'Year must be a valid 4-digit year (e.g., 2024)';
|
return 'Year must be a valid 4-digit year (e.g., 2024)';
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Month is optional but must be valid if provided
|
// Month is optional but must be valid if provided, and requires year
|
||||||
if (params.month) {
|
if (params.month) {
|
||||||
|
if (!params.year) {
|
||||||
|
return 'Month parameter requires a year parameter';
|
||||||
|
}
|
||||||
const month = parseInt(params.month, 10);
|
const month = parseInt(params.month, 10);
|
||||||
if (isNaN(month) || month < 1 || month > 12) {
|
if (isNaN(month) || month < 1 || month > 12) {
|
||||||
return 'Month must be a number between 1 and 12';
|
return 'Month must be a number between 1 and 12';
|
||||||
@@ -60,7 +63,11 @@ const photoArchiveMacro: MacroDefinition = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
editorPreview(params: MacroParams): string {
|
editorPreview(params: MacroParams): string {
|
||||||
const year = params.year || '????';
|
// No year = recent mode
|
||||||
|
if (!params.year) {
|
||||||
|
return '📅 Photo Archive: Recent';
|
||||||
|
}
|
||||||
|
const year = params.year;
|
||||||
if (params.month) {
|
if (params.month) {
|
||||||
const monthNum = parseInt(params.month, 10);
|
const monthNum = parseInt(params.month, 10);
|
||||||
const monthName = getMonthName(monthNum);
|
const monthName = getMonthName(monthNum);
|
||||||
@@ -73,13 +80,17 @@ const photoArchiveMacro: MacroDefinition = {
|
|||||||
const { year, month } = params;
|
const { year, month } = params;
|
||||||
|
|
||||||
// Build data attributes for hydration
|
// Build data attributes for hydration
|
||||||
const dataAttrs = [
|
const dataAttrs: string[] = [];
|
||||||
`data-year="${year}"`,
|
|
||||||
];
|
|
||||||
|
|
||||||
|
// If no year, use recent mode (newest 10 months with images)
|
||||||
|
if (!year) {
|
||||||
|
dataAttrs.push('data-recent="10"');
|
||||||
|
} else {
|
||||||
|
dataAttrs.push(`data-year="${year}"`);
|
||||||
if (month) {
|
if (month) {
|
||||||
dataAttrs.push(`data-month="${month}"`);
|
dataAttrs.push(`data-month="${month}"`);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (context.postId) {
|
if (context.postId) {
|
||||||
dataAttrs.push(`data-post-id="${context.postId}"`);
|
dataAttrs.push(`data-post-id="${context.postId}"`);
|
||||||
@@ -87,7 +98,9 @@ const photoArchiveMacro: MacroDefinition = {
|
|||||||
|
|
||||||
// CSS classes
|
// CSS classes
|
||||||
const classes = ['macro-photo-archive'];
|
const classes = ['macro-photo-archive'];
|
||||||
if (month) {
|
if (!year) {
|
||||||
|
classes.push('photo-archive-recent-months');
|
||||||
|
} else if (month) {
|
||||||
classes.push('photo-archive-single-month');
|
classes.push('photo-archive-single-month');
|
||||||
} else {
|
} else {
|
||||||
classes.push('photo-archive-full-year');
|
classes.push('photo-archive-full-year');
|
||||||
@@ -96,7 +109,17 @@ const photoArchiveMacro: MacroDefinition = {
|
|||||||
// Generate placeholder HTML - actual content is hydrated by Editor.tsx
|
// Generate placeholder HTML - actual content is hydrated by Editor.tsx
|
||||||
let html = `<div class="${classes.join(' ')}" ${dataAttrs.join(' ')}>`;
|
let html = `<div class="${classes.join(' ')}" ${dataAttrs.join(' ')}>`;
|
||||||
html += `<div class="photo-archive-container">`;
|
html += `<div class="photo-archive-container">`;
|
||||||
html += `<div class="photo-archive-loading">Loading photo archive for ${year}${month ? ` / ${getMonthName(parseInt(month, 10))}` : ''}...</div>`;
|
|
||||||
|
// Loading message based on mode
|
||||||
|
let loadingMsg: string;
|
||||||
|
if (!year) {
|
||||||
|
loadingMsg = 'Loading recent photos...';
|
||||||
|
} else if (month) {
|
||||||
|
loadingMsg = `Loading photo archive for ${year} / ${getMonthName(parseInt(month, 10))}...`;
|
||||||
|
} else {
|
||||||
|
loadingMsg = `Loading photo archive for ${year}...`;
|
||||||
|
}
|
||||||
|
html += `<div class="photo-archive-loading">${loadingMsg}</div>`;
|
||||||
html += `</div>`;
|
html += `</div>`;
|
||||||
html += `</div>`;
|
html += `</div>`;
|
||||||
|
|
||||||
|
|||||||
@@ -18,12 +18,12 @@ describe('photo_archive macro', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('validation', () => {
|
describe('validation', () => {
|
||||||
it('should require year parameter', () => {
|
it('should accept no parameters (recent mode)', () => {
|
||||||
const macro = getMacro('photo_archive');
|
const macro = getMacro('photo_archive');
|
||||||
|
|
||||||
expect(macro).toBeDefined();
|
expect(macro).toBeDefined();
|
||||||
const error = macro!.validate?.({});
|
const error = macro!.validate?.({});
|
||||||
expect(error).toBe('photo_archive macro requires a "year" parameter');
|
expect(error).toBeUndefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should reject non-numeric year', () => {
|
it('should reject non-numeric year', () => {
|
||||||
@@ -170,6 +170,59 @@ describe('photo_archive macro', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('render - no parameters (recent mode)', () => {
|
||||||
|
it('should accept no parameters', () => {
|
||||||
|
const macro = getMacro('photo_archive');
|
||||||
|
|
||||||
|
const error = macro!.validate?.({});
|
||||||
|
expect(error).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render with data-recent attribute when no params', () => {
|
||||||
|
const macro = getMacro('photo_archive');
|
||||||
|
const context: MacroRenderContext = { isPreview: true, postId: 'test-post-id' };
|
||||||
|
|
||||||
|
const html = macro!.render({}, context);
|
||||||
|
|
||||||
|
expect(html).toContain('data-recent="10"');
|
||||||
|
expect(html).not.toContain('data-year=');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should have recent-months class when no params', () => {
|
||||||
|
const macro = getMacro('photo_archive');
|
||||||
|
const context: MacroRenderContext = { isPreview: true, postId: 'test-post-id' };
|
||||||
|
|
||||||
|
const html = macro!.render({}, context);
|
||||||
|
|
||||||
|
expect(html).toContain('photo-archive-recent-months');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should show appropriate loading message for recent mode', () => {
|
||||||
|
const macro = getMacro('photo_archive');
|
||||||
|
const context: MacroRenderContext = { isPreview: true, postId: 'test-post-id' };
|
||||||
|
|
||||||
|
const html = macro!.render({}, context);
|
||||||
|
|
||||||
|
expect(html).toContain('Loading recent photos');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should show recent photos preview', () => {
|
||||||
|
const macro = getMacro('photo_archive');
|
||||||
|
|
||||||
|
const preview = macro!.editorPreview?.({});
|
||||||
|
expect(preview).toBe('📅 Photo Archive: Recent');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should include post-id data attribute in recent mode', () => {
|
||||||
|
const macro = getMacro('photo_archive');
|
||||||
|
const context: MacroRenderContext = { isPreview: true, postId: 'post-123' };
|
||||||
|
|
||||||
|
const html = macro!.render({}, context);
|
||||||
|
|
||||||
|
expect(html).toContain('data-post-id="post-123"');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('self-registration', () => {
|
describe('self-registration', () => {
|
||||||
it('should self-register on import', () => {
|
it('should self-register on import', () => {
|
||||||
const macro = getMacro('photo_archive');
|
const macro = getMacro('photo_archive');
|
||||||
|
|||||||
Reference in New Issue
Block a user