mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-08 09:01:03 -08:00
fix(parental-lock): tell Enter in the PIN field from a click on Save
Moving on to an empty repeat was decided by document.activeElement in submit(). WebKit does not focus a clicked button, so a click on Save with the PIN field focused looked like Enter: focus moved and no error showed. The advance is now a keydown.enter handler on the PIN field that prevents the submission, and submit() refuses an empty repeat on every path. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
9d1f3d34fc
commit
24fcea999d
5 files changed
+52
-9
No files matched your search
@@ -110,7 +110,16 @@ test.describe('Electron parental-lock PIN dialog', () => {
|
||||
'new-password'
|
||||
);
|
||||
|
||||
// Enter after the PIN moves on to the empty repeat; Save
|
||||
// with the repeat still empty is refused.
|
||||
await pin.fill(PIN);
|
||||
await pin.press('Enter');
|
||||
await expect(confirm).toBeFocused();
|
||||
await expect(mismatch).toBeHidden();
|
||||
await submit.click();
|
||||
await expect(mismatch).toBeVisible();
|
||||
await expect(dialog).toBeVisible();
|
||||
|
||||
await confirm.fill(PIN.slice(0, 3));
|
||||
await expect(mismatch).toBeHidden();
|
||||
await expect(confirm).toHaveAttribute('aria-invalid', 'false');
|
||||
|
||||
@@ -429,7 +429,8 @@ so a cancelled or refused PIN leaves it showing the real state.
|
||||
disabled default button; `submit()` refuses instead, shakes the field
|
||||
(no shake under `prefers-reduced-motion: reduce`) and focuses it — except
|
||||
that Enter in the PIN field with the repeat still empty only moves focus
|
||||
to the repeat. Set
|
||||
to the repeat (a `keydown.enter` handler: focus cannot tell Enter from a
|
||||
click on Save, since WebKit does not focus a clicked button). Set
|
||||
mode marks both inputs `autocomplete="new-password"`. The Electron
|
||||
`parental-lock-pin-dialog.e2e.ts` covers the labels and the
|
||||
mismatch → fix → save flow.
|
||||
|
||||
+1
@@ -26,6 +26,7 @@
|
||||
[errorStateMatcher]="pinErrorState"
|
||||
[ngModel]="pin()"
|
||||
(ngModelChange)="onPinInput($event)"
|
||||
(keydown.enter)="onPinEnter($event)"
|
||||
name="pin"
|
||||
/>
|
||||
<mat-hint>
|
||||
|
||||
+23
-2
@@ -171,16 +171,37 @@ describe('ParentalLockPinDialogComponent', () => {
|
||||
|
||||
it('moves on to an empty repeat on Enter in the PIN field', async () => {
|
||||
const dialog = await openDialog({ mode: 'set' });
|
||||
const enter = new KeyboardEvent('keydown', {
|
||||
key: 'Enter',
|
||||
cancelable: true,
|
||||
});
|
||||
|
||||
await dialog.type('parental-lock-pin', '2468');
|
||||
dialog.query('parental-lock-pin')?.focus();
|
||||
await dialog.pressEnter();
|
||||
dialog.query('parental-lock-pin')?.dispatchEvent(enter);
|
||||
await dialog.fixture.whenStable();
|
||||
|
||||
// Handled before the form sees it: no implicit submission.
|
||||
expect(enter.defaultPrevented).toBe(true);
|
||||
expect(document.activeElement).toBe(
|
||||
dialog.query('parental-lock-pin-confirm')
|
||||
);
|
||||
expect(dialog.query('parental-lock-pin-mismatch')).toBeNull();
|
||||
expect(dialog.component.shake()).toBeNull();
|
||||
});
|
||||
|
||||
it('refuses an empty repeat on Save while the PIN field keeps focus', async () => {
|
||||
// WebKit does not focus a clicked button, so focus says nothing
|
||||
// about how the form was submitted.
|
||||
const dialog = await openDialog({ mode: 'set' });
|
||||
|
||||
await dialog.type('parental-lock-pin', '2468');
|
||||
dialog.query('parental-lock-pin')?.focus();
|
||||
await dialog.pressEnter();
|
||||
|
||||
expect(text(dialog.query('parental-lock-pin-mismatch'))).toBe(
|
||||
KEYS.mismatch
|
||||
);
|
||||
expect(dialog.component.shake()).toBe('confirmation');
|
||||
expect(dialog.close).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
|
||||
+17
-6
@@ -186,6 +186,23 @@ export class ParentalLockPinDialogComponent {
|
||||
this.clearRefusal('confirmation');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set mode: Enter after a complete first PIN moves on to an empty repeat
|
||||
* instead of submitting. Handled on the key, not in `submit()`: focus
|
||||
* cannot tell Enter from a click on Save, as WebKit does not focus a
|
||||
* clicked button.
|
||||
*/
|
||||
onPinEnter(event: Event): void {
|
||||
if (
|
||||
this.isSetMode() &&
|
||||
!this.confirmation() &&
|
||||
isValidParentalLockPin(this.pin())
|
||||
) {
|
||||
event.preventDefault();
|
||||
this.confirmationInput()?.nativeElement.focus();
|
||||
}
|
||||
}
|
||||
|
||||
async submit(): Promise<void> {
|
||||
if (this.busy() || this.inCooldown()) {
|
||||
return;
|
||||
@@ -197,12 +214,6 @@ export class ParentalLockPinDialogComponent {
|
||||
return;
|
||||
}
|
||||
if (this.isSetMode()) {
|
||||
const pinField = this.pinInput()?.nativeElement;
|
||||
if (!this.confirmation() && document.activeElement === pinField) {
|
||||
// Enter after the first PIN moves on to the repeat.
|
||||
this.confirmationInput()?.nativeElement.focus();
|
||||
return;
|
||||
}
|
||||
if (this.confirmation() !== pin) {
|
||||
this.refuse('confirmation');
|
||||
return;
|
||||
|
||||
Reference in new issue
Block a user