Skip to content

Commit

Permalink
feat(module:input): support setting status (#7472)
Browse files Browse the repository at this point in the history
* feat(module:input): support setting status

* feat(module:input): add tests

* feat(module:input): add tests for rtl

* feat(module:input): move input group tests to correct spec file
  • Loading branch information
simplejason committed May 31, 2022
1 parent 0d8249b commit 999215e
Show file tree
Hide file tree
Showing 9 changed files with 239 additions and 14 deletions.
4 changes: 3 additions & 1 deletion components/input/demo/module
Expand Up @@ -7,6 +7,7 @@ import { NzIconModule } from 'ng-zorro-antd/icon';
import { NzToolTipModule } from 'ng-zorro-antd/tooltip';
import { NzButtonModule } from 'ng-zorro-antd/button';
import { NzFormModule } from 'ng-zorro-antd/form';
import { NzSpaceModule } from 'ng-zorro-antd/space';
import { FormsModule } from '@angular/forms';

export const moduleList = [
Expand All @@ -19,5 +20,6 @@ export const moduleList = [
NzDatePickerModule,
NzIconModule,
NzToolTipModule,
NzButtonModule
NzButtonModule,
NzSpaceModule
];
14 changes: 14 additions & 0 deletions components/input/demo/status.md
@@ -0,0 +1,14 @@
---
order: 13
title:
zh-CN: 自定义状态
en-US: Status
---

## zh-CN

使用 `nzStatus` 为 Input 添加状态,可选 `error` 或者 `warning`

## en-US

Add status to Input with `nzStatus`, which could be `error` or `warning`.
21 changes: 21 additions & 0 deletions components/input/demo/status.ts
@@ -0,0 +1,21 @@
import { Component } from '@angular/core';

@Component({
selector: 'nz-demo-input-status',
template: `
<nz-space nzDirection="vertical" style="width: 100%">
<input *nzSpaceItem nz-input placeholder="Error" [(ngModel)]="value" nzStatus="error" />
<input *nzSpaceItem nz-input placeholder="Warning" [(ngModel)]="value" nzStatus="warning" />
<nz-input-group *nzSpaceItem [nzPrefix]="prefixTemplateClock" nzStatus="error">
<input type="text" nz-input placeholder="Error with prefix" />
</nz-input-group>
<nz-input-group *nzSpaceItem [nzPrefix]="prefixTemplateClock" nzStatus="warning">
<input type="text" nz-input placeholder="Warning with prefix" />
</nz-input-group>
<ng-template #prefixTemplateClock><i nz-icon nzType="clock-circle" nzTheme="outline"></i></ng-template>
</nz-space>
`
})
export class NzDemoInputStatusComponent {
value?: string;
}
3 changes: 2 additions & 1 deletion components/input/doc/index.en-US.md
Expand Up @@ -28,7 +28,7 @@ All props of input supported by [w3c standards](https://www.w3schools.com/tags/t
| `[nzSize]` | The size of the input box. Note: in the context of a form, the `large` size is used. | `'large' \| 'small' \| 'default'` | `'default'` |
| `[nzAutosize]` | Only used for `textarea`, height autosize feature, can be set to `boolean` or an object `{ minRows: 2, maxRows: 6 }` | `boolean \| { minRows: number, maxRows: number }` | `false` |
| `[nzBorderless]` | Whether hide border | `boolean` | `false` |

| `[nzStatus]` | Set validation status | `'error' \| 'warning'` | - |

### nz-input-group

Expand All @@ -40,6 +40,7 @@ All props of input supported by [w3c standards](https://www.w3schools.com/tags/t
| `[nzSuffix]` | The suffix icon for the Input, can work with `nzPrefix` | `string \| TemplateRef<void>` | - |
| `[nzCompact]` | Whether use compact style | `boolean` | `false` |
| `[nzSize]` | The size of `nz-input-group` specifies the size of the included `nz-input` fields | `'large' \| 'small' \| 'default'` | `'default'` |
| `[nzStatus]` | Set validation status | `'error' \| 'warning'` | - |

### nz-textarea-count

Expand Down
2 changes: 2 additions & 0 deletions components/input/doc/index.zh-CN.md
Expand Up @@ -28,6 +28,7 @@ nz-input 可以使用所有的W3C标准下的所有 [使用方式](https://www.w
| `[nzSize]` | 控件大小。注:标准表单内的输入框大小限制为 `large` | `'large' \| 'small' \| 'default'` | `'default'` |
| `[nzAutosize]` | 只可以用于 `textarea`,自适应内容高度,可设置为 `boolean` 或对象:`{ minRows: 2, maxRows: 6 }` | `boolean \| { minRows: number, maxRows: number }` | `false` |
| `[nzBorderless]` | 是否隐藏边框 | `boolean` | `false` |
| `[nzStatus]` | 设置校验状态 | `'error' \| 'warning'` | - |

### nz-input-group

Expand All @@ -40,6 +41,7 @@ nz-input 可以使用所有的W3C标准下的所有 [使用方式](https://www.w
| `[nzCompact]` | 是否用紧凑模式 | `boolean` | `false` |
| `[nzSearch]` | 是否用搜索框 | `boolean` | `false` |
| `[nzSize]` | `nz-input-group` 中所有的 `nz-input` 的大小 | `'large' \| 'small' \| 'default'` | `'default'` |
| `[nzStatus]` | 设置校验状态 | `'error' \| 'warning'` | - |

### nz-textarea-count

Expand Down
32 changes: 29 additions & 3 deletions components/input/input-group.component.ts
Expand Up @@ -19,15 +19,16 @@ import {
OnInit,
Optional,
QueryList,
Renderer2,
SimpleChanges,
TemplateRef,
ViewEncapsulation
} from '@angular/core';
import { merge, Subject } from 'rxjs';
import { map, mergeMap, startWith, switchMap, takeUntil } from 'rxjs/operators';

import { BooleanInput, NzSizeLDSType } from 'ng-zorro-antd/core/types';
import { InputBoolean } from 'ng-zorro-antd/core/util';
import { BooleanInput, NgClassInterface, NzSizeLDSType, NzStatus } from 'ng-zorro-antd/core/types';
import { getStatusClassNames, InputBoolean } from 'ng-zorro-antd/core/util';

import { NzInputDirective } from './input.directive';

Expand Down Expand Up @@ -131,6 +132,7 @@ export class NzInputGroupComponent implements AfterContentInit, OnChanges, OnIni
@Input() nzAddOnBefore?: string | TemplateRef<void>;
@Input() nzAddOnAfter?: string | TemplateRef<void>;
@Input() nzPrefix?: string | TemplateRef<void>;
@Input() nzStatus?: NzStatus;
@Input() nzSuffix?: string | TemplateRef<void>;
@Input() nzSize: NzSizeLDSType = 'default';
@Input() @InputBoolean() nzSearch = false;
Expand All @@ -142,11 +144,17 @@ export class NzInputGroupComponent implements AfterContentInit, OnChanges, OnIni
focused = false;
disabled = false;
dir: Direction = 'ltr';
// status
prefixCls: string = 'ant-input';
affixStatusCls: NgClassInterface = {};
groupStatusCls: NgClassInterface = {};
hasFeedback: boolean = false;
private destroy$ = new Subject<void>();

constructor(
private focusMonitor: FocusMonitor,
private elementRef: ElementRef,
private renderer: Renderer2,
private cdr: ChangeDetectorRef,
@Optional() private directionality: Directionality
) {}
Expand Down Expand Up @@ -197,7 +205,8 @@ export class NzInputGroupComponent implements AfterContentInit, OnChanges, OnIni
nzAddOnAfter,
nzAddOnBefore,
nzAddOnAfterIcon,
nzAddOnBeforeIcon
nzAddOnBeforeIcon,
nzStatus
} = changes;
if (nzSize) {
this.updateChildrenInputSize();
Expand All @@ -210,10 +219,27 @@ export class NzInputGroupComponent implements AfterContentInit, OnChanges, OnIni
if (nzAddOnAfter || nzAddOnBefore || nzAddOnAfterIcon || nzAddOnBeforeIcon) {
this.isAddOn = !!(this.nzAddOnAfter || this.nzAddOnBefore || this.nzAddOnAfterIcon || this.nzAddOnBeforeIcon);
}
if (nzStatus) {
this.setStatusStyles();
}
}
ngOnDestroy(): void {
this.focusMonitor.stopMonitoring(this.elementRef);
this.destroy$.next();
this.destroy$.complete();
}

private setStatusStyles(): void {
// render status if nzStatus is set
this.affixStatusCls = getStatusClassNames(`${this.prefixCls}-affix-wrapper`, this.nzStatus, this.hasFeedback);
this.groupStatusCls = getStatusClassNames(`${this.prefixCls}-group-wrapper`, this.nzStatus, this.hasFeedback);
const statusCls = this.isAffix ? this.affixStatusCls : this.isAddOn ? this.groupStatusCls : {};
Object.keys(statusCls).forEach(status => {
if (statusCls[status]) {
this.renderer.addClass(this.elementRef.nativeElement, status);
} else {
this.renderer.removeClass(this.elementRef.nativeElement, status);
}
});
}
}
65 changes: 63 additions & 2 deletions components/input/input-group.spec.ts
@@ -1,9 +1,10 @@
import { Component, TemplateRef, ViewChild } from '@angular/core';
import { Component, DebugElement, TemplateRef, ViewChild } from '@angular/core';
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { By } from '@angular/platform-browser';

import { dispatchFakeEvent } from 'ng-zorro-antd/core/testing';
import { NzStatus } from 'ng-zorro-antd/core/types';
import { NzIconTestModule } from 'ng-zorro-antd/icon/testing';

import { NzInputGroupComponent } from './input-group.component';
Expand All @@ -19,7 +20,8 @@ describe('input-group', () => {
NzTestInputGroupAffixComponent,
NzTestInputGroupMultipleComponent,
NzTestInputGroupColComponent,
NzTestInputGroupMixComponent
NzTestInputGroupMixComponent,
NzTestInputGroupWithStatusComponent
],
providers: []
}).compileComponents();
Expand Down Expand Up @@ -239,6 +241,45 @@ describe('input-group', () => {
);
});
});
describe('status', () => {
let fixture: ComponentFixture<NzTestInputGroupWithStatusComponent>;
let inputElement: DebugElement;

beforeEach(() => {
fixture = TestBed.createComponent(NzTestInputGroupWithStatusComponent);
fixture.detectChanges();
inputElement = fixture.debugElement.query(By.directive(NzInputGroupComponent));
});

it('should className correct with prefix', () => {
fixture.detectChanges();
expect(inputElement.nativeElement.classList).toContain('ant-input-affix-wrapper-status-error');

fixture.componentInstance.status = 'warning';
fixture.detectChanges();
expect(inputElement.nativeElement.className).toContain('ant-input-affix-wrapper-status-warning');

fixture.componentInstance.status = '';
fixture.detectChanges();
expect(inputElement.nativeElement.className).not.toContain('ant-input-affix-wrapper-status-warning');
});

it('should className correct with addon', () => {
fixture.componentInstance.isAddon = true;
fixture.detectChanges();
// re-query input element
inputElement = fixture.debugElement.query(By.directive(NzInputGroupComponent));
expect(inputElement.nativeElement.classList).toContain('ant-input-group-wrapper-status-error');

fixture.componentInstance.status = 'warning';
fixture.detectChanges();
expect(inputElement.nativeElement.className).toContain('ant-input-group-wrapper-status-warning');

fixture.componentInstance.status = '';
fixture.detectChanges();
expect(inputElement.nativeElement.className).not.toContain('ant-input-group-wrapper-status-warning');
});
});
});
});

