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(module:cascader): support setting status #7452

Merged
merged 1 commit into from May 24, 2022
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
50 changes: 43 additions & 7 deletions components/cascader/cascader.component.ts
Expand Up @@ -17,12 +17,14 @@ import {
HostListener,
Input,
NgZone,
OnChanges,
OnDestroy,
OnInit,
Optional,
Output,
QueryList,
Renderer2,
SimpleChanges,
TemplateRef,
ViewChild,
ViewChildren,
Expand All @@ -37,8 +39,15 @@ import { NzConfigKey, NzConfigService, WithConfig } from 'ng-zorro-antd/core/con
import { NzNoAnimationDirective } from 'ng-zorro-antd/core/no-animation';
import { DEFAULT_CASCADER_POSITIONS } from 'ng-zorro-antd/core/overlay';
import { NzDestroyService } from 'ng-zorro-antd/core/services';
import { BooleanInput, NgClassType, NgStyleInterface, NzSafeAny } from 'ng-zorro-antd/core/types';
import { InputBoolean, toArray } from 'ng-zorro-antd/core/util';
import {
BooleanInput,
NgClassInterface,
NgClassType,
NgStyleInterface,
NzSafeAny,
NzStatus
} from 'ng-zorro-antd/core/types';
import { getStatusClassNames, InputBoolean, toArray } from 'ng-zorro-antd/core/util';
import { NzCascaderI18nInterface, NzI18nService } from 'ng-zorro-antd/i18n';

import { NzCascaderOptionComponent } from './cascader-li.component';
Expand Down Expand Up @@ -210,7 +219,9 @@ const defaultDisplayRender = (labels: string[]): string => labels.join(' / ');
'[class.ant-select-rtl]': `dir ==='rtl'`
}
})
export class NzCascaderComponent implements NzCascaderComponentAsSource, OnInit, OnDestroy, ControlValueAccessor {
export class NzCascaderComponent
implements NzCascaderComponentAsSource, OnInit, OnDestroy, OnChanges, ControlValueAccessor
{
readonly _nzModuleName: NzConfigKey = NZ_CONFIG_MODULE_NAME;
static ngAcceptInputType_nzShowInput: BooleanInput;
static ngAcceptInputType_nzShowArrow: BooleanInput;
Expand Down Expand Up @@ -256,6 +267,8 @@ export class NzCascaderComponent implements NzCascaderComponentAsSource, OnInit,
@Input() nzMenuStyle: NgStyleInterface | null = null;
@Input() nzMouseEnterDelay: number = 150; // ms
@Input() nzMouseLeaveDelay: number = 150; // ms
@Input() nzStatus?: NzStatus;

@Input() nzTriggerAction: NzCascaderTriggerType | NzCascaderTriggerType[] = ['click'] as NzCascaderTriggerType[];
@Input() nzChangeOn?: (option: NzCascaderOption, level: number) => boolean;
@Input() nzLoadData?: (node: NzCascaderOption, index: number) => PromiseLike<NzSafeAny>;
Expand All @@ -277,6 +290,10 @@ export class NzCascaderComponent implements NzCascaderComponentAsSource, OnInit,
@Output() readonly nzSelect = new EventEmitter<{ option: NzCascaderOption; index: number } | null>();
@Output() readonly nzClear = new EventEmitter<void>();

prefixCls: string = 'ant-select';
statusCls: NgClassInterface = {};
nzHasFeedback: boolean = false;

/**
* If the dropdown should show the empty content.
* `true` if there's no options.
Expand Down Expand Up @@ -359,15 +376,15 @@ export class NzCascaderComponent implements NzCascaderComponentAsSource, OnInit,
private cdr: ChangeDetectorRef,
private i18nService: NzI18nService,
private destroy$: NzDestroyService,
elementRef: ElementRef,
renderer: Renderer2,
private elementRef: ElementRef,
private renderer: Renderer2,
@Optional() private directionality: Directionality,
@Host() @Optional() public noAnimation?: NzNoAnimationDirective
) {
this.el = elementRef.nativeElement;
this.cascaderService.withComponent(this);
renderer.addClass(elementRef.nativeElement, 'ant-select');
renderer.addClass(elementRef.nativeElement, 'ant-cascader');
this.renderer.addClass(this.elementRef.nativeElement, 'ant-select');
this.renderer.addClass(this.elementRef.nativeElement, 'ant-cascader');
}

ngOnInit(): void {
Expand Down Expand Up @@ -430,6 +447,13 @@ export class NzCascaderComponent implements NzCascaderComponentAsSource, OnInit,
this.setupKeydownListener();
}

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

ngOnDestroy(): void {
this.clearDelayMenuTimer();
this.clearDelaySelectTimer();
Expand Down Expand Up @@ -761,6 +785,18 @@ export class NzCascaderComponent implements NzCascaderComponentAsSource, OnInit,
}
}

private setStatusStyles(): void {
// render status if nzStatus is set
this.statusCls = getStatusClassNames(this.prefixCls, this.nzStatus, this.nzHasFeedback);
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);
}
});
}

private setLocale(): void {
this.locale = this.i18nService.getLocaleData('global');
this.cdr.markForCheck();
Expand Down
38 changes: 37 additions & 1 deletion components/cascader/cascader.spec.ts
Expand Up @@ -68,7 +68,12 @@ describe('cascader', () => {
NzCascaderModule,
NzIconTestModule
],
declarations: [NzDemoCascaderDefaultComponent, NzDemoCascaderLoadDataComponent, NzDemoCascaderRtlComponent]
declarations: [
NzDemoCascaderDefaultComponent,
NzDemoCascaderLoadDataComponent,
NzDemoCascaderRtlComponent,
NzDemoCascaderStatusComponent
]
}).compileComponents();

inject([OverlayContainer], (oc: OverlayContainer) => {
Expand Down Expand Up @@ -1786,6 +1791,29 @@ describe('cascader', () => {
expect(itemEl21.querySelector('.anticon')?.classList).toContain('anticon-left');
}));
});

describe('Status', () => {
let fixture: ComponentFixture<NzDemoCascaderStatusComponent>;
let cascader: DebugElement;

beforeEach(() => {
fixture = TestBed.createComponent(NzDemoCascaderStatusComponent);
cascader = fixture.debugElement.query(By.directive(NzCascaderComponent));
});

it('should className correct', () => {
fixture.detectChanges();
expect(cascader.nativeElement.className).toContain('ant-select-status-error');

fixture.componentInstance.status = 'warning';
fixture.detectChanges();
expect(cascader.nativeElement.className).toContain('ant-select-status-warning');

fixture.componentInstance.status = '';
fixture.detectChanges();
expect(cascader.nativeElement.className).not.toContain('ant-select-status-warning');
});
});
});

const ID_NAME_LIST = [
Expand Down Expand Up @@ -2171,3 +2199,11 @@ export class NzDemoCascaderRtlComponent {
@ViewChild(Dir) dir!: Dir;
direction = 'rtl';
}

@Component({
template: ` <nz-cascader [nzOptions]="nzOptions" [nzStatus]="status"></nz-cascader> `
})
export class NzDemoCascaderStatusComponent {
public nzOptions: any[] | null = options1;
public status = 'error';
}
14 changes: 14 additions & 0 deletions components/cascader/demo/status.md
@@ -0,0 +1,14 @@
---
order: 18
title:
zh-CN: 自定义状态
en-US: Status
---

## zh-CN

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

## en-US

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

import { NzCascaderOption } from 'ng-zorro-antd/cascader';

@Component({
selector: 'nz-demo-cascader-status',
template: `
<nz-cascader [nzOptions]="nzOptions" nzStatus="error"></nz-cascader>
<nz-cascader [nzOptions]="nzOptions" nzStatus="warning"></nz-cascader>
`,
styles: [
`
.ant-cascader {
width: 100%;
margin-bottom: 8px;
}
`
]
})
export class NzDemoCascaderStatusComponent {
nzOptions: NzCascaderOption[] = [];
}
1 change: 1 addition & 0 deletions components/cascader/doc/index.en-US.md
Expand Up @@ -50,6 +50,7 @@ import { NzCascaderModule } from 'ng-zorro-antd/cascader';
| `[nzShowInput]` | Whether show input | `boolean` | `true` |
| `[nzShowSearch]` | Whether support search. Cannot be used with `[nzLoadData]` at the same time | `boolean\|NzShowSearchOptions` | `false` |
| `[nzSize]` | input size, one of `large` `default` `small` | `'large'\|'small'\|'default'` | `'default'` | ✅ |
| `[nzStatus]` | Set validation status | `'error' \| 'warning'` | - |
| `[nzSuffixIcon]` | The custom suffix icon | `string\|TemplateRef<void>` | - |
| `[nzValueProperty]` | the value property name of options | `string` | `'value'` |
| `(ngModelChange)` | Emit on values change | `EventEmitter<any[]>` | - |
Expand Down
1 change: 1 addition & 0 deletions components/cascader/doc/index.zh-CN.md
Expand Up @@ -51,6 +51,7 @@ import { NzCascaderModule } from 'ng-zorro-antd/cascader';
| `[nzShowInput]` | 显示输入框 | `boolean` | `true` |
| `[nzShowSearch]` | 是否支持搜索,默认情况下对 `label` 进行全匹配搜索,不能和 `[nzLoadData]` 同时使用 | `boolean\|NzShowSearchOptions` | `false` |
| `[nzSize]` | 输入框大小,可选 `large` `default` `small` | `'large'\|'small'\|'default'` | `'default'` | ✅ |
| `[nzStatus]` | 设置校验状态 | `'error' \| 'warning'` | - |
| `[nzSuffixIcon]` | 自定义的选择框后缀图标 | `string\|TemplateRef<void>` | - |
| `[nzValueProperty]` | 选项的实际值的属性名 | `string` | `'value'` |
| `(ngModelChange)` | 值发生变化时触发 | `EventEmitter<any[]>` | - |
Expand Down
2 changes: 2 additions & 0 deletions components/core/types/public-api.ts
Expand Up @@ -15,3 +15,5 @@ export * from './any';
export * from './control-value-accessor';
export * from './convert-input';
export * from './input-observable';
export * from './type';
export * from './status';
11 changes: 11 additions & 0 deletions components/core/types/status.ts
@@ -0,0 +1,11 @@
/**
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
*/

import { tuple } from './type';

export type NzStatus = '' | 'error' | 'warning';

const ValidateStatuses = tuple('success', 'warning', 'error', 'validating', '');
export type NzValidateStatus = typeof ValidateStatuses[number];
6 changes: 6 additions & 0 deletions components/core/types/type.ts
@@ -0,0 +1,6 @@
/**
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
*/

export const tuple = <T extends string[]>(...args: T): T => args;
1 change: 1 addition & 0 deletions components/core/util/public-api.ts
Expand Up @@ -19,3 +19,4 @@ export * from './measure-scrollbar';
export * from './ensure-in-bounds';
export * from './tick';
export * from './observable';
export * from './status-util';
20 changes: 20 additions & 0 deletions components/core/util/status-util.ts
@@ -0,0 +1,20 @@
/**
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
*/

import { NgClassInterface, NzValidateStatus } from 'ng-zorro-antd/core/types';

export function getStatusClassNames(
prefixCls: string,
status?: NzValidateStatus,
hasFeedback?: boolean
): NgClassInterface {
return {
[`${prefixCls}-status-success`]: status === 'success',
[`${prefixCls}-status-warning`]: status === 'warning',
[`${prefixCls}-status-error`]: status === 'error',
[`${prefixCls}-status-validating`]: status === 'validating',
[`${prefixCls}-has-feedback`]: hasFeedback
};
}