feat: editor language menu, title field, header actions, publish dialogs, read-only rendering and draft recovery
This commit is contained in:
1 parent
a671b81f80
commit
339e646eb8
85 files changed
+6369
-624
No files matched your search
+13
-12
@@ -4,16 +4,15 @@
|
||||
|
||||
`docs/design/components.md`'s `ErrorView` only documents two fixed copies (`kind: 'not-found'` →
|
||||
"Page not found" / Home, and `kind: 'error'` → the generic retry copy). The editor's own 404-on-
|
||||
load state needs different copy ("This article doesn't exist." / "All articles" → /article),
|
||||
per `docs/design/states.md`'s HTTP-status-to-copy table. Either widen `ErrorView` with an optional
|
||||
title/description/action override, or add a third `kind`; this is a same-layer prop widening, not
|
||||
a new component, so it doesn't need its own slice.
|
||||
load state shows the Version unavailable copy ("This version isn't available" / "Back to
|
||||
Articles" → /article), per `features/localization_editor_navigation.feature`, which supersedes the
|
||||
original "This article doesn't exist." copy; it is rendered by `EditorVersionGone`.
|
||||
|
||||
Known limitation, not a bug to silently fix: per `features/public_article_read.feature`,
|
||||
`GET /v1/article/{id}` falls back to public online-only visibility on a malformed/expired jwt
|
||||
(no 401). So if a session is already stale when the owner opens an *offline* article, the GET
|
||||
answers 404 exactly like a truly deleted article — the editor cannot tell the two apart from this
|
||||
response alone, and correctly shows the "This article doesn't exist." state.
|
||||
response alone, and correctly shows the "This version isn't available" state.
|
||||
|
||||
## Automated (Vitest + Vue Test Utils, `task test:web`)
|
||||
|
||||
@@ -46,9 +45,9 @@ Fixture article (reused across cases unless a scenario overrides a field): id 12
|
||||
6. **Relative save time after an hour** — after a 200 save at 11:02, advance the fake clock past
|
||||
one hour with no further edit; assert SaveStatus switches to "Saved {n} min ago".
|
||||
7. **Save failed, Examples-driven** — (a) `PATCH` resolves 500, (b) `PATCH` rejects (network);
|
||||
in each, assert SaveStatus "Save failed" with a visible Retry button, and the editor still
|
||||
in each, assert SaveStatus "Save failed" with the primary button "Try saving again", and the editor still
|
||||
shows the typed body.
|
||||
8. **Retry recovers** — from Save failed (500), click Retry; assert `PATCH` is called again with
|
||||
8. **Retry recovers** — from Save failed (500), click "Try saving again"; assert `PATCH` is called again with
|
||||
the same body; resolve 200; assert SaveStatus "Saved at {time}".
|
||||
9. **404 on save shows a toast, not the error SaveStatus** — from Unsaved changes, save; resolve
|
||||
`PATCH` 404; assert a toast "That article no longer exists." appears and SaveStatus never
|
||||
@@ -74,15 +73,17 @@ Fixture article (reused across cases unless a scenario overrides a field): id 12
|
||||
assert the router navigates to `/article` with no ConfirmDialog ever rendered.
|
||||
17. **Session expiry mid-save mirrors and restores the draft** — from Unsaved changes with body
|
||||
"Paragraph added just before the session expired.", save; resolve `PATCH` 401; assert
|
||||
`sessionStorage` holds that body keyed by article id 12, and the router navigates to
|
||||
`sessionStorage` holds that body keyed by user id and article id 12 (key
|
||||
`renewcms:article-draft:{userId}:12`, a JSON object with title, body, search fields and the
|
||||
mirror time), and the router navigates to
|
||||
`/login?redirect=/article/12/edit&reason=expired`. Then, simulating the return trip, mount
|
||||
EditorPage again at `/article/12/edit` with `GET /v1/article/12` resolving 200 with a body
|
||||
that lacks that paragraph; assert the rendered body is the restored sessionStorage draft
|
||||
(with the paragraph) and SaveStatus reads "Unsaved changes".
|
||||
18. **Not found on load** — mock `GET /v1/article/9999` resolving 404; mount at
|
||||
`/article/9999/edit`; assert only the error state renders (title "This article doesn't
|
||||
exist.", action "All articles"), with no breadcrumb-title/SaveStatus/Save/PublishControl/
|
||||
ArticleEditor; click "All articles"; assert the router navigates to `/article`.
|
||||
`/article/9999/edit`; assert only the Version unavailable state renders (title "This version
|
||||
isn't available", action "Back to Articles"), with no header/SaveStatus/Save/PublishControl/
|
||||
ArticleEditor; click "Back to Articles"; assert the router navigates to `/article`.
|
||||
|
||||
## Visual (manual verification — desktop and mobile, light and dark)
|
||||
|
||||
@@ -98,7 +99,7 @@ reusing the sample data below and a fixed `now` of "2026-09-26T11:02:00Z" (updat
|
||||
"N words · 4,318 characters", details panel (Created/Updated/ID/Images/Public API URL).
|
||||
- [ ] Unsaved changes: "Unsaved changes" SaveStatus, enabled Save, "Save and publish".
|
||||
- [ ] Saving: "Saving…" SaveStatus, disabled Save.
|
||||
- [ ] Save failed: "Save failed" with Retry, per the SaveStatus error spec.
|
||||
- [ ] Save failed: "Save failed" with the "Try saving again" primary, per the SaveStatus error spec.
|
||||
- [ ] Very long article: count "19,845 words · 120,000 characters", neutral, Save/Publish enabled.
|
||||
- [ ] Preview: read-only rendered title and body in the content scale, toolbar hidden.
|
||||
- [ ] Offline: SaveStatus shows the wifi-off icon and reads "Waiting for connection", disabled
|
||||
|
||||
Reference in new issue
Block a user