Skip to content

Commit

Permalink
fix: add min max and disabled props to input counter (#1154)
Browse files Browse the repository at this point in the history
* fix: add min max and disabled props to input counter

* fix: do not set count as min value when it is passed

* chore: improve coverage

* style: fix button borders and hover inside counter wrapper

* feat: allowing set max of digits
  • Loading branch information
yuri-silva-brisa authored Oct 8, 2024
1 parent bf91fb9 commit 19f5e6c
Show file tree
Hide file tree
Showing 6 changed files with 136 additions and 23 deletions.
5 changes: 5 additions & 0 deletions projects/ion/src/lib/core/types/input-counter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,9 @@ export type InputCountSize = 'sm' | 'md';

export interface IonInputCount {
inputSize: InputCountSize;
minValue?: number;
maxValue?: number;
maxDigits?: number;
disabled?: boolean;
count?: number;
}
11 changes: 8 additions & 3 deletions projects/ion/src/lib/input-counter/input-counter.component.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<div class="counter">
<ion-button
[disabled]="disabled"
data-testid="iconSub"
iconType="sub"
type="ghost"
Expand All @@ -9,14 +10,18 @@

<input
type="text"
oninput="this.value = this.value.replace(/[^0-9]/g, '').replace(/(\..*)\./g, '$1');"
[(ngModel)]="count"
data-testid="input-count"
(ngModelChange)="changeCount($event)"
[maxlength]="maxDigits"
[value]="count"
[(ngModel)]="count"
[disabled]="disabled"
(blur)="onBlurInput()"
(ngModelChange)="changeCount($event)"
oninput="this.value = this.value.replace(/[^0-9]/g, '').replace(/(\..*)\./g, '$1');"
/>

<ion-button
[disabled]="disabled"
data-testid="iconAdd"
iconType="add"
type="ghost"
Expand Down
20 changes: 20 additions & 0 deletions projects/ion/src/lib/input-counter/input-counter.component.scss
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,33 @@
display: inline-flex;
border: 1px solid $neutral-5;
border-radius: 6px;
overflow: hidden;

::ng-deep .ion-btn-ghost {
border-radius: 0;

&:disabled {
background-color: $neutral-2 !important;
cursor: not-allowed;

&:hover {
background-color: $neutral-2 !important;
}
}
}

& input {
border: none;
outline: none;
text-align: center;
font-size: 12px;
color: $neutral-5;

&:disabled {
background: $neutral-2;
color: $neutral-4;
cursor: not-allowed;
}
}
}

Expand Down
44 changes: 44 additions & 0 deletions projects/ion/src/lib/input-counter/input-counter.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ describe('InputCounter', () => {
const inputCounter = screen.getByTestId('input-count') as HTMLInputElement;
const value = '111';
userEvent.type(inputCounter, value);
fireEvent.blur(inputCounter);
expect(inputCounter.value).toBe(value);
userEvent.type(inputCounter, 'abc');
expect(inputCounter.value).toBe(value);
Expand All @@ -96,3 +97,46 @@ describe('InputCounter / Size', () => {
);
});
});

describe('InputCounter / Limits', () => {
it('should set the maximum value when a bigger number is texted', async () => {
const maxValue = 50;
await sut({ maxValue });
const inputCounter = screen.getByTestId('input-count') as HTMLInputElement;
userEvent.type(inputCounter, '1588');
fireEvent.blur(inputCounter);
expect(inputCounter).toHaveValue(maxValue.toString());
});

it('should set the minimum value when a smaller number is texted', async () => {
const minValue = 50;
await sut({ minValue });
const inputCounter = screen.getByTestId('input-count') as HTMLInputElement;
userEvent.clear(inputCounter);
userEvent.type(inputCounter, '10');
fireEvent.blur(inputCounter);
expect(inputCounter).toHaveValue(minValue.toString());
});

it('should dont exceed the max value by the increase button when it is clicked', async () => {
const maxValue = 100;
await sut({ maxValue, count: maxValue });
const inputCounter = screen.getByTestId('input-count') as HTMLInputElement;
const addButton = screen.getByTestId('iconAdd');
userEvent.click(addButton.firstChild as HTMLElement);
expect(inputCounter).toHaveValue(maxValue.toString());
});
});

