Skip to content

Commit

Permalink
feat(number-field): add new event step-up and step-down (#910)
Browse files Browse the repository at this point in the history
  • Loading branch information
Theeraphat-Sorasetsakul authored Sep 6, 2023
1 parent 781b9a8 commit 3f42c66
Show file tree
Hide file tree
Showing 3 changed files with 166 additions and 8 deletions.
43 changes: 43 additions & 0 deletions packages/elements/src/number-field/__demo__/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,49 @@
</script>
</demo-block>

<demo-block layout="normal" header="Step up event and Step down event" tags="event">
<ef-number-field id="step-up-down" placeholder="step-up and step-down"></ef-number-field>
<div>Event called: <code id="step-up-down-event-text"></code></div>
<p>Value: <code id="step-up-down-value-text"></code></p>

<ef-number-field
id="step-up-down-prevent-default"
placeholder="step-up and step-down"
></ef-number-field>
<p>
Set preventDefault. Value changed event should not call:
<code id="step-up-down-prevent-default-text"></code>
</p>
<script>
(function () {
const element = document.getElementById('step-up-down');
const eventNameText = document.getElementById('step-up-down-event-text');
const valueText = document.getElementById('step-up-down-value-text');
element.addEventListener('step-up', (event) => {
eventNameText.innerHTML = 'step-up';
});
element.addEventListener('step-down', (event) => {
eventNameText.innerHTML = 'step-down';
});
element.addEventListener('value-changed', (event) => {
valueText.innerHTML = event.detail.value;
});

const preventDefaultElement = document.getElementById('step-up-down-prevent-default');
const valueChangedText = document.getElementById('step-up-down-prevent-default-text');
preventDefaultElement.addEventListener('step-up', (event) => {
event.preventDefault();
});
preventDefaultElement.addEventListener('step-down', (event) => {
event.preventDefault();
});
preventDefaultElement.addEventListener('value-changed', (event) => {
valueChangedText.innerHTML = 'value-change called';
});
})();
</script>
</demo-block>

<demo-block layout="normal" header="Focus/Blur/Select" tags="event">
<ef-number-field class="focusblur" placeholder="Focus/Blur"></ef-number-field>
<p>
Expand Down
97 changes: 97 additions & 0 deletions packages/elements/src/number-field/__test__/number-field.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,71 @@ describe('number-field/NumberField', function () {
await elementUpdated(el);
expect(el.value).to.equal('0', 'value should not be less then zero');
});
it('Tapping spinner up should fire step-up and value-changed', async function () {
let upClickedCount = 0;
let valueChangedCount = 0;
el.addEventListener('step-up', () => {
upClickedCount += 1;
});
el.addEventListener('value-changed', () => {
valueChangedCount += 1;
});
setTimeout(() => dispatchTapEvent(spinnerUpEl));
await oneEvent(spinnerUpEl, 'tap');
await elementUpdated(el);
expect(el.value).to.equal('1');
expect(upClickedCount).to.equal(1);
expect(valueChangedCount).to.equal(1);
});
it('Tapping spinner down should fire step-down and value-changed', async function () {
let downClickedCount = 0;
let valueChangedCount = 0;
el.addEventListener('step-down', () => {
downClickedCount += 1;
});
el.addEventListener('value-changed', () => {
valueChangedCount += 1;
});
setTimeout(() => dispatchTapEvent(spinnerDownEl));
await oneEvent(spinnerDownEl, 'tap');
await elementUpdated(el);
expect(el.value).to.equal('-1');
expect(downClickedCount).to.equal(1);
expect(valueChangedCount).to.equal(1);
});
it('value-changed event should not fire when set prevent default to step-up', async function () {
const value = el.value;
let valueChangedCount = 0;
el.addEventListener('step-up', (event) => {
event.preventDefault();
});
el.addEventListener('value-changed', () => {
valueChangedCount += 1;
});
setTimeout(() => dispatchTapEvent(spinnerUpEl));
await oneEvent(spinnerUpEl, 'tap');
await elementUpdated(el);
expect(el.value).to.equal(value, 'Should not update value if step-up does prevent default');
expect(valueChangedCount).to.equal(0, 'Should not call value-changed if step-up does prevent default');
});
it('value-changed event should not fire when set prevent default to step-down', async function () {
const value = el.value;
let valueChangedCount = 0;
el.addEventListener('step-down', (event) => {
event.preventDefault();
});
el.addEventListener('value-changed', () => {
valueChangedCount += 1;
});
setTimeout(() => dispatchTapEvent(spinnerDownEl));
await oneEvent(spinnerDownEl, 'tap');
await elementUpdated(el);
expect(el.value).to.equal(value, 'Should not update value if step-down does prevent default');
expect(valueChangedCount).to.equal(
0,
'Should not call value-changed if step-down does prevent default'
);
});
});

