Skip to content

Commit

Permalink
fix(form-core): only preserve caret if value changed
Browse files Browse the repository at this point in the history
  • Loading branch information
gerjanvangeest committed Jul 12, 2021
1 parent 5e7e43d commit e87b629
Show file tree
Hide file tree
Showing 8 changed files with 115 additions and 6 deletions.
7 changes: 7 additions & 0 deletions .changeset/thin-bulldogs-warn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@lion/form-core': patch
'@lion/input': patch
'@lion/textarea': patch
---

only preserve caret if value changed, which fixes a safari bug
15 changes: 12 additions & 3 deletions packages/form-core/src/NativeTextFieldMixin.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,16 +67,25 @@ const NativeTextFieldMixinImplementation = superclass =>
}
}

/**
* The view value. Will be delegated to `._inputNode.value`
* @override FormatMixin
*/
get value() {
return (this._inputNode && this._inputNode.value) || this.__value || '';
}

// We don't delegate, because we want to preserve caret position via _setValueAndPreserveCaret
/** @param {string} value */
/**
* @param {string} value
* @override FormatMixin - We don't delegate, because we want to preserve caret position via _setValueAndPreserveCaret
*/
set value(value) {
// if not yet connected to dom can't change the value
if (this._inputNode) {
this._setValueAndPreserveCaret(value);
// Only set if newValue is new, fix for Safari bug: https://github.com/ing-bank/lion/issues/1415
if (this._inputNode.value !== value) {
this._setValueAndPreserveCaret(value);
}
/** @type {string | undefined} */
this.__value = undefined;
} else {
Expand Down
2 changes: 1 addition & 1 deletion packages/form-core/test-helpers/getFormControlMembers.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export function getFormControlMembers(el) {
// eslint-disable-next-line
const { _inputNode, _helpTextNode, _labelNode, _feedbackNode, _allValidators } = el;
return {
_inputNode,
_inputNode: /** @type {* & FormControlHost} */ (el)._inputNode,
_helpTextNode,
_labelNode,
_feedbackNode,
Expand Down
69 changes: 69 additions & 0 deletions packages/form-core/test-suites/NativeTextFieldMixin.suite.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { LitElement } from '@lion/core';
import { getFormControlMembers } from '@lion/form-core/test-helpers';
import { defineCE, expect, fixture, html, triggerFocusFor, unsafeStatic } from '@open-wc/testing';
import { sendKeys } from '@web/test-runner-commands';
import { spy } from 'sinon';
import { NativeTextFieldMixin } from '../src/NativeTextFieldMixin.js';

/**
* @typedef {import('../types/FormControlMixinTypes').FormControlHost} FormControlHost
* @typedef {ArrayConstructor | ObjectConstructor | NumberConstructor | BooleanConstructor | StringConstructor | DateConstructor | 'iban' | 'email'} modelValueType
*/

/**
* @param {{tagString?: string, modelValueType?: modelValueType}} [customConfig]
*/
export function runNativeTextFieldMixinSuite(customConfig) {
const cfg = {
tagString: null,
...customConfig,
};

describe('NativeTextFieldMixin', () => {
class NativeTextFieldClass extends NativeTextFieldMixin(LitElement) {
get slots() {
return {
// NativeTextFieldClass needs to have an _inputNode defined in order to work...
input: () => document.createElement('input'),
};
}
}

cfg.tagString = cfg.tagString ? cfg.tagString : defineCE(NativeTextFieldClass);
const tag = unsafeStatic(cfg.tagString);

it('preserves the caret position on value change', async () => {
const el = /** @type {NativeTextFieldClass} */ (await fixture(html`<${tag}></${tag}>`));
// @ts-ignore [allow-protected] in test
const setValueAndPreserveCaretSpy = spy(el, '_setValueAndPreserveCaret');
const { _inputNode } = getFormControlMembers(el);
await triggerFocusFor(el);
await el.updateComplete;
_inputNode.value = 'hello world';
_inputNode.selectionStart = 2;
_inputNode.selectionEnd = 2;
el.value = 'hey there universe';
expect(setValueAndPreserveCaretSpy.calledOnce).to.be.true;
expect(_inputNode.selectionStart).to.equal(2);
expect(_inputNode.selectionEnd).to.equal(2);
});

it('move focus to a next focusable element after writing some text', async () => {
const el = /** @type {NativeTextFieldClass} */ (await fixture(html`<${tag}></${tag}>`));
// @ts-ignore [allow-protected] in test
const setValueAndPreserveCaretSpy = spy(el, '_setValueAndPreserveCaret');
const { _inputNode } = getFormControlMembers(el);
await triggerFocusFor(el);
await el.updateComplete;
expect(document.activeElement).to.equal(_inputNode);
await sendKeys({
press: 'h',
});
await sendKeys({
press: 'Tab',
});
expect(document.activeElement).to.not.equal(_inputNode);
expect(setValueAndPreserveCaretSpy.calledOnce).to.be.false;
});
});
}
1 change: 1 addition & 0 deletions packages/form-core/test-suites/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@ export { runFormGroupMixinSuite } from './form-group/FormGroupMixin.suite.js';
export { runFormatMixinSuite } from './FormatMixin.suite.js';
export { runRegistrationSuite } from './FormRegistrationMixins.suite.js';
export { runInteractionStateMixinSuite } from './InteractionStateMixin.suite.js';
export { runNativeTextFieldMixinSuite } from './NativeTextFieldMixin.suite.js';
export { runValidateMixinSuite } from './ValidateMixin.suite.js';
export { runValidateMixinFeedbackPart } from './ValidateMixinFeedbackPart.suite.js';
3 changes: 3 additions & 0 deletions packages/form-core/test/NativeTextFieldMixin.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { runNativeTextFieldMixinSuite } from '../test-suites/NativeTextFieldMixin.suite.js';

runNativeTextFieldMixinSuite();
10 changes: 9 additions & 1 deletion packages/input/test/input-integrations.test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import { defineCE } from '@open-wc/testing';
import { runInteractionStateMixinSuite, runFormatMixinSuite } from '@lion/form-core/test-suites';
import {
runInteractionStateMixinSuite,
runFormatMixinSuite,
runNativeTextFieldMixinSuite,
} from '@lion/form-core/test-suites';

import { LionInput } from '../src/LionInput.js';

Expand All @@ -23,4 +27,8 @@ describe('<lion-input> integrations', () => {
runFormatMixinSuite({
tagString: fieldTagString,
});

runNativeTextFieldMixinSuite({
tagString: fieldTagString,
});
});
14 changes: 13 additions & 1 deletion packages/textarea/test/lion-textarea-integrations.test.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,23 @@
import { runFormatMixinSuite } from '@lion/form-core/test-suites';
import {
runInteractionStateMixinSuite,
runFormatMixinSuite,
runNativeTextFieldMixinSuite,
} from '@lion/form-core/test-suites';

import '@lion/textarea/define';

const tagString = 'lion-textarea';

describe('<lion-textarea> integrations', () => {
runInteractionStateMixinSuite({
tagString,
});

runFormatMixinSuite({
tagString,
});

runNativeTextFieldMixinSuite({
tagString,
});
});

0 comments on commit e87b629

Please sign in to comment.