feat: upgrade to Emotion 10 (#2166)

This commit is contained in:
Shawn Erquhart
2019-03-15 10:19:57 -04:00
committed by GitHub
parent 7d6992e464
commit ccef446d72
109 changed files with 4672 additions and 3875 deletions

View File

@ -19,44 +19,44 @@
],
"license": "MIT",
"dependencies": {
"ajv": "^6.4.0",
"ajv-errors": "^1.0.0",
"copy-text-to-clipboard": "^1.0.4",
"@emotion/core": "^10.0.9",
"@emotion/styled": "^10.0.9",
"ajv": "^6.10.0",
"ajv-errors": "^1.0.1",
"copy-text-to-clipboard": "^2.0.0",
"diacritics": "^1.3.0",
"emotion": "^9.2.6",
"fuzzy": "^0.1.1",
"gotrue-js": "^0.9.15",
"gray-matter": "^4.0.1",
"gotrue-js": "^0.9.24",
"gray-matter": "^4.0.2",
"history": "^4.7.2",
"immutable": "^3.7.6",
"js-base64": "^2.1.9",
"js-yaml": "^3.10.0",
"js-base64": "^2.5.1",
"js-yaml": "^3.12.2",
"jwt-decode": "^2.1.0",
"lodash": "^4.17.10",
"moment": "^2.11.2",
"lodash": "^4.17.11",
"moment": "^2.24.0",
"netlify-cms-editor-component-image": "^2.2.0",
"netlify-cms-lib-auth": "^2.0.5",
"netlify-cms-lib-util": "^2.1.2",
"netlify-cms-ui-default": "^2.4.1-alpha.0",
"node-polyglot": "^2.3.0",
"prop-types": "^15.5.10",
"react": "^16.8.1",
"react-dnd": "^7.0.0",
"react-dnd-html5-backend": "^7.0.0",
"react-dom": "^16.8.1",
"react-emotion": "^9.2.5",
"react-frame-component": "^4.0.2",
"react-hot-loader": "^4.0.0",
"prop-types": "^15.7.2",
"react": "^16.8.4",
"react-dnd": "^7.3.2",
"react-dnd-html5-backend": "^7.2.0",
"react-dom": "^16.8.4",
"react-frame-component": "^4.1.0",
"react-hot-loader": "^4.8.0",
"react-immutable-proptypes": "^2.1.0",
"react-is": "16.3.1",
"react-modal": "^3.1.5",
"react-is": "16.8.4",
"react-modal": "^3.8.1",
"react-polyglot": "^0.2.6",
"react-redux": "^5.1.1",
"react-router-dom": "^4.2.2",
"react-router-redux": "^5.0.0-alpha.8",
"react-scroll-sync": "^0.6.0",
"react-sortable-hoc": "^0.6.8",
"react-split-pane": "^0.1.82",
"react-split-pane": "^0.1.85",
"react-topbar-progress-indicator": "^2.0.0",
"react-waypoint": "^8.1.0",
"redux": "^4.0.1",
@ -68,13 +68,13 @@
"toml-j0.4": "^1.1.1",
"tomlify-j0.4": "^3.0.0-alpha.0",
"url": "^0.11.0",
"what-input": "^5.0.3"
"what-input": "^5.1.4"
},
"devDependencies": {
"cross-env": "^5.2.0",
"css-loader": "^1.0.0",
"css-loader": "^2.1.1",
"to-string-loader": "^1.1.5",
"webpack": "^4.16.1",
"webpack-cli": "^3.1.0"
"webpack": "^4.29.6",
"webpack-cli": "^3.2.3"
}
}

View File

