docs: make widget docs editable (#1782)

* make widget docs editable

* update widget doc frontmatter keys

* improve docs preview

* fix formatting

* fix preview

* add prism highlighting for previews

* fix formatting

* restore cms branch
This commit is contained in:
Shawn Erquhart 2018-10-01 20:00:57 -04:00 committed by GitHub
parent c261163eab
commit 6e453b3f08
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 143 additions and 162 deletions

46
website/.babelrc Normal file
View File

@ -0,0 +1,46 @@
{
presets: [
[
"@babel/preset-env",
{
loose: true,
modules: false,
useBuiltIns: "usage",
shippedProposals: true,
targets: {
browsers: [">0.25%", "not dead"],
},
},
],
[
"@babel/preset-react",
{
useBuiltIns: true,
pragma: "React.createElement",
},
],
],
plugins: [
["prismjs", {
"languages": ["javascript", "css", "markup", "yaml", "json"],
"plugins": ["line-numbers"],
"theme": "tomorrow",
"css": true,
}],
[
"@babel/plugin-proposal-class-properties",
{
loose: true,
},
],
"@babel/plugin-syntax-dynamic-import",
"babel-plugin-macros",
[
"@babel/plugin-transform-runtime",
{
helpers: true,
regenerator: true,
},
],
],
}

View File

@ -1,6 +1,6 @@
--- ---
title: boolean
label: "Boolean" label: "Boolean"
target: boolean
--- ---
The boolean widget translates a toggle switch input to a true/false value. The boolean widget translates a toggle switch input to a true/false value.

View File

@ -1,6 +1,6 @@
--- ---
title: date
label: "Date" label: "Date"
target: date
--- ---
The date widget translates a date picker input to a date string. For saving date and time together, use the datetime widget. The date widget translates a date picker input to a date string. For saving date and time together, use the datetime widget.

View File

@ -1,6 +1,6 @@
--- ---
title: datetime
label: "DateTime" label: "DateTime"
target: datetime
--- ---
The datetime widget translates a datetime picker to a datetime string. For saving the date only, use the date widget. The datetime widget translates a datetime picker to a datetime string. For saving the date only, use the date widget.

View File

@ -1,6 +1,6 @@
--- ---
label: "File" label: "File"
target: file title: file
--- ---
The file widget allows editors to upload a file or select an existing one from the media library. The path to the file will be saved to the field as a string. The file widget allows editors to upload a file or select an existing one from the media library. The path to the file will be saved to the field as a string.

View File

@ -1,6 +1,6 @@
--- ---
label: "Hidden" label: "Hidden"
target: hidden title: hidden
--- ---
Hidden widgets do not display in the UI. In folder collections that allow users to create new items, you will often want to set a default for hidden fields, so they will be set without requiring an input. Hidden widgets do not display in the UI. In folder collections that allow users to create new items, you will often want to set a default for hidden fields, so they will be set without requiring an input.

View File

@ -1,6 +1,6 @@
--- ---
label: "Image" label: "Image"
target: image title: image
--- ---
The image widget allows editors to upload an image or select an existing one from the media library. The path to the image file will be saved to the field as a string. The image widget allows editors to upload an image or select an existing one from the media library. The path to the image file will be saved to the field as a string.

View File

@ -1,6 +1,6 @@
--- ---
label: "List" label: "List"
target: list title: list
--- ---
The list widget allows you to create a repeatable item in the UI which saves as a list of widget values. map a user-provided string with a comma delimiter into a list. You can choose any widget as a child of a list widget—even other lists. The list widget allows you to create a repeatable item in the UI which saves as a list of widget values. map a user-provided string with a comma delimiter into a list. You can choose any widget as a child of a list widget—even other lists.

View File

@ -1,6 +1,6 @@
--- ---
label: "Markdown" label: "Markdown"
target: markdown title: markdown
--- ---
The markdown widget provides a full fledged text editor - which is based on [slate](https://github.com/ianstormtaylor/slate) - that allows users to format text with features such as headings and blockquotes. Users are also allowed to write in markdown by simply flipping a switch. The markdown widget provides a full fledged text editor - which is based on [slate](https://github.com/ianstormtaylor/slate) - that allows users to format text with features such as headings and blockquotes. Users are also allowed to write in markdown by simply flipping a switch.

View File

@ -1,6 +1,6 @@
--- ---
label: "Number" label: "Number"
target: number title: number
--- ---
The number widget uses an HTML number input, saving the value as a string, integer, or floating point number. The number widget uses an HTML number input, saving the value as a string, integer, or floating point number.

View File

@ -1,6 +1,6 @@
--- ---
label: "Object" label: "Object"
target: object title: object
--- ---
The object widget allows you to group multiple widgets together, nested under a single field. You can choose any widget as a child of an object widget—even other objects. The object widget allows you to group multiple widgets together, nested under a single field. You can choose any widget as a child of an object widget—even other objects.

View File

@ -1,6 +1,6 @@
--- ---
label: "Relation" label: "Relation"
target: relation title: relation
--- ---
The relation widget allows you to reference items from another collection. It provides a search input with a list of entries from the collection you're referencing, and the list automatically updates with matched entries based on what you've typed. The relation widget allows you to reference items from another collection. It provides a search input with a list of entries from the collection you're referencing, and the list automatically updates with matched entries based on what you've typed.

View File

@ -1,6 +1,6 @@
--- ---
label: "Select" label: "Select"
target: select title: select
--- ---
The select widget allows you to pick a single string value from a dropdown menu. The select widget allows you to pick a single string value from a dropdown menu.

View File

@ -1,6 +1,6 @@
--- ---
label: "String" label: "String"
target: string title: string
--- ---
The string widget translates a basic text input to a string value. For larger textarea inputs, use the text widget. The string widget translates a basic text input to a string value. For larger textarea inputs, use the text widget.

View File

@ -1,6 +1,6 @@
--- ---
label: "Text" label: "Text"
target: text title: text
--- ---
The text widget takes a multiline text field and saves it as a string. For shorter text inputs, use the string widget. The text widget takes a multiline text field and saves it as a string. For shorter text inputs, use the string widget.

View File

@ -53,7 +53,12 @@ module.exports = {
// prettier-ignore // prettier-ignore
plugins: [ plugins: [
'gatsby-remark-autolink-headers', 'gatsby-remark-autolink-headers',
'gatsby-remark-prismjs' {
resolve: 'gatsby-remark-prismjs',
options: {
noInlineHighlight: true,
},
},
] ]
}, },
}, },