describe('Keyboard Events', function () {
Expand Down Expand Up @@ -361,6 +426,38 @@ describe('number-field/NumberField', function () {
el.inputElement.dispatchEvent(keyboardEvent('keydown', { key: 'ArrowDown' }));
expect(el.value).to.be.equal('2');
});
it('Arrow up should fire step-up', async function () {
const el = await fixture('<ef-number-field></ef-number-field>');
let upClickedCount = 0;
let valueChangedCount = 0;
el.addEventListener('step-up', () => {
upClickedCount += 1;
});
el.addEventListener('value-changed', () => {
valueChangedCount += 1;
});
el.inputElement.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowUp' }));
await elementUpdated(el);
expect(el.value).to.equal('1');
expect(upClickedCount).to.equal(1);
expect(valueChangedCount).to.equal(1);
});
it('Arrow down should fire step-down', async function () {
const el = await fixture('<ef-number-field></ef-number-field>');
let downClickedCount = 0;
let valueChangedCount = 0;
el.addEventListener('step-down', () => {
downClickedCount += 1;
});
el.addEventListener('value-changed', () => {
valueChangedCount += 1;
});
el.inputElement.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown' }));
await elementUpdated(el);
expect(el.value).to.equal('-1');
expect(downClickedCount).to.equal(1);
expect(valueChangedCount).to.equal(1);
});
});

describe('No Spinner', function () {
Expand Down
34 changes: 26 additions & 8 deletions packages/elements/src/number-field/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ enum Direction {
*
* @fires value-changed - Fired when user commits a value change. The event is not triggered if `value` property is changed programmatically.
* @fires error-changed - Fired when user inputs invalid value. The event is not triggered if `error` property is changed programmatically.
* @fires step-up - Fired when user acts value up on both pressing arrow up or tapping the spinner up. The event is not triggered if stepUp method is called programmatically.
* @fires step-down - Fired when user acts value down on both pressing arrow down or tapping the spinner down. The event is not triggered if stepDown method is called programmatically.
*
* @attr {boolean} disabled - Set disabled state
* @prop {boolean} [disabled=false] - Set disabled state
Expand Down Expand Up @@ -307,6 +309,20 @@ export class NumberField extends FormFieldElement {
event.preventDefault();
}

/**
* Trigger step-up or step-down event and return the event is cancelled
* @param direction Up or Down
* @returns {boolean} false if cancelled event. And true otherwise.
*/
private dispatchStepEvent(direction: Direction): boolean {
const eventName = direction === Direction.Up ? 'step-up' : 'step-down';
return this.dispatchEvent(
new CustomEvent(eventName, {
cancelable: true
})
);
}

/**
* Run when spinner has been tapped
* @param event tap event
Expand All @@ -318,7 +334,6 @@ export class NumberField extends FormFieldElement {
}

const target = event.target;

if (target === this.spinnerDownEl) {
this.onApplyStep(Direction.Down);
} else if (target === this.spinnerUpEl) {
Expand All @@ -332,13 +347,16 @@ export class NumberField extends FormFieldElement {
* @returns {void}
*/
protected onApplyStep(direction: Direction): void {
try {
this.applyStepDirection(direction);
this.dispatchEvent(new InputEvent('input'));
this.setSilentlyValueAndNotify();
} catch (error) {
// According to specs stepDown/stepUp may fail for some invalid inputs
// do nothing and report nothing in that case
const event = this.dispatchStepEvent(direction);
if (event) {
try {
this.applyStepDirection(direction);
this.dispatchEvent(new InputEvent('input'));
this.setSilentlyValueAndNotify();
} catch (error) {
// According to specs stepDown/stepUp may fail for some invalid inputs
// do nothing and report nothing in that case
}
}
}

Expand Down

0 comments on commit 3f42c66

Please sign in to comment.