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

feat(slider): add input event #911

Merged
merged 2 commits into from
Aug 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions packages/core/src/elements/BasicElement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ const NOTIFY_REGEXP = /([a-zA-Z])(?=[A-Z])/g;
const toChangedEvent = (name: string): string =>
`${name.replace(NOTIFY_REGEXP, '$1-').toLowerCase()}-changed`;

const toInputEvent = (name: string): string =>
name === 'value' ? 'input' : `${name.replace(NOTIFY_REGEXP, '$1-').toLowerCase()}-input`;

/**
* Gets a computed style value from any HTML element
* @param el Element to get computed styles from
Expand Down Expand Up @@ -186,6 +189,30 @@ export abstract class BasicElement extends LitElement {
return !event.defaultPrevented;
}

/**
* Dispatch input event when the property's value is being input.
* Event name is transformed to hyphen case, e.g. myProperty -> my-property-input.
* Except for value property it will transformed to input instead of value-input.
* Event details contain the new value.
* @param name Property name
* @param value New value
* @param [cancelable=false] Set to true if the event can be cancelled
* @returns false if the event is prevented
*/
protected notifyPropertyInput(name: string, value: unknown, cancelable = false): boolean {
const event = new CustomEvent(toInputEvent(name), {
cancelable,
bubbles: false,
detail: {
value
}
});

this.dispatchEvent(event);

return !event.defaultPrevented;
}

