Skip to content

SL-1900: VersionHistory legacy buttons and text to MUI (scope reduced)#73107

Draft
ebeastlake wants to merge 10 commits into
stagingfrom
rebrand/version-history-buttons-typography-sl-1900
Draft

SL-1900: VersionHistory legacy buttons and text to MUI (scope reduced)#73107
ebeastlake wants to merge 10 commits into
stagingfrom
rebrand/version-history-buttons-typography-sl-1900

Conversation

@ebeastlake
Copy link
Copy Markdown
Contributor

Partial migration of apps/src/templates/VersionHistory.jsx (and its child VersionRow.jsx) for SL-1900. Scope was deliberately reduced after discovering that the full ticket scope conflicts with the DS primitives that exist today (details in Follow-ups below).

What this PR does:

  • Swap all raw <button> for MUI Button (aliased as MuiButton).
  • Swap raw <h5> / <p> text for MUI Typography (aliased as MuiTypography).
  • Preserve every legacy CSS class on the wrapping element so existing global selectors still hit (dialog-title, caption-text, template-level-warning, the row <tr className="versionRow">, etc.).
  • Preserve all inline style={} props, ids (start-over-button, again-button), and onClick wiring unchanged.

Button-class -> MUI mapping used:

Legacy MUI
btn-danger (Start Over / Confirm Clear) variant="contained" color="error"
no-class (Cancel / again-button) variant="outlined" color="secondary"
btn-info (Restore on selected row, View on non-selected row) variant="contained" color="primary"
img-upload (Restore on non-selected row) variant="outlined" color="primary"
btn-default disabled (Latest Version, View on selected row) variant="text" color="secondary" disabled

One structural change worth calling out: the old <a target=\"_blank\"><button class=\"btn-info\">View</button></a> was invalid HTML (button-in-anchor). It is now a single <MuiButton component=\"a\" href=... target=\"_blank\" rel=\"noopener noreferrer\"> -- semantically equivalent, valid markup, same UX.

No colors changed: the touched files have no inline hex / color imports, so the semantic-CSS-var part of the ticket is a no-op here.

Links

Follow-ups deferred from this ticket

These pieces of the ticket are NOT in this PR. Each needs a design call before implementation:

  1. Wrap in DSCO dialogVersionHistory.jsx does not own its modal today. The shell comes from a legacy Bootstrap modal (createModalDialog) wrapped around it by apps/src/StudioApp.js (getVersionHistoryHandler / initVersionHistoryUI). Switching to DSCO would require the component owning isOpen/onClose and refactoring that StudioApp.js plumbing. Also: DSCO Dialog mandates a single primaryButtonProps row, but the panel has three modes with different buttons -- so the right primitive is CustomDialog, not Dialog. Worth confirming with design.

  2. Use DSCO list for version rows — DSCO SimpleList is a bulleted list of {key, label} items with a mandatory icon. The rows here are a two-column layout (timestamp + action buttons). It is a forced fit; per the design-system skill that means flagging rather than shipping.

  3. Javalab AC — the ticket says "Verified in applab and javalab", but javalab actually renders apps/src/templates/VersionHistoryWithCommitsDialog.jsx, a different file. That file should get its own ticket.

Testing story

Hey, human! Add screenshots here. I did not browser-verify this myself -- opening the legacy applab version-history modal requires an authenticated session with a saved applab/weblab project, and the local /projects/.../edit path can 500 on the AWS STS token. Please verify in a real session.

Before screenshot

After screenshot

