fix: ensure draft changes (#3306)

This commit is contained in:
Bartholomew
2020-02-28 11:40:51 +01:00
committed by GitHub
parent 414218c1ee
commit cf81f587cf
86 changed files with 232 additions and 196 deletions

View File

@ -219,10 +219,15 @@ export function discardDraft() {
return { type: DRAFT_DISCARD };
}
export function changeDraftField(field: string, value: string, metadata: Record<string, unknown>) {
export function changeDraftField(
field: string,
value: string,
metadata: Record<string, unknown>,
entries: EntryMap[],
) {
return {
type: DRAFT_CHANGE_FIELD,
payload: { field, value, metadata },
payload: { field, value, metadata, entries },
};
}

View File

@ -227,6 +227,11 @@ export class Editor extends React.Component {
if (entry) this.props.createDraftFromEntry(entry, metadata);
};
handleChangeDraftField = (field, value, metadata) => {
const entries = [this.props.unPublishedEntry, this.props.publishedEntry].filter(Boolean);
this.props.changeDraftField(field, value, metadata, entries);
};
handleChangeStatus = newStatusName => {
const {
entryDraft,
@ -378,7 +383,6 @@ export class Editor extends React.Component {
entryDraft,
fields,
collection,
changeDraftField,
changeDraftFieldValidation,
user,
hasChanged,
@ -421,7 +425,7 @@ export class Editor extends React.Component {
fields={fields}
fieldsMetaData={entryDraft.get('fieldsMetaData')}
fieldsErrors={entryDraft.get('fieldsErrors')}
onChange={changeDraftField}
onChange={this.handleChangeDraftField}
onValidate={changeDraftFieldValidation}
onPersist={this.handlePersistEntry}
onDelete={this.handleDeleteEntry}
@ -463,8 +467,9 @@ function mapStateToProps(state, ownProps) {
const useOpenAuthoring = globalUI.get('useOpenAuthoring', false);
const isModification = entryDraft.getIn(['entry', 'isModification']);
const collectionEntriesLoaded = !!entries.getIn(['pages', collectionName]);
const unpublishedEntry = selectUnpublishedEntry(state, collectionName, slug);
const currentStatus = unpublishedEntry && unpublishedEntry.getIn(['metaData', 'status']);
const unPublishedEntry = selectUnpublishedEntry(state, collectionName, slug);
const publishedEntry = selectEntry(state, collectionName, slug);
const currentStatus = unPublishedEntry && unPublishedEntry.getIn(['metaData', 'status']);
const deployPreview = selectDeployPreview(state, collectionName, slug);
const localBackup = entryDraft.get('localBackup');
const draftKey = entryDraft.get('key');
@ -488,6 +493,8 @@ function mapStateToProps(state, ownProps) {
deployPreview,
localBackup,
draftKey,
publishedEntry,
unPublishedEntry,
};
}

View File

@ -179,14 +179,14 @@ describe('Frontmatter', () => {
'title: YAML',
'---',
'Some content',
'On another line\n',
'On another line',
].join('\n'),
);
});
it('should stringify YAML with missing body', () => {
expect(FrontmatterInfer.toFile({ tags: ['front matter', 'yaml'], title: 'YAML' })).toEqual(
['---', 'tags:', ' - front matter', ' - yaml', 'title: YAML', '---', '', ''].join('\n'),
['---', 'tags:', ' - front matter', ' - yaml', 'title: YAML', '---', ''].join('\n'),
);
});
@ -206,7 +206,7 @@ describe('Frontmatter', () => {
'title: YAML',
'---',
'Some content',
'On another line\n',
'On another line',
].join('\n'),
);
});
@ -227,7 +227,7 @@ describe('Frontmatter', () => {
'title: YAML',
'~~~',
'Some content',
'On another line\n',
'On another line',
].join('\n'),
);
});
@ -248,7 +248,7 @@ describe('Frontmatter', () => {
'title: YAML',
'^^^',
'Some content',
'On another line\n',
'On another line',
].join('\n'),
);
});
@ -267,7 +267,7 @@ describe('Frontmatter', () => {
'title = "TOML"',
'+++',
'Some content',
'On another line\n',
'On another line',
].join('\n'),
);
});
@ -286,7 +286,7 @@ describe('Frontmatter', () => {
'title = "TOML"',
'~~~',
'Some content',
'On another line\n',
'On another line',
].join('\n'),
);
});
@ -308,7 +308,7 @@ describe('Frontmatter', () => {
' "title": "JSON"',
'}',
'Some content',
'On another line\n',
'On another line',
].join('\n'),
);
});
@ -330,8 +330,24 @@ describe('Frontmatter', () => {
' "title": "JSON"',
'~~~',
'Some content',
'On another line\n',
'On another line',
].join('\n'),
);
});
it('should trim last line break if added by grey-matter', () => {
expect(
frontmatterYAML().toFile({
body: 'noLineBreak',
}),
).toEqual('noLineBreak');
});
it('should not trim last line break if not added by grey-matter', () => {
expect(
frontmatterYAML().toFile({
body: 'withLineBreak\n',
}),
).toEqual('withLineBreak\n');
});
});

View File

@ -70,9 +70,11 @@ class FrontmatterFormatter {
const format = this.format || inferFrontmatterFormat(content);
if (this.customDelimiter) this.format.delimiters = this.customDelimiter;
const result = matter(content, { engines: parsers, ...format });
// in the absent of a body when serializing an entry we use an empty one
// when calling `toFile`, so we don't want to add it when parsing.
return {
...result.data,
body: result.content,
...(result.content.trim() && { body: result.content }),
};
}
@ -83,8 +85,13 @@ class FrontmatterFormatter {
const format = this.format || getFormatOpts('yaml');
if (this.customDelimiter) this.format.delimiters = this.customDelimiter;
// gray-matter always adds a line break at the end which trips our
// change detection logic
// https://github.com/jonschlinkert/gray-matter/issues/96
const trimLastLineBreak = body.slice(-1) !== '\n' ? true : false;
// `sortedKeys` is not recognized by gray-matter, so it gets passed through to the parser
return matter.stringify(body, meta, { engines: parsers, sortedKeys, ...format });
const file = matter.stringify(body, meta, { engines: parsers, sortedKeys, ...format });
return trimLastLineBreak && file.slice(-1) === '\n' ? file.substring(0, file.length - 1) : file;
}
}

View File

@ -88,13 +88,14 @@ const entryDraftReducer = (state = Map(), action) => {
});
return state.set('localBackup', newState);
}
case DRAFT_CHANGE_FIELD:
case DRAFT_CHANGE_FIELD: {
return state.withMutations(state => {
state.setIn(['entry', 'data', action.payload.field], action.payload.value);
state.mergeDeepIn(['fieldsMetaData'], fromJS(action.payload.metadata));
state.set('hasChanged', true);
const newData = state.getIn(['entry', 'data']);
state.set('hasChanged', !action.payload.entries.some(e => newData.equals(e.get('data'))));
});
}
case DRAFT_VALIDATION_ERRORS:
if (action.payload.errors.length === 0) {
return state.deleteIn(['fieldsErrors', action.payload.uniquefieldId]);