fix: keep editor slug path (#2934)

* fix: keep editor slug path

* fix: cleanup

* refactor: remove unused path preview component
This commit is contained in:
Bartholomew 2019-12-01 09:46:45 +01:00 committed by Erez Rokah
parent 09ca1a7f03
commit 3c4865f2a7
9 changed files with 10 additions and 215 deletions

View File

@ -104,8 +104,8 @@ describe('test backend implementation', () => {
const backend = new TestBackend();
const slug = encodeURIComponent('dir1/dir2/some-post.md');
const path = `posts/${decodeURIComponent(slug)}`;
const slug = 'dir1/dir2/some-post.md';
const path = `posts/${slug}`;
const entry = { path, raw: 'content', slug };
await backend.persistEntry(entry, [], { newEntry: true });
@ -138,8 +138,8 @@ describe('test backend implementation', () => {
const backend = new TestBackend();
const slug = encodeURIComponent('dir1/dir2/some-post.md');
const path = `posts/${decodeURIComponent(slug)}`;
const slug = 'dir1/dir2/some-post.md';
const path = `posts/${slug}`;
const entry = { path, raw: 'new content', slug };
await backend.persistEntry(entry, [], { newEntry: false });

View File

@ -190,7 +190,7 @@ class App extends React.Component {
path="/collections/:name/new"
render={props => <Editor {...props} newRecord />}
/>
<Route path="/collections/:name/entries/:slug" component={Editor} />
<Route path="/collections/:name/entries/*" component={Editor} />
<Route
path="/search/:searchTerm"
render={props => <Collection {...props} isSearchResults />}

View File

@ -94,8 +94,7 @@ const EntryCard = ({
const label = entry.get('label');
const entryData = entry.get('data');
const defaultTitle = label || entryData.get(inferedFields.titleField);
const slug = entry.get('slug');
const path = `/collections/${collection.get('name')}/entries/${encodeURIComponent(slug)}`;
const path = `/collections/${collection.get('name')}/entries/${entry.get('slug')}`;
const summary = collection.get('summary');
const date = parseDateFromEntry(entry, collection) || null;
const identifier = entryData.get(selectIdentifier(collection));

View File

@ -40,7 +40,7 @@ const navigateCollection = collectionPath => history.push(`/collections/${collec
const navigateToCollection = collectionName => navigateCollection(collectionName);
const navigateToNewEntry = collectionName => navigateCollection(`${collectionName}/new`);
const navigateToEntry = (collectionName, slug) =>
navigateCollection(`${collectionName}/entries/${encodeURIComponent(slug)}`);
navigateCollection(`${collectionName}/entries/${slug}`);
export class Editor extends React.Component {
static propTypes = {
@ -435,7 +435,7 @@ export class Editor extends React.Component {
function mapStateToProps(state, ownProps) {
const { collections, entryDraft, auth, config, entries, globalUI } = state;
const slug = ownProps.match.params.slug && decodeURIComponent(ownProps.match.params.slug);
const slug = ownProps.match.params[0];
const collection = collections.get(ownProps.match.params.name);
const collectionName = collection.get('name');
const newEntry = ownProps.newRecord === true;

View File

@ -1,47 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import ImmutablePropTypes from 'react-immutable-proptypes';
import { connect } from 'react-redux';
import { translate } from 'react-polyglot';
import styled from '@emotion/styled';
import { colors, transitions } from 'netlify-cms-ui-default';
import { selectDraftPath } from 'Selectors/entryDraft';
const PathLabel = styled.label`
color: ${colors.text};
display: inline-block;
font-size: 12px;
font-weight: 600;
border: 0;
padding-left: 5px;
transition: all ${transitions.main};
position: relative;
`;
export const PathPreview = ({ value }) => {
return <PathLabel data-testid="site_path-preview">{value}</PathLabel>;
};
PathPreview.propTypes = {
value: PropTypes.string.isRequired,
t: PropTypes.func.isRequired,
};
const mapStateToProps = (state, ownProps) => {
const collection = ownProps.collection;
const entry = ownProps.entry;
const newRecord = entry.get('newRecord');
const value = newRecord ? selectDraftPath(state, collection, entry) : entry.get('path');
return { value };
};
const ConnectedPathPreview = connect(mapStateToProps)(translate()(PathPreview));
ConnectedPathPreview.propTypes = {
collection: ImmutablePropTypes.map.isRequired,
entry: ImmutablePropTypes.map.isRequired,
};
export default ConnectedPathPreview;

View File

@ -1,84 +0,0 @@
import React from 'react';
import { Map } from 'immutable';
import { createStore } from 'redux';
import { Provider } from 'react-redux';
import { render } from '@testing-library/react';
import ConnectedPathPreview, { PathPreview } from '../PathPreview';
jest.mock('Reducers', () => {
return () => ({});
});
jest.mock('Selectors/entryDraft');
jest.mock('react-polyglot', () => {
return {
translate: () => Component => props => <Component {...props} t={jest.fn(key => key)} />,
};
});
describe('PathPreview', () => {
it('should render successfully and match snapshot', () => {
const props = {
value: 'posts/2019/index.md',
t: jest.fn(key => key),
};
const { asFragment } = render(<PathPreview {...props} />);
expect(asFragment()).toMatchSnapshot();
});
});
function renderWithRedux(ui, { initialState, store = createStore(() => ({}), initialState) } = {}) {
return {
...render(<Provider store={store}>{ui}</Provider>),
store,
};
}
describe('ConnectedPathPreview', () => {
const { selectDraftPath } = require('Selectors/entryDraft');
beforeEach(() => {
jest.clearAllMocks();
});
it('should use existing path when newRecord is false', () => {
const props = {
collection: Map({
name: 'posts',
}),
entry: Map({
newRecord: false,
data: {},
path: 'existing-path/index.md',
}),
};
const { asFragment, getByTestId } = renderWithRedux(<ConnectedPathPreview {...props} />);
expect(selectDraftPath).toHaveBeenCalledTimes(0);
expect(getByTestId('site_path-preview')).toHaveTextContent('existing-path/index.md');
expect(asFragment()).toMatchSnapshot();
});
it('should evaluate preview path when newRecord is true', () => {
selectDraftPath.mockReturnValue('preview-path/index.md');
const props = {
collection: Map({
name: 'posts',
}),
entry: Map({
newRecord: true,
data: {},
}),
};
const { asFragment, getByTestId } = renderWithRedux(<ConnectedPathPreview {...props} />);
expect(selectDraftPath).toHaveBeenCalledTimes(1);
expect(selectDraftPath).toHaveBeenCalledWith({}, props.collection, props.entry);
expect(getByTestId('site_path-preview')).toHaveTextContent('preview-path/index.md');
expect(asFragment()).toMatchSnapshot();
});
});

View File

@ -1,70 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`ConnectedPathPreview should evaluate preview path when newRecord is true 1`] = `
<DocumentFragment>
.emotion-0 {
color: #798291;
display: inline-block;
font-size: 12px;
font-weight: 600;
border: 0;
padding-left: 5px;
-webkit-transition: all .2s ease;
transition: all .2s ease;
position: relative;
}
<label
class="emotion-0 emotion-1"
data-testid="site_path-preview"
>
preview-path/index.md
</label>
</DocumentFragment>
`;
exports[`ConnectedPathPreview should use existing path when newRecord is false 1`] = `
<DocumentFragment>
.emotion-0 {
color: #798291;
display: inline-block;
font-size: 12px;
font-weight: 600;
border: 0;
padding-left: 5px;
-webkit-transition: all .2s ease;
transition: all .2s ease;
position: relative;
}
<label
class="emotion-0 emotion-1"
data-testid="site_path-preview"
>
existing-path/index.md
</label>
</DocumentFragment>
`;
exports[`PathPreview should render successfully and match snapshot 1`] = `
<DocumentFragment>
.emotion-0 {
color: #798291;
display: inline-block;
font-size: 12px;
font-weight: 600;
border: 0;
padding-left: 5px;
-webkit-transition: all .2s ease;
transition: all .2s ease;
position: relative;
}
<label
class="emotion-0 emotion-1"
data-testid="site_path-preview"
>
posts/2019/index.md
</label>
</DocumentFragment>
`;

View File

@ -14,7 +14,7 @@ function mapStateToProps(state, ownProps) {
showDelete: !ownProps.newEntry && selectAllowDeletion(collection),
};
if (isEditorialWorkflow) {
const slug = ownProps.match.params.slug && decodeURIComponent(ownProps.match.params.slug);
const slug = ownProps.match.params[0];
const unpublishedEntry = selectUnpublishedEntry(state, collection.get('name'), slug);
if (unpublishedEntry) {
returnObj.unpublishedEntry = true;

View File

@ -205,10 +205,7 @@ class WorkflowList extends React.Component {
{entries.map(entry => {
const timestamp = moment(entry.getIn(['metaData', 'timeStamp'])).format('MMMM D');
const slug = entry.get('slug');
const editLink = `collections/${entry.getIn([
'metaData',
'collection',
])}/entries/${encodeURIComponent(slug)}`;
const editLink = `collections/${entry.getIn(['metaData', 'collection'])}/entries/${slug}`;
const ownStatus = entry.getIn(['metaData', 'status']);
const collection = entry.getIn(['metaData', 'collection']);
const isModification = entry.get('isModification');