refactor(sign-in): migrate sign-in page to the design system#73605
refactor(sign-in): migrate sign-in page to the design system#73605levadadenys wants to merge 7 commits into
Conversation
Replace the server-rendered sign-in form and section-code entry with React components using DSCO TextField, MUI Button/Typography, and semantic color tokens, matching the sign-up flow. OAuth buttons, course blocks, and the LTI account-linking variant are unchanged. - New SignInForm + SectionCodeEntry (apps/src/signIn/) with a layout-only module.scss (semantic tokens, no hex/legacy vars) - Mount both from devise/sessions/_login.js; retire the old jQuery handlers (userReturnTo write moves into SignInForm's effect) - Retire #signin / .section-sign-in CSS from application.scss; keep .flex-container, .vertical-or, #change-password and .blue-button (shared with the out-of-scope LTI variant / other pages) - Preserve the UI-test DOM hooks (#signin, #user_login, #user_password, #signin-button, #section_code); update one stale cucumber selector - Keep show_flashes server-rendered so failed-login errors still surface Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Migrates the /users/sign_in page from server-rendered HAML form markup to React components built with DSCO/MUI primitives, keeping the underlying Devise/Rails session POST and flash-error rendering server-side.
Changes:
- Replaced the legacy HAML
#signinform with a React-mountedSignInFormthat posts to/users/sign_inand preserves existing UI-test DOM hooks. - Replaced the legacy section-code HAML form with a React-mounted
SectionCodeEntry, and updated the Cucumber selector accordingly. - Retired legacy sign-in/section-code CSS from
application.scssin favor of a colocated CSS module, and added unit tests for both new components.
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| dashboard/test/ui/features/platform/signing_in.feature | Updates UI test click target to the new #section_code_submit button id. |
| dashboard/app/views/devise/shared/_oauth_links.haml | Replaces the section-code HAML form with a React mount point and data attributes. |
| dashboard/app/views/devise/sessions/_login.html.haml | Replaces the sign-in HAML form with a React mount point and passes required props via data-*. |
| dashboard/app/assets/stylesheets/application.scss | Removes legacy sign-in/section-code styling and leaves #change-password styling intact. |
| apps/test/unit/signIn/SignInFormTest.tsx | Adds unit coverage for form fields, submit wiring, analytics event, and userReturnTo storage behavior. |
| apps/test/unit/signIn/SectionCodeEntryTest.tsx | Adds unit coverage for section-code heading/input/button and GET submit behavior. |
| apps/src/sites/studio/pages/devise/sessions/_login.js | Mounts SignInForm and SectionCodeEntry React roots using server-provided data-*. |
| apps/src/signIn/signInStyles.module.scss | Adds layout-only CSS module styles for the new sign-in React components. |
| apps/src/signIn/SignInForm.tsx | Introduces the DSCO/MUI-based sign-in form that preserves Devise POST field names and key DOM hooks. |
| apps/src/signIn/SectionCodeEntry.tsx | Introduces the DSCO/MUI-based section-code entry form with preserved input/button ids. |
The sign-in column was capped at max-width 28rem while the OAuth column keeps flex-grow: 2 uncapped, so the OAuth column grew to fill the row and dwarfed the form. Restore the even split by matching the legacy #signin sizing (flex-grow: 2, min-width: 450px) and constrain the form content to a readable width via a .formArea wrapper. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Move the "Sign in" page title out of the server-rendered HAML <h2> into a React MUI Typography (variant h2) mounted at #sign-in-title, so it follows the design system's type scale like the rest of the migrated page. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Render the section-code label via the TextField's own label prop instead of a separate Typography h6, so both columns use the identical DSCO field label (same size, weight, padding, and top alignment). Also give the dev-only OAuth notice a little vertical breathing room (display:block + margin-block) between the section-code field and the OAuth buttons. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3149c743b3
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…y #signin styles Addresses the drone failures and PR review feedback: - GlobalEditionTest / fa sign_in_page: the form action was hard-coded to /users/sign_in, dropping the region/locale prefix (e.g. /fa/users/sign_in). Pass session_path(resource_name) from the server via the mount's data-sign-in-path and use it as the form action; restore id="new_user". Update global_edition_test to assert the region-versioned URL on the mount (the form is React-rendered now). - #signin regression: several server-rendered pages (existing_account, PD/Foorm logged-out) still use id="signin" and relied on the shared block. Move that styling to a .signin-legacy class those pages opt into, so the React sign-in page (which reuses #signin as a test hook) doesn't inherit legacy form/button rules and the other pages keep their styling. - Guard the forgot-password link on both path and label to avoid an empty link. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…legacy hash-on-submit) devise/sessions/new.js attaches a submit handler to form#new_user that calls window.dashboard.hashEmail against #user_hashed_email. The React sign-in form has no such element, so once the form carried id="new_user" that handler broke the native POST and login never reached /home (platform/signing_in and login_redirect UI tests failed). The id was only added to satisfy an old assertion in global_edition_test, which now checks the mount's data-sign-in-path instead, so the id is unnecessary. Remove it. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
artem-vavilov
left a comment
There was a problem hiding this comment.
Tested this locally, and it looks great to me. Nice work! 🙌🏻
I am wondering if we could also use this opportunity to improve the forms’ accessibility a bit by marking the relevant fields as required.
It would also be helpful to have someone from the @code-dot-org/platform team take a look at these changes.
Migrates the sign-in page (
/users/sign_in) from server-rendered HAML toReact + DSCO/MUI with semantic color tokens, matching the sign-up flow. The
sign-in form, the section-code entry, and the page title are now React; their
styles move out of
application.scssinto a componentmodule.scss.Before:

After:


In scope:
SignInForm(login/password fields, forgot-password link, sign-inSectionCodeEntry, the page title (Typography), andretiring their legacy CSS.
Out of scope, unchanged: OAuth/social login buttons, the course blocks
("Try it out without signing in"), the global flash banner, and the LTI
account-linking variant.
What's here
SignInForm+SectionCodeEntry(apps/src/signIn/) using DSCOTextField/Link+ MUIButton/Typography.Typography(was a HAML<h2>).the section-code label now matches the sign-in field labels (same DSCO label).
dev-only OAuth notice got a little vertical spacing.
#signin/.section-sign-inCSS retired fromapplication.scss;kept
.flex-container,.vertical-or,#change-password, and.blue-button(shared with the out-of-scope LTI variant / other pages).
Notable decisions
show_flashesstays server-rendered — it consumes the flash and the viewrenders before the layout, so it's what surfaces failed-login errors.
#signin,#user_login,#user_password,#signin-button,#section_code); one stale Cucumber selector updated to#section_code_submit.hashed_emailis server-set and threaded through unchanged (no client-sidehashing on sign-in).
Links
Testing story
Automated (green locally):
apps/test/unit/signIn/SignInFormTest.tsx+SectionCodeEntryTest.tsx(13 tests),yarn run typecheck, ESLint, Stylelint,scss-lint(application.scss),haml-lint(both views).Manual, in-browser on staging + local (iterated on QA feedback): page renders,
two-column layout balance, title, label alignment, and
?brand=codeai-nextsemantic tokens. Reviewer/QA should still confirm the functional flows end to
end: a real login, a failed login (flash error), a section-code submit to
/users/new, and the forgot-password link.Follow-ups (not in this PR)
later PR; drags in shared pegasus CSS + per-card server data, and there's no
DSCO
Cardprimitive yet.Privacy and security
No auth or Devise controller/session changes. The form still POSTs
user[login],user[password],user[hashed_email]to/users/sign_inwith aCSRF token via
RailsAuthenticityToken;hashed_emailis server-set and passedthrough unchanged.
🤖 Generated with Claude Code