Moved persisting logic to entryDraft reducer + added tests.
This commit is contained in:
parent
ffe27acc10
commit
e53262d92c
@ -211,13 +211,14 @@ export function createEmptyDraft(collection) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function persistEntry(collection, entry) {
|
export function persistEntry(collection, entryDraft) {
|
||||||
return (dispatch, getState) => {
|
return (dispatch, getState) => {
|
||||||
const state = getState();
|
const state = getState();
|
||||||
const backend = currentBackend(state.config);
|
const backend = currentBackend(state.config);
|
||||||
const mediaProxies = entry.get('mediaFiles').map(path => getMedia(state, path));
|
const mediaProxies = entryDraft.get('mediaFiles').map(path => getMedia(state, path));
|
||||||
|
const entry = entryDraft.get('entry');
|
||||||
dispatch(entryPersisting(collection, entry));
|
dispatch(entryPersisting(collection, entry));
|
||||||
backend.persistEntry(state.config, collection, entry, mediaProxies.toJS()).then(
|
backend.persistEntry(state.config, collection, entryDraft, mediaProxies.toJS()).then(
|
||||||
() => dispatch(entryPersisted(collection, entry)),
|
() => dispatch(entryPersisted(collection, entry)),
|
||||||
error => dispatch(entryPersistFail(collection, entry, error))
|
error => dispatch(entryPersistFail(collection, entry, error))
|
||||||
);
|
);
|
||||||
|
@ -35,12 +35,12 @@ class EntryPage extends React.Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
const { entry, collection, slug } = this.props;
|
const { entry, newEntry, collection, slug, createEmptyDraft, loadEntry } = this.props;
|
||||||
|
|
||||||
if (this.props.newEntry) {
|
if (newEntry) {
|
||||||
this.props.createEmptyDraft(this.props.collection);
|
createEmptyDraft(collection);
|
||||||
} else {
|
} else {
|
||||||
this.props.loadEntry(entry, collection, slug);
|
loadEntry(entry, collection, slug);
|
||||||
this.createDraft(entry);
|
this.createDraft(entry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -63,7 +63,8 @@ class EntryPage extends React.Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
handlePersistEntry = () => {
|
handlePersistEntry = () => {
|
||||||
this.props.persistEntry(this.props.collection, this.props.entryDraft);
|
const { persistEntry, collection, entryDraft } = this.props;
|
||||||
|
persistEntry(collection, entryDraft);
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
@ -105,7 +106,15 @@ function mapStateToProps(state, ownProps) {
|
|||||||
const slug = ownProps.params.slug;
|
const slug = ownProps.params.slug;
|
||||||
const entry = newEntry ? null : selectEntry(state, collection.get('name'), slug);
|
const entry = newEntry ? null : selectEntry(state, collection.get('name'), slug);
|
||||||
const boundGetMedia = getMedia.bind(null, state);
|
const boundGetMedia = getMedia.bind(null, state);
|
||||||
return { collection, collections, newEntry, entryDraft, boundGetMedia, slug, entry };
|
return {
|
||||||
|
collection,
|
||||||
|
collections,
|
||||||
|
newEntry,
|
||||||
|
entryDraft,
|
||||||
|
boundGetMedia,
|
||||||
|
slug,
|
||||||
|
entry,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export default connect(
|
export default connect(
|
||||||
|
@ -1,16 +1,15 @@
|
|||||||
import Immutable, { Map, OrderedMap, fromJS } from 'immutable';
|
import { Map, OrderedMap, fromJS } from 'immutable';
|
||||||
import * as actions from '../../actions/entries';
|
import * as actions from '../../actions/entries';
|
||||||
import reducer from '../entries';
|
import reducer from '../entries';
|
||||||
|
|
||||||
let initialState;
|
const initialState = OrderedMap({
|
||||||
|
posts: Map({ name: 'posts' }),
|
||||||
|
});
|
||||||
|
|
||||||
describe('entries', () => {
|
describe('entries', () => {
|
||||||
it('should mark entries as fetching', () => {
|
it('should mark entries as fetching', () => {
|
||||||
const state = OrderedMap({
|
|
||||||
posts: Map({ name: 'posts' }),
|
|
||||||
});
|
|
||||||
expect(
|
expect(
|
||||||
reducer(state, actions.entriesLoading(Map({ name: 'posts' })))
|
reducer(initialState, actions.entriesLoading(Map({ name: 'posts' })))
|
||||||
).toEqual(
|
).toEqual(
|
||||||
OrderedMap(fromJS({
|
OrderedMap(fromJS({
|
||||||
posts: { name: 'posts' },
|
posts: { name: 'posts' },
|
||||||
@ -22,12 +21,9 @@ describe('entries', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should handle loaded entries', () => {
|
it('should handle loaded entries', () => {
|
||||||
const state = OrderedMap({
|
|
||||||
posts: Map({ name: 'posts' }),
|
|
||||||
});
|
|
||||||
const entries = [{ slug: 'a', path: '' }, { slug: 'b', title: 'B' }];
|
const entries = [{ slug: 'a', path: '' }, { slug: 'b', title: 'B' }];
|
||||||
expect(
|
expect(
|
||||||
reducer(state, actions.entriesLoaded(Map({ name: 'posts' }), entries, 0))
|
reducer(initialState, actions.entriesLoaded(Map({ name: 'posts' }), entries, 0))
|
||||||
).toEqual(
|
).toEqual(
|
||||||
OrderedMap(fromJS(
|
OrderedMap(fromJS(
|
||||||
{
|
{
|
||||||
@ -46,51 +42,4 @@ describe('entries', () => {
|
|||||||
))
|
))
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('entry persisting', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
initialState = Immutable.fromJS({
|
|
||||||
entities: {
|
|
||||||
'posts.slug': {
|
|
||||||
collection: 'posts',
|
|
||||||
slug: 'slug',
|
|
||||||
path: 'content/blog/art-and-wine-festival.md',
|
|
||||||
partial: false,
|
|
||||||
raw: '',
|
|
||||||
data: {},
|
|
||||||
metaData: null,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
pages: {},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should handle persisting request', () => {
|
|
||||||
const newState = reducer(
|
|
||||||
initialState,
|
|
||||||
actions.entryPersisting(Map({ name: 'posts' }), Map({ slug: 'slug' }))
|
|
||||||
);
|
|
||||||
expect(newState.getIn(['entities', 'posts.slug', 'isPersisting'])).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should handle persisting success', () => {
|
|
||||||
let newState = reducer(initialState,
|
|
||||||
actions.entryPersisting(Map({ name: 'posts' }), Map({ slug: 'slug' }))
|
|
||||||
);
|
|
||||||
newState = reducer(newState,
|
|
||||||
actions.entryPersisted(Map({ name: 'posts' }), Map({ slug: 'slug' }))
|
|
||||||
);
|
|
||||||
expect(newState.getIn(['entities', 'posts.slug', 'isPersisting'])).toBeUndefined();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should handle persisting error', () => {
|
|
||||||
let newState = reducer(initialState,
|
|
||||||
actions.entryPersisting(Map({ name: 'posts' }), Map({ slug: 'slug' }))
|
|
||||||
);
|
|
||||||
newState = reducer(newState,
|
|
||||||
actions.entryPersistFail(Map({ name: 'posts' }), Map({ slug: 'slug' }), 'Error message')
|
|
||||||
);
|
|
||||||
expect(newState.getIn(['entities', 'posts.slug', 'isPersisting'])).toBeUndefined();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
126
src/reducers/__tests__/entryDraft.spec.js
Normal file
126
src/reducers/__tests__/entryDraft.spec.js
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
import { Map, List, fromJS } from 'immutable';
|
||||||
|
import * as actions from '../../actions/entries';
|
||||||
|
import reducer from '../entryDraft';
|
||||||
|
|
||||||
|
let initialState = Map({ entry: Map(), mediaFiles: List() });
|
||||||
|
|
||||||
|
const entry = {
|
||||||
|
collection: 'posts',
|
||||||
|
slug: 'slug',
|
||||||
|
path: 'content/blog/art-and-wine-festival.md',
|
||||||
|
partial: false,
|
||||||
|
raw: '',
|
||||||
|
data: {},
|
||||||
|
metaData: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('entryDraft reducer', () => {
|
||||||
|
describe('DRAFT_CREATE_FROM_ENTRY', () => {
|
||||||
|
it('should create draft from the entry', () => {
|
||||||
|
expect(
|
||||||
|
reducer(
|
||||||
|
initialState,
|
||||||
|
actions.createDraftFromEntry(fromJS(entry))
|
||||||
|
)
|
||||||
|
).toEqual(
|
||||||
|
fromJS({
|
||||||
|
entry: {
|
||||||
|
...entry,
|
||||||
|
newRecord: false,
|
||||||
|
},
|
||||||
|
mediaFiles: [],
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('DRAFT_CREATE_EMPTY', () => {
|
||||||
|
it('should create a new draft ', () => {
|
||||||
|
expect(
|
||||||
|
reducer(
|
||||||
|
initialState,
|
||||||
|
actions.emmptyDraftCreated(fromJS(entry))
|
||||||
|
)
|
||||||
|
).toEqual(
|
||||||
|
fromJS({
|
||||||
|
entry: {
|
||||||
|
...entry,
|
||||||
|
newRecord: true,
|
||||||
|
},
|
||||||
|
mediaFiles: [],
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('DRAFT_DISCARD', () => {
|
||||||
|
it('should discard the draft and return initial state', () => {
|
||||||
|
expect(reducer(initialState, actions.discardDraft()))
|
||||||
|
.toEqual(initialState);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('DRAFT_CHANGE', () => {
|
||||||
|
it.skip('should update the draft', () => {
|
||||||
|
const newEntry = {
|
||||||
|
...entry,
|
||||||
|
raw: 'updated',
|
||||||
|
};
|
||||||
|
expect(reducer(initialState, actions.changeDraft(newEntry)))
|
||||||
|
.toEqual(fromJS({
|
||||||
|
entry: {
|
||||||
|
...entry,
|
||||||
|
raw: 'updated',
|
||||||
|
},
|
||||||
|
mediaFiles: [],
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('persisting', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
initialState = fromJS({
|
||||||
|
entities: {
|
||||||
|
'posts.slug': {
|
||||||
|
collection: 'posts',
|
||||||
|
slug: 'slug',
|
||||||
|
path: 'content/blog/art-and-wine-festival.md',
|
||||||
|
partial: false,
|
||||||
|
raw: '',
|
||||||
|
data: {},
|
||||||
|
metaData: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
pages: {},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle persisting request', () => {
|
||||||
|
const newState = reducer(
|
||||||
|
initialState,
|
||||||
|
actions.entryPersisting(Map({ name: 'posts' }), Map({ slug: 'slug' }))
|
||||||
|
);
|
||||||
|
expect(newState.getIn(['entry', 'isPersisting'])).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle persisting success', () => {
|
||||||
|
let newState = reducer(initialState,
|
||||||
|
actions.entryPersisting(Map({ name: 'posts' }), Map({ slug: 'slug' }))
|
||||||
|
);
|
||||||
|
newState = reducer(newState,
|
||||||
|
actions.entryPersisted(Map({ name: 'posts' }), Map({ slug: 'slug' }))
|
||||||
|
);
|
||||||
|
expect(newState.getIn(['entry', 'isPersisting'])).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle persisting error', () => {
|
||||||
|
let newState = reducer(initialState,
|
||||||
|
actions.entryPersisting(Map({ name: 'posts' }), Map({ slug: 'slug' }))
|
||||||
|
);
|
||||||
|
newState = reducer(newState,
|
||||||
|
actions.entryPersistFail(Map({ name: 'posts' }), Map({ slug: 'slug' }), 'Error message')
|
||||||
|
);
|
||||||
|
expect(newState.getIn(['entry', 'isPersisting'])).toBeUndefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -2,9 +2,6 @@ import { Map, List, fromJS } from 'immutable';
|
|||||||
import {
|
import {
|
||||||
ENTRY_REQUEST,
|
ENTRY_REQUEST,
|
||||||
ENTRY_SUCCESS,
|
ENTRY_SUCCESS,
|
||||||
ENTRY_PERSIST_REQUEST,
|
|
||||||
ENTRY_PERSIST_SUCCESS,
|
|
||||||
ENTRY_PERSIST_FAILURE,
|
|
||||||
ENTRIES_REQUEST,
|
ENTRIES_REQUEST,
|
||||||
ENTRIES_SUCCESS,
|
ENTRIES_SUCCESS,
|
||||||
SEARCH_ENTRIES_REQUEST,
|
SEARCH_ENTRIES_REQUEST,
|
||||||
@ -16,10 +13,6 @@ let loadedEntries;
|
|||||||
let page;
|
let page;
|
||||||
let searchTerm;
|
let searchTerm;
|
||||||
|
|
||||||
function getEntryPath(collectionName, entrySlug) {
|
|
||||||
return `${ collectionName }.${ entrySlug }`;
|
|
||||||
}
|
|
||||||
|
|
||||||
const entries = (state = Map({ entities: Map(), pages: Map() }), action) => {
|
const entries = (state = Map({ entities: Map(), pages: Map() }), action) => {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case ENTRY_REQUEST:
|
case ENTRY_REQUEST:
|
||||||
@ -31,17 +24,6 @@ const entries = (state = Map({ entities: Map(), pages: Map() }), action) => {
|
|||||||
fromJS(action.payload.entry)
|
fromJS(action.payload.entry)
|
||||||
);
|
);
|
||||||
|
|
||||||
case ENTRY_PERSIST_REQUEST: {
|
|
||||||
const { collectionName, entrySlug } = action.payload;
|
|
||||||
return state.setIn(['entities', getEntryPath(collectionName, entrySlug), 'isPersisting'], true);
|
|
||||||
}
|
|
||||||
|
|
||||||
case ENTRY_PERSIST_SUCCESS:
|
|
||||||
case ENTRY_PERSIST_FAILURE: {
|
|
||||||
const { collectionName, entrySlug } = action.payload;
|
|
||||||
return state.deleteIn(['entities', getEntryPath(collectionName, entrySlug), 'isPersisting']);
|
|
||||||
}
|
|
||||||
|
|
||||||
case ENTRIES_REQUEST:
|
case ENTRIES_REQUEST:
|
||||||
return state.setIn(['pages', action.payload.collection, 'isFetching'], true);
|
return state.setIn(['pages', action.payload.collection, 'isFetching'], true);
|
||||||
|
|
||||||
|
@ -1,10 +1,21 @@
|
|||||||
import { Map, List, fromJS } from 'immutable';
|
import { Map, List, fromJS } from 'immutable';
|
||||||
import { DRAFT_CREATE_FROM_ENTRY, DRAFT_CREATE_EMPTY, DRAFT_DISCARD, DRAFT_CHANGE } from '../actions/entries';
|
import {
|
||||||
import { ADD_MEDIA, REMOVE_MEDIA } from '../actions/media';
|
DRAFT_CREATE_FROM_ENTRY,
|
||||||
|
DRAFT_CREATE_EMPTY,
|
||||||
|
DRAFT_DISCARD,
|
||||||
|
DRAFT_CHANGE,
|
||||||
|
ENTRY_PERSIST_REQUEST,
|
||||||
|
ENTRY_PERSIST_SUCCESS,
|
||||||
|
ENTRY_PERSIST_FAILURE,
|
||||||
|
} from '../actions/entries';
|
||||||
|
import {
|
||||||
|
ADD_MEDIA,
|
||||||
|
REMOVE_MEDIA,
|
||||||
|
} from '../actions/media';
|
||||||
|
|
||||||
const initialState = Map({ entry: Map(), mediaFiles: List() });
|
const initialState = Map({ entry: Map(), mediaFiles: List() });
|
||||||
|
|
||||||
const entryDraft = (state = Map(), action) => {
|
const entryDraftReducer = (state = Map(), action) => {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case DRAFT_CREATE_FROM_ENTRY:
|
case DRAFT_CREATE_FROM_ENTRY:
|
||||||
// Existing Entry
|
// Existing Entry
|
||||||
@ -25,14 +36,23 @@ const entryDraft = (state = Map(), action) => {
|
|||||||
case DRAFT_CHANGE:
|
case DRAFT_CHANGE:
|
||||||
return state.set('entry', action.payload);
|
return state.set('entry', action.payload);
|
||||||
|
|
||||||
|
case ENTRY_PERSIST_REQUEST: {
|
||||||
|
return state.setIn(['entry', 'isPersisting'], true);
|
||||||
|
}
|
||||||
|
|
||||||
|
case ENTRY_PERSIST_SUCCESS:
|
||||||
|
case ENTRY_PERSIST_FAILURE: {
|
||||||
|
return state.deleteIn(['entry', 'isPersisting']);
|
||||||
|
}
|
||||||
|
|
||||||
case ADD_MEDIA:
|
case ADD_MEDIA:
|
||||||
return state.update('mediaFiles', (list) => list.push(action.payload.public_path));
|
return state.update('mediaFiles', list => list.push(action.payload.public_path));
|
||||||
case REMOVE_MEDIA:
|
case REMOVE_MEDIA:
|
||||||
return state.update('mediaFiles', (list) => list.filterNot((path) => path === action.payload));
|
return state.update('mediaFiles', list => list.filterNot(path => path === action.payload));
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export default entryDraft;
|
export default entryDraftReducer;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user