-
Notifications
You must be signed in to change notification settings - Fork 1.1k
usage: open the custom date range on request instead of on arrival #4093
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,55 @@ | ||
| # Usage custom date range — manual-query disclosure | ||
|
|
||
| Triggered by a maintainer browser comment on `/#usage`: the custom date range block should be a | ||
| dropdown (manual lookup) by default, with the fields below it, and the current layout is visually | ||
| wrong — control heights do not line up. Scope is the usage page filter area only. | ||
|
|
||
| ## Design read (cxc-dev-uiux-design) | ||
|
|
||
| Reading this as: a dense local analytics page for a single operator who reads the presets almost | ||
| every time and reaches for an explicit interval rarely, in the quiet utilitarian language the rest | ||
| of the dashboard already speaks. Tokens come from `gui/src/styles.css`; nothing new is invented. | ||
|
|
||
| ``` | ||
| DESIGN_VARIANCE: 3 | ||
| MOTION_INTENSITY: 1 | ||
| Product density profile: D5 | ||
| Reasoning: dashboard/admin surface for repeated operator work — the expressive default kit is | ||
| domain-gated off, so the work is restraint, alignment and disclosure rather than decoration. | ||
| ``` | ||
|
|
||
| Do's: one obvious path (presets), expert control demoted behind a labelled disclosure, every | ||
| control on one height, left-aligned so the block reads with the page it belongs to. | ||
| Don'ts: no second full-width flex-end row, no decorative motion, no hidden applied state. | ||
|
|
||
| ## Problem | ||
|
|
||
| `Usage.tsx` renders the custom-range `<form>` unconditionally under the page subtitle and reuses | ||
| `.usage-filters`, which is `justify-content: flex-end`. Three consequences: | ||
|
|
||
| 1. Two empty `datetime-local` fields are the second thing on the page even though the answer the | ||
| page exists to give is already rendered from a preset (UX-LAZY-01: an expert fork at top level). | ||
| 2. The row is pushed to the right edge with a wide empty gutter, and the help caption underneath | ||
| starts at the left edge, so the two halves do not read as one control. | ||
| 3. `align-items: center` centers a label+input stack (≈57px) against `btn-sm` buttons (≈26px), so | ||
| Apply/Clear float in the middle of the fields instead of sitting on their baseline. | ||
|
|
||
| ## Work phases | ||
|
|
||
| 1. wp1: collapse the block behind a closed-by-default disclosure trigger, render the fields in a | ||
| bottom-aligned grid panel on one control height, keep the applied interval visible while | ||
| collapsed, and update `gui/tests/usage-custom-range.test.tsx`. | ||
| 2. wp2: publish as a PR against `dev` with a GUI screenshot and merge on exact-head CI; depends | ||
| on wp1. | ||
|
|
||
| ## Contract | ||
|
|
||
| - Trigger reuses the existing `usage.range.custom` label, so no locale catalog gains a key. | ||
| - Collapsed state renders no date inputs; `aria-expanded`/`aria-controls` carry the state. | ||
| - An applied window keeps its `role="status"` interval line outside the panel, so collapsing never | ||
| hides which interval the numbers cover (progressive disclosure names what stays hidden). | ||
| - Draft text, validation, request identity and cache behavior are untouched: this is presentation. | ||
|
|
||
| Verification: see `010_audit.md`. The maintainer forbade local suite runs mid-unit, so the | ||
| repository-wide `bun run test` is NOT RUN locally and remote exact-head CI is the only | ||
| full-suite evidence for this change. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| # Audit and verification record | ||
|
|
||
| ## Independent review (explorer reviewer, A gate) | ||
|
|
||
| Verdict NEAR-PASS. The reviewer read the four touched files, ran the focused suite and | ||
| typecheck, and measured the rendered panel in headless Chrome rather than trusting the CSS | ||
| comments. It cleared the behavioral half: `rangeOpen` feeds only `aria-expanded`, the | ||
| `is-active` class and the conditional render, and `loadUsage`, `resourceKey`, `presetKey`, | ||
| the held-cache gating and the `UsageWindowMismatchError` receipt check are untouched, so | ||
| request identity and caching cannot have moved. Every `var()` in the new block resolves, no | ||
| other `.usage-range*` selector exists in `gui/src`, and `usage.range.custom` is already in all | ||
| ten catalogs, so promoting it from `aria-label` to visible text adds no key. | ||
|
|
||
| Findings folded in: | ||
|
|
||
| 1. **BLOCKING — measured spacing defect.** `repeat(2, minmax(0, 200px))` capped each date | ||
| track at 200px, but `.input` carries `min-width: auto` and a `datetime-local` control's | ||
| intrinsic minimum is ~206px in Chrome at this font size. Measured `input w=206.0` in a | ||
| 200px track, leaving a 2px visible gap where the grid declares 8px — and worse for locales | ||
| whose date format is longer than `mm/dd/yyyy`. That is the exact rhythm defect this change | ||
| exists to repair. Fixed by sizing every track to its content: `repeat(4, auto)` with | ||
| `justify-content: start`. | ||
| 2. **MINOR — dangling IDREF.** `aria-controls` named a panel that is unmounted while closed. | ||
| Now emitted only while open. | ||
| 3. **MINOR — hidden validation state.** `rangeError` survived a collapse, so a submitted | ||
| invalid range left an alert behind an unmarked trigger. Closing now retires the error while | ||
| keeping the draft; covered by a new regression test. | ||
| 4. **MINOR — vacuous-assertion risk.** The `interval()` helper was class-coupled; it is now | ||
| scoped to `.usage-range-bar [role="status"]`. | ||
| 5. **MINOR, accepted.** Disclosure state across a preset click stays open and is left unpinned. | ||
|
|
||
| ## Verification status | ||
|
|
||
| | Check | Result | | ||
| |---|---| | ||
| | `bun run typecheck` | pass (before the no-local-suite instruction) | | ||
| | `cd gui && bun test tests` | 1937 pass / 0 fail (before the instruction; 26 in the usage suite after the review fixes) | | ||
| | `cd gui && bun run lint` | pass | | ||
| | `cd gui && bun run build` | pass | | ||
| | `bun run test` (repository-wide) | **NOT RUN** — killed on the maintainer's explicit instruction | | ||
| | Rendered browser check | Collapsed, open, applied-then-collapsed, validation error, dark theme and 430px width, against a live proxy | | ||
|
|
||
| Screenshots in `assets/`: `010_before.png` is the shipped 2.49.0 layout, `020_after_collapsed.png` | ||
| and `030_after_open.png` are this change. Remote exact-head CI is the full-suite authority. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2508,6 +2508,45 @@ button.prov-account-row.active { cursor: default; } | |
| /* Top-align with Storage: centering against the taller filter chips dropped the "Usage" title. */ | ||
| .usage-head { flex-wrap: wrap; align-items: flex-start; } | ||
| .usage-filters { display: flex; align-items: center; justify-content: flex-end; gap: 8px; flex-wrap: wrap; } | ||
| /* Custom range: a disclosure, not a second filter row. It reuses the chip language of the | ||
| segmented presets above it so the page reads as one control area, and the panel is | ||
| `width: max-content` because a four-control form stretched to 1200px is what made the old | ||
| flex-end row look like a stray band across the page. */ | ||
| .usage-range { display: grid; justify-items: start; gap: 8px; margin: 10px 0 4px; } | ||
| .usage-range-bar { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; max-width: 100%; } | ||
| .usage-range-toggle { | ||
| display: inline-flex; align-items: center; gap: 6px; | ||
| min-height: var(--control-sm); padding: 4px 12px; | ||
| border: 1px solid var(--border); border-radius: var(--radius-pill); | ||
| background: var(--surface); color: var(--muted); | ||
| font: inherit; font-size: var(--text-label); font-weight: var(--weight-medium); | ||
| white-space: nowrap; cursor: pointer; | ||
| transition: color var(--motion-fast), background var(--motion-fast), border-color var(--motion-fast); | ||
| } | ||
| .usage-range-toggle:hover { background: var(--raised); color: var(--text); } | ||
| .usage-range-toggle[aria-expanded="true"] { color: var(--text); } | ||
| .usage-range-toggle.is-active { border-color: var(--accent); color: var(--text); } | ||
| .usage-range-chevron { flex: 0 0 auto; transition: transform var(--motion-fast); } | ||
| .usage-range-toggle[aria-expanded="true"] .usage-range-chevron { transform: rotate(90deg); } | ||
| .usage-range-applied { margin: 0; min-width: 0; } | ||
| .usage-range-panel { | ||
| display: grid; gap: 10px; width: max-content; max-width: 100%; | ||
| padding: 12px; border: 1px solid var(--border); border-radius: var(--radius-sm); | ||
| background: var(--surface); | ||
| } | ||
| /* `align-items: end` is the fix for the old row: a label+input stack is twice the height of a | ||
| `btn-sm`, so centering left Apply and Clear floating beside the middle of the fields. | ||
| Every track is `auto` on purpose. A fixed 200px cap does not survive contact with a | ||
| `datetime-local` control: its intrinsic minimum is about 206px in Chrome at this font size, | ||
| and it grows again for locales whose date format is longer than `mm/dd/yyyy`. The fields | ||
| then overflow their tracks and eat the gap, which is the exact rhythm defect this block | ||
| exists to fix. */ | ||
| .usage-range-fields { display: grid; grid-template-columns: repeat(4, auto); justify-content: start; align-items: end; gap: 8px; } | ||
| .usage-range-field { display: grid; gap: 4px; min-width: 0; } | ||
| .usage-range-field .field-label { margin: 0; } | ||
| .usage-range-fields .input { height: var(--control-md); padding-block: 0; } | ||
| .usage-range-action { min-height: var(--control-md); } | ||
| .usage-range-panel > p { margin: 0; } | ||
| .usage-segmented { display: inline-flex; border: 1px solid var(--border); border-radius: var(--radius-pill); padding: 2px; gap: 2px; background: var(--surface); } | ||
| .usage-segmented-btn { display: inline-flex; align-items: center; justify-content: center; gap: 6px; border: none; background: transparent; color: var(--muted); padding: 4px 12px; border-radius: var(--radius-pill); cursor: pointer; font: inherit; white-space: nowrap; } | ||
| .usage-segmented-btn.active { background: var(--raised); color: var(--text); font-weight: var(--weight-semibold); } | ||
|
|
@@ -2529,6 +2568,11 @@ button.prov-account-row.active { cursor: default; } | |
|
|
||
| @media (max-width: 640px) { | ||
| .usage-source-btn .usage-source-label-collapsible { display: none; } | ||
| /* Two 200px columns plus both actions stop fitting a phone content width; the panel takes | ||
| the full row and every control keeps the same height it has on desktop. */ | ||
| .usage-range-panel { width: 100%; } | ||
| .usage-range-fields { grid-template-columns: minmax(0, 1fr); } | ||
|
Comment on lines
2569
to
+2574
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
At common tablet widths just above 760px (for example, 768px), the desktop AGENTS.md reference: gui/AGENTS.md:L31-L34 Useful? React with 👍 / 👎. |
||
| .usage-range-action { width: 100%; } | ||
| } | ||
|
|
||
| @media (max-width: 360px) { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Add a language to the fenced code block.
markdownlintreports MD040 because this fence has no language identifier. Usetextfor this configuration-style content.Proposed fix
📝 Committable suggestion
🧰 Tools
🪛 markdownlint-cli2 (0.23.2)
[warning] 13-13: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
🤖 Prompt for AI Agents
Source: Linters/SAST tools