View File

@ -45,6 +45,7 @@
"smooth-scroll": "^14.2.0" "smooth-scroll": "^14.2.0"
}, },
"devDependencies": { "devDependencies": {
"babel-plugin-prismjs": "^1.0.2",
"eslint": "^3.1.1", "eslint": "^3.1.1",
"eslint-plugin-import": "^1.11.1" "eslint-plugin-import": "^1.11.1"
}, },

View File

@ -1,16 +1,45 @@
import React from 'react'; import React from 'react';
import CMS from 'netlify-cms'; import CMS from 'netlify-cms';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import Prism from 'prismjs';
import { BlogPostTemplate } from '../templates/blog-post'; import { BlogPostTemplate } from '../templates/blog-post';
import { DocsTemplate } from '../templates/doc-page'; import { DocsTemplate } from '../templates/doc-page';
import WidgetDoc from '../components/widget-doc';
import Release from '../components/release'; import Release from '../components/release';
import WhatsNew from '../components/whats-new'; import WhatsNew from '../components/whats-new';
import Notification from '../components/notification'; import Notification from '../components/notification';
import '../css/lib/prism.css';
import '../css/imports/docs.css'; import '../css/imports/docs.css';
import '../css/imports/whatsnew.css'; import '../css/imports/whatsnew.css';
import '../css/imports/header.css'; import '../css/imports/header.css';
const withHighlight = WrappedComponent =>
class Highlight extends React.Component {
constructor(props) {
super(props);
this.ref = React.createRef();
}
highlight() {
Prism.highlightAllUnder(this.ref.current);
}
componentDidMount() {
this.highlight();
}
componentDidUpdate() {
this.highlight();
}
render() {
return (
<div className="language-markup" ref={this.ref}>
<WrappedComponent {...this.props} />
</div>
);
}
};
const BlogPostPreview = ({ entry, widgetFor }) => { const BlogPostPreview = ({ entry, widgetFor }) => {
const data = entry.get('data'); const data = entry.get('data');
return ( return (
@ -27,6 +56,10 @@ const DocsPreview = ({ entry, widgetFor }) => (
<DocsTemplate title={entry.getIn(['data', 'title'])} body={widgetFor('body')} /> <DocsTemplate title={entry.getIn(['data', 'title'])} body={widgetFor('body')} />
); );
const WidgetDocPreview = ({ entry, widgetFor }) => (
<WidgetDoc visible={true} label={entry.get('label')} body={widgetFor('body')} />
);
const ReleasePreview = ({ entry }) => ( const ReleasePreview = ({ entry }) => (
<WhatsNew> <WhatsNew>
{entry.getIn(['data', 'updates']).map((release, idx) => ( {entry.getIn(['data', 'updates']).map((release, idx) => (
@ -50,7 +83,8 @@ const NotificationPreview = ({ entry }) =>
</Notification> </Notification>
)); ));
CMS.registerPreviewTemplate('blog', BlogPostPreview); CMS.registerPreviewTemplate('blog', withHighlight(BlogPostPreview));
CMS.registerPreviewTemplate('docs', DocsPreview); CMS.registerPreviewTemplate('docs', withHighlight(DocsPreview));
CMS.registerPreviewTemplate('widget_docs', withHighlight(WidgetDocPreview));
CMS.registerPreviewTemplate('releases', ReleasePreview); CMS.registerPreviewTemplate('releases', ReleasePreview);
CMS.registerPreviewTemplate('notifications', NotificationPreview); CMS.registerPreviewTemplate('notifications', NotificationPreview);

View File

@ -0,0 +1,11 @@
import React from 'react';
import classnames from 'classnames';
const WidgetDoc = ({ visible, label, body, html }) => (
<div className={classnames('widget', { widget_open: visible })}>
<h3>{label}</h3>
{body ? body : <div dangerouslySetInnerHTML={{ __html: html }} />}
</div>
);
export default WidgetDoc;

View File

@ -1,5 +1,6 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import classnames from 'classnames'; import classnames from 'classnames';
import WidgetDoc from './widget-doc';
import '../css/imports/widgets.css'; import '../css/imports/widgets.css';
@ -13,7 +14,7 @@ class Widgets extends Component {
const hash = window.location.hash ? window.location.hash.replace('#', '') : ''; const hash = window.location.hash ? window.location.hash.replace('#', '') : '';
const widgetsContainHash = widgets.edges.some(w => w.node.frontmatter.target === hash); const widgetsContainHash = widgets.edges.some(w => w.node.frontmatter.title === hash);
if (widgetsContainHash) { if (widgetsContainHash) {
return this.setState({ return this.setState({
@ -22,18 +23,18 @@ class Widgets extends Component {
} }
this.setState({ this.setState({
currentWidget: widgets.edges[0].node.frontmatter.target, currentWidget: widgets.edges[0].node.frontmatter.title,
}); });
} }
handleWidgetChange = (event, target) => { handleWidgetChange = (event, title) => {
event.preventDefault(); event.preventDefault();
this.setState( this.setState(
{ {
currentWidget: target, currentWidget: title,
}, },
() => { () => {
window.history.pushState(null, null, `#${target}`); window.history.pushState(null, null, `#${title}`);
}, },
); );
}; };
@ -47,14 +48,14 @@ class Widgets extends Component {
<section className="widgets"> <section className="widgets">
<div className="widgets__cloud"> <div className="widgets__cloud">
{widgets.edges.map(({ node }) => { {widgets.edges.map(({ node }) => {
const { label, target } = node.frontmatter; const { label, title } = node.frontmatter;
return ( return (
<button <button
key={target} key={title}
className={classnames('widgets__item', { className={classnames('widgets__item', {
widgets__item_active: currentWidget === target, widgets__item_active: currentWidget === title,
})} })}
onClick={event => this.handleWidgetChange(event, target)} onClick={event => this.handleWidgetChange(event, title)}
> >
{label} {label}
</button> </button>
@ -63,18 +64,10 @@ class Widgets extends Component {
</div> </div>
<div className="widgets__container"> <div className="widgets__container">
{widgets.edges.map(({ node }) => { {widgets.edges.map(({ node }) => {
const { label, target } = node.frontmatter; const { frontmatter, html } = node;
return ( const { title, label } = frontmatter;
<div const isVisible = currentWidget === title;
key={label} return <WidgetDoc key={label} visible={isVisible} label={label} html={html} />;
className={classnames('widget', {
widget_open: currentWidget === target,
})}
>
<h3>{label}</h3>
<div dangerouslySetInnerHTML={{ __html: node.html }} />
</div>
);
})} })}
</div> </div>
</section> </section>

View File

@ -1,120 +0,0 @@
/**
* prism.js tomorrow night eighties for JavaScript, CoffeeScript, CSS and HTML
* Based on https://github.com/chriskempson/tomorrow-theme
* @author Rose Pritchard
*/
code[class*='language-'],
pre[class*='language-'] {
color: #ccc;
background: none;
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
text-align: left;
white-space: pre;
word-spacing: normal;
word-break: normal;
word-wrap: normal;
line-height: 1.5;
-moz-tab-size: 4;
-o-tab-size: 4;
tab-size: 4;
-webkit-hyphens: none;
-moz-hyphens: none;
-ms-hyphens: none;
hyphens: none;
}
/* Code blocks */
pre[class*='language-'] {
padding: 1em;
margin: 0.5em 0;
overflow: auto;
}
:not(pre) > code[class*='language-'],
pre[class*='language-'] {
background: #2d2d2d;
}
/* Inline code */
:not(pre) > code[class*='language-'] {
padding: 0.1em;
border-radius: 0.3em;
white-space: normal;
}
.token.comment,
.token.block-comment,
.token.prolog,
.token.doctype,
.token.cdata {
color: #999;
}
.token.punctuation {
color: #ccc;
}
.token.tag,
.token.attr-name,
.token.namespace,
.token.deleted {
color: #e2777a;
}
.token.function-name {
color: #6196cc;
}
.token.boolean,
.token.number,
.token.function {
color: #f08d49;
}
.token.property,
.token.class-name,
.token.constant,
.token.symbol {
color: #f8c555;
}
.token.selector,
.token.important,
.token.atrule,
.token.keyword,
.token.builtin {
color: #cc99cd;
}
.token.string,
.token.char,
.token.attr-value,
.token.regex,
.token.variable {
color: #7ec699;
}
.token.operator,
.token.entity,
.token.url {
color: #67cdcc;
}
.token.important,
.token.bold {
font-weight: bold;
}
.token.italic {
font-style: italic;
}
.token.entity {
cursor: help;
}
.token.inserted {
color: green;
}

View File

@ -1,6 +1,7 @@
import React from 'react'; import React from 'react';
import Helmet from 'react-helmet'; import Helmet from 'react-helmet';
import { graphql } from 'gatsby'; import { graphql } from 'gatsby';
import 'prismjs/themes/prism-tomorrow.css';
import Layout from '../components/layout'; import Layout from '../components/layout';
import EditLink from '../components/edit-link'; import EditLink from '../components/edit-link';
@ -8,7 +9,6 @@ import Widgets from '../components/widgets';
import DocsNav from '../components/docs-nav'; import DocsNav from '../components/docs-nav';
import MobileNav from '../components/mobile-nav'; import MobileNav from '../components/mobile-nav';
import '../css/lib/prism.css';
import '../css/imports/docs.css'; import '../css/imports/docs.css';
const toMenu = (menu, nav) => const toMenu = (menu, nav) =>
@ -124,8 +124,8 @@ export const pageQuery = graphql`
edges { edges {
node { node {
frontmatter { frontmatter {
title
label label
target
} }
html html
} }

View File

@ -1,7 +1,6 @@
backend: backend:
name: github name: github
repo: netlify/netlify-cms repo: netlify/netlify-cms
branch: master
squash_merges: true squash_merges: true
publish_mode: editorial_workflow publish_mode: editorial_workflow
@ -19,6 +18,14 @@ collections: # A list of collections the CMS should be able to edit
- {label: "Group", name: "group", widget: "string"} - {label: "Group", name: "group", widget: "string"}
- {label: "Weight", name: "weight", widget: "number"} - {label: "Weight", name: "weight", widget: "number"}
- {label: "Body", name: "body", widget: "markdown"} - {label: "Body", name: "body", widget: "markdown"}
- name: "widget_docs" # Used in routes, ie.: /admin/collections/:slug/edit
label: "Widget Docs" # Used in the UI, ie.: "New Post"
folder: "website/content/docs/widgets" # The path to the folder where the documents are stored
create: true # Allow users to create new documents in this collection
fields: # The fields each document in this collection have
- {label: "Name", name: "title"}
- {label: "Label", name: "label"}
- {label: "Body", name: "body", widget: "markdown"}
- name: "blog" - name: "blog"
label: "Blog" label: "Blog"
label_singular: "Post" label_singular: "Post"

View File

@ -1520,6 +1520,10 @@ babel-plugin-macros@^2.0.0, babel-plugin-macros@^2.4.0:
dependencies: dependencies:
cosmiconfig "^5.0.5" cosmiconfig "^5.0.5"
babel-plugin-prismjs@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/babel-plugin-prismjs/-/babel-plugin-prismjs-1.0.2.tgz#837bf6b32168b3ba624c054fc755946deb1b63fa"
babel-plugin-remove-graphql-queries@^2.0.2-rc.2: babel-plugin-remove-graphql-queries@^2.0.2-rc.2:
version "2.0.2-rc.2" version "2.0.2-rc.2"
resolved "https://registry.yarnpkg.com/babel-plugin-remove-graphql-queries/-/babel-plugin-remove-graphql-queries-2.0.2-rc.2.tgz#85ae3b8d46ebaa27e128d15321d67acd409d95e2" resolved "https://registry.yarnpkg.com/babel-plugin-remove-graphql-queries/-/babel-plugin-remove-graphql-queries-2.0.2-rc.2.tgz#85ae3b8d46ebaa27e128d15321d67acd409d95e2"