feat(website): add a "Modify entries" menu and fix the search action row layout - #6938
Draft
theosanderson-agent wants to merge 23 commits into
Draft
feat(website): add a "Modify entries" menu and fix the search action row layout#6938theosanderson-agent wants to merge 23 commits into
theosanderson-agent wants to merge 23 commits into
Conversation
)" This reverts commit e20d010.
`DownloadSubmittedDataButton` rendered itself as a fixed-width 18rem button inside two nested wrapper divs, so it behaved as its own layout box in the search action row rather than as a peer of the buttons beside it. On the group released sequences page -- the only page that renders it -- that pushed the row past the viewport on narrow screens. Return the `Button` directly, matching `DownloadDialogButton`, and let it size to its content. The two wrappers only existed to position a hand-rolled tooltip and an absolutely-positioned error box: - the tooltip is now the shared `HoverTooltip` (react-tooltip), applied only when the selection exceeds the download limit - download errors now go through react-toastify, as elsewhere in the app Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Contributor
|
This PR may be related to: #6662 |
Reverting #6935 wholesale went too far: with the button row back to a plain non-wrapping `flex`, the buttons were squeezed narrower than their labels. Because the labels then wrapped *inside* a fixed-height `h-10` button, each button became three or four lines of text spilling outside its own border, and the row still overflowed horizontally below 640px. Absorb the narrowing in two stages instead: - `flex-wrap` on the outer row drops the sequence count onto its own line as soon as the buttons stop fitting beside it, so they get the full width before they begin wrapping among themselves. This is driven by the content rather than a breakpoint, so it happens exactly when needed. - In the button row, `whitespace-nowrap` (inherited by the buttons) stops any label wrapping inside its box, and `*:shrink-0` holds buttons at their natural width so they wrap as whole buttons. Spacing is now the container's `gap` alone; the per-button `mr-4`/`ml-2` margins that fought with it are gone, along with the leftover `ml-2` wrapper around the submitted-data button. Measured on a mock of the fullest possible row (all six buttons) with the real compiled Tailwind: previously the row overflowed horizontally at 640px and below, with buttons rendering 3-4 lines of clipped text. It now reflows cleanly -- count alone from 1280px, buttons over two lines from 1100px -- with no horizontal overflow until 375px. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Holding every label to a single line kept the row tidy down to about 480px, but below that the longest button -- "Download originally submitted data (12,345 selected)", some 330px wide on its own -- could not fit a phone viewport, so the row overflowed horizontally instead. Wrapping that label over two lines is the better trade at that width. Buttons could not previously wrap without looking broken: `h-10` fixed their height, so a wrapped label spilled outside its own border. Give the size classes a minimum height and vertical padding instead. A single-line button is unchanged at 40px; one whose label wraps now grows to fit it. In the search action row, replace the blanket `whitespace-nowrap` with `*:max-w-full` alongside the existing `*:shrink-0`. Together these say: a button keeps its natural width and never gets squeezed, but never exceeds the row either -- so a label wraps only when the button genuinely cannot fit on one line. This surfaced a latent bug in `DownloadDialogButton`, whose fixed `w-60` was, per its own comment, "fine for up to two digit numbers": larger counts were being clipped by the fixed height. Making those widths `min-w-*` keeps them steady against layout shifts, as intended, while letting an over-long count widen the button. Measured on a mock of the fullest row with the real compiled Tailwind: no horizontal overflow at any width from 1440px down to 320px, every button on one line except the longest, which wraps over two below 375px. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Reverse the order in which the action row gives up space. It previously held every button at its natural width and reflowed the row around them, so labels only ever wrapped as a last resort at phone widths. Wrapping a label over two lines is the cheaper concession, so it should come first. The row now degrades in four stages: buttons wrap their own labels; then the sequence count moves onto its own line to hand them the full width; and only then do the buttons break onto separate lines. Getting that order needs breakpoints. Flexbox breaks a line at an item's *unshrunk* width, so a wrapping row always puts buttons on separate lines before it lets any of them narrow and wrap a label -- the two stages cannot be sequenced by content alone. Hence `flex-nowrap` until the last stage, and `*:shrink-0` is gone so buttons may narrow. The breakpoints are container queries, not viewport ones, because the search form sits beside this column from `md` up: the width actually available here is not monotonic in viewport width, being narrower at a 768px viewport (448px) than at a 640px one (608px). No single viewport breakpoint can express the staging -- one row of buttons is safe from 708-767px and again above 996px, a disjoint range. The container is scoped to the action row alone so the result table is untouched. Measured on the fullest row (six buttons) against real compiled Tailwind, by container width: nothing wrapped above 1500px; labels wrapping with the count still alongside from ~1430px; count on its own line with the buttons still in one row from ~896px; buttons on separate lines below 768px. No horizontal overflow anywhere from 1800px down to 320px. The common three-button public page never wraps a label at any width. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The count moved onto its own line at a fixed container width of 896px, which was picked for the widest row that exists -- the five buttons of the released-sequences page. A public search page carries two or three much shorter buttons, so that breakpoint stacked the row while roughly 250px of empty space was still going spare. Make that stage content-driven again: `flex-wrap` on the outer row keeps the count alongside for exactly as long as the buttons fit beside it. On a three-button row it now holds to ~700px of container rather than 896px; on the five-button row it gives way earlier, as it must. The button row keeps its `@3xl` breakpoint, which still has to be one: flexbox breaks a line at an item's unshrunk width, so a wrapping row would put buttons on separate lines before letting any of them narrow and wrap a label. An earlier attempt to keep both stages content-driven -- flex-basis of min-content on the button row, so the count would give way only once the buttons had been squeezed as far as they go -- is not used here: it made the count's position non-monotonic in the container width (own line at 850px, back alongside at 768px), because min-content means something different eitherside of the wrap switch. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Lower the threshold at which the search action row gives up and breaks its buttons onto separate lines, from 768px of container width to 512px. Between those, the row now stays on a single line and lets the labels wrap instead, which is the stated preference. That was not previously possible: the widest row that exists could not compress below 609px, so a threshold under it would have overflowed. The whole of that floor is the download button's `min-w-60`, which alone accounts for 609px against 473px without it. Make the floor conditional on `@2xl` so it still holds the button steady against layout shifts at the widths where there is room to notice them, and lifts once the row needs to compress. The row's minimum then falls to 473px, comfortably inside 512px. The cost, accepted deliberately: labels get tall before the row finally breaks. On the five-button released row the tallest button runs to three lines at 768px and four from 640px down to the 512px threshold. Nothing overflows horizontally at any width from 1800px down to 320px, and the common public row never wraps a label above 640px. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This reverts commit 01bb542.
Undo the successive attempts at staging how the search action row responds to narrowing, returning the row, the button styles and the download button's width to exactly the state they had after #6935 was reverted and the submitted-data button was made a plain button. Keeps: the revert of #6935, and `DownloadSubmittedDataButton` rendering as a plain button with its tooltip on the shared `HoverTooltip` and its errors on react-toastify. Drops: the container-query staging, the outer row's content-driven wrap, the minimum-height button sizes, and the download button's `min-w-*` floors. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add a "Modify entries" dropdown to the search action row, in the style of the existing "Tools" menu, holding the two actions that change entries a group has already released: editing their data use terms, and downloading what was originally submitted. Both are long-labelled, rarely reached for, and shown only to a group's own members on their own released sequences, so as standalone buttons they crowded out the controls everyone needs. On that page the row goes from five buttons to four, two of which were the widest. The submitted-data download is relabelled to say what it is for -- "Download 2 selected entries for bulk revision" -- since editing those files and submitting them back is the reason to want them, and that is more use than naming the format. To let both actions be driven from a menu item, `EditDataUseTermsModal` now takes its open state from its caller rather than owning a trigger button, and the submitted-data download logic moves into a `useSubmittedDataDownload` hook. Its over-limit tooltip survives the move. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Give the menu button the same treatment as the download button beside it, so the two describe their scope alike: "Modify all entries" with nothing selected, "Modify 12,345 selected entries" with a selection. A `min-w-44` floor keeps it from jumping about as the count changes while still allowing a long one, in the spirit of the download button's fixed width but without its habit of clipping counts too long to fit. Prefix it with the Pajamas admin icon, marked `aria-hidden` since the label already says what the button does. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
`h-10` fixed a button's height, so a label too long for its width wrapped *inside* the button and the text spilled outside its own border. Give the size classes a minimum height and vertical padding instead. A single-line button is unchanged -- 40px, 32px and 24px as before, and circular buttons keep their fixed dimensions, since they must stay round -- and one whose label wraps grows to fit it. This already mattered for `DownloadDialogButton`, whose fixed `w-60` is, by its own comment, "fine for up to two digit numbers": longer counts were being clipped. Such a count now takes two lines in a taller button. Drop `whitespace-nowrap` from the "Modify entries" button at the same time. It carries a count too, and while the minimum height applied to it already, its label could never wrap, so it would have overflowed sideways instead of growing: in a 260px slot it ran to 331px wide rather than wrapping to two lines. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The count gave way to the buttons only below a 640px viewport, by which point the row had been overflowing for a long while -- it first does so around 900px. `sm:flex-row` keys off viewport width, which is a poor guide to the width this row actually has: the search form sits beside this column from `md` up, so there is less room here at a 768px viewport (448px) than at a 640px one (608px). Let `flex-wrap` decide it from the content instead, so the count gives way exactly when the buttons no longer fit beside it, whatever buttons the page happens to show. Give the count `min-w-80` as well, so it claims a little more width than its text needs and the two separate while the buttons still have room rather than at the moment they stop fitting. Measured, by viewport, with the search form's 288px accounted for: on a public search page the count now takes its own line from 1100px rather than 560px, and on a group's released sequences with a selection made, from 1280px. Handing the buttons the full width that much earlier also clears the row's overflow at 900, 800, 700, 640 and 560px on the public page. A container query with a fixed threshold was tried and dropped: sized for the widest row it stacks the count on every laptop, and the widths the row needs differ by 250px between pages, so no one threshold suits both. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
`min-w-70` rather than `min-w-80`. The count claims slightly less width before the row splits; the viewport at which it takes its own line is unchanged on both pages at the widths measured. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The count took its own line as soon as its text no longer fitted beside the buttons at full width. Break the row on how little space the count will accept instead of how much its text would like: `basis-0` makes the wrap decision turn on its 160px floor, `grow` still hands it the space going spare so it reads as one line at ordinary widths, and in between it narrows and wraps beside the buttons rather than leaving. By viewport, with the search form's 288px accounted for, the count now stays alongside down to about 1000px on a public search page (was ~1120) and about 1244px on a group's released sequences with a selection made (was ~1360). That is close to the floor. The stack point is the button row's width plus whatever the count will accept, and the button row cannot compress: the download button is a fixed `w-60`, the menu has a `min-w-44`, and the links carry `mr-4`. So each pixel off the count's floor buys just one pixel, at the cost of the count wrapping into an ever narrower ribbon. Going meaningfully narrower means narrowing the buttons. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
With the count on its own line the buttons were alone on theirs, and `justify-between` places a lone item at the start, so they jumped from the right edge to the left as the row wrapped. `ml-auto` holds them against the right edge there. Above that width the count has already grown into the space between them, leaving none for the margin to take, so it changes nothing. Measured on both the public and released rows: the buttons now sit flush right at every width where they fit, wrapped or not. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
"Download data for bulk revision (3 sequences)", scoped in parentheses the way the data use terms item beside it is, so the two menu items read alike. Covers the singular, and falls back to "(all sequences)" when the count is not yet known. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
"Download original data to prepare bulk revision (2 selected sequences)", saying what the data is as well as what it is for, and distinguishing a selection from the whole result set: "(1,234 sequences)" when nothing is selected, "(2 selected sequences)" when something is. Covers the new branch with a test. The `/for bulk revision/` matchers the tests and the page object used no longer match the wording, so they now look for `/bulk revision/`; one of them asserted the item was *absent* and would have passed for the wrong reason. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
`w-44` and `w-60` were narrower than the labels they held: "Download all entries" wrapped over two lines at every screen size, and any count of more than two digits did the same -- the fixed width was, by its own comment, "fine for up to two digit numbers". Until buttons could grow this was hidden, since the fixed height clipped the overflow instead. Make them `min-w-*`. The button still holds a steady width against small changes in the count, which is what the fixed width was for, but widens rather than wrapping when the label needs it. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Running `npm run format` reformatted this file as a side effect, and CI rejected the result: the local install is prettier 3.8.4 where the project pins ^3.9.5, and the two disagree on how to wrap the union type for `trace`. The file is nothing to do with this branch, so put it back as it is on main. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ezed Screenshots of the preview at 430px showed the released page's row collapsing: with the row a plain non-wrapping `flex`, the buttons were squeezed to their minimum width and "Customize columns" came out one letter per line, 288px tall. That is the behaviour this branch inherits from before #6935, and it would have shipped as a regression against main, which wraps. Let them wrap among themselves, with the container's `gap` replacing the per-button `mr-4` and `ml-2` margins that fought with it. The wrapper div left around the menu once its margin was gone is dropped too. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Go back to squeezing the buttons as the row narrows rather than wrapping them, but stop it short of the absurd: at 430px "Customize columns" was coming out 13px wide and 260px tall, one letter per line. `min-width: auto` should have floored each button at its longest word, and did not, because `base.css` applies `word-break: break-word` to every element. That legacy value permits a line break between any two characters, and unlike `overflow-wrap: break-word` it does so when computing intrinsic sizes as well -- so a flex item's automatic minimum size collapses to the width of one character. `*:break-normal` on the row restores word-boundary breaking for the buttons, which puts the floor back at the longest word: 69px rather than 13px for that button. Squeezing is unaffected until then. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
`*:break-normal` did not take effect: the `word-break: break-word` rule in `base.css` sits outside any cascade layer, and unlayered declarations beat layered ones whatever their specificity, so a Tailwind utility loses to it. Verified on the preview, where the button was still 13px wide. Use the important modifier, which does win. The underlying rule is worth revisiting separately: `@apply break-words` alone already stops long words overflowing, and the extra `word-break: break-word` only adds the intrinsic-sizing behaviour that causes this, app-wide. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Two things: the action row above the search results gets a "Modify entries" menu, and the layout problems that #6935 papered over are fixed at source. #6935 is reverted here.
Screenshots below are from this branch's preview deployment, with real submitted sequences.
1. A "Modify entries" menu
A group's released sequences page carried five buttons, two of them much the widest: "Edit data use terms (12,345 sequences)" and "Download originally submitted data (12,345 selected)". Both are rarely reached for, and both are shown only to a group's own members, so as standalone buttons they crowded out the controls everyone needs.
They now sit behind one button, which names its scope the way the download button beside it does:
Nothing selected
With a selection
The submitted-data download is relabelled to say what it is for — editing those files and submitting them back is the reason to want them — and to distinguish a selection from the whole result set: "(1,234 sequences)" versus "(2 selected sequences)". It keeps its behaviour, including the tooltip explaining why it is unavailable above the 500-entry limit.
The released page's row goes from five buttons to four:
Supporting refactors
Both actions owned their own trigger button, so neither could be driven from a menu item:
EditDataUseTermsModaltakesisOpen/onClosefrom its caller instead of owning a trigger, and exportseditDataUseTermsLabel. The search row was its only caller.DownloadSubmittedDataButtoninto auseSubmittedDataDownloadhook; the component is gone.2. Layout fixed at source
The row overflowed on narrow screens. #6935 addressed that by letting it wrap; this fixes the four things that made it too wide in the first place.
DownloadSubmittedDataButtonwas not behaving like a button. It rendered as a fixed-widthw-[18rem]button inside two wrapperdivs, so it occupied a hard 288px regardless of content and formed its own layout box. Its wrappers existed to position a hand-rolled tooltip and an absolutely-positioned error box; the tooltip is now the sharedHoverTooltip, and errors go throughreact-toastifylike the rest of the app.Buttons could not grow.
h-10fixed their height, so a label too long for its width wrapped inside the button and spilled outside its own border. The size classes now set a minimum height and vertical padding. Single-line buttons are unchanged at 40/32/24px and circular buttons keep their fixed dimensions; only a button whose label wraps is affected.The download button was narrower than its own label.
w-44andw-60were, by their own comment, "fine for up to two digit numbers" — "Download all entries" wrapped over two lines at every screen size, and longer counts were being clipped. They are nowmin-w-*, so the button still resists jitter as the count changes but widens when the label needs it.The sequence count gave way far too late.
sm:flex-rowmoved it onto its own line only below a 640px viewport, long after the row started overflowing. It keyed off the wrong thing: the search form sits beside this column frommdup, so there is less room at a 768px viewport (448px) than at a 640px one (608px).flex-wrapnow decides from the content, and the count takesbasis-0 grow min-w-40so the row breaks on how little space it will accept rather than how much its text wants — it narrows and wraps beside the buttons first.ml-autokeeps the buttons right-aligned once it does give way.Public search page
Released sequences page
Buttons squeeze, but only so far
As the row narrows the buttons are squeezed rather than wrapped onto separate lines. Left alone that went absurdly far: at 430px "Customize columns" came out 13px wide and 260px tall, one letter per line.
min-width: autoshould have floored each button at its longest word and did not, becausebase.cssappliesword-break: break-wordto every element. That legacy value permits a break between any two characters, and — unlikeoverflow-wrap: break-word— it does so when computing intrinsic sizes too, so a flex item's automatic minimum size collapses to the width of one character.*:break-normal!on the row restores word-boundary breaking for the buttons, putting the floor back at the longest word: 69px rather than 13px. Squeezing below that is unaffected, and nothing overflows.The important modifier is needed rather than a plain utility: that rule in
base.csssits outside any cascade layer, and unlayered declarations beat layered ones whatever their specificity. Worth fixing at source separately —@apply break-wordsalone already stops long words overflowing, and the extraword-break: break-wordonly contributes the intrinsic-sizing behaviour that causes this, app-wide.Testing
CI=1 npm run test— 709 passed, including six new tests for the menu: that it keeps its actions hidden until opened, that both appear when both apply, that each is omitted when it does not, and that the download names its purpose and scope for a selection and for the whole result set.npm run check-types— 0 errors.eslint— clean.download-submitted-data.spec.tsnow opens the menu and clicks the item, via two newSearchPagehelpers.One thing worth noting for anyone running the checks locally:
npm run formathere reformattedplaywright.config.tsin a way CI rejected, because the local install was prettier 3.8.4 where the project pins ^3.9.5 and the two disagree on wrapping a union type. Worth checking your prettier version matches before trusting a local format run.Notes for review
The minimum-height change is in the shared
buttonStyles, so it reaches every sized button in the app, not just this row. Single-line buttons are pixel-identical; it only takes effect where a label would otherwise have been clipped.Accessibility, pre-existing. The over-limit state keeps the download item
disabled, hencepointer-events-noneand no keyboard focus, so the explanation of why it is disabled is reachable only by hovering with a mouse. Unchanged by this PR, but leaving it enabled and surfacing the limit as a toast on click would fix it — happy to switch.PR Checklist
🤖 Generated with Claude Code
🚀 Preview: Add
previewlabel to enable