@ -3,7 +3,6 @@ import { Map } from 'immutable';
import { stripIndent } from 'common-tags';
import moment from 'moment';
import fuzzy from 'fuzzy';
import { localForage } from 'netlify-cms-lib-util';
import { resolveFormat } from 'Formats/formats';
import { selectIntegration } from 'Reducers/integrations';
import {
@ -19,7 +18,7 @@ import {
import { createEntry } from 'ValueObjects/Entry';
import { sanitizeSlug } from 'Lib/urlHelper';
import { getBackend } from 'Lib/registry';
import { Cursor, CURSOR_COMPATIBILITY_SYMBOL } from 'netlify-cms-lib-util';
import { localForage, Cursor, CURSOR_COMPATIBILITY_SYMBOL } from 'netlify-cms-lib-util';
import { EDITORIAL_WORKFLOW, status } from 'Constants/publishModes';
class LocalStorageAuthStore {

View File

@ -8,6 +8,7 @@ import store from 'Redux';
import { mergeConfig } from 'Actions/config';
import { getPhrases } from 'Constants/defaultPhrases';
import { I18n } from 'react-polyglot';
import { GlobalStyles } from 'netlify-cms-ui-default';
import { ErrorBoundary } from 'UI';
import App from 'App/App';
import 'EditorWidgets';
@ -62,15 +63,18 @@ function bootstrap(opts = {}) {
* Create connected root component.
*/
const Root = () => (
<I18n locale={'en'} messages={getPhrases()}>
<ErrorBoundary showBackup>
<Provider store={store}>
<ConnectedRouter history={history}>
<Route component={App} />
</ConnectedRouter>
</Provider>
</ErrorBoundary>
</I18n>
<>
<GlobalStyles />
<I18n locale={'en'} messages={getPhrases()}>
<ErrorBoundary showBackup>
<Provider store={store}>
<ConnectedRouter history={history}>
<Route component={App} />
</ConnectedRouter>
</Provider>
</ErrorBoundary>
</I18n>
</>
);
/**

View File

@ -3,7 +3,7 @@ import React from 'react';
import { hot } from 'react-hot-loader';
import { translate } from 'react-polyglot';
import ImmutablePropTypes from 'react-immutable-proptypes';
import styled from 'react-emotion';
import styled from '@emotion/styled';
import { connect } from 'react-redux';
import { Route, Switch, Redirect } from 'react-router-dom';
import { Notifs } from 'redux-notifications';

View File

@ -1,7 +1,9 @@
/** @jsx jsx */
import PropTypes from 'prop-types';
import React from 'react';
import ImmutablePropTypes from 'react-immutable-proptypes';
import styled, { css } from 'react-emotion';
import styled from '@emotion/styled';
import { jsx, css } from '@emotion/core';
import { translate } from 'react-polyglot';
import { NavLink } from 'react-router-dom';
import {
@ -22,15 +24,20 @@ const styles = {
`,
};
const AppHeader = styled.header`
${shadows.dropMain};
position: sticky;
width: 100%;
top: 0;
background-color: ${colors.foreground};
z-index: 300;
height: ${lengths.topBarHeight};
`;
const AppHeader = props => (
<header
css={css`
${shadows.dropMain};
position: sticky;
width: 100%;
top: 0;
background-color: ${colors.foreground};
z-index: 300;
height: ${lengths.topBarHeight};
`}
{...props}
/>
);
const AppHeaderContent = styled.div`
display: flex;

View File

@ -1,5 +1,5 @@
import React from 'react';
import styled from 'react-emotion';
import styled from '@emotion/styled';
import { translate } from 'react-polyglot';
import { lengths } from 'netlify-cms-ui-default';
import PropTypes from 'prop-types';

View File

@ -1,7 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import ImmutablePropTypes from 'react-immutable-proptypes';
import styled from 'react-emotion';
import styled from '@emotion/styled';
import { connect } from 'react-redux';
import { lengths } from 'netlify-cms-ui-default';
import { getNewEntryUrl } from 'Lib/urlHelper';

View File

@ -1,6 +1,6 @@
import PropTypes from 'prop-types';
import React from 'react';
import styled from 'react-emotion';
import styled from '@emotion/styled';
import { translate } from 'react-polyglot';
import { Link } from 'react-router-dom';
import { Icon, components, buttons, shadows, colors } from 'netlify-cms-ui-default';

View File

@ -1,5 +1,5 @@
import React from 'react';
import styled from 'react-emotion';
import styled from '@emotion/styled';
import { Link } from 'react-router-dom';
import { resolvePath } from 'netlify-cms-lib-util';
import { colors, colorsRaw, components, lengths } from 'netlify-cms-ui-default';

View File

@ -1,7 +1,7 @@
import PropTypes from 'prop-types';
import React from 'react';
import ImmutablePropTypes from 'react-immutable-proptypes';
import styled from 'react-emotion';
import styled from '@emotion/styled';
import Waypoint from 'react-waypoint';
import { Map } from 'immutable';
import { Cursor } from 'netlify-cms-lib-util';

View File

@ -1,7 +1,8 @@
import React from 'react';
import PropTypes from 'prop-types';
import ImmutablePropTypes from 'react-immutable-proptypes';
import styled, { css } from 'react-emotion';
import styled from '@emotion/styled';
import { css } from '@emotion/core';
import { translate } from 'react-polyglot';
import { NavLink } from 'react-router-dom';
import { Icon, components, colors, colorsRaw, lengths } from 'netlify-cms-ui-default';

View File

@ -31,8 +31,7 @@ import { loadDeployPreview } from 'Actions/deploys';
import { deserializeValues } from 'Lib/serializeEntryValues';
import { selectEntry, selectUnpublishedEntry, selectDeployPreview, getAsset } from 'Reducers';
import { selectFields } from 'Reducers/collections';
import { status } from 'Constants/publishModes';
import { EDITORIAL_WORKFLOW } from 'Constants/publishModes';
import { status, EDITORIAL_WORKFLOW } from 'Constants/publishModes';
import EditorInterface from './EditorInterface';
import withWorkflow from './withWorkflow';

View File

@ -1,16 +1,17 @@
/** @jsx jsx */
import React from 'react';
import PropTypes from 'prop-types';
import ImmutablePropTypes from 'react-immutable-proptypes';
import { translate } from 'react-polyglot';
import styled, { css, cx } from 'react-emotion';
import { jsx, ClassNames, Global, css as coreCss } from '@emotion/core';
import styled from '@emotion/styled';
import { partial, uniqueId } from 'lodash';
import { connect } from 'react-redux';
import { colors, colorsRaw, transitions, lengths, borders } from 'netlify-cms-ui-default';
import { resolveWidget, getEditorComponents } from 'Lib/registry';
import { clearFieldErrors } from 'Actions/entries';
import { clearFieldErrors, loadEntry } from 'Actions/entries';
import { addAsset } from 'Actions/media';
import { query, clearSearch } from 'Actions/search';
import { loadEntry } from 'Actions/entries';
import {
openMediaLibrary,
removeInsertedMedia,
@ -20,8 +21,13 @@ import {
import { getAsset } from 'Reducers';
import Widget from './Widget';
const styles = {
label: css`
/**
* This is a necessary bridge as we are still passing classnames to widgets
* for styling. Once that changes we can stop storing raw style strings like
* this.
*/
const styleStrings = {
label: `
color: ${colors.controlLabel};
background-color: ${colors.textFieldBorder};
display: inline-block;
@ -55,15 +61,15 @@ const styles = {
background-color: #fff;
}
`,
labelActive: css`
labelActive: `
background-color: ${colors.active};
color: ${colors.textLight};
`,
labelError: css`
labelError: `
background-color: ${colors.errorText};
color: ${colorsRaw.white};
`,
widget: css`
widget: `
display: block;
width: 100%;
padding: ${lengths.inputPadding};
@ -85,10 +91,10 @@ const styles = {
height: 58px;
}
`,
widgetActive: css`
widgetActive: `
border-color: ${colors.active};
`,
widgetError: css`
widgetError: `
border-color: ${colors.errorText};
`,
};
@ -96,7 +102,7 @@ const styles = {
const ControlContainer = styled.div`
margin-top: 16px;
&:first-child {
&:first-of-type {
margin-top: 36px;
}
`;
@ -117,8 +123,8 @@ export const ControlHint = styled.p`
margin-bottom: 0;
padding: 3px 0;
font-size: 12px;
color: ${({ active, error }) =>
error ? colors.errorText : active ? colors.active : colors.controlLabel};
color: ${props =>
props.error ? colors.errorText : props.active ? colors.active : colors.controlLabel};
transition: color ${transitions.main};
`;
@ -191,75 +197,108 @@ class EditorControl extends React.Component {
const metadata = fieldsMetaData && fieldsMetaData.get(fieldName);
const errors = fieldsErrors && fieldsErrors.get(this.uniqueFieldId);
return (
<ControlContainer>
<ControlErrorsList>
{errors &&
errors.map(
error =>
error.message &&
typeof error.message === 'string' && (
<li key={error.message.trim().replace(/[^a-z0-9]+/gi, '-')}>{error.message}</li>
),
<ClassNames>
{({ css, cx }) => (
<ControlContainer>
{widget.globalStyles && <Global styles={coreCss`${widget.globalStyles}`} />}
<ControlErrorsList>
{errors &&
errors.map(
error =>
error.message &&
typeof error.message === 'string' && (
<li key={error.message.trim().replace(/[^a-z0-9]+/gi, '-')}>
{error.message}
</li>
),
)}
</ControlErrorsList>
<label
className={cx(
css`
${styleStrings.label};
`,
this.state.styleActive &&
css`
${styleStrings.labelActive};
`,
!!errors &&
css`
${styleStrings.labelError};
`,
)}
htmlFor={this.uniqueFieldId}
>
{`${field.get('label', field.get('name'))}${isFieldOptional ? ' (optional)' : ''}`}
</label>
<Widget
classNameWrapper={cx(
css`
${styleStrings.widget};
`,
{
[css`
${styleStrings.widgetActive};
`]: this.state.styleActive,
},
{
[css`
${styleStrings.widgetError};
`]: !!errors,
},
)}
classNameWidget={css`
${styleStrings.widget};
`}
classNameWidgetActive={css`
${styleStrings.widgetActive};
`}
classNameLabel={css`
${styleStrings.label};
`}
classNameLabelActive={css`
${styleStrings.labelActive};
`}
controlComponent={widget.control}
field={field}
uniqueFieldId={this.uniqueFieldId}
value={value}
mediaPaths={mediaPaths}
metadata={metadata}
onChange={(newValue, newMetadata) => onChange(fieldName, newValue, newMetadata)}
onValidate={onValidate && partial(onValidate, this.uniqueFieldId)}
onOpenMediaLibrary={openMediaLibrary}
onClearMediaControl={clearMediaControl}
onRemoveMediaControl={removeMediaControl}
onRemoveInsertedMedia={removeInsertedMedia}
onAddAsset={addAsset}
getAsset={boundGetAsset}
hasActiveStyle={this.state.styleActive}
setActiveStyle={() => this.setState({ styleActive: true })}
setInactiveStyle={() => this.setState({ styleActive: false })}
resolveWidget={resolveWidget}
getEditorComponents={getEditorComponents}
ref={processControlRef && partial(processControlRef, field)}
controlRef={controlRef}
editorControl={ConnectedEditorControl}
query={query}
loadEntry={loadEntry}
queryHits={queryHits}
clearSearch={clearSearch}
clearFieldErrors={clearFieldErrors}
isFetching={isFetching}
fieldsErrors={fieldsErrors}
onValidateObject={onValidateObject}
t={t}
/>
{fieldHint && (
<ControlHint active={this.state.styleActive} error={!!errors}>
{fieldHint}
</ControlHint>
)}
</ControlErrorsList>
<label
className={cx(
styles.label,
{ [styles.labelActive]: this.state.styleActive },
{ [styles.labelError]: !!errors },
)}
htmlFor={this.uniqueFieldId}
>
{`${field.get('label', field.get('name'))}${isFieldOptional ? ' (optional)' : ''}`}
</label>
<Widget
classNameWrapper={cx(
styles.widget,
{ [styles.widgetActive]: this.state.styleActive },
{ [styles.widgetError]: !!errors },
)}
classNameWidget={styles.widget}
classNameWidgetActive={styles.widgetActive}
classNameLabel={styles.label}
classNameLabelActive={styles.labelActive}
controlComponent={widget.control}
field={field}
uniqueFieldId={this.uniqueFieldId}
value={value}
mediaPaths={mediaPaths}
metadata={metadata}
onChange={(newValue, newMetadata) => onChange(fieldName, newValue, newMetadata)}
onValidate={onValidate && partial(onValidate, this.uniqueFieldId)}
onOpenMediaLibrary={openMediaLibrary}
onClearMediaControl={clearMediaControl}
onRemoveMediaControl={removeMediaControl}
onRemoveInsertedMedia={removeInsertedMedia}
onAddAsset={addAsset}
getAsset={boundGetAsset}
hasActiveStyle={this.state.styleActive}
setActiveStyle={() => this.setState({ styleActive: true })}
setInactiveStyle={() => this.setState({ styleActive: false })}
resolveWidget={resolveWidget}
getEditorComponents={getEditorComponents}
ref={processControlRef && partial(processControlRef, field)}
controlRef={controlRef}
editorControl={ConnectedEditorControl}
query={query}
loadEntry={loadEntry}
queryHits={queryHits}
clearSearch={clearSearch}
clearFieldErrors={clearFieldErrors}
isFetching={isFetching}
fieldsErrors={fieldsErrors}
onValidateObject={onValidateObject}
t={t}
/>
{fieldHint && (
<ControlHint active={this.state.styleActive} error={!!errors}>
{fieldHint}
</ControlHint>
</ControlContainer>
)}
</ControlContainer>
</ClassNames>
);
}
}

View File

@ -1,17 +1,14 @@
import React from 'react';
import PropTypes from 'prop-types';
import ImmutablePropTypes from 'react-immutable-proptypes';
import styled from 'react-emotion';
import EditorControl, { ControlHint } from './EditorControl';
import styled from '@emotion/styled';
import EditorControl from './EditorControl';
const ControlPaneContainer = styled.div`
max-width: 800px;
margin: 0 auto;
padding-bottom: 16px;
p:not(${ControlHint}) {
font-size: 16px;
}
font-size: 16px;
`;
export default class ControlPane extends React.Component {
@ -56,21 +53,20 @@ export default class ControlPane extends React.Component {
return (
<ControlPaneContainer>
{fields.map(
(field, i) =>
field.get('widget') === 'hidden' ? null : (
<EditorControl
key={i}
field={field}
value={entry.getIn(['data', field.get('name')])}
fieldsMetaData={fieldsMetaData}
fieldsErrors={fieldsErrors}
onChange={onChange}
onValidate={onValidate}
processControlRef={this.controlRef.bind(this)}
controlRef={this.controlRef}
/>
),
{fields.map((field, i) =>
field.get('widget') === 'hidden' ? null : (
<EditorControl
key={i}
field={field}
value={entry.getIn(['data', field.get('name')])}
fieldsMetaData={fieldsMetaData}
fieldsErrors={fieldsErrors}
onChange={onChange}
onValidate={onValidate}
processControlRef={this.controlRef.bind(this)}
controlRef={this.controlRef}
/>
),
)}
</ControlPaneContainer>
);

View File

@ -1,7 +1,8 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import ImmutablePropTypes from 'react-immutable-proptypes';
import styled, { css, injectGlobal } from 'react-emotion';
import { css, Global } from '@emotion/core';
import styled from '@emotion/styled';
import SplitPane from 'react-split-pane';
import { colors, colorsRaw, components, transitions } from 'netlify-cms-ui-default';
import { ScrollSync, ScrollSyncPane } from 'react-scroll-sync';
@ -25,33 +26,33 @@ const styles = {
`,
};
injectGlobal`
/**
* React Split Pane
*/
.Resizer.vertical {
width: 21px;
cursor: col-resize;
position: relative;
transition: background-color ${transitions.main};
const ReactSplitPaneGlobalStyles = () => (
<Global
styles={css`
.Resizer.vertical {
width: 21px;
cursor: col-resize;
position: relative;
transition: background-color ${transitions.main};
&:before {
content: '';
width: 1px;
height: 100%;
position: relative;
left: 10px;
background-color: ${colors.textFieldBorder};
display: block;
}
&:before {
content: '';
width: 1px;
height: 100%;
position: relative;
left: 10px;
background-color: ${colors.textFieldBorder};
display: block;
}
&:hover,
&:active {
background-color: ${colorsRaw.GrayLight};
}
}
`;
&:hover,
&:active {
background-color: ${colorsRaw.GrayLight};
}
}
`}
/>
);
const StyledSplitPane = styled(SplitPane)`
${styles.splitPane};
@ -195,6 +196,7 @@ class EditorInterface extends Component {
const editorWithPreview = (
<ScrollSync enabled={this.state.scrollSyncEnabled}>
<div>
<ReactSplitPaneGlobalStyles />
<StyledSplitPane
maxSize={-100}
defaultSize="50%"

View File

@ -1,7 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import ImmutablePropTypes from 'react-immutable-proptypes';
import styled from 'react-emotion';
import styled from '@emotion/styled';
function isVisible(field) {
return field.get('widget') !== 'hidden';

View File

@ -1,6 +1,6 @@
import PropTypes from 'prop-types';
import React from 'react';
import styled from 'react-emotion';
import styled from '@emotion/styled';
import { List, Map } from 'immutable';
import ImmutablePropTypes from 'react-immutable-proptypes';
import Frame from 'react-frame-component';

View File

@ -1,6 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
import styled from 'react-emotion';
import styled from '@emotion/styled';
import { Icon, colors, colorsRaw, shadows, buttons } from 'netlify-cms-ui-default';
const EditorToggleButton = styled.button`

View File

@ -1,7 +1,8 @@
import React from 'react';
import PropTypes from 'prop-types';
import ImmutablePropTypes from 'react-immutable-proptypes';
import styled, { css } from 'react-emotion';
import { css } from '@emotion/core';
import styled from '@emotion/styled';
import { translate } from 'react-polyglot';
import { Map } from 'immutable';
import { Link } from 'react-router-dom';

View File

@ -1,6 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
import styled from 'react-emotion';
import styled from '@emotion/styled';
import { colors } from 'netlify-cms-ui-default';
const EmptyMessageContainer = styled.div`

View File

@ -1,6 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import styled, { css } from 'react-emotion';
import { css } from '@emotion/core';
import styled from '@emotion/styled';
import { FileUploadButton } from 'UI';
import { buttons, shadows } from 'netlify-cms-ui-default';

View File

@ -1,7 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import ImmutablePropTypes from 'react-immutable-proptypes';
import styled from 'react-emotion';
import styled from '@emotion/styled';
import { colors, borders, lengths, shadows, effects } from 'netlify-cms-ui-default';
const IMAGE_HEIGHT = 160;

View File

@ -1,6 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
import styled from 'react-emotion';
import styled from '@emotion/styled';
import Waypoint from 'react-waypoint';
import MediaLibraryCard from './MediaLibraryCard';
import { Map } from 'immutable';

View File

@ -1,6 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
import styled from 'react-emotion';
import styled from '@emotion/styled';
import { Icon, shadows, colors, buttons } from 'netlify-cms-ui-default';
const CloseButton = styled.button`

View File

@ -1,6 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
import styled from 'react-emotion';
import styled from '@emotion/styled';
import { isEmpty } from 'lodash';
import { translate } from 'react-polyglot';
import { Modal } from 'UI';

View File

@ -1,6 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
import styled from 'react-emotion';
import styled from '@emotion/styled';
import { Icon, lengths, colors } from 'netlify-cms-ui-default';
const SearchContainer = styled.div`

View File

@ -1,42 +1,42 @@
import React from 'react';
import PropTypes from 'prop-types';
import { translate } from 'react-polyglot';
import styled, { css } from 'react-emotion';
import styled from '@emotion/styled';
import copyToClipboard from 'copy-text-to-clipboard';
import { localForage } from 'netlify-cms-lib-util';
import { buttons, colors } from 'netlify-cms-ui-default';
const ISSUE_URL = 'https://github.com/netlify/netlify-cms/issues/new?template=bug_report.md';
const styles = {
errorBoundary: css`
padding: 40px;
const ErrorBoundaryContainer = styled.div`
padding: 40px;
h1 {
font-size: 28px;
}
h1 {
font-size: 28px;
color: ${colors.text};
}
h2 {
font-size: 20px;
}
h2 {
font-size: 20px;
}
strong {
color: ${colors.textLead};
font-weight: 500;
}
strong {
color: ${colors.textLead};
font-weight: 500;
}
hr {
width: 200px;
margin: 30px 0;
border: 0;
height: 1px;
background-color: ${colors.text};
}
`,
errorText: css`
color: ${colors.errorText};
`,
};
hr {
width: 200px;
margin: 30px 0;
border: 0;
height: 1px;
background-color: ${colors.text};
}
a {
color: ${colors.text};
}
`;
const CopyButton = styled.button`
${buttons.button};
@ -104,16 +104,11 @@ class ErrorBoundary extends React.Component {
return this.props.children;
}
return (
<div className={styles.errorBoundary}>
<h1 className={styles.errorBoundaryText}>{t('ui.errorBoundary.title')}</h1>
<ErrorBoundaryContainer>
<h1>{t('ui.errorBoundary.title')}</h1>
<p>
<span>{t('ui.errorBoundary.details')}</span>
<a
href={ISSUE_URL}
target="_blank"
rel="noopener noreferrer"
className={styles.errorBoundaryText}
>
<a href={ISSUE_URL} target="_blank" rel="noopener noreferrer">
{t('ui.errorBoundary.reportIt')}
</a>
</p>
@ -121,7 +116,7 @@ class ErrorBoundary extends React.Component {
<h2>{t('ui.errorBoundary.detailsHeading')}</h2>
<p>{errorMessage}</p>
{backup && showBackup && <RecoveredEntry entry={backup} t={t} />}
</div>
</ErrorBoundaryContainer>
);
}
}

View File

@ -1,17 +1,21 @@
import React from 'react';
import PropTypes from 'prop-types';
import { css, cx, injectGlobal } from 'react-emotion';
import { css, Global, ClassNames } from '@emotion/core';
import ReactModal from 'react-modal';
import { transitions, shadows, lengths } from 'netlify-cms-ui-default';
injectGlobal`
.ReactModal__Body--open {
overflow: hidden;
}
`;
const ReactModalGlobalStyles = () => (
<Global
styles={css`
.ReactModal__Body--open {
overflow: hidden;
}
`}
/>
);
const styles = {
modalBody: css`
const styleStrings = {
modalBody: `
${shadows.dropDeep};
background-color: #fff;
border-radius: ${lengths.borderRadius};
@ -24,7 +28,7 @@ const styles = {
outline: none;
}
`,
overlay: css`
overlay: `
z-index: 99999;
position: fixed;
top: 0;
@ -38,11 +42,11 @@ const styles = {
background-color: rgba(0, 0, 0, 0);
transition: background-color ${transitions.main}, opacity ${transitions.main};
`,
overlayAfterOpen: css`
overlayAfterOpen: `
background-color: rgba(0, 0, 0, 0.6);
opacity: 1;
`,
overlayBeforeClose: css`
overlayBeforeClose: `
background-color: rgba(0, 0, 0, 0);
opacity: 0;
`,
@ -63,23 +67,41 @@ export class Modal extends React.Component {
render() {
const { isOpen, children, className, onClose } = this.props;
return (
<ReactModal
isOpen={isOpen}
onRequestClose={onClose}
closeTimeoutMS={300}
className={{
base: cx(styles.modalBody, className),
afterOpen: '',
beforeClose: '',
}}
overlayClassName={{
base: styles.overlay,
afterOpen: styles.overlayAfterOpen,
beforeClose: styles.overlayBeforeClose,
}}
>
{children}
</ReactModal>
<>
<ReactModalGlobalStyles />
<ClassNames>
{({ css, cx }) => (
<ReactModal
isOpen={isOpen}
onRequestClose={onClose}
closeTimeoutMS={300}
className={{
base: cx(
css`
${styleStrings.modalBody};
`,
className,
),
afterOpen: '',
beforeClose: '',
}}
overlayClassName={{
base: css`
${styleStrings.overlay};
`,
afterOpen: css`
${styleStrings.overlayAfterOpen};
`,
beforeClose: css`
${styleStrings.overlayBeforeClose};
`,
}}
>
{children}
</ReactModal>
)}
</ClassNames>
</>
);
}
}

View File

@ -1,6 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import styled, { css } from 'react-emotion';
import { css } from '@emotion/core';
import styled from '@emotion/styled';
import { translate } from 'react-polyglot';
import { Icon, Dropdown, DropdownItem, DropdownButton, colors } from 'netlify-cms-ui-default';
import { stripProtocol } from 'Lib/urlHelper';

View File

@ -1,18 +1,24 @@
/** @jsx jsx */
// eslint-disable-next-line no-unused-vars
import React from 'react';
import PropTypes from 'prop-types';
import { css, injectGlobal, cx } from 'react-emotion';
import { jsx, css, Global } from '@emotion/core';
import { translate } from 'react-polyglot';
import reduxNotificationsStyles from 'redux-notifications/lib/styles.css';
import { shadows, colors, lengths } from 'netlify-cms-ui-default';
injectGlobal`
${reduxNotificationsStyles};
const ReduxNotificationsGlobalStyles = () => (
<Global
styles={css`
${reduxNotificationsStyles};
.notif__container {
z-index: 10000;
white-space: pre-wrap;
}
`;
.notif__container {
z-index: 10000;
white-space: pre-wrap;
}
`}
/>
);
const styles = {
toast: css`
@ -43,7 +49,8 @@ const styles = {
};
const Toast = ({ kind, message, t }) => (
<div className={cx(styles.toast, styles[kind])}>
<div css={[styles.toast, styles[kind]]}>
<ReduxNotificationsGlobalStyles />
{t(message.key, { details: message.details })}
</div>
);

View File

@ -1,7 +1,7 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import ImmutablePropTypes from 'react-immutable-proptypes';
import styled from 'react-emotion';
import styled from '@emotion/styled';
import { OrderedMap } from 'immutable';
import { translate } from 'react-polyglot';
import { connect } from 'react-redux';

View File

@ -1,6 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import styled, { css } from 'react-emotion';
import { css } from '@emotion/core';
import styled from '@emotion/styled';
import { translate } from 'react-polyglot';
import { Link } from 'react-router-dom';
import { components, colors, colorsRaw, transitions, buttons } from 'netlify-cms-ui-default';

View File

@ -1,7 +1,9 @@
/** @jsx jsx */
import PropTypes from 'prop-types';
import React from 'react';
import ImmutablePropTypes from 'react-immutable-proptypes';
import styled, { css, cx } from 'react-emotion';
import { jsx, css } from '@emotion/core';
import styled from '@emotion/styled';
import moment from 'moment';
import { translate } from 'react-polyglot';
import { colors, lengths } from 'netlify-cms-ui-default';
@ -16,22 +18,16 @@ const WorkflowListContainer = styled.div`
`;
const styles = {
column: css`
margin: 0 20px;
transition: background-color 0.5s ease;
border: 2px dashed transparent;
border-radius: 4px;
position: relative;
&:first-child {
margin-left: 0;
}
&:last-child {
margin-right: 0;
}
&:not(:first-child):not(:last-child) {
columnPosition: idx =>
(idx === 0 &&
css`
margin-left: 0;
`) ||
(idx === 2 &&
css`
margin-right: 0;
`) ||
css`
&:before,
&:after {
content: '';
@ -50,7 +46,14 @@ const styles = {
&:after {
right: -23px;
}
}
`,
column: css`
margin: 0 20px;
transition: background-color 0.5s ease;
border: 2px dashed transparent;
border-radius: 4px;
position: relative;
height: 100%;
`,
columnHovered: css`
border-color: ${colors.active};
@ -140,11 +143,12 @@ class WorkflowList extends React.Component {
this.props.handlePublish(collection, slug);
};
// eslint-disable-next-line react/display-name
renderColumns = (entries, column) => {
if (!entries) return null;
if (!column) {
return entries.entrySeq().map(([currColumn, currEntries]) => (
return entries.entrySeq().map(([currColumn, currEntries], idx) => (
<DropTarget
namespace={DNDNamespace}
key={currColumn}
@ -152,16 +156,24 @@ class WorkflowList extends React.Component {
>
{(connect, { isHovered }) =>
connect(
<div className={cx(styles.column, { [styles.columnHovered]: isHovered })}>
<ColumnHeader name={currColumn}>
{getColumnHeaderText(currColumn, this.props.t)}
</ColumnHeader>
<ColumnCount>
{this.props.t('workflow.workflowList.currentEntries', {
smart_count: currEntries.size,
})}
</ColumnCount>
{this.renderColumns(currEntries, currColumn)}
<div style={{ height: '100%' }}>
<div
css={[
styles.column,
styles.columnPosition(idx),
isHovered && styles.columnHovered,
]}
>
<ColumnHeader name={currColumn}>
{getColumnHeaderText(currColumn, this.props.t)}
</ColumnHeader>
<ColumnCount>
{this.props.t('workflow.workflowList.currentEntries', {
smart_count: currEntries.size,
})}
</ColumnCount>
{this.renderColumns(currEntries, currColumn)}
</div>
</div>,
)
}

View File

@ -84,7 +84,7 @@ describe('config', () => {
media_folder: 'baz',
collections: [],
});
}).toThrowError("'collections' should NOT have less than 1 items");
}).toThrowError("'collections' should NOT have fewer than 1 items");
});
it('should throw if collections is an array with a single null element in config', () => {

View File

@ -1,4 +1,5 @@
import { Map } from 'immutable';
import { oneLine } from 'common-tags';
import EditorComponent from 'ValueObjects/EditorComponent';
/**
@ -59,10 +60,39 @@ export function getPreviewTemplate(name) {
* Editor Widgets
*/
export function registerWidget(name, control, preview) {
// A registered widget control can be reused by a new widget, allowing
// multiple copies with different previews.
const newControl = typeof control === 'string' ? registry.widgets[control].control : control;
registry.widgets[name] = { control: newControl, preview };
if (Array.isArray(name)) {
name.forEach(widget => {
if (typeof widget !== 'object') {
console.error(`Cannot register widget: ${widget}`);
} else {
registerWidget(widget);
}
});
} else if (typeof name === 'string') {
// A registered widget control can be reused by a new widget, allowing
// multiple copies with different previews.
const newControl = typeof control === 'string' ? registry.widgets[control].control : control;
registry.widgets[name] = { control: newControl, preview };
} else if (typeof name === 'object') {
const {
name: widgetName,
controlComponent: control,
previewComponent: preview,
globalStyles,
} = name;
if (registry.widgets[widgetName]) {
console.error(oneLine`
Multiple widgets registered with name "${widgetName}". Only the last widget registered with
this name will be used.
`);
}
if (!control) {
throw Error(`Widget "${widgetName}" registered without \`controlComponent\`.`);
}
registry.widgets[widgetName] = { control, preview, globalStyles };
} else {
console.error('`registerWidget` failed, called with incorrect arguments.');
}
}
export function getWidget(name) {
return registry.widgets[name];

View File

@ -1,7 +1,7 @@
import { Map } from 'immutable';
/*
* Reducer for some global UI state that we want to share between components
* */
* Reducer for some global UI state that we want to share between components
* */
const globalUI = (state = Map({ isFetching: false }), action) => {
// Generic, global loading indicator
if (action.type.indexOf('REQUEST') > -1) {