Then, make sure you manually test the following in applab and weblab (NOT javalab -- that's a different file, see Follow-ups):

  • Open the Version History dialog from a saved project. Spinner renders, then the version list appears.
  • Click "Start Over" on the last row -> confirmation view appears with red "Start Over" + outlined "Cancel". Cancel returns to the list. Confirm reloads the project at the initial state.
  • On a non-selected (older) row, "Restore" works. "View" opens the old version in a new tab.
  • On the selected row, "View" is disabled and "Latest Version" (or no Restore) renders.
  • Visual comparison vs prod: button colors/sizes are reasonable. The MUI disabled state looks different from the old btn-default disabled (it uses theme-disabled grey instead of the old color: white override). Flag if that's wrong on the dark row background.
  • Keyboard nav: tab order through the buttons works; Enter activates.
  • Try the above on Firefox and Safari.

There is no "ask @designer" section for this PR -- the swap is mechanical. The genuine design questions are in the Follow-ups section above and belong to the deferred tickets.

🤖 Generated with Claude Code

ebeastlake and others added 10 commits June 5, 2026 21:45
Scope reduced from the full ticket (per discussion with reviewer):
this PR only swaps raw <button> and text tags to MUI Button +
Typography. Wrapping the panel in a DSCO dialog, swapping rows to
DSCO SimpleList, and refactoring the StudioApp.js modal plumbing
are deferred to follow-up tickets (see PR description).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Two fixes on the swap:

* "Latest Version" and disabled "View" were rendering as faint
  MuiButton disabled-text. They are status labels, not buttons --
  the legacy code wrapped them in <button disabled> only as a
  styling hack. Render as MuiTypography body1 with secondary text
  color so they are readable.
* MuiButton has no built-in sibling margin (Bootstrap btn did).
  Wrap the row's actions in MuiStack with spacing.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The Typography labels were rendering as <p>, which hit a legacy
global rule (apps/style/common.scss:1170, `#showVersionsModal p {
margin: 0 }`) that wins on specificity over MUI Stack's
margin-based sibling spacing -- buttons still got spacing because
they were not <p>, but the labels collapsed flush.

* Render the status-label Typography as <span> so the legacy `p`
  rule does not match.
* Switch MuiStack to useFlexGap so spacing uses CSS gap (immune
  to global margin overrides) instead of injected sibling margin.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Two fixes:

* The non-selected View was MuiButton component="a", rendering as
  <a class="MuiButton-...">. The global a:hover rule in
  apps/style/common.scss:66-72 sets `background: none` and wins
  on stylesheet order over MUI's hover bg -- so on hover the
  button went transparent with white text on a white modal, i.e.
  invisible. Restore the legacy <a><MuiButton> wrapper so the
  a:hover rule applies to the outer link, not the inner button.

* Per reviewer: render disabled View in the selected row as a
  disabled MuiButton (variant="outlined" color="secondary") for
  layout consistency with the active rows, instead of as
  MuiTypography. "Latest Version" stays as MuiTypography.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* Latest Version typography: drop color=text.secondary so it
  renders in default primary text color (per reviewer).
* Disabled View: switch from outlined to contained (color
  secondary, disabled) for higher contrast against the row bg.
* Add versionRow.module.scss with padding-right on the action
  cell so the selected-row highlight extends past the View
  button's right edge.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
align Start Over to View

* Latest Version typography: variant body2 (1rem/16px) so it
  matches the MUI Button medium font size next to it.
* Disabled View: override Mui-disabled colors in the module to
  --background-neutral-tertiary + --text-neutral-primary (was
  --background-neutral-disabled + --text-neutral-disabled-inverse,
  which failed a11y contrast).
* Initial-version row td: widen from 250 to 275 and apply the
  same actionCell padding-right so Start Over right-aligns with
  the View buttons in the rows above. Also rename the module to
  versionHistory.module.scss since it is now shared by both
  components in this table.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
MUI emotion styles inject at runtime, so they win against module
CSS with equal specificity. Nest the rule under .actionCell to
get specificity 0,3,0 and beat MUI's 0,2,0 `&.Mui-disabled`.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
MUI's variant selector is .MuiButton-root.MuiButton-contained
.MuiButton-colorSecondary.Mui-disabled (0,4,0). Adding more
selector chain still loses to that on specificity, so use
!important -- this matches the existing convention in
component-library button.tsx where Mui-disabled state values
are also marked !important.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
CSS modules were hashing .Mui-disabled inside the selector,
so the override never matched MUI's runtime class. Mark it
with :global() so it stays literal.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
White text on gray-90 (#576575) = 5.96:1, comfortably AA, and
reads as muted/inactive next to the vivid purple. The theme's
default disabled state was white-on-light-gray (fails contrast),
and no --background-* semantic token is dark enough in the light
theme, so use the gray-90 primitive (allowed second-priority per
the color mapping rules).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant