diff --git a/frontend/src/components/ArticleReader.jsx b/frontend/src/components/ArticleReader.jsx index 246ae7c..01fc3a7 100644 --- a/frontend/src/components/ArticleReader.jsx +++ b/frontend/src/components/ArticleReader.jsx @@ -130,7 +130,13 @@ export default function ArticleReader({ const everySection = (article.sections || []).map( sec => ({ ...sec, variant: sec.variant || 'long' })) const present = VIEWS.filter(v => everySection.some(sec => sec.variant === v.key)) - const [view, setView] = useState(present[0]?.key || 'long') + // The article opens as the article. It used to open on the high-yield view, + // because that is first in the list of views — and an author who had written + // ten sections of prose opened their own page and saw a summary, reasonably + // concluding the rest had not been saved. High yield is something you ask + // for; the switch is lit when you have. + const fullest = present.find(v => v.key !== SUMMARY)?.key + const [view, setView] = useState(fullest || present[0]?.key || 'long') const allSections = everySection.filter(sec => sec.variant === view) // One section is not a contents page. Short and Clinical are usually a single diff --git a/frontend/src/pages/ArticlesPage.test.jsx b/frontend/src/pages/ArticlesPage.test.jsx index 8fa2df9..437ea10 100644 --- a/frontend/src/pages/ArticlesPage.test.jsx +++ b/frontend/src/pages/ArticlesPage.test.jsx @@ -271,20 +271,25 @@ describe('topic reading', () => { await screen.findByText('Introduction markdown') const toc = () => document.querySelector('.article-sections') - expect(within(toc()).getByRole('button', { name: 'Key points' })).toBeInTheDocument() - expect(within(toc()).queryByRole('button', { name: 'Pathophysiology' })).toBeNull() + // The article opens as the article. Opening on the summary made an author + // who had written ten sections see two and conclude the rest had not + // saved — and made every reader's first impression of a topic its + // revision card. + expect(within(toc()).getByRole('button', { name: 'Pathophysiology' })).toBeInTheDocument() + expect(within(toc()).queryByRole('button', { name: 'Key points' })).toBeNull() + // The label is the same in both states — it is lit or it is not. What it // must still say out loud is which of the two it is in, so a reader who // cannot see the lit state can tell why two thirds of the contents are // not there. const summary = () => screen.getByRole('button', { name: 'High yield' }) - expect(summary()).toHaveAttribute('aria-pressed', 'true') + expect(summary()).toHaveAttribute('aria-pressed', 'false') await userEvent.click(summary()) - expect(within(toc()).getByRole('button', { name: 'Pathophysiology' })).toBeInTheDocument() - expect(within(toc()).queryByRole('button', { name: 'Key points' })).toBeNull() - expect(screen.queryByText('Key body')).not.toBeInTheDocument() - expect(summary()).toHaveAttribute('aria-pressed', 'false') + expect(within(toc()).getByRole('button', { name: 'Key points' })).toBeInTheDocument() + expect(within(toc()).queryByRole('button', { name: 'Pathophysiology' })).toBeNull() + expect(screen.queryByText('Long body')).not.toBeInTheDocument() + expect(summary()).toHaveAttribute('aria-pressed', 'true') }) it('nests a sub-section under its parent and keeps references last', async () => { diff --git a/frontend/src/pages/EditorialPage.jsx b/frontend/src/pages/EditorialPage.jsx index 4c7b66d..f6fac6b 100644 --- a/frontend/src/pages/EditorialPage.jsx +++ b/frontend/src/pages/EditorialPage.jsx @@ -57,6 +57,18 @@ export default function EditorialPage() { useEffect(() => { load() }, [load]) + //: Deleting from the queue, which is where an editor is standing when they + //: decide an article should not exist. The editor page has the same control; + //: having it only there meant opening an article to get rid of it. + const [removing, setRemoving] = useState(null) + + const remove = async (article) => { + setBusy(true); setError('') + try { await api.delete(`/articles/${article.id}`); setRemoving(null); load() } + catch (err) { setError(apiError(err, 'Could not delete that article')) } + finally { setBusy(false) } + } + const restore = async (article) => { setBusy(true); setError('') try { await api.post(`/articles/${article.id}/restore`); load() } @@ -96,7 +108,9 @@ export default function EditorialPage() { reachable from the library — a page about reading, behind a button an educator arriving here to work has no reason to look for. */}
- Library + {/* No Library button. It is the Reading page, it is in the menu, + and a second door to it beside the two controls that are this + page's own job is a door in the way. */} Write one Draft with AI
@@ -165,6 +179,20 @@ export default function EditorialPage() { aria-label={`Unpublish ${article.title}`} onClick={() => setStatus(article, 'draft')}>Unpublish )} + {removing === article.id ? ( + <> + + + + ) : ( + + )} ))}