Skip to content

Commit

Permalink
fix(kit): improve caret management for InputNumber on step action (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
nsbarsukov authored Feb 20, 2025
1 parent 123a46a commit 0422514
Show file tree
Hide file tree
Showing 3 changed files with 114 additions and 4 deletions.
14 changes: 14 additions & 0 deletions projects/cdk/utils/dom/value-binding.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,21 @@ export function tuiValueBinding(
const el = tuiInjectElement<HTMLInputElement>();

effect(() => {
if (el.value === value()) {
return;
}

const {selectionStart, selectionEnd} = el;

el.value = value();

if (el.matches(':focus')) {
/**
* After programmatic updates of input's value, caret is automatically placed at the end –
* revert to the previous position
*/
el.setSelectionRange(selectionStart, selectionEnd);
}
});

return value;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,96 @@ describe('InputNumber', () => {
});
});
});

describe('caret position on step action', () => {
beforeEach(async ({page}) => {
await tuiGoto(page, `${DemoRoute.InputNumber}/API?step=1&postfix=kg`);

await expect(inputNumber.textfield).toHaveValue('');
await expect(inputNumber.textfield).not.toBeFocused();
});

test('Empty unfocused textfield => Click + => Textfield is focused & Caret is placed before postfix', async () => {
await inputNumber.stepUp.click();

await expect(inputNumber.textfield).toHaveValue('1kg');
await expect(inputNumber.textfield).toHaveJSProperty(
'selectionStart',
1,
);
await expect(inputNumber.textfield).toHaveJSProperty(
'selectionEnd',
1,
);
});

test('Focused textfield with postfix only => Press ArrowDown => Caret is placed before postfix', async () => {
await inputNumber.textfield.focus();
await expect(inputNumber.textfield).toHaveValue('kg');
await inputNumber.textfield.press('ArrowDown');

await expect(inputNumber.textfield).toHaveValue(`${CHAR_MINUS}1kg`);
await expect(inputNumber.textfield).toHaveJSProperty(
'selectionStart',
2,
);
await expect(inputNumber.textfield).toHaveJSProperty(
'selectionEnd',
2,
);
});

describe('Keeps caret position on step', () => {
beforeEach(async () => {
await inputNumber.textfield.fill('42');

await expect(inputNumber.textfield).toHaveValue('42kg');
await expect(inputNumber.textfield).toHaveJSProperty(
'selectionStart',
2,
);
await expect(inputNumber.textfield).toHaveJSProperty(
'selectionEnd',
2,
);
await inputNumber.textfield.press('ArrowLeft');
await expect(inputNumber.textfield).toHaveJSProperty(
'selectionStart',
1,
);
await expect(inputNumber.textfield).toHaveJSProperty(
'selectionEnd',
1,
);
});

test('via button', async () => {
await inputNumber.stepUp.click();
await expect(inputNumber.textfield).toHaveValue('43kg');
await expect(inputNumber.textfield).toHaveJSProperty(
'selectionStart',
1,
);
await expect(inputNumber.textfield).toHaveJSProperty(
'selectionEnd',
1,
);
});

test('via keyboard arrow', async () => {
await inputNumber.textfield.press('ArrowUp');
await expect(inputNumber.textfield).toHaveValue('43kg');
await expect(inputNumber.textfield).toHaveJSProperty(
'selectionStart',
1,
);
await expect(inputNumber.textfield).toHaveJSProperty(
'selectionEnd',
1,
);
});
});
});
});

describe('[prefix] & [postfix] props', () => {
Expand Down
14 changes: 10 additions & 4 deletions projects/kit/components/input-number/input-number.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -202,11 +202,17 @@ export class TuiInputNumber extends TuiControl<number | null> {
}

protected onStep(step: number): void {
this.textfieldValue.set(
this.formatNumber(
tuiClamp((this.value() ?? 0) + step, this.min(), this.max()),
),
const newValue = this.formatNumber(
tuiClamp((this.value() ?? 0) + step, this.min(), this.max()),
);

if (this.value() === null) {
const caretIndex = newValue.length - this.postfix().length;

setTimeout(() => this.element.setSelectionRange(caretIndex, caretIndex));
}

this.textfieldValue.set(newValue);
}

private formatNumber(value: number | null): string {
Expand Down

0 comments on commit 0422514

Please sign in to comment.