From 94bbf14333db271b2cfbc67367dc1eb7a7632fb9 Mon Sep 17 00:00:00 2001 From: Daniel Lautzenheiser Date: Wed, 5 Apr 2023 16:32:17 -0400 Subject: [PATCH] docs: add migration guide (#685) --- BREAKING_CHANGES.md | 23 -- packages/core/test/data/widgets.mock.ts | 2 - packages/demo/src/cms.jsx | 117 ++++++---- .../docs/content/docs/collection-overview.mdx | 49 ++--- .../docs/content/docs/custom-previews.mdx | 41 ++-- packages/docs/content/docs/custom-widgets.mdx | 20 +- .../docs/content/docs/migration-guide-v2.mdx | 200 ++++++++++++++++++ .../docs/src/components/docs/DocsContent.tsx | 11 +- .../components/docs/components/CodeTabs.tsx | 2 +- .../table_of_contents/DocsTableOfContents.tsx | 8 +- 10 files changed, 359 insertions(+), 114 deletions(-) delete mode 100644 BREAKING_CHANGES.md create mode 100644 packages/docs/content/docs/migration-guide-v2.mdx diff --git a/BREAKING_CHANGES.md b/BREAKING_CHANGES.md deleted file mode 100644 index 652f915b..00000000 --- a/BREAKING_CHANGES.md +++ /dev/null @@ -1,23 +0,0 @@ -BREAKING_CHANGES - -- Card preview only is used for card view (viewStyle prop removed, theme prop added). Field preview can be used in table view. -- Deprecated stuff removed (getAsset, createReactClass, isFieldDuplicate, isFieldHidden) -- widget prop `isDisabled` renamed to `disabled` -- widget prop `isDuplicate` renamed to `duplicate` -- widget prop `isHidden` renamed to `hidden` -- useMediaInsert now requires collection to be passed -- media path changed from `string | string[]` to `{ path: string | string[], alt?: string }` -- Nested collections, meta config moved into nested config. - -CHANGES - -- Default styles are now provided in the preview frame. If you provide your own via `registerPreviewStyle`, then these default styles will not be included. - -ADDED -- `forSingleList` - Allows for changing styles for single list items - -TODO - -- Docs on table columns -- Docs on field previews -- Re-add collection description OR document as breaking change diff --git a/packages/core/test/data/widgets.mock.ts b/packages/core/test/data/widgets.mock.ts index 2887d552..2e39c130 100644 --- a/packages/core/test/data/widgets.mock.ts +++ b/packages/core/test/data/widgets.mock.ts @@ -19,8 +19,6 @@ export const createMockWidgetControlProps = < | 'field' | 'data' | 'hasErrors' - | 'isFieldDuplicate' - | 'isFieldHidden' | 'onChange' | 'clearMediaControl' | 'openMediaLibrary' diff --git a/packages/demo/src/cms.jsx b/packages/demo/src/cms.jsx index 6b47afb8..c528c8f4 100644 --- a/packages/demo/src/cms.jsx +++ b/packages/demo/src/cms.jsx @@ -18,48 +18,81 @@ const PostPreview = ({ entry, widgetFor }) => { ); }; -const PostPreviewCard = ({ entry, widgetFor, viewStyle }) => { - return ( -
- {viewStyle === "grid" ? widgetFor("image") : null} -
-
-
- {entry.data.title} - {entry.data.date} -
-
- {entry.data.draft === true ? "Draft" : "Published"} -
-
-
-
+const PostPreviewCard = ({ entry, theme }) => { + const date = new Date(entry.data.date); + + const month = date.getMonth() + 1; + const day = date.getDate(); + + return h( + 'div', + { style: { width: '100%' } }, + h( + 'div', + { style: { padding: '16px', width: '100%' } }, + h( + 'div', + { + style: { + display: 'flex', + width: '100%', + justifyContent: 'space-between', + alignItems: 'start', + gap: '4px', + color: theme === 'dark' ? 'white' : 'inherit', + }, + }, + h( + 'div', + { + style: { + display: 'flex', + flexDirection: 'column', + alignItems: 'baseline', + gap: '4px', + }, + }, + h( + 'div', + { + style: { + fontSize: '14px', + fontWeight: 700, + color: 'rgb(107, 114, 128)', + fontSize: '14px', + lineHeight: '18px', + }, + }, + entry.data.title, + ), + h( + 'span', + { style: { fontSize: '14px' } }, + `${date.getFullYear()}-${month < 10 ? `0${month}` : month}-${ + day < 10 ? `0${day}` : day + }`, + ), + ), + h( + 'div', + { + style: { + backgroundColor: entry.data.draft === true ? 'blue' : 'green', + color: 'white', + border: 'none', + padding: '2px 6px', + textAlign: 'center', + textDecoration: 'none', + display: 'inline-block', + cursor: 'pointer', + borderRadius: '4px', + fontSize: '14px', + }, + }, + entry.data.draft === true ? 'Draft' : 'Published', + ), + ), + ), ); }; diff --git a/packages/docs/content/docs/collection-overview.mdx b/packages/docs/content/docs/collection-overview.mdx index b2bbc963..e39efabf 100644 --- a/packages/docs/content/docs/collection-overview.mdx +++ b/packages/docs/content/docs/collection-overview.mdx @@ -6,29 +6,30 @@ weight: 9 `collections` accepts a list of collection objects, each with the following options -| Name | Type | Default | Description | -| --------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | -| name | string | | Unique identifier for the collection, used as the key when referenced in other contexts (like the [relation widget](/docs/widgets/#relation)) | -| identifier_field | string | `'title'` | _Optional_. See [identifier_field](#identifier_field) below | -| label | string | `name` | _Optional_. Label for the collection in the editor UI | -| label_singular | string | `label` | _Optional_. Singular label for certain elements in the editor | -| icon | string | | _Optional_. Unique name of icon to use in main menu. See [Custom Icons](/docs/custom-icons) | -| description | string | | _Optional_. Text displayed below the label when viewing a collection | -| files or folder | [Collection Files](/docs/collection-types#file-collections)
\| [Collection Folder](/docs/collection-types#folder-collections) | | **Requires one of these**: Specifies the collection type and location; details in [Collection Types](/docs/collection-types) | -| filter | FilterRule | | _Optional_. Filter for [Folder Collections](/docs/collection-types#folder-collections) | -| create | boolean | `false` | _Optional_. **For [Folder Collections](/docs/collection-types#folder-collections) only**
`true` - Allows users to create new items in the collection | -| hide | boolean | `false` | _Optional_. `true` hides a collection in the CMS UI. Useful when using the relation widget to hide referenced collections | -| delete | boolean | `true` | _Optional_. `false` prevents users from deleting items in a collection | -| extension | string | | _Optional_. See [extension](#extension-and-format) below | -| format | 'yaml'
\| 'yml'
\| 'json'
\| 'frontmatter'
\| 'json-frontmatter'
\| 'yaml-frontmatter' | | _Optional_. See [format](#extension-and-format) below | -| frontmatter_delimiter | string
\| [string, string] | | _Optional_. See [frontmatter_delimiter](#frontmatter_delimiter) below | -| slug | string | | _Optional_. See [slug](#slug) below | -| fields (required) | Field | | _Optional_. See [fields](#fields) below. Ignored if [Files Collection](/docs/collection-types#file-collections) | -| editor | EditorConfig | | _Optional_. See [editor](#editor) below | -| summary | string | | _Optional_. See [summary](#summary) below | -| sortable_fields | SortableFields | | _Optional_. See [sortable_fields](#sortable_fields) below | -| view_filters | ViewFilter | | _Optional_. See [view_filters](#view_filters) below | -| view_groups | ViewGroup | | _Optional_. See [view_groups](#view_groups) below | +| Name | Type | Default | Description | +| --------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | +| name | string | | Unique identifier for the collection, used as the key when referenced in other contexts (like the [relation widget](/docs/widgets/#relation)) | +| identifier_field | string | `'title'` | _Optional_. See [identifier_field](#identifier_field) below | +| label | string | `name` | _Optional_. Label for the collection in the editor UI | +| label_singular | string | `label` | _Optional_. Singular label for certain elements in the editor | +| icon | string | | _Optional_. Unique name of icon to use in main menu. See [Custom Icons](/docs/custom-icons) | +| description | string | | _Optional_. Text displayed below the label when viewing a collection | +| files or folder | [Collection Files](/docs/collection-types#file-collections)
\| [Collection Folder](/docs/collection-types#folder-collections) | | **Requires one of these**: Specifies the collection type and location; details in [Collection Types](/docs/collection-types) | +| filter | FilterRule | | _Optional_. Filter for [Folder Collections](/docs/collection-types#folder-collections) | +| create | boolean | `false` | _Optional_. **For [Folder Collections](/docs/collection-types#folder-collections) only**
`true` - Allows users to create new items in the collection | +| hide | boolean | `false` | _Optional_. `true` hides a collection in the CMS UI. Useful when using the relation widget to hide referenced collections | +| delete | boolean | `true` | _Optional_. `false` prevents users from deleting items in a collection | +| extension | string | | _Optional_. See [extension](#extension-and-format) below | +| format | 'yaml'
\| 'yml'
\| 'json'
\| 'frontmatter'
\| 'json-frontmatter'
\| 'yaml-frontmatter' | | _Optional_. See [format](#extension-and-format) below | +| frontmatter_delimiter | string
\| [string, string] | | _Optional_. See [frontmatter_delimiter](#frontmatter_delimiter) below | +| slug | string | | _Optional_. See [slug](#slug) below | +| fields (required) | Field | | _Optional_. See [fields](#fields) below. Ignored if [Files Collection](/docs/collection-types#file-collections) | +| editor | EditorConfig | | _Optional_. See [editor](#editor) below | +| summary | string | | _Optional_. See [summary](#summary) below | +| summary_fields | list of strings | ['summary'] | _Optional_. A list of fields to show in the table view | +| sortable_fields | SortableFields | | _Optional_. See [sortable_fields](#sortable_fields) below | +| view_filters | ViewFilter | | _Optional_. See [view_filters](#view_filters) below | +| view_groups | ViewGroup | | _Optional_. See [view_groups](#view_groups) below | ## `identifier_field` @@ -192,7 +193,7 @@ editor: { ## `summary` -This setting allows the customization of the collection list view. Similar to the `slug` field, a string with templates can be used to include values of different fields, e.g. `{{title}}`. This option over-rides the default of `title` field and `identifier_field`. +This setting allows the customization of the collection table view. Similar to the `slug` field, a string with templates can be used to include values of different fields, e.g. `{{title}}`. This option over-rides the default of `title` field and `identifier_field`. **Available Template Tags** diff --git a/packages/docs/content/docs/custom-previews.mdx b/packages/docs/content/docs/custom-previews.mdx index 6000df0a..17555e0d 100644 --- a/packages/docs/content/docs/custom-previews.mdx +++ b/packages/docs/content/docs/custom-previews.mdx @@ -326,6 +326,22 @@ CMS.registerPreviewTemplate('general', GeneralPreview); +## Editor Preview Styles + +Register a custom stylesheet to use on the preview pane. + +```js +CMS.registerPreviewStyle(url); +``` + +### Raw Styles + +If you want to provide a raw CSS string instead of a url, you can pass `{ raw: true }` as the second parameter. + +```js +CMS.registerPreviewStyle('.main { color: blue; border: 1px solid gree; }', { raw: true }); +``` + ## Collection Card Preview `registerPreviewCard` allows you to create a card template that overrides the cards displayed in the collection view. @@ -341,7 +357,6 @@ The following parameters will be passed to your `react_component` during render: | Param | Type | Description | | ---------- | ---------------------- | ------------------------------------------------------------------------------------------------- | -| viewStyle | 'list'
\| 'grid' | The current view style being displayed | | entry | object | Object with a `data` field that contains the current value of all widgets in the editor | | widgetFor | Function | Given a field name, returns the rendered preview of that field's widget and value | | widgetsFor | Function | Given a field name, returns the rendered previews of that field's nested child widgets and values | @@ -352,11 +367,11 @@ The following parameters will be passed to your `react_component` during render: ```js -const PostPreviewCard = ({ entry, widgetFor, viewStyle }) => { +const PostPreviewCard = ({ entry, widgetFor }) => { return h( 'div', { style: { width: '100%' } }, - viewStyle === 'grid' ? widgetFor('image') : null, + widgetFor('image'), h( 'div', { style: { padding: '16px', width: '100%' } }, @@ -375,7 +390,7 @@ const PostPreviewCard = ({ entry, widgetFor, viewStyle }) => { { style: { display: 'flex', - flexDirection: viewStyle === 'grid' ? 'column' : 'row', + flexDirection: 'column', alignItems: 'baseline', gap: '8px', }, @@ -411,10 +426,10 @@ CMS.registerPreviewCard('posts', PostPreviewCard); ```jsx import CMS from '@staticcms/core'; -const PostPreviewCard = ({ entry, widgetFor, viewStyle }) => { +const PostPreviewCard = ({ entry, widgetFor }) => { return (
- {viewStyle === 'grid' ? widgetFor('image') : null} + {widgetFor('image')}
{
) => { +const PostPreviewCard = ({ entry, widgetFor }: TemplatePreviewCardProps) => { return (
- {viewStyle === 'grid' ? widgetFor('image') : null} + {widgetFor('image')}
-##### Table View - -![Post Preview Card Table View](/img/preview_card_list.png) - ##### Grid View ![Post Preview Card Grid View](/img/preview_card_grid.png) -## Field Preview +## Collection Field Preview `registerFieldPreview` allows you to create a custom preview for a specific field in the table view for collections. diff --git a/packages/docs/content/docs/custom-widgets.mdx b/packages/docs/content/docs/custom-widgets.mdx index 61c23424..21e7dcfe 100644 --- a/packages/docs/content/docs/custom-widgets.mdx +++ b/packages/docs/content/docs/custom-widgets.mdx @@ -373,7 +373,11 @@ If you want to use the media library in your custom widget you will need to use ```js const FileControl = ({ collection, field, value, entry, onChange }) => { - const handleOpenMediaLibrary = useMediaInsert(value, { field, controlID }, onChange); + const handleChange = ({ path }) => { + onChange(path); + }; + + const handleOpenMediaLibrary = useMediaInsert(value, { collection, field, controlID }, onChange); const assetSource = useMediaAsset(value, collection, field, entry); @@ -389,7 +393,11 @@ import useMediaAsset from '@staticcms/core/lib/hooks/useMediaAsset'; import useMediaInsert from '@staticcms/core/lib/hooks/useMediaInsert'; const FileControl = ({ collection, field, value, entry, onChange }) => { - const handleOpenMediaLibrary = useMediaInsert(value, { field, controlID }, onChange); + const handleChange = ({ path }) => { + onChange(path); + }; + + const handleOpenMediaLibrary = useMediaInsert(value, { collection, field, controlID }, handleChange); const assetSource = useMediaAsset(value, collection, field, entry); @@ -408,7 +416,7 @@ const FileControl = ({ collection, field, value, entry, onChange }) => { import useMediaAsset from '@staticcms/core/lib/hooks/useMediaAsset'; import useMediaInsert from '@staticcms/core/lib/hooks/useMediaInsert'; -import type { WidgetControlProps } from '@staticcms/core/interface'; +import type { WidgetControlProps, MediaPath } from '@staticcms/core/interface'; import type { FC } from 'react'; const FileControl: FC> = ({ @@ -418,7 +426,11 @@ const FileControl: FC> = ({ entry, onChange, }) => { - const handleOpenMediaLibrary = useMediaInsert(internalValue, { field, controlID }, onChange); + const handleChange = ({ path }: MediaPath) => { + onChange(path); + }; + + const handleOpenMediaLibrary = useMediaInsert(internalValue, { collection, field, controlID }, onChange); const assetSource = useMediaAsset(value, collection, field, entry); diff --git a/packages/docs/content/docs/migration-guide-v2.mdx b/packages/docs/content/docs/migration-guide-v2.mdx new file mode 100644 index 00000000..b8d0313d --- /dev/null +++ b/packages/docs/content/docs/migration-guide-v2.mdx @@ -0,0 +1,200 @@ +--- +group: Migration +title: How to Upgrade to v2 +weight: 101 +--- + +This guide is a work in progress and subject to change before the final `v2.0.0` release. + +Static CMS v2 introduces a brand new UI and an updated media library. In this guide, we will walk you through the steps for upgrading to Static CMS v2. + +Please [report any issues](https://github.com/StaticJsCMS/static-cms/issues/new) you encounter while upgrading to Static CMS v2. + +## Installing + +To install the latest version of Static CMS: + +```bash +npm install @staticcms/core@^2.0.0-beta.1 +``` + +Or if you’re using yarn: + +```bash +yarn add @staticcms/core@^2.0.0-beta.1 +``` + +If you are using a CDN to load Static CMS, simply change your URL: + +```html + +``` + +## Deprecated Items Removed + +All previously deprecated items have been removed as part of this release. + +- `getAsset` - Use `useMediaAsset` React hook instead +- `createReactClass` - Use [react functional components](https://react.dev/learn) instead +- `isFieldDuplicate` - Use `duplicate` variable instead +- `isFieldHidden` - Use `hidden` variable instead + +## Importing Static CMS Styles + +In `v2.0.0` the apps stylings are not longer bundled into the main javscript file. For both CDN and bundled setups, you will need to include the css file yourself. + +**CDN**: + +```html + +``` + +**Bundling**: + +```js +import '@staticcms/core/dist/main.css'; +``` + +### Custom Preview Styles + +Some basic preview styles are now provided in order to properly support dark mode and make the basic previews look a bit better. However, if you [provide your own preview styles](/docs/custom-previews#editor-preview-styles) these default styles will not be included in the preview. + +## Nested Collections + +While still in beta, [Nested Collections](/docs/collection-types#nested-collections) are now fully working and supported in `v2.0.0`. However there are 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', + }, + }, + }, + ]; +} +``` + + + +**New Config** + + +```yaml +collections: + - name: pages + label: Pages + label_singular: 'Page' + folder: content/pages + create: true + nested: + depth: 100 + summary: '{{title}}' + path: { label: 'Path', index_file: 'index' } + fields: + - label: Title + name: title + widget: string + - label: Body + name: body + widget: markdown +``` + +```js +{ + collections: [ + { + name: 'pages', + label: 'Pages', + label_singular: 'Page', + folder: 'content/pages', + create: true, + nested: { + depth: 100, + summary: '{{title}}', + path: { + label: 'Path', + index_file: 'index', + }, + }, + fields: [ + { + label: 'Title', + name: 'title', + widget: 'string', + }, + { + label: 'Body', + name: 'body', + widget: 'markdown', + }, + ], + }, + ]; +} +``` + + + +## Other Breaking Changes + +- [Card previews](/docs/custom-previews#collection-card-preview) now are only used for the card view. The `viewStyle` has been removed. [Field previews](/docs/custom-previews#field-preview) can be used to change the table view. +- Widget Control component property changes: + - `isDisabled` renamed to `disabled` + - `isDuplicate` renamed to `duplicate` + - `isHidden` renamed to `hidden` + - `mediaPaths` is now object of id mapped to an object containing the `path` and optional `alt` +- `useMediaInsert` hook now requires a collection to be passed in. Its callback function now receives an object containing the `path` and optional `alt` instead of a string. + +## Other Changes + +- `summary_fields` property added to [collection configuration](/docs/collection-overview) to allow customization of the table view. This works with the new [field preview](/docs/custom-previews). +- New widget control property: `forSingleList`. It specifies if the widget is within a singleton `list` widget (string array, number array, etc) diff --git a/packages/docs/src/components/docs/DocsContent.tsx b/packages/docs/src/components/docs/DocsContent.tsx index 370d4b05..6cba725d 100644 --- a/packages/docs/src/components/docs/DocsContent.tsx +++ b/packages/docs/src/components/docs/DocsContent.tsx @@ -32,7 +32,7 @@ const DocsContent = styled('div')( & p:not(:first-of-type) { margin-top: 8px; } - + & pre + p:not(:first-of-type) { margin-top: 20px; } @@ -174,6 +174,15 @@ const DocsContent = styled('div')( padding: 1rem; overflow: auto; margin: 0; + border-radius: 12px; + } + + .dark & pre.code-tabpanel, + .dark & pre.code-tabpanel[class*='language-'], + .light & pre.code-tabpanel, + .light & pre.code-tabpanel[class*='language-'] { + border-top-left-radius: 0; + border-top-right-radius: 0; } & pre code { diff --git a/packages/docs/src/components/docs/components/CodeTabs.tsx b/packages/docs/src/components/docs/components/CodeTabs.tsx index cc8f5bf9..12658649 100644 --- a/packages/docs/src/components/docs/components/CodeTabs.tsx +++ b/packages/docs/src/components/docs/components/CodeTabs.tsx @@ -142,7 +142,7 @@ const CodeTabs = ({ children }: CodeTabsProps) => { {tabs.map((tabData, index) => ( -
+          
              {
   const nestedHeadings: NestedHeading[] = [];
 
   headingElements.forEach(heading => {
-    const { innerText: title, id } = heading;
+    const { innerText, id } = heading;
+    const title = innerText
+      .replace(/\n/g, '')
+      .replace(/Beta Feature$/g, '')
+      .trim();
 
     if (heading.nodeName === 'H1' || heading.nodeName === 'H2') {
       nestedHeadings.push({ id, title, items: [] });
@@ -118,7 +122,7 @@ const StyledNav = styled('nav')(
     max-height: calc(100vh - 72px);
     overflow-y: auto;
     top: 16px;
-    
+
     ${theme.breakpoints.between('md', 'lg')} {
       top: 24px;
     }