feat(input-password): add a password field with a caller scored meter - #231
Merged
Merged
Conversation
Requested in #228. The first answer was to decline and document the composition instead, which was wrong on two counts: ThemeModeButton is 89 lines and does less, so thinness is not where this library draws its line, and the two extra bundled icons measure at about half a kilobyte before compression. The component wraps Input rather than reimplementing it the way InputTags and InputNumber do, because those two need to change what sits inside the field box while this one only needs a trailing control and blocks below. FormField and Form integration comes for free as a result: label association, error state and aria-describedby all flow through. What it handles: a visibility toggle whose accessible name follows the next action, purpose mapping to the correct autocomplete value for sign in, sign up and password change, a caps lock warning cleared on release or blur, and a guarantee that pasting is never intercepted. Scoring stays with the caller. strength takes a number and the component renders the meter, picks the colour and debounces the screen reader announcement so it fires on level changes rather than on every keystroke. A naive scorer is worse than no meter: counting character classes rates Password1! as strong while penalising a long passphrase for having no digit, and scoring properly needs a dictionary or a breach list, neither of which belongs in a UI library. Adds passwordShow and passwordHide to the icon defaults and regenerates the bundled set. Closes #228
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.
Summary
Requested in #228. I declined it first and then reversed, because the two objections I raised did not hold up:
ThemeModeButtonis 89 lines and does less, so thinness is not where this library draws its line, and the two extra bundled icons measure at roughly half a kilobyte before compression. What the reporter got right is that the cost is not writing those lines once, it is keeping them identical across projects.Closes #228
Note
Stacked on #230, which fixes the
Inputslots this component relies on. The base retargets todevonce that merges. The diff here isInputPasswordonly.Type of change
Changes
InputPassword, wrappingInputrather than reimplementing it the wayInputTagsandInputNumberdo. Those two reimplement because they change what sits inside the field box; this one only needs a trailing control and blocks below, so wrapping is enough.FormFieldandFormintegration comes for free: label association, error state andaria-describedbyall flow through, verified in a browser.passwordShowandpasswordHideto the icon defaults and regenerates the bundled set, 25 icons to 27.What it handles
aria-pressedreports the state, and the button is inert while the field is disabled or loading. Its size follows the field, including when a size is set through global config.logingivescurrent-password,signupandchangegivenew-password. Getting this wrong fails quietly: a sign up form markedcurrent-passwordstops a password manager offering a generated password and may fill an existing one.Scoring stays with the caller
strengthtakes a number the caller computed. The component renders the meter, picks the colour and debounces the screen reader announcement so it fires on level changes rather than on every keystroke.A naive scorer is worse than no meter. Measured, counting character classes against a length first approach:
Password1!is in every attack dictionary, and a long passphrase is punished for having no digit. Any rule set would be wrong for someone, and scoring properly needs a dictionary or a breach list, neither of which belongs in a UI library.Checklist
Closes #228)pnpm checkpasses (0 errors, 0 warnings)pnpm lintpassespnpm testpassesCHANGELOG.mdunder[Unreleased]*.types.ts, Material 3 design tokens)Notes
37 tests. They cover the toggle in both directions and its accessible name,
aria-pressed, the inert states, all threepurposevalues, paste not being cancelled, six caps lock cases including the warning appearing and clearing, the meter present and absent, score clamping at both ends, custom levels, the live region existing only alongside a meter, size staying in step with the field across three scenarios including a config override,ui.rootapplying exactly once while field slots still forward, thefootersnippet, andref.Two defects were found and fixed during review rather than shipped.
ui.rootwas landing on two elements, because both slot sets have arootkey anduiwas forwarded wholesale; the component now filters its own slot names before passing the rest down. The toggle also fell out of step with the field whenever a size was set through global config, since each resolved its default separately; size is now resolved once and shared.The caps lock tests initially passed while testing nothing:
KeyboardEventtakes nomodifierStatesoption, sogetModifierStatereturned false and only the off path was covered. They now patchgetModifierStateon the event and exercise the real path.Tests: 3909 passing across 108 files.