feat: remove the 10,000-character article body limit and make the New article body optional
This commit is contained in:
1 parent
5e05b606a7
commit
10ec74c8cf
28 files changed
+860
-397
No files matched your search
@@ -1,10 +1,10 @@
|
||||
Feature: Creating a new article from NewArticleModal
|
||||
|
||||
As a verified RenewCMS owner
|
||||
I want NewArticleModal to take a title and a first paragraph, create the article as a draft
|
||||
over POST /v1/article/, and take me straight to the editor to keep writing
|
||||
So that starting a new article is a two-field, low-friction step instead of a full form, and I
|
||||
land where I can actually write it
|
||||
I want NewArticleModal to take only a title, create the article as a draft
|
||||
over POST /v1/article/, and take me straight to the editor to write it
|
||||
So that starting a new article is a one-field, low-friction step and I land where I can
|
||||
actually write it
|
||||
|
||||
Background:
|
||||
Given NewArticleModal is a UModal opened from Home's "New article" action (mobile header
|
||||
@@ -14,42 +14,37 @@ Feature: Creating a new article from NewArticleModal
|
||||
And every time it opens, it starts in the Default state: title "New article", text "It's
|
||||
saved as a draft. Titles can't be changed later, so check it now.", an empty, focused Title
|
||||
field with placeholder "e.g. Choosing tiles for a small bathroom", a CharacterCounter
|
||||
reading "0 / 200" and help "3 to 200 characters.", an empty First paragraph textarea with
|
||||
placeholder "Start writing. You'll continue in the editor." and help "You can format it and
|
||||
add images in the editor.", a "Cancel" button and a "Create draft" button
|
||||
reading "0 / 200" and help "3 to 200 characters.", a "Cancel" button and a "Create draft"
|
||||
button; there is no body or First paragraph field
|
||||
And the Title field is sent as the request's title and is validated required, 3 to 200
|
||||
characters, counted the same way as the CharacterCounter ([...str].length, which is rune
|
||||
count, matching article_title_rules.feature's server-side rune counting)
|
||||
And the First paragraph field is sent as the request's body and is validated required (at
|
||||
least one character)
|
||||
And each field validates on blur and again on submit; submitting while either field is
|
||||
invalid sends no request, and shows both fields' errors at once if both are invalid: Title
|
||||
under 3 characters shows "Use at least 3 characters." in place of its help text, with the
|
||||
counter in its error style; an empty First paragraph shows "Add a first paragraph. It can
|
||||
be a single sentence." in place of its help text
|
||||
And activating "Create draft" while both fields are valid sends POST /v1/article/ with JSON
|
||||
{"title": "<title>", "body": "<first paragraph>"}; while the request is in flight both
|
||||
fields and both buttons are disabled, the confirm button reads "Creating…" with a loading
|
||||
indicator, and the modal cannot be dismissed by Cancel, Esc or a backdrop click
|
||||
And the request carries no body key; the server treats an absent body as "" per
|
||||
features/article_body_unlimited.feature
|
||||
And the Title field validates on blur and again on submit; submitting while it is invalid
|
||||
sends no request: a title under 3 characters shows "Use at least 3 characters." in place of
|
||||
its help text, with the counter in its error style
|
||||
And activating "Create draft" while the title is valid sends POST /v1/article/ with JSON
|
||||
{"title": "<title>"}; while the request is in flight the Title field and both buttons are
|
||||
disabled, the confirm button reads "Creating…" with a loading indicator, and the modal
|
||||
cannot be dismissed by Cancel, Esc or a backdrop click
|
||||
And a 201 response closes the modal, emits created(article) with the response body, and (for
|
||||
this slice) HomeView.vue's created handler navigates to /article/{id}/edit and raises a
|
||||
toast: color success, icon i-lucide-circle-check, title "Draft created", description
|
||||
"\"{title}\" is saved as a draft.", with id "article-created-{id}" per
|
||||
docs/design/components.md's toast-id convention
|
||||
And a 400 response — whether apperr.ArticleTitleErrorMessage for a title that passed this
|
||||
form's own check but still failed the server's, or any other 400 — is never shown verbatim;
|
||||
it re-enables the form with both fields' current values kept, shows "Check this title and
|
||||
try again." in place of the Title field's help text, and shows an alert "Something in the
|
||||
form isn't valid. Check the highlighted fields."; the First paragraph field shows no error
|
||||
And a 500 response or a request that never reaches the server re-enables the form with both
|
||||
fields' current values kept and shows an alert "Couldn't create the article. Your text is
|
||||
still here. Try again in a moment.", with no field-level error
|
||||
And "Cancel" always closes the modal immediately and discards both fields, with no
|
||||
And a 400 response is never shown verbatim; it re-enables the form with the title kept,
|
||||
shows "Check this title and try again." in place of the Title field's help text, and shows
|
||||
an alert "Something in the form isn't valid. Check the highlighted fields."
|
||||
And a 500 response or a request that never reaches the server re-enables the form with the
|
||||
title kept and shows an alert "Couldn't create the article. Your text is still here. Try
|
||||
again in a moment.", with no field-level error
|
||||
And "Cancel" always closes the modal immediately and discards the title, with no
|
||||
confirmation prompt, whenever the form is not disabled by an in-flight request
|
||||
And a 401 response is handled entirely by useApi's existing global handling
|
||||
(features/api_client_global_error_handling.feature): the session is cleared and the app
|
||||
navigates to /login?redirect=<path>&reason=expired; this slice adds no draft-preservation
|
||||
behavior for the modal's fields, unlike the editor's session-expiry handling
|
||||
behavior for the modal's field, unlike the editor's session-expiry handling
|
||||
|
||||
Scenario: Opening the modal shows the Default state
|
||||
Given the user is on /home
|
||||
@@ -57,7 +52,7 @@ Feature: Creating a new article from NewArticleModal
|
||||
Then NewArticleModal opens with title "New article" and the text "It's saved as a draft.
|
||||
Titles can't be changed later, so check it now."
|
||||
And the Title field is empty, focused, and its counter reads "0 / 200"
|
||||
And the First paragraph field is empty
|
||||
And there is no First paragraph field
|
||||
And "Create draft" is present but the form has not been submitted
|
||||
|
||||
Scenario: Validation — a title under 3 characters on blur
|
||||
@@ -67,34 +62,23 @@ Feature: Creating a new article from NewArticleModal
|
||||
And the counter reads "2 / 200" in its error style
|
||||
And no request is sent
|
||||
|
||||
Scenario: Validation — an empty first paragraph on blur
|
||||
Given the modal is open with a valid Title
|
||||
When the user leaves the First paragraph field empty and moves focus away
|
||||
Then the First paragraph field shows the error "Add a first paragraph. It can be a single
|
||||
sentence."
|
||||
And no request is sent
|
||||
|
||||
Scenario: Validation — submitting with both fields invalid shows both errors and sends nothing
|
||||
Given the modal is open with Title "Ti" and an empty First paragraph
|
||||
Scenario: Validation — submitting an invalid title sends nothing
|
||||
Given the modal is open with Title "Ti"
|
||||
When the user activates "Create draft"
|
||||
Then the Title field shows "Use at least 3 characters." and the First paragraph field shows
|
||||
"Add a first paragraph. It can be a single sentence."
|
||||
Then the Title field shows "Use at least 3 characters."
|
||||
And POST /v1/article/ is never sent
|
||||
|
||||
Scenario: Submitting disables the form and cannot be dismissed
|
||||
Given the modal is open with Title "Choosing tiles for a small bathroom" and First paragraph
|
||||
"Small rooms show every grout line, so we start with the floor."
|
||||
Given the modal is open with Title "Choosing tiles for a small bathroom"
|
||||
When the user activates "Create draft"
|
||||
Then POST /v1/article/ is sent with JSON {"title": "Choosing tiles for a small bathroom",
|
||||
"body": "Small rooms show every grout line, so we start with the floor."}
|
||||
And both fields become disabled, "Cancel" becomes disabled, and the confirm button reads
|
||||
Then POST /v1/article/ is sent with JSON {"title": "Choosing tiles for a small bathroom"}
|
||||
And the Title field becomes disabled, "Cancel" becomes disabled, and the confirm button reads
|
||||
"Creating…" with a loading indicator
|
||||
And pressing Esc or clicking the backdrop does not close the modal
|
||||
|
||||
Scenario: A successful create closes the modal and hands off to the editor with a toast
|
||||
Given the user is on /home with the modal open, Title "Choosing tiles for a small bathroom"
|
||||
and First paragraph "Small rooms show every grout line, so we start with the floor.", and
|
||||
"Create draft" has been activated
|
||||
Given the user is on /home with the modal open, Title "Choosing tiles for a small bathroom",
|
||||
and "Create draft" has been activated
|
||||
When POST /v1/article/ resolves 201 with the created article, id 42, is_online false
|
||||
Then the modal closes
|
||||
And the app navigates to /article/42/edit
|
||||
@@ -104,33 +88,29 @@ Feature: Creating a new article from NewArticleModal
|
||||
|
||||
Scenario: Title rejected — a 400 that got past this form's own check
|
||||
Given the modal is open with Title "Choosing tiles for a small bathroom" (already valid by
|
||||
this form's own rule) and First paragraph "Small rooms show every grout line, so we start
|
||||
with the floor.", and "Create draft" has been activated
|
||||
this form's own rule), and "Create draft" has been activated
|
||||
When POST /v1/article/ resolves 400
|
||||
Then the modal re-enables, keeping both field values
|
||||
Then the modal re-enables, keeping the title
|
||||
And the Title field shows "Check this title and try again." in place of its help text
|
||||
And an alert reads "Something in the form isn't valid. Check the highlighted fields."
|
||||
And the First paragraph field shows no error
|
||||
|
||||
Scenario Outline: Error — a 500 or a network failure keeps the text and offers to retry
|
||||
Given the modal is open with Title "Choosing tiles for a small bathroom" and First paragraph
|
||||
"Small rooms show every grout line, so we start with the floor.", and "Create draft" has
|
||||
been activated
|
||||
Given the modal is open with Title "Choosing tiles for a small bathroom", and "Create draft"
|
||||
has been activated
|
||||
When POST /v1/article/ <failure>
|
||||
Then the modal re-enables, keeping both field values
|
||||
Then the modal re-enables, keeping the title
|
||||
And an alert reads "Couldn't create the article. Your text is still here. Try again in a
|
||||
moment."
|
||||
And neither field shows a field-level error
|
||||
And the Title field shows no field-level error
|
||||
|
||||
Examples:
|
||||
| failure |
|
||||
| failure |
|
||||
| resolves with a 500 response |
|
||||
| never reaches the server |
|
||||
|
||||
Scenario: Retrying after an Error succeeds
|
||||
Given the modal is in the Error state after a 500, with Title "Choosing tiles for a small
|
||||
bathroom" and First paragraph "Small rooms show every grout line, so we start with the
|
||||
floor." still in the fields
|
||||
bathroom" still in the field
|
||||
When the user activates "Create draft" again
|
||||
Then POST /v1/article/ is sent again with the same JSON body
|
||||
When it resolves 201 with the created article, id 42
|
||||
@@ -138,9 +118,8 @@ Feature: Creating a new article from NewArticleModal
|
||||
success, icon i-lucide-circle-check, title "Draft created", description "\"Choosing tiles
|
||||
for a small bathroom\" is saved as a draft.", id "article-created-42"
|
||||
|
||||
Scenario: Cancel discards the fields with no confirmation
|
||||
Given the modal is open with Title "Choosing tiles for a small bathroom" and First paragraph
|
||||
"Small rooms show every grout line, so we start with the floor."
|
||||
Scenario: Cancel discards the title with no confirmation
|
||||
Given the modal is open with Title "Choosing tiles for a small bathroom"
|
||||
When the user activates "Cancel"
|
||||
Then the modal closes immediately, with no confirmation prompt
|
||||
And no request is sent
|
||||
Reference in new issue
Block a user