diff --git a/BREAKING_CHANGES.md b/BREAKING_CHANGES.md new file mode 100644 index 000000000..c39a7e55c --- /dev/null +++ b/BREAKING_CHANGES.md @@ -0,0 +1,2 @@ +View Filters config structure changed +View Groups config structure changed \ No newline at end of file diff --git a/packages/core/dev-test/backends/bitbucket/config.yml b/packages/core/dev-test/backends/bitbucket/config.yml index c84043d20..5abb6a71b 100644 --- a/packages/core/dev-test/backends/bitbucket/config.yml +++ b/packages/core/dev-test/backends/bitbucket/config.yml @@ -23,21 +23,28 @@ collections: field: title create: true view_filters: - - label: Posts With Index - field: title - pattern: 'This is post #' - - label: Posts Without Index - field: title - pattern: front matter post - - label: Drafts - field: draft - pattern: true + filters: + - name: posts-with-index + label: Posts With Index + field: title + pattern: 'This is post #' + - name: posts-without-index + label: Posts Without Index + field: title + pattern: front matter post + - name: drafts + label: Drafts + field: draft + pattern: true view_groups: - - label: Year - field: date - pattern: '\d{4}' - - label: Drafts - field: draft + groups: + - name: by-year + label: Year + field: date + pattern: '\d{4}' + - name: drafts + label: Drafts + field: draft fields: - label: Title name: title diff --git a/packages/core/dev-test/backends/git-gateway/config.yml b/packages/core/dev-test/backends/git-gateway/config.yml index e6410057b..a3268c0a0 100644 --- a/packages/core/dev-test/backends/git-gateway/config.yml +++ b/packages/core/dev-test/backends/git-gateway/config.yml @@ -22,21 +22,28 @@ collections: field: title create: true view_filters: - - label: Posts With Index - field: title - pattern: 'This is post #' - - label: Posts Without Index - field: title - pattern: front matter post - - label: Drafts - field: draft - pattern: true + filters: + - name: posts-with-index + label: Posts With Index + field: title + pattern: 'This is post #' + - name: posts-without-index + label: Posts Without Index + field: title + pattern: front matter post + - name: drafts + label: Drafts + field: draft + pattern: true view_groups: - - label: Year - field: date - pattern: '\d{4}' - - label: Drafts - field: draft + groups: + - name: by-year + label: Year + field: date + pattern: '\d{4}' + - name: drafts + label: Drafts + field: draft fields: - label: Title name: title diff --git a/packages/core/dev-test/backends/github/config.yml b/packages/core/dev-test/backends/github/config.yml index 3104d449a..e109be785 100644 --- a/packages/core/dev-test/backends/github/config.yml +++ b/packages/core/dev-test/backends/github/config.yml @@ -25,21 +25,28 @@ collections: field: title create: true view_filters: - - label: Posts With Index - field: title - pattern: 'This is post #' - - label: Posts Without Index - field: title - pattern: front matter post - - label: Drafts - field: draft - pattern: true + filters: + - name: posts-with-index + label: Posts With Index + field: title + pattern: 'This is post #' + - name: posts-without-index + label: Posts Without Index + field: title + pattern: front matter post + - name: drafts + label: Drafts + field: draft + pattern: true view_groups: - - label: Year - field: date - pattern: '\d{4}' - - label: Drafts - field: draft + groups: + - name: by-year + label: Year + field: date + pattern: '\d{4}' + - name: drafts + label: Drafts + field: draft fields: - label: Title name: title diff --git a/packages/core/dev-test/backends/gitlab/config.yml b/packages/core/dev-test/backends/gitlab/config.yml index cedd921ef..c3e0b98b3 100644 --- a/packages/core/dev-test/backends/gitlab/config.yml +++ b/packages/core/dev-test/backends/gitlab/config.yml @@ -30,21 +30,28 @@ collections: - date create: true view_filters: - - label: Posts With Index - field: title - pattern: 'This is post #' - - label: Posts Without Index - field: title - pattern: front matter post - - label: Drafts - field: draft - pattern: true + filters: + - name: posts-with-index + label: Posts With Index + field: title + pattern: 'This is post #' + - name: posts-without-index + label: Posts Without Index + field: title + pattern: front matter post + - name: drafts + label: Drafts + field: draft + pattern: true view_groups: - - label: Year - field: date - pattern: '\d{4}' - - label: Drafts - field: draft + groups: + - name: by-year + label: Year + field: date + pattern: '\d{4}' + - name: drafts + label: Drafts + field: draft fields: - label: Title name: title diff --git a/packages/core/dev-test/backends/proxy/config.yml b/packages/core/dev-test/backends/proxy/config.yml index fbebe0db6..6a437b325 100644 --- a/packages/core/dev-test/backends/proxy/config.yml +++ b/packages/core/dev-test/backends/proxy/config.yml @@ -41,21 +41,28 @@ collections: field: title create: true view_filters: - - label: Posts With Index - field: title - pattern: 'This is post #' - - label: Posts Without Index - field: title - pattern: front matter post - - label: Drafts - field: draft - pattern: true + filters: + - name: posts-with-index + label: Posts With Index + field: title + pattern: 'This is post #' + - name: posts-without-index + label: Posts Without Index + field: title + pattern: front matter post + - name: drafts + label: Drafts + field: draft + pattern: true view_groups: - - label: Year - field: date - pattern: '\d{4}' - - label: Drafts - field: draft + groups: + - name: by-year + label: Year + field: date + pattern: '\d{4}' + - name: drafts + label: Drafts + field: draft fields: - label: Title name: title diff --git a/packages/core/dev-test/config.yml b/packages/core/dev-test/config.yml index ae845d87a..60a5896ff 100644 --- a/packages/core/dev-test/config.yml +++ b/packages/core/dev-test/config.yml @@ -39,21 +39,28 @@ collections: field: title create: true view_filters: - - label: Posts With Index - field: title - pattern: 'This is post #' - - label: Posts Without Index - field: title - pattern: front matter post - - label: Drafts - field: draft - pattern: true + filters: + - name: posts-with-index + label: Posts With Index + field: title + pattern: 'This is post #' + - name: posts-without-index + label: Posts Without Index + field: title + pattern: front matter post + - name: drafts + label: Drafts + field: draft + pattern: true view_groups: - - label: Year - field: date - pattern: '\d{4}' - - label: Drafts - field: draft + groups: + - name: by-year + label: Year + field: date + pattern: '\d{4}' + - name: drafts + label: Drafts + field: draft fields: - label: Title name: title diff --git a/packages/core/src/__tests__/testConfig.ts b/packages/core/src/__tests__/testConfig.ts index 070d8bd59..b35b1c640 100644 --- a/packages/core/src/__tests__/testConfig.ts +++ b/packages/core/src/__tests__/testConfig.ts @@ -33,34 +33,44 @@ const testConfig: Config = { }, }, create: true, - view_filters: [ - { - label: 'Posts With Index', - field: 'title', - pattern: 'This is post #', - }, - { - label: 'Posts Without Index', - field: 'title', - pattern: 'front matter post', - }, - { - label: 'Drafts', - field: 'draft', - pattern: true, - }, - ], - view_groups: [ - { - label: 'Year', - field: 'date', - pattern: '\\d{4}', - }, - { - label: 'Drafts', - field: 'draft', - }, - ], + view_filters: { + default: 'posts-without-index', + filters: [ + { + name: 'posts-with-index', + label: 'Posts With Index', + field: 'title', + pattern: 'This is post #', + }, + { + name: 'posts-without-index', + label: 'Posts Without Index', + field: 'title', + pattern: 'front matter post', + }, + { + name: 'draft', + label: 'Drafts', + field: 'draft', + pattern: true, + }, + ], + }, + view_groups: { + groups: [ + { + name: 'by-year', + label: 'Year', + field: 'date', + pattern: '\\d{4}', + }, + { + name: 'draft', + label: 'Drafts', + field: 'draft', + }, + ], + }, fields: [ { label: 'Title', diff --git a/packages/core/src/actions/config.ts b/packages/core/src/actions/config.ts index 1596ecaaf..a380691fe 100644 --- a/packages/core/src/actions/config.ts +++ b/packages/core/src/actions/config.ts @@ -259,19 +259,25 @@ export function applyDefaults( }; } - collection.view_filters = (view_filters || []).map(filter => { - return { - ...filter, - id: `${filter.field}__${filter.pattern}`, - }; - }); + collection.view_filters = { + default: collection.view_filters?.default, + filters: (view_filters?.filters ?? []).map(filter => { + return { + ...filter, + id: `${filter.field}__${filter.pattern}`, + }; + }), + }; - collection.view_groups = (view_groups || []).map(group => { - return { - ...group, - id: `${group.field}__${group.pattern}`, - }; - }); + collection.view_groups = { + default: collection.view_groups?.default, + groups: (view_groups?.groups ?? []).map(group => { + return { + ...group, + id: `${group.field}__${group.pattern}`, + }; + }), + }; } }); } diff --git a/packages/core/src/components/collections/CollectionView.tsx b/packages/core/src/components/collections/CollectionView.tsx index ae049db16..94eaa2c1e 100644 --- a/packages/core/src/components/collections/CollectionView.tsx +++ b/packages/core/src/components/collections/CollectionView.tsx @@ -155,7 +155,9 @@ const CollectionView = ({ } const defaultSort = collection?.sortable_fields?.default; - if (!defaultSort || !defaultSort.field) { + const defaultViewGroupName = collection?.view_groups?.default; + const defaultViewFilterName = collection?.view_filters?.default; + if (!defaultViewGroupName && !defaultViewFilterName && (!defaultSort || !defaultSort.field)) { if (!readyToLoad) { setReadyToLoad(true); } @@ -166,9 +168,27 @@ const CollectionView = ({ let alive = true; - const sortEntries = () => { + const sortGroupFilterEntries = () => { setTimeout(async () => { - await onSortClick(defaultSort.field, defaultSort.direction ?? SORT_DIRECTION_ASCENDING); + if (defaultSort && defaultSort.field) { + await onSortClick(defaultSort.field, defaultSort.direction ?? SORT_DIRECTION_ASCENDING); + } + + if (defaultViewGroupName) { + const defaultViewGroup = viewGroups?.groups.find(g => g.name === defaultViewGroupName); + if (defaultViewGroup) { + await onGroupClick(defaultViewGroup); + } + } + + if (defaultViewFilterName) { + const defaultViewFilter = viewFilters?.filters.find( + f => f.name === defaultViewFilterName, + ); + if (defaultViewFilter) { + await onFilterClick(defaultViewFilter); + } + } if (alive) { setReadyToLoad(true); @@ -176,12 +196,22 @@ const CollectionView = ({ }); }; - sortEntries(); + sortGroupFilterEntries(); return () => { alive = false; }; - }, [collection, onSortClick, prevCollection, readyToLoad, sort]); + }, [ + collection, + onFilterClick, + onGroupClick, + onSortClick, + prevCollection, + readyToLoad, + sort, + viewFilters?.filters, + viewGroups?.groups, + ]); const collectionDescription = collection?.description; @@ -204,8 +234,8 @@ const CollectionView = ({ sortableFields={sortableFields} onSortClick={onSortClick} sort={sort} - viewFilters={viewFilters ?? []} - viewGroups={viewGroups ?? []} + viewFilters={viewFilters?.filters ?? []} + viewGroups={viewGroups?.groups ?? []} onFilterClick={onFilterClick} onGroupClick={onGroupClick} filter={filter} diff --git a/packages/core/src/constants/configSchema.tsx b/packages/core/src/constants/configSchema.tsx index 031bc1146..5868415bb 100644 --- a/packages/core/src/constants/configSchema.tsx +++ b/packages/core/src/constants/configSchema.tsx @@ -135,6 +135,7 @@ const viewFilters = { items: { type: 'object', properties: { + name: { type: 'string' }, label: { type: 'string' }, field: { type: 'string' }, pattern: { @@ -149,7 +150,7 @@ const viewFilters = { ], }, }, - required: ['label', 'field', 'pattern'], + required: ['name', 'label', 'field', 'pattern'], }, }; @@ -159,11 +160,12 @@ const viewGroups = { items: { type: 'object', properties: { + name: { type: 'string' }, label: { type: 'string' }, field: { type: 'string' }, pattern: { type: 'string' }, }, - required: ['label', 'field'], + required: ['name', 'label', 'field'], }, }; @@ -260,8 +262,26 @@ function getConfigSchema() { }, required: ['fields'], }, - view_filters: viewFilters, - view_groups: viewGroups, + view_filters: { + type: 'object', + properties: { + default: { + type: 'string', + }, + filters: viewFilters, + }, + required: ['filters'], + }, + view_groups: { + type: 'object', + properties: { + default: { + type: 'string', + }, + groups: viewGroups, + }, + required: ['groups'], + }, i18n: i18nCollection, hide: { type: 'boolean' }, editor: { diff --git a/packages/core/src/interface.ts b/packages/core/src/interface.ts index 68b9fcf1a..e7d580d89 100644 --- a/packages/core/src/interface.ts +++ b/packages/core/src/interface.ts @@ -262,8 +262,8 @@ export interface BaseCollection { label_singular?: string; label: string; sortable_fields?: SortableFields; - view_filters?: ViewFilter[]; - view_groups?: ViewGroup[]; + view_filters?: ViewFilters; + view_groups?: ViewGroups; i18n?: boolean | I18nInfo; hide?: boolean; editor?: EditorConfig; @@ -850,18 +850,30 @@ export type Field = export interface ViewFilter { id?: string; + name: string; label: string; field: string; pattern: string | boolean | number; } +export interface ViewFilters { + default?: string; + filters: ViewFilter[]; +} + export interface ViewGroup { id?: string; + name: string; label: string; field: string; pattern?: string; } +export interface ViewGroups { + default?: string; + groups: ViewGroup[]; +} + export type SortDirection = | typeof SORT_DIRECTION_ASCENDING | typeof SORT_DIRECTION_DESCENDING diff --git a/packages/core/src/lib/util/__tests__/nested.util.spec.ts b/packages/core/src/lib/util/__tests__/nested.util.spec.ts index dc884f8ac..28e0c411f 100644 --- a/packages/core/src/lib/util/__tests__/nested.util.spec.ts +++ b/packages/core/src/lib/util/__tests__/nested.util.spec.ts @@ -20,8 +20,8 @@ const collection: Collection = { name: 'pages', nested: { depth: 100, summary: '{{title}}', path: { label: 'Path', index_file: 'index' } }, sortable_fields: { fields: Array(1) }, - view_filters: [], - view_groups: [], + view_filters: { filters: [] }, + view_groups: { groups: [] }, }; const entries: Entry[] = [ diff --git a/packages/docs/content/docs/collection-overview.mdx b/packages/docs/content/docs/collection-overview.mdx index 7c09e0d96..3824c1041 100644 --- a/packages/docs/content/docs/collection-overview.mdx +++ b/packages/docs/content/docs/collection-overview.mdx @@ -280,75 +280,91 @@ sortable_fields: { ## View Filters -The `view_filters` setting is an optional list of predefined view filters to show in the UI. - -Defaults to an empty list. +The `view_filters` setting is an optional property which takes a list of predefined view filters to show in the UI and an optional default view filter. ```yaml view_filters: - - label: "Alice's and Bob's Posts" - field: author - pattern: 'Alice|Bob' - - label: 'Posts published in 2020' - field: date - pattern: '2020' - - label: Drafts - field: draft - pattern: true + default: drafts + filters: + - name: alice-and-bob + label: "Alice's and Bob's Posts" + field: author + pattern: 'Alice|Bob' + - name: posts-2020 + label: 'Posts published in 2020' + field: date + pattern: '2020' + - name: drafts + label: Drafts + field: draft + pattern: true ``` ```js -view_filters: [ - { - label: "Alice's and Bob's Posts", - field: 'author', - pattern: 'Alice|Bob', - }, - { - label: 'Posts published in 2020', - field: 'date', - pattern: '2020', - }, - { - label: 'Drafts', - field: 'draft', - pattern: true, - }, -], +view_filters: { + default: 'drafts', + filters: [ + { + name: 'alice-and-bob', + label: "Alice's and Bob's Posts", + field: 'author', + pattern: 'Alice|Bob', + }, + { + name: 'posts-2020', + label: 'Posts published in 2020', + field: 'date', + pattern: '2020', + }, + { + name: 'drafts', + label: 'Drafts', + field: 'draft', + pattern: true, + }, + ], +}, ``` ## View Groups -The `view_groups` setting is an optional list of predefined view groups to show in the UI. - -Defaults to an empty list. +The `view_groups` setting is an optional property which takes a list of predefined view groups to show in the UI and an optional default view group. ```yaml view_groups: - - label: Year - field: date - # groups items based on the value matched by the pattern - pattern: \d{4} - - label: Drafts - field: draft + default: by-year + groups: + - name: by-year + label: Year + field: date + # groups items based on the value matched by the pattern + pattern: \d{4} + - name: drafts + label: Drafts + field: draft ``` ```js -view_groups: [ - { - label: 'Year', - field: 'date', - pattern: '\\d{4}', - }, - { - label: 'Drafts', - field: 'draft', - }, -], +view_groups: { + default: by-year + groups: [ + { + name: 'by-year', + label: 'Year', + field: 'date', + pattern: '\\d{4}', + }, + { + name: 'drafts', + label: 'Drafts', + field: 'draft', + }, + ], +}, ``` diff --git a/packages/docs/content/docs/decap-migration-guide.mdx b/packages/docs/content/docs/decap-migration-guide.mdx deleted file mode 100644 index 061b9febb..000000000 --- a/packages/docs/content/docs/decap-migration-guide.mdx +++ /dev/null @@ -1,250 +0,0 @@ ---- -group: Migration -title: Decap CMS Migration Guide -weight: 190 ---- - -Static CMS is a fork of [Decap](https://github.com/decaporg/decap-cms) (previously Netlify CMS) . Many changes have been made, some big, some small. - -In this guide, we will walk you through the steps of migrating from Decap to Static CMS. - -## How to Migrate - -Start by replacing Decap with Static CMS, then address the changes below. - -### CDN - -Decap: - -```html - -``` - -Static CMS: - -```html - -``` - -### Bundling - -```bash -# Uninstall Decap -npm uninstall netlify-cms-app -npm uninstall netlify-cms-core - -# Install Static CMS -npm install @staticcms/core -``` - -#### Change your imports - -Decap: - -```js -import CMS from 'netlify-cms-app'; -``` - -Static CMS: - -```js -import CMS from '@staticcms/core'; -``` - -## Changes - -### React 18 - -React `18.2.0` is now the minimum supported React version. If you are using Static CMS through a CDN, this comes bundled. - -### Static CMS Styles - -Static CMS bundles its styles separately from the main javascript file, so you will need to import them separately. - -**CDN**: - -```html - -``` - -**Bundling**: - -```js -import '@staticcms/core/dist/main.css'; -``` - -### Backends - -The Azure backend has been removed. All other backends are still supported. - -However, the Gitlab, Client-Side Implicit Grant has been removed as a method of authentication. - -### Dates - -[Moment](https://momentjs.com/) has been dropped as the date library used. Instead we are now using [date-fns](https://date-fns.org/). Date formats in your configuration will need to be updated. See [format docs](https://date-fns.org/v2.29.3/docs/format). - -### Initializing Static CMS - -CMS must be explicitly initiated by calling `CMS.init()`. Passing a config to `CMS.init()` will now completely override `config.yml` (they are now exclusive), instead of being merged with `config.yml` - -### Markdown Editor - -A [new markdown editor](/docs/widget-markdown) has been added. It comes with a new [shortcode](/docs/widget-markdown#shortcodes) system, old editor components no longer work. - -### Sortable Fields - -The `sortable_fields` configuration option has been slightly changed, as we now allow a [default sorting option](/docs/collection-overview#sortable_fields). - -**Decap**: - -```yaml -sortable_fields: - field1 - field2 -``` - -**Static CMS**: - -```yaml -sortable_fields: fields: - field1 - field2 -``` - -### List Widget - -Support in the List Widget for the `field` property has been dropped. A single field in the `fields` property [achieves the same behavior](/docs/widget-list). - -### Custom Widgets - -Custom widget creation has changed. `createClass` has been removed. Custom widgets should all be [functional components](https://reactjs.org/docs/components-and-props.html#function-and-class-components) now. - -There have also been changes to how custom widgets are registered and the properties passed to the controls and previews. See [custom widgets](/docs/custom-widgets) for full details. - -### Custom Previews - -Custom preview creation has changed. `createClass` has been removed. Custom previews should all be [functional components](https://reactjs.org/docs/components-and-props.html#function-and-class-components) now. - -There have also been changes to the properties passed to custom previews. See [custom previews](/docs/custom-previews) for full details. - -### External integrations - -The following external integrations have been removed: - -- Algolia -- AssetStore -- Cloudinary -- Uploadcare - -### Deprecated Features - -- All deprecated features were removed - - `date` widget has been removed - - `datetime` widget - - `dateFormat` has been removed (Use `date_format` instead) - - `timeFormat` has been removed (Use `time_format` instead) - -### Media Library - -The `getAsset` method has been removed, the new `useMediaAsset` hook should be used instead. See [Interacting With The Media Library](/docs/custom-widgets#interacting-with-the-media-library). - -### Beta Features - -The following beta features from Decap have been dropped: - -- GraphQL support for GitHub and GitLab -- Remark plugins (new markdown editor has its own plugin system) -- Dynamic Default Values -- Custom Mount Element - -#### Nested Collections - -[Nested Collections](/docs/collection-types#nested-collections) have some breaking config changes. The `meta` config has been dropped and its `path` property has been moved into the `nested` prop. You can also no longer specify the widget type for the path. - -**Old Config** - - -```yaml -collections: - - name: pages - label: Pages - label_singular: 'Page' - folder: content/pages - create: true - nested: - depth: 100 - summary: '{{title}}' - fields: - - label: Title - name: title - widget: string - - label: Body - name: body - widget: markdown - meta: { path: { widget: string, label: 'Path', index_file: 'index' } } -``` - -```js -{ - collections: [ - { - name: 'pages', - label: 'Pages', - label_singular: 'Page', - folder: 'content/pages', - create: true, - nested: { - depth: 100, - summary: '{{title}}', - }, - fields: [ - { - label: 'Title', - name: 'title', - widget: 'string', - }, - { - label: 'Body', - name: 'body', - widget: 'markdown', - }, - ], - meta: { - path: { - widget: 'string', - label: 'Path', - index_file: 'index', - }, - }, - }, - ]; -} -``` - - - -## Platform Changes - -### Gatsby - -If you are using Gatsby you will need to change out your CMS plugin. - -```bash -# Uninstall Decap plugin -npm uninstall gatsby-plugin-netlify-cms - -# Install Static CMS plugin -npm install gatsby-plugin-static-cms -``` - -## Local Development Changes - -If you are using the local backend you will need to switch the proxy server package you are using. - -Decap: - -```bash -npx netlify-cms-proxy-server -``` - -Static CMS: - -```bash -npx @staticcms/proxy-server -``` diff --git a/packages/docs/content/docs/updating-your-cms.mdx b/packages/docs/content/docs/updating-your-cms.mdx index f61234ed4..6c67f3a2a 100644 --- a/packages/docs/content/docs/updating-your-cms.mdx +++ b/packages/docs/content/docs/updating-your-cms.mdx @@ -14,12 +14,12 @@ If you are using a package manager like Yarn or NPM, use their standard procedur If you are using Static CMS through a CDN like Unpkg, then that depends on the version tag you are using. You can find the version tag in the `/admin/index.html` file of your site. -- (Recommended) If you use `^2.0.0`, Static CMS does all updates except major versions automatically. - - It upgrades to `2.0.1`, `2.1.0`, `2.1.1`. - - It does not upgrade to `2.0.0` or higher. +- (Recommended) If you use `^4.0.0`, Static CMS does all updates except major versions automatically. + - It upgrades to `4.0.1`, `4.1.0`, `4.1.1`. + - It does not upgrade to `4.0.0` or higher. - It does not upgrade to beta versions. -- If you use `~2.0.0`, Static CMS will do only patch updates automatically. - - It upgrades `2.0.1`, `2.0.2`. - - It does not upgrade to `2.1.0` or higher. +- If you use `~4.0.0`, Static CMS will do only patch updates automatically. + - It upgrades `4.0.1`, `4.0.2`. + - It does not upgrade to `4.1.0` or higher. - It does not upgrade beta versions.