Expand Down Expand Up @@ -314,3 +355,23 @@ export class NzTestInputGroupMixComponent {}
`
})
export class NzTestInputGroupColComponent {}

@Component({
template: `
<ng-container *ngIf="!isAddon">
<nz-input-group [nzPrefix]="prefixTemplateClock" [nzStatus]="status">
<input type="text" nz-input />
</nz-input-group>
<ng-template #prefixTemplateClock><i nz-icon nzType="clock-circle" nzTheme="outline"></i></ng-template>
</ng-container>
<ng-container *ngIf="isAddon">
<nz-input-group nzAddOnAfterIcon="setting" [nzStatus]="status">
<input type="text" nz-input />
</nz-input-group>
</ng-container>
`
})
export class NzTestInputGroupWithStatusComponent {
isAddon = false;
status: NzStatus = 'error';
}
30 changes: 25 additions & 5 deletions components/input/input.directive.ts
Expand Up @@ -20,8 +20,8 @@ import { NgControl } from '@angular/forms';
import { Subject } from 'rxjs';
import { filter, takeUntil } from 'rxjs/operators';

import { BooleanInput, NzSizeLDSType } from 'ng-zorro-antd/core/types';
import { InputBoolean } from 'ng-zorro-antd/core/util';
import { BooleanInput, NgClassInterface, NzSizeLDSType, NzStatus } from 'ng-zorro-antd/core/types';
import { getStatusClassNames, InputBoolean } from 'ng-zorro-antd/core/util';

@Directive({
selector: 'input[nz-input],textarea[nz-input]',
Expand All @@ -40,6 +40,7 @@ export class NzInputDirective implements OnChanges, OnInit, OnDestroy {
static ngAcceptInputType_nzBorderless: BooleanInput;
@Input() @InputBoolean() nzBorderless = false;
@Input() nzSize: NzSizeLDSType = 'default';
@Input() nzStatus?: NzStatus;
@Input()
get disabled(): boolean {
if (this.ngControl && this.ngControl.disabled !== null) {
Expand All @@ -53,12 +54,16 @@ export class NzInputDirective implements OnChanges, OnInit, OnDestroy {
_disabled = false;
disabled$ = new Subject<boolean>();
dir: Direction = 'ltr';
// status
prefixCls: string = 'ant-input';
statusCls: NgClassInterface = {};
hasFeedback: boolean = false;
private destroy$ = new Subject<void>();

constructor(
@Optional() @Self() public ngControl: NgControl,
renderer: Renderer2,
elementRef: ElementRef,
private renderer: Renderer2,
private elementRef: ElementRef,
@Optional() private directionality: Directionality
) {
renderer.addClass(elementRef.nativeElement, 'ant-input');
Expand All @@ -83,14 +88,29 @@ export class NzInputDirective implements OnChanges, OnInit, OnDestroy {
}

ngOnChanges(changes: SimpleChanges): void {
const { disabled } = changes;
const { disabled, nzStatus } = changes;
if (disabled) {
this.disabled$.next(this.disabled);
}
if (nzStatus) {
this.setStatusStyles();
}
}

ngOnDestroy(): void {
this.destroy$.next();
this.destroy$.complete();
}

private setStatusStyles(): void {
// render status if nzStatus is set
this.statusCls = getStatusClassNames(this.prefixCls, this.nzStatus, this.hasFeedback);
Object.keys(this.statusCls).forEach(status => {
if (this.statusCls[status]) {
this.renderer.addClass(this.elementRef.nativeElement, status);
} else {
this.renderer.removeClass(this.elementRef.nativeElement, status);
}
});
}
}

0 comments on commit 999215e

Please sign in to comment.