From 9e0b8ac4b88908d953a9714732b906c8c349fa7e Mon Sep 17 00:00:00 2001 From: stefanprobst Date: Thu, 15 Oct 2020 16:21:36 +0200 Subject: [PATCH] feat: allow per-file preview_path in file collection (#4413) --- .../src/constants/configSchema.js | 2 + .../src/lib/__tests__/formatters.spec.js | 64 ++++++++++++++++++- .../netlify-cms-core/src/lib/formatters.ts | 33 +++++++--- packages/netlify-cms-core/src/types/redux.ts | 2 + website/content/docs/deploy-preview-links.md | 18 +++++- 5 files changed, 105 insertions(+), 14 deletions(-) diff --git a/packages/netlify-cms-core/src/constants/configSchema.js b/packages/netlify-cms-core/src/constants/configSchema.js index 23417465..cd1d7fd0 100644 --- a/packages/netlify-cms-core/src/constants/configSchema.js +++ b/packages/netlify-cms-core/src/constants/configSchema.js @@ -187,6 +187,8 @@ const getConfigSchema = () => ({ label_singular: { type: 'string' }, description: { type: 'string' }, file: { type: 'string' }, + preview_path: { type: 'string' }, + preview_path_date_field: { type: 'string' }, fields: fieldsConfig(), }, required: ['name', 'label', 'file', 'fields'], diff --git a/packages/netlify-cms-core/src/lib/__tests__/formatters.spec.js b/packages/netlify-cms-core/src/lib/__tests__/formatters.spec.js index a06adc44..972ed8f7 100644 --- a/packages/netlify-cms-core/src/lib/__tests__/formatters.spec.js +++ b/packages/netlify-cms-core/src/lib/__tests__/formatters.spec.js @@ -1,4 +1,4 @@ -import { Map, fromJS } from 'immutable'; +import { List, Map, fromJS } from 'immutable'; import { commitMessageFormatter, prepareSlug, @@ -369,6 +369,68 @@ describe('formatters', () => { ).toBe('https://www.example.com/2020/backendslug/title/entryslug'); }); + it('should return preview url for files in file collection', () => { + const file = Map({ name: 'about-file', preview_path: '{{slug}}/{{fields.slug}}/{{title}}' }); + + const { getFileFromSlug } = require('../../reducers/collections'); + getFileFromSlug.mockReturnValue(file); + + expect( + previewUrlFormatter( + 'https://www.example.com', + Map({ + preview_path: '{{slug}}/{{title}}/{{fields.slug}}', + type: 'file_based_collection', + files: List([file]), + }), + 'backendSlug', + slugConfig, + Map({ data: Map({ slug: 'about-the-project', title: 'title' }), slug: 'about-file' }), + ), + ).toBe('https://www.example.com/backendslug/about-the-project/title'); + }); + + it('should return preview url for files in file collection when defined on file-level only', () => { + const file = Map({ name: 'about-file', preview_path: '{{slug}}/{{fields.slug}}/{{title}}' }); + + const { getFileFromSlug } = require('../../reducers/collections'); + getFileFromSlug.mockReturnValue(file); + + expect( + previewUrlFormatter( + 'https://www.example.com', + Map({ + type: 'file_based_collection', + files: List([file]), + }), + 'backendSlug', + slugConfig, + Map({ data: Map({ slug: 'about-the-project', title: 'title' }), slug: 'about-file' }), + ), + ).toBe('https://www.example.com/backendslug/about-the-project/title'); + }); + + it('should fall back to collection preview url for files in file collection', () => { + const file = Map({ name: 'about-file' }); + + const { getFileFromSlug } = require('../../reducers/collections'); + getFileFromSlug.mockReturnValue(file); + + expect( + previewUrlFormatter( + 'https://www.example.com', + Map({ + preview_path: '{{slug}}/{{title}}/{{fields.slug}}', + type: 'file_based_collection', + files: List([file]), + }), + 'backendSlug', + slugConfig, + Map({ data: Map({ slug: 'about-the-project', title: 'title' }), slug: 'about-file' }), + ), + ).toBe('https://www.example.com/backendslug/title/about-the-project'); + }); + it('should infer date field when preview_path_date_field is not configured', () => { const { selectInferedField } = require('../../reducers/collections'); selectInferedField.mockReturnValue('date'); diff --git a/packages/netlify-cms-core/src/lib/formatters.ts b/packages/netlify-cms-core/src/lib/formatters.ts index b3ff74ed..3859847a 100644 --- a/packages/netlify-cms-core/src/lib/formatters.ts +++ b/packages/netlify-cms-core/src/lib/formatters.ts @@ -8,9 +8,11 @@ import { COMMIT_AUTHOR, COMMIT_DATE, selectInferedField, + getFileFromSlug, } from '../reducers/collections'; import { Collection, SlugConfig, Config, EntryMap } from '../types/redux'; import { stripIndent } from 'common-tags'; +import { FILES } from '../constants/collectionTypes'; const { compileStringTemplate, @@ -153,24 +155,35 @@ export const previewUrlFormatter = ( return; } + const basePath = trimEnd(baseUrl, '/'); + + const isFileCollection = collection.get('type') === FILES; + const file = isFileCollection ? getFileFromSlug(collection, entry.get('slug')) : undefined; + + const getPathTemplate = () => { + return file?.get('preview_path') ?? collection.get('preview_path'); + }; + const getDateField = () => { + return file?.get('preview_path_date_field') ?? collection.get('preview_path_date_field'); + }; + /** - * Without a `previewPath` for the collection (via config), the preview URL + * If a `previewPath` is provided for the collection/file, use it to construct the + * URL path. + */ + const pathTemplate = getPathTemplate(); + + /** + * Without a `previewPath` for the collection/file (via config), the preview URL * will be the URL provided by the backend. */ - if (!collection.get('preview_path')) { + if (!pathTemplate) { return baseUrl; } - /** - * If a `previewPath` is provided for the collection, use it to construct the - * URL path. - */ - const basePath = trimEnd(baseUrl, '/'); - const pathTemplate = collection.get('preview_path') as string; let fields = entry.get('data') as Map; fields = addFileTemplateFields(entry.get('path'), fields, collection.get('folder')); - const dateFieldName = - collection.get('preview_path_date_field') || selectInferedField(collection, 'date'); + const dateFieldName = getDateField() || selectInferedField(collection, 'date'); const date = parseDateFromEntry((entry as unknown) as Map, dateFieldName); // Prepare and sanitize slug variables only, leave the rest of the diff --git a/packages/netlify-cms-core/src/types/redux.ts b/packages/netlify-cms-core/src/types/redux.ts index 67c2ec1a..6d290f6a 100644 --- a/packages/netlify-cms-core/src/types/redux.ts +++ b/packages/netlify-cms-core/src/types/redux.ts @@ -140,6 +140,8 @@ export type CollectionFile = StaticallyTypedRecord<{ label: string; media_folder?: string; public_folder?: string; + preview_path?: string; + preview_path_date_field?: string; }>; export type CollectionFiles = List; diff --git a/website/content/docs/deploy-preview-links.md b/website/content/docs/deploy-preview-links.md index 3e522fed..968806f1 100644 --- a/website/content/docs/deploy-preview-links.md +++ b/website/content/docs/deploy-preview-links.md @@ -12,7 +12,7 @@ of your unmerged content. Deploy preview links will work without configuration when all of the following requirements are met: -* Netlify CMS version is 2.4.0+ for GitHub support and 2.10.6+ for GitLab/Bitbucket support +* Netlify CMS version is 2.4.0+ for GitHub support and 2.10.6+ for GitLab/Bitbucket support * Using editorial workflow * Have a continuous deployment platform that builds every commit and provides statuses to your repo @@ -40,7 +40,7 @@ pending, which a content editor can use to manually check for a finished preview Deploy preview links point to the site root by default, but you'll probably want them to point to the specific piece of content that the content editor is viewing. You can do this by providing a -`preview_path` string template for each collection. +`preview_path` string template for each collection, or for inidividual files in a files collection. Let's say we have a `blog` collection that stores content in our repo under `content/blog`. The path to a post in your repo may look like `content/blog/2018-01-new-post.md`, but the path to that post @@ -55,6 +55,18 @@ collections: preview_path: blog/{{slug}} ``` +Similarly, for an `about` page in a files collection under `content/pages` which maps to `/about-the-project` +on your site, you would configure `preview_path` like this: + +```yaml +collections: + - name: pages + files: + - name: about + file: content/pages/about.md + preview_path: about-the-project +``` + With the above configuration, the deploy preview URL from your backend will be combined with your preview path to create a URL to a specific blog post. @@ -134,4 +146,4 @@ a commit status back to your repository host with the URL. The deploy preview URL provided by a backend will lead to the root of the deployed site. Netlify CMS will then use the `preview_path` template in an entry's collection configuration to build a path to a specific piece of content. If a `preview_path` is not provided for an entry's collection, the URL -will be used as is. \ No newline at end of file +will be used as is.