/**
* Registers the connection to the DOM
* @returns {void}
Expand Down
9 changes: 9 additions & 0 deletions packages/elements/src/slider/__demo__/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,15 @@
valueToText.nodeValue = 'To:' + e.detail.value;
minRangeText.nodeValue = 'MinRage:' + slider.minRange;
});
slider.addEventListener('input', function (e) {
valueText.nodeValue = 'Value Input:' + e.detail.value;
});
slider.addEventListener('from-input', function (e) {
valueFromText.nodeValue = 'From Input:' + e.detail.value;
});
slider.addEventListener('to-input', function (e) {
valueToText.nodeValue = 'To Input:' + e.detail.value;
});
});
</script>
</body>
Expand Down
270 changes: 267 additions & 3 deletions packages/elements/src/slider/__test__/slider.event.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { calculateValue, tabSliderPosition } from './utils.js';

const isDragging = (el) => el.dragging;
const getSliderTrackElement = (el) => el.sliderRef.value;
const getNumberField = (el, name) => el.shadowRoot.querySelector(`ef-number-field[name=${name}]`);

describe('slider/Events', function () {
let el;
Expand All @@ -17,7 +18,7 @@ describe('slider/Events', function () {
slider = getSliderTrackElement(el);
});

it('Drag thumb slider on desktop', async function () {
it('Drag thumb slider with mouse', async function () {
setTimeout(() => slider.dispatchEvent(new MouseEvent('mousedown')));
await oneEvent(slider, 'mousedown');
expect(isDragging(el)).to.be.true;
Expand All @@ -31,7 +32,7 @@ describe('slider/Events', function () {
expect(el.value).to.equal(calculateValue(el, 100).toFixed(0).toString());
});

it('Drag thumb slider has range on desktop', async function () {
it('Drag thumb slider has range with mouse', async function () {
el.range = true;
await elementUpdated(el);
expect(el.from).to.equal('0');
Expand Down Expand Up @@ -154,7 +155,7 @@ describe('slider/Events', function () {
expect(el.to).to.equal(el.from);
});

it('Click near from thumb and click near to thumb has range slider on desktop', async function () {
it('Click near from thumb and click near to thumb has range slider with mouse', async function () {
el.range = true;
await elementUpdated(el);
expect(el.from).to.equal('0');
Expand Down Expand Up @@ -1270,4 +1271,267 @@ describe('slider/Events', function () {
// Check call fire event
expect(callCountValue).to.equal(1);
});
it('Should fires input event when dragging from thumb slider with mouse', async function () {
// Drag 'value' from 0 to 10
const dragPositionStart = tabSliderPosition(el, 0);
const dragPosition10 = tabSliderPosition(el, 10);

let callCountValue = 0;
let inputValue = 0;
el.addEventListener('input', (e) => {
callCountValue += 1;
inputValue = e.detail.value;
});
setTimeout(() =>
slider.dispatchEvent(new MouseEvent('mousedown', { clientX: dragPositionStart, clientY: 0 }))
);
await oneEvent(slider, 'mousedown');
setTimeout(() =>
window.dispatchEvent(new MouseEvent('mousemove', { clientX: dragPosition10, clientY: 0 }))
);
await oneEvent(window, 'mousemove');
setTimeout(() =>
window.dispatchEvent(new MouseEvent('mouseup', { clientX: dragPosition10, clientY: 0 }))
);
await oneEvent(window, 'mouseup');

// Check call fire event
expect(callCountValue).to.equal(1);
expect(inputValue).to.equal(calculateValue(el, dragPosition10).toString());
});

it('Should fires input event twice when start dragging far from thumb slider with mouse', async function () {
// Drag 'value' from 10 to 0
const dragPositionStart = tabSliderPosition(el, 0);
const dragPosition10 = tabSliderPosition(el, 10);

let callCountValue = 0;
let inputValue = 0;
el.addEventListener('input', (e) => {
callCountValue += 1;
inputValue = e.detail.value;
});
setTimeout(() =>
slider.dispatchEvent(new MouseEvent('mousedown', { clientX: dragPosition10, clientY: 0 }))
);
await oneEvent(slider, 'mousedown');
setTimeout(() =>
window.dispatchEvent(new MouseEvent('mousemove', { clientX: dragPositionStart, clientY: 0 }))
);
await oneEvent(window, 'mousemove');
setTimeout(() =>
window.dispatchEvent(new MouseEvent('mouseup', { clientX: dragPositionStart, clientY: 0 }))
);
await oneEvent(window, 'mouseup');

// Check call fire event
expect(callCountValue).to.equal(2);
expect(inputValue).to.equal(calculateValue(el, dragPositionStart).toString());
});

it('Should fires from-input event when dragging thumb slider range with mouse', async function () {
// Drag 'from' from 0 to 10
const dragPositionStart = tabSliderPosition(el, 0);
const dragPosition10 = tabSliderPosition(el, 10);

el.range = true;
await elementUpdated(el);

let callCountValue = 0;
let inputFromValue = 0;
el.addEventListener('from-input', (e) => {
callCountValue += 1;
inputFromValue = e.detail.value;
});

// Drag from
setTimeout(() =>
slider.dispatchEvent(new MouseEvent('mousedown', { clientX: dragPositionStart, clientY: 0 }))
);
await oneEvent(slider, 'mousedown');
setTimeout(() =>
window.dispatchEvent(new MouseEvent('mousemove', { clientX: dragPosition10, clientY: 0 }))
);
await oneEvent(window, 'mousemove');
setTimeout(() =>
window.dispatchEvent(new MouseEvent('mouseup', { clientX: dragPosition10, clientY: 0 }))
);
await oneEvent(window, 'mouseup');

// Check call fire event
expect(callCountValue).to.equal(1);
expect(inputFromValue).to.equal(calculateValue(el, dragPosition10).toString());
});

it('Should fires to-input event when dragging thumb slider range with mouse', async function () {
// Drag 'to' from 100 to 80
const dragPositionEnd = tabSliderPosition(el, 100);
const dragPosition80 = tabSliderPosition(el, 80);

el.range = true;
await elementUpdated(el);

let callCountValue = 0;
let inputToValue = 0;
el.addEventListener('to-input', (e) => {
callCountValue += 1;
inputToValue = e.detail.value;
});

// Drag to
setTimeout(() =>
slider.dispatchEvent(new MouseEvent('mousedown', { clientX: dragPositionEnd, clientY: 0 }))
);
await oneEvent(slider, 'mousedown');
setTimeout(() =>
window.dispatchEvent(new MouseEvent('mousemove', { clientX: dragPosition80, clientY: 0 }))
);
await oneEvent(window, 'mousemove');
setTimeout(() =>
window.dispatchEvent(new MouseEvent('mouseup', { clientX: dragPosition80, clientY: 0 }))
);
await oneEvent(window, 'mouseup');
// Check call fire event
expect(callCountValue).to.equal(1);
expect(inputToValue).to.equal(calculateValue(el, dragPosition80).toString());
});

it('Should fires input event when input value from number-field', async function () {
el.showInputField = '';
await elementUpdated(el);

const inputEvent = 'input';
let callCountValue = 0;
let inputValue = 0;
el.addEventListener(inputEvent, (e) => {
callCountValue += 1;
inputValue = e.detail.value;
});
const input = getNumberField(el, 'value');
input.value = '40';
setTimeout(() => input.dispatchEvent(new Event('input')));
await oneEvent(el, inputEvent);

// Check call fire event
expect(callCountValue).to.equal(1);
expect(inputValue).to.equal('40');
});

it('Should fires from-input event when input value from `from` number-field', async function () {
el.showInputField = '';
el.range = true;
await elementUpdated(el);

const inputFromEvent = 'from-input';
let callCountValue = 0;
let inputFromValue = 0;
el.addEventListener(inputFromEvent, (e) => {
callCountValue += 1;
inputFromValue = e.detail.value;
});
const input = getNumberField(el, 'from');
input.value = '40';
setTimeout(() => input.dispatchEvent(new Event('input')));
await oneEvent(el, inputFromEvent);

// Check call fire event
expect(callCountValue).to.equal(1);
expect(inputFromValue).to.equal('40');
});

it('Should fires to-input event when input value from `to` number-field', async function () {
el.showInputField = '';
el.range = true;
await elementUpdated(el);

const inputToEvent = 'to-input';
let callCountValue = 0;
let inputToValue = 0;
el.addEventListener(inputToEvent, (e) => {
callCountValue += 1;
inputToValue = e.detail.value;
});
const input = getNumberField(el, 'to');
input.value = '40';
setTimeout(() => input.dispatchEvent(new Event('input')));
await oneEvent(el, inputToEvent);

// Check call fire event
expect(callCountValue).to.equal(1);
expect(inputToValue).to.equal('40');
});

it('Should fires input event when press ArrowUp/ArrowDown from number-field', async function () {
el.showInputField = '';
await elementUpdated(el);

const inputEvent = 'input';
let callCountValue = 0;
let inputValue = 0;
el.addEventListener(inputEvent, (e) => {
callCountValue += 1;
inputValue = e.detail.value;
});
const input = getNumberField(el, 'value');

setTimeout(() => input.inputElement.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowUp' })));
await oneEvent(el, inputEvent);
expect(callCountValue).to.equal(1);
expect(inputValue).to.equal('1');

setTimeout(() => input.inputElement.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown' })));
await oneEvent(el, inputEvent);
expect(callCountValue).to.equal(2);
expect(inputValue).to.equal('0');
});

it('Should fires from-input event when press ArrowUp/ArrowDown from `from` number-field', async function () {
el.showInputField = '';
el.range = true;
await elementUpdated(el);

const inputFromEvent = 'from-input';
let callCountValue = 0;
let inputFromValue = 0;
el.addEventListener(inputFromEvent, (e) => {
callCountValue += 1;
inputFromValue = e.detail.value;
});
const input = getNumberField(el, 'from');

setTimeout(() => input.inputElement.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowUp' })));
await oneEvent(el, inputFromEvent);
expect(callCountValue).to.equal(1);
expect(inputFromValue).to.equal('1');

setTimeout(() => input.inputElement.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown' })));
await oneEvent(el, inputFromEvent);
expect(callCountValue).to.equal(2);
expect(inputFromValue).to.equal('0');
});

it('Should fires to-input event when press ArrowUp/ArrowDown from `to` number-field', async function () {
el.showInputField = '';
el.range = true;
await elementUpdated(el);

const inputToEvent = 'to-input';
let callCountValue = 0;
let inputToValue = 0;
el.addEventListener(inputToEvent, (e) => {
callCountValue += 1;
inputToValue = e.detail.value;
});
const input = getNumberField(el, 'to');

setTimeout(() => input.inputElement.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown' })));
await oneEvent(el, inputToEvent);
expect(callCountValue).to.equal(1);
expect(inputToValue).to.equal('99');

setTimeout(() => input.inputElement.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowUp' })));
await oneEvent(el, inputToEvent);
expect(callCountValue).to.equal(2);
expect(inputToValue).to.equal('100');
});
});
Loading