Skip to content

Commit

Permalink
capricorn86#945@major: Add support for set valueAsNumber.
Browse files Browse the repository at this point in the history
  • Loading branch information
malko committed Jun 4, 2023
1 parent 796f16a commit 932f0f0
Show file tree
Hide file tree
Showing 2 changed files with 158 additions and 0 deletions.
Expand Up @@ -23,6 +23,24 @@ import IShadowRoot from '../shadow-root/IShadowRoot';
import NodeList from '../node/NodeList';
import EventPhaseEnum from '../../event/EventPhaseEnum';

/**
* Return iso-week number from given date
*
* @param date Date|number.
* @returns Iso-week string.
*/
export const dateIsoWeek = (date: Date | number): string => {
date = new Date(date);
const day = (date.getDay() + 6) % 7;
date.setDate(date.getDate() - day + 3);
const firstThursday = date.getTime();
date.setMonth(0, 1);
if (date.getDay() !== 4) {
date.setMonth(0, 1 + ((4 - date.getDay() + 7) % 7));
}
return String(1 + Math.ceil((firstThursday - date.getTime()) / 604800000)).padStart(2, '0');
};

/**
* HTML Input Element.
*
Expand Down Expand Up @@ -841,6 +859,67 @@ export default class HTMLInputElement extends HTMLElement implements IHTMLInputE
}
}

/**
* Sets value from a number.
*
* @param value number.
*/
public set valueAsNumber(value: number) {
// Specs at https://html.spec.whatwg.org/multipage/input.html
switch (this.type) {
case 'number':
case 'range':
// We Rely on HTMLInputElementValueSanitizer
this.value = Number(value).toString();
break;
case 'date':
case 'datetime-local': {
const d = new Date(Number(value));
if (isNaN(d.getTime())) {
// Reset to default value
this.value = '';
break;
}
if (this.type == 'date') {
this.value = d.toISOString().slice(0, 10);
} else {
this.value = d.toISOString().slice(0, -1);
}
break;
}
case 'month':
if (!Number.isInteger(value) || value < 0) {
this.value = '';
} else {
this.value = new Date(Date.UTC(1970, Number(value))).toISOString().slice(0, 7);
}
break;
case 'time':
if (!Number.isInteger(value) || value < 0) {
this.value = '';
} else {
this.value = new Date(Number(value)).toISOString().slice(11, -1);
}
break;
case 'week':
case 'week': {
const d = new Date(Number(value));
if (isNaN(d.getTime())) {
this.value = '';
} else {
d.setTime(d.getTime() + d.getTimezoneOffset() * 60000);
this.value = d.toISOString().split('T')[0].slice(0, 5) + 'W' + dateIsoWeek(d);
}
break;
}
default:
throw new DOMException(
"Failed to set the 'valueAsNumber' property on 'HTMLInputElement': This input element does not support Number values.",
DOMExceptionNameEnum.invalidStateError
);
}
}

/**
* Returns the associated label elements.
*
Expand Down
Expand Up @@ -9,6 +9,7 @@ import HTMLInputElementSelectionModeEnum from '../../../src/nodes/html-input-ele
import HTMLInputElementSelectionDirectionEnum from '../../../src/nodes/html-input-element/HTMLInputElementSelectionDirectionEnum';
import ValidityState from '../../../src/validity-state/ValidityState';
import { IHTMLFormElement } from '../../../src';
import DOMExceptionNameEnum from '../../../src/exception/DOMExceptionNameEnum';

describe('HTMLInputElement', () => {
let window: IWindow;
Expand Down Expand Up @@ -275,6 +276,84 @@ describe('HTMLInputElement', () => {
});
});
});
describe('set valueAsNumber()', () => {
describe('Should throw exception for non-numeric input', () => {
it.each([
'button',
'checkbox',
'color',
'email',
'file',
'hidden',
'image',
'password',
'radio',
'reset',
'search',
'submit',
'tel',
'text',
'url'
])('Of type %s.', (type) => {
element.type = type;
expect(() => (element.valueAsNumber = 0)).toThrowError(
new DOMException(
"Failed to set the 'valueAsNumber' property on 'HTMLInputElement': This input element does not support Number values.",
DOMExceptionNameEnum.invalidStateError
)
);
});
});

describe('With invalid value for', () => {
it.each(['number', 'date', 'datetime-local', 'month', 'time', 'week'])(
'Type "%s" should set default empty value.',
(type) => {
element.type = type;
expect(() => {
// @ts-ignore
element.valueAsNumber = 'x';
}).not.toThrow();
expect(element.value).toBe('');
}
);
it(`Type "range" should set default middle range value.`, () => {
element.type = 'range';
expect(() => {
// @ts-ignore
element.valueAsNumber = 'x';
}).not.toThrow();
expect(element.value).toBe('50');
});
});

describe('With valid value for', () => {
const testCases = [
{ type: 'number', value: 123, want: '123' },
{ type: 'number', value: 1.23, want: '1.23' },
{ type: 'range', value: 75, want: '75' },
{ type: 'range', value: 12.5, want: '12.5' },
{ type: 'date', value: new Date('2019-01-01').getTime(), want: '2019-01-01' },
{ type: 'datetime-local', value: 1546300800000, want: '2019-01-01T00:00' },
{ type: 'month', value: 588, want: '2019-01' },
{ type: 'time', value: 0, want: '00:00' },
{ type: 'time', value: 43200000, want: '12:00' },
{ type: 'time', value: 68100000, want: '18:55' },
{ type: 'time', value: 83709010, want: '23:15:09.01' },
{ type: 'week', value: 1685318400000, want: '2023-W22' },
{ type: 'week', value: 1672531200000, want: '2022-W52' }
];
it.each(testCases)(
`Type "$type" should set a corresponding value`,
({ type, value, want }) => {
element.type = type;
element.valueAsNumber = value;
expect(element.value).toEqual(want);
}
);
});
});

describe('get selectionStart()', () => {
it('Returns the length of the attribute "value" if value has not been set using the property.', () => {
element.setAttribute('value', 'TEST_VALUE');
Expand Down

0 comments on commit 932f0f0

Please sign in to comment.