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 #888

Merged
merged 34 commits into from
Aug 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
3976c30
feat(slider): add input event
Nantawat-Poothong Aug 7, 2023
ad73bea
refactor(BasicElement): update input event
Nantawat-Poothong Aug 7, 2023
2e729c9
feat(slider): add input event to number-field
Nantawat-Poothong Aug 8, 2023
c0e7c20
test(slider): add input test case
Nantawat-Poothong Aug 9, 2023
1dd249a
Merge branch 'v7' into feat/slider-input-event-v7
Nantawat-Poothong Aug 9, 2023
6f2f130
Merge branch 'v7' into feat/slider-input-event-v7
Nantawat-Poothong Aug 9, 2023
23af0a6
docs(slider): add input events document
Nantawat-Poothong Aug 10, 2023
3def02b
Merge branch 'v7' into feat/slider-input-event-v7
wattachai-lseg Aug 15, 2023
30aed91
docs(slider): update input event comment
Nantawat-Poothong Aug 15, 2023
10c6907
docs(slider): update to-input event desc
Nantawat-Poothong Aug 15, 2023
488690b
docs(slider): update from-input event desc
Nantawat-Poothong Aug 15, 2023
c5bd645
docs: update input comment in basic element
Nantawat-Poothong Aug 15, 2023
37301f6
chore(slider): update demo page
Nantawat-Poothong Aug 15, 2023
fd2fa05
chore(slider): update demo page
Nantawat-Poothong Aug 15, 2023
f9f0e6e
chore(slider): update demo page
Nantawat-Poothong Aug 15, 2023
f39ab06
Merge branch 'v7' into feat/slider-input-event-v7
wattachai-lseg Aug 16, 2023
e98b543
fix(slider): fired input event align with native
Nantawat-Poothong Aug 16, 2023
9410ea8
Merge remote-tracking branch 'origin/feat/slider-input-event-v7' into…
Nantawat-Poothong Aug 16, 2023
05cc515
test(slider): add number-field input event test
Nantawat-Poothong Aug 16, 2023
20a3a4a
Merge branch 'v7' into feat/slider-input-event-v7
Nantawat-Poothong Aug 16, 2023
2698088
test(slider): typo
Nantawat-Poothong Aug 17, 2023
5c08d9e
test(slider): typo
Nantawat-Poothong Aug 17, 2023
126e6d9
test(slider): typo
Nantawat-Poothong Aug 17, 2023
0d8fdf8
test(slider): typo
Nantawat-Poothong Aug 17, 2023
864d7ca
test(slider): typo
Nantawat-Poothong Aug 17, 2023
46998fc
test(slider): typo
Nantawat-Poothong Aug 17, 2023
5f68897
docs(slider): typo fires event
Nantawat-Poothong Aug 17, 2023
21ca940
docs(slider): typo fires event
Nantawat-Poothong Aug 17, 2023
522daeb
chore(slider): add comment for dynamically accessed variable
Nantawat-Poothong Aug 17, 2023
d10beb9
docs(slider): typo fires event
Nantawat-Poothong Aug 17, 2023
408df4a
test(slider): type
Nantawat-Poothong Aug 17, 2023
984321d
Merge remote-tracking branch 'origin/feat/slider-input-event-v7' into…
Nantawat-Poothong Aug 17, 2023
7e1f262
test(slider): typo
Nantawat-Poothong Aug 17, 2023
c6bc107
fix(slider): add value-changed listener for slider input event
Nantawat-Poothong Aug 18, 2023
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 @@ -15,6 +15,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`;

/**
* Basic element base class.
* Usually used for creating low-level elements.
Expand Down Expand Up @@ -141,6 +144,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;
wattachai-lseg marked this conversation as resolved.
Show resolved Hide resolved
}

/**
* 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 @@ -149,6 +149,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
271 changes: 268 additions & 3 deletions packages/elements/src/slider/__test__/slider.event.test.js
wattachai-lseg marked this conversation as resolved.
Show resolved Hide resolved
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 @@ -157,7 +158,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 @@ -1273,4 +1274,268 @@ 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