feat: stage edits to published articles as drafts (#14842)

This commit is contained in:
Sivin Varghese
2026-07-16 12:44:10 +05:30
committed by GitHub
parent f1a07657e9
commit 0fccb7dacd
21 changed files with 1280 additions and 59 deletions
@@ -96,6 +96,32 @@ export const actions = {
}
},
// Push the draft to live and clear it, optionally changing status in the same
// update. Only edited fields are sent so an untouched live value survives.
publishDraft: ({ dispatch, state }, { portalSlug, articleId, status }) => {
const article = state.articles.byId[articleId];
const payload = {
portalSlug,
articleId,
status,
draft_title: null,
draft_content: null,
};
if (article?.draftTitle != null) payload.title = article.draftTitle;
if (article?.draftContent != null) payload.content = article.draftContent;
return dispatch('update', payload);
},
// Clear the draft (optionally changing status); live content is left untouched.
discardDraft: ({ dispatch }, { portalSlug, articleId, status }) =>
dispatch('update', {
portalSlug,
articleId,
status,
draft_title: null,
draft_content: null,
}),
updateArticleMeta: async ({ commit }, { portalSlug, locale }) => {
try {
const { data } = await articlesAPI.getArticles({
@@ -150,6 +150,101 @@ describe('#actions', () => {
});
});
describe('#publishDraft', () => {
const state = {
articles: {
byId: {
1: {
id: 1,
draftTitle: 'Draft title',
draftContent: 'Draft content',
},
},
},
};
it('dispatches update promoting the edited fields and clearing the draft', async () => {
await actions.publishDraft(
{ dispatch, state },
{ portalSlug: 'room-rental', articleId: 1 }
);
expect(dispatch).toHaveBeenCalledWith('update', {
portalSlug: 'room-rental',
articleId: 1,
status: undefined,
draft_title: null,
draft_content: null,
title: 'Draft title',
content: 'Draft content',
});
});
it('only sends the fields that were actually edited', async () => {
const partialState = {
articles: { byId: { 1: { id: 1, draftContent: 'Only content' } } },
};
await actions.publishDraft(
{ dispatch, state: partialState },
{ portalSlug: 'room-rental', articleId: 1 }
);
expect(dispatch).toHaveBeenCalledWith('update', {
portalSlug: 'room-rental',
articleId: 1,
status: undefined,
draft_title: null,
draft_content: null,
content: 'Only content',
});
});
it('forwards a status to change it in the same update', async () => {
await actions.publishDraft(
{ dispatch, state },
{ portalSlug: 'room-rental', articleId: 1, status: 'archived' }
);
expect(dispatch).toHaveBeenCalledWith(
'update',
expect.objectContaining({
status: 'archived',
title: 'Draft title',
content: 'Draft content',
draft_title: null,
draft_content: null,
})
);
});
});
describe('#discardDraft', () => {
it('dispatches update clearing the draft columns', async () => {
await actions.discardDraft(
{ dispatch },
{ portalSlug: 'room-rental', articleId: 1 }
);
expect(dispatch).toHaveBeenCalledWith('update', {
portalSlug: 'room-rental',
articleId: 1,
status: undefined,
draft_title: null,
draft_content: null,
});
});
it('forwards a status to change it in the same update', async () => {
await actions.discardDraft(
{ dispatch },
{ portalSlug: 'room-rental', articleId: 1, status: 'draft' }
);
expect(dispatch).toHaveBeenCalledWith('update', {
portalSlug: 'room-rental',
articleId: 1,
status: 'draft',
draft_title: null,
draft_content: null,
});
});
});
describe('#updateArticleMeta', () => {
it('sends correct actions if API is success', async () => {
axios.get.mockResolvedValue({