Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(number-field): show decimal on iPad minimized keyboard #4784

Merged
merged 7 commits into from
Oct 10, 2024
50 changes: 22 additions & 28 deletions packages/number-field/src/NumberField.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ OF ANY KIND, either express or implied. See the License for the specific languag
governing permissions and limitations under the License.
*/

import { NumberFormatter, NumberParser } from '@internationalized/number';
import {
CSSResultArray,
html,
Expand All @@ -21,24 +22,24 @@ import {
property,
query,
} from '@spectrum-web-components/base/src/decorators.js';
import { streamingListener } from '@spectrum-web-components/base/src/streaming-listener.js';
import {
LanguageResolutionController,
languageResolverUpdatedSymbol,
} from '@spectrum-web-components/reactive-controllers/src/LanguageResolution.js';
import { streamingListener } from '@spectrum-web-components/base/src/streaming-listener.js';
import { NumberFormatter, NumberParser } from '@internationalized/number';

import '@spectrum-web-components/icons-ui/icons/sp-icon-chevron50.js';
import '@spectrum-web-components/icons-ui/icons/sp-icon-chevron75.js';
import chevronStyles from '@spectrum-web-components/icon/src/spectrum-icon-chevron.css.js';
import '@spectrum-web-components/icons-ui/icons/sp-icon-chevron100.js';
import '@spectrum-web-components/icons-ui/icons/sp-icon-chevron200.js';
import '@spectrum-web-components/icons-ui/icons/sp-icon-chevron50.js';
import '@spectrum-web-components/icons-ui/icons/sp-icon-chevron75.js';
import '@spectrum-web-components/infield-button/sp-infield-button.js';
import {
isAndroid,
isIOS,
isIPhone,
} from '@spectrum-web-components/shared/src/platform.js';
import { TextfieldBase } from '@spectrum-web-components/textfield';
import chevronStyles from '@spectrum-web-components/icon/src/spectrum-icon-chevron.css.js';
import styles from './number-field.css.js';

export const FRAMES_PER_CHANGE = 5;
Expand Down Expand Up @@ -241,7 +242,7 @@ export class NumberField extends TextfieldBase {
const uniqueSeparators = new Set(separators);

if (
isIPhone() &&
isIOS() &&
this.inputElement.inputMode === 'decimal' &&
normalizedValue !== this.valueBeforeFocus
) {
Expand Down Expand Up @@ -802,30 +803,23 @@ export class NumberField extends TextfieldBase {
}

if (changes.has('min') || changes.has('formatOptions')) {
let inputMode = 'numeric';
const hasNegative = typeof this.min !== 'undefined' && this.min < 0;
const hasOnlyPositives =
typeof this.min !== 'undefined' && this.min >= 0;

const { maximumFractionDigits } =
this.numberFormatter.resolvedOptions();
const hasDecimals = maximumFractionDigits > 0;
/* c8 ignore next 18 */
if (isIPhone()) {
// iPhone doesn't have a minus sign in either numeric or decimal.
// Note this is only for iPhone, not iPad, which always has both
// minus and decimal in numeric.
if (hasNegative) {
inputMode = 'text';
} else if (hasDecimals) {
inputMode = 'decimal';
}
} else if (isAndroid()) {
// Android numeric has both a decimal point and minus key.
// decimal does not have a minus key.
if (hasNegative) {
inputMode = 'numeric';
} else if (hasDecimals) {
inputMode = 'decimal';
}
}
const hasDecimals =
maximumFractionDigits && maximumFractionDigits > 0;

let inputMode = 'numeric';
/* c8 ignore next 5 */
// iPhone doesn't have a minus sign in either numeric or decimal.
if (isIPhone() && !hasOnlyPositives) inputMode = 'text';
else if (isIOS() && hasDecimals) inputMode = 'decimal';
// Android numeric has both a decimal point and minus key. Decimal does not have a minus key.
else if (isAndroid() && hasDecimals && hasOnlyPositives)
inputMode = 'decimal';

this.inputElement.inputMode = inputMode;
}
if (
Expand Down
35 changes: 35 additions & 0 deletions packages/number-field/test/number-field.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1228,6 +1228,28 @@ describe('NumberField', () => {
expect(changeSpy.callCount).to.equal(1);
expect(lastChangeValue, 'last change value').to.equal(10);
});
xit('manages `inputMode` in iPad', async () => {
// setUserAgent is not currently supported by Playwright
await setUserAgent(
'Mozilla/5.0 (iPad; CPU OS 17_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.4 Mobile15E148 Safari/604.1'
);
el.min = 0;
await elementUpdated(el);
expect(el.focusElement.inputMode).to.equal('numeric');
el.min = -10;
await elementUpdated(el);
expect(el.focusElement.inputMode).to.equal('numeric');
el.min = undefined;
await elementUpdated(el);
expect(el.focusElement.inputMode).to.equal('numeric');
el.formatOptions = {
minimumFractionDigits: 1,
maximumFractionDigits: 2,
};
el.min = 0;
await elementUpdated(el);
expect(el.focusElement.inputMode).to.equal('decimal');
});
xit('manages `inputMode` in iPhone', async () => {
// setUserAgent is not currently supported by Playwright
await setUserAgent(
Expand All @@ -1239,6 +1261,9 @@ describe('NumberField', () => {
el.min = -10;
await elementUpdated(el);
expect(el.focusElement.inputMode).to.equal('text');
el.min = undefined;
await elementUpdated(el);
expect(el.focusElement.inputMode).to.equal('text');
el.formatOptions = {
minimumFractionDigits: 1,
maximumFractionDigits: 2,
Expand All @@ -1258,13 +1283,23 @@ describe('NumberField', () => {
el.min = -10;
await elementUpdated(el);
expect(el.focusElement.inputMode).to.equal('numeric');
el.min = undefined;
await elementUpdated(el);
expect(el.focusElement.inputMode).to.equal('numeric');
el.formatOptions = {
minimumFractionDigits: 1,
maximumFractionDigits: 2,
};
el.min = 0;
await elementUpdated(el);
expect(el.focusElement.inputMode).to.equal('decimal');
el.formatOptions = {
minimumFractionDigits: 1,
maximumFractionDigits: 2,
};
el.min = -10;
await elementUpdated(el);
expect(el.focusElement.inputMode).to.equal('numeric');
});
it('constrains `value`', async () => {
el.value = 0;
Expand Down
Loading