Skip to content

Commit b5a7de0

Browse files
fix: slugFormatter populates date template variables using date from entry if it exists (decaporg#7633)
* fix(6801): slugFormatter uses date from entry if it exists * test: date synonym handling --------- Co-authored-by: Martin Jagodic <jagodicmartin1@gmail.com>
1 parent 5d0e417 commit b5a7de0

File tree

4 files changed

+87
-4
lines changed

4 files changed

+87
-4
lines changed

packages/decap-cms-core/src/lib/__tests__/formatters.spec.js

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -274,8 +274,8 @@ describe('formatters', () => {
274274
};
275275

276276
describe('slugFormatter', () => {
277-
const date = new Date('2020-01-01');
278-
jest.spyOn(global, 'Date').mockImplementation(() => date);
277+
const date = new Date('2020-01-01').valueOf();
278+
Date.now = jest.spyOn(Date, 'now').mockImplementation(() => date);
279279

280280
const { selectIdentifier } = require('../../reducers/collections');
281281

@@ -312,6 +312,34 @@ describe('formatters', () => {
312312
).toBe('entry-slug');
313313
});
314314

315+
it('should see date filters applied to date from entry if it exists', () => {
316+
const { selectInferredField } = require('../../reducers/collections');
317+
selectInferredField.mockReturnValue('date');
318+
const entryDate = new Date('2026-10-20');
319+
320+
expect(
321+
slugFormatter(
322+
Map({ slug: '{{year}}-{{month}}-{{day}}-{{title}}' }),
323+
Map({ date: entryDate, title: 'post title' }),
324+
slugConfig,
325+
),
326+
).toBe('2026-10-20-post-title');
327+
});
328+
329+
it('should see date filters applied to publishDate from entry if it exists', () => {
330+
const { selectInferredField } = require('../../reducers/collections');
331+
selectInferredField.mockReturnValue('publishDate');
332+
const entryDate = new Date('2026-10-20');
333+
334+
expect(
335+
slugFormatter(
336+
Map({ slug: '{{year}}-{{month}}-{{day}}-{{title}}' }),
337+
Map({ publishDate: entryDate, title: 'post title' }),
338+
slugConfig,
339+
),
340+
).toBe('2026-10-20-post-title');
341+
});
342+
315343
it('should return slug', () => {
316344
selectIdentifier.mockReturnValueOnce('title');
317345

packages/decap-cms-core/src/lib/formatters.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import type { Map } from 'immutable';
2121
const {
2222
compileStringTemplate,
2323
parseDateFromEntry,
24+
parseDateFromEntryData,
2425
SLUG_MISSING_REQUIRED_DATE,
2526
keyToPathArray,
2627
addFileTemplateFields,
@@ -129,7 +130,11 @@ export function slugFormatter(
129130
}
130131

131132
const processSegment = getProcessSegment(slugConfig);
132-
const date = new Date();
133+
const date =
134+
parseDateFromEntryData(
135+
entryData as unknown as Map<string, unknown>,
136+
selectInferredField(collection, 'date'),
137+
) || new Date(Date.now());
133138
const slug = compileStringTemplate(slugTemplate, date, identifier, entryData, processSegment);
134139

135140
if (!collection.has('path')) {

packages/decap-cms-core/src/reducers/__tests__/collections.spec.js

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import collections, {
1111
getFieldsNames,
1212
selectField,
1313
updateFieldByKey,
14+
selectInferredField,
1415
} from '../collections';
1516
import { FILES, FOLDER } from '../../constants/collectionTypes';
1617

@@ -568,4 +569,42 @@ describe('collections', () => {
568569
);
569570
});
570571
});
572+
573+
describe("selectInferredField(collection, 'date')", () => {
574+
it('should return publishDate if set', () => {
575+
const collection = fromJS({
576+
fields: [{ name: 'title' }, { name: 'publishDate', widget: 'datetime' }],
577+
});
578+
579+
expect(selectInferredField(collection, 'date')).toEqual('publishDate');
580+
});
581+
582+
it('should return publish_date if set', () => {
583+
const collection = fromJS({
584+
fields: [{ name: 'title' }, { name: 'publish_date', widget: 'datetime' }],
585+
});
586+
587+
expect(selectInferredField(collection, 'date')).toEqual('publish_date');
588+
});
589+
590+
it('should return date if set', () => {
591+
const collection = fromJS({
592+
fields: [{ name: 'title' }, { name: 'date', widget: 'datetime' }],
593+
});
594+
595+
expect(selectInferredField(collection, 'date')).toEqual('date');
596+
});
597+
598+
it('should return first date field if multiple synonyms are present', () => {
599+
const collection = fromJS({
600+
fields: [
601+
{ name: 'title' },
602+
{ name: 'publishDate', widget: 'datetime' },
603+
{ name: 'date', widget: 'datetime' },
604+
],
605+
});
606+
607+
expect(selectInferredField(collection, 'date')).toEqual('publishDate');
608+
});
609+
});
571610
});

packages/decap-cms-lib-widgets/src/stringTemplate.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,18 @@ export function parseDateFromEntry(entry: Map<string, unknown>, dateFieldName?:
6161
return;
6262
}
6363

64-
const dateValue = entry.getIn(['data', dateFieldName]);
64+
const entryData = entry.getIn(['data']);
65+
return parseDateFromEntryData(entryData, dateFieldName);
66+
}
67+
68+
export function parseDateFromEntryData(
69+
entryData: Map<string, unknown>,
70+
dateFieldName?: string | null,
71+
) {
72+
if (!dateFieldName) {
73+
return;
74+
}
75+
const dateValue = entryData.getIn([dateFieldName]);
6576
const dateDayjs = dateValue && dayjs(dateValue);
6677
if (dateDayjs && dateDayjs.isValid()) {
6778
return dateDayjs.toDate();

0 commit comments

Comments
 (0)