describe('InputCounter / Disabled', () => {
it('should show the disabled state when it is setted', async () => {
await sut({ disabled: true });
const inputCounter = screen.getByTestId('input-count') as HTMLInputElement;
const subButton = screen.getByTestId('iconSub');
const addButton = screen.getByTestId('iconAdd');

expect(inputCounter).toBeDisabled();
expect(subButton.firstChild).toBeDisabled();
expect(addButton.firstChild).toBeDisabled();
});
});
30 changes: 26 additions & 4 deletions projects/ion/src/lib/input-counter/input-counter.component.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,25 @@
import { Component, Input, Output, EventEmitter } from '@angular/core';
import { Component, Input, Output, EventEmitter, OnInit } from '@angular/core';
import { IonInputCount } from '../core/types';

@Component({
selector: 'ion-input-counter',
templateUrl: './input-counter.component.html',
styleUrls: ['./input-counter.component.scss'],
})
export class IonInputCounterComponent {
export class IonInputCounterComponent implements OnInit {
@Input() inputSize: IonInputCount['inputSize'] = 'md';
@Input() maxValue?: number;
@Input() minValue = 0;
@Input() disabled = false;
@Input() count = 0;
@Input() maxDigits = 9;
@Output() changedValue = new EventEmitter();

private minValue = 0;
ngOnInit(): void {
if (this.minValue && !this.count) {
this.count = this.minValue;
}
}

public emitEvent(): void {
this.changedValue.emit({ newValue: this.count });
Expand All @@ -25,6 +33,7 @@ export class IonInputCounterComponent {
}

public countIncrement(): void {
if (this.maxValue && this.maxValue === this.count) return;
this.count++;
this.emitEvent();
}
Expand All @@ -33,7 +42,20 @@ export class IonInputCounterComponent {
const countNumeric = Number(count);
if (!isNaN(countNumeric)) {
this.count = countNumeric;
this.emitEvent();
}
}

public onBlurInput(): void {
this.count = this.getValidCount();
this.emitEvent();
}

private getValidCount(): number {
if (this.count < this.minValue) {
return this.minValue;
} else if (this.maxValue && this.count > this.maxValue) {
return this.maxValue;
}
return this.count;
}
}
49 changes: 33 additions & 16 deletions stories/InputCounter.stories.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,50 @@
import { Meta } from '@storybook/angular/types-6-0';
import { moduleMetadata, Story } from '@storybook/angular';
import { Meta, StoryObj } from '@storybook/angular/types-6-0';
import { moduleMetadata } from '@storybook/angular';
import { IonInputCounterComponent } from '../projects/ion/src/lib/input-counter/input-counter.component';
import { FormsModule } from '@angular/forms';
import { IonButtonModule } from '../projects/ion/src/lib/button/button.module';
import { InputCountSize } from 'ion/public-api';
import { CommonModule } from '@angular/common';

export default {
title: 'Ion/Data Entry/Input-Counter',
component: IonInputCounterComponent,
argTypes: {
inputSize: {
options: ['sm', 'md'] as InputCountSize[],
control: { type: 'radio' },
},
},
decorators: [
moduleMetadata({
imports: [FormsModule, IonButtonModule],
imports: [FormsModule, IonButtonModule, CommonModule],
declarations: [],
}),
],
} as Meta;
} as Meta<IonInputCounterComponent>;

const Template: Story<IonInputCounterComponent> = (
args: IonInputCounterComponent
) => ({
component: IonInputCounterComponent,
props: args,
});
type Story = StoryObj<IonInputCounterComponent>;

export const Medium: Story = {
args: {},
};

export const Small: Story = {
args: {
inputSize: 'sm',
},
};

export const Small = Template.bind({});
Small.args = {
InputSize: 'sm',
export const Disabled: Story = {
args: {
disabled: true,
},
};

export const Medium = Template.bind({});
Medium.args = {
InputSize: 'md',
export const WithMaxAndMinValues: Story = {
name: 'With max and min values',
args: {
maxValue: 100,
minValue: 1,
},
};

0 comments on commit 19f5e6c

Please sign in to comment.