Skip to content

Commit 3d66d22

Browse files
authored
refactor(material-experimental/mdc-chips): remove usage of MDC adapter (#24990)
Reworks the MDC-based chips not to use the MDC adapters.
1 parent 988faa6 commit 3d66d22

22 files changed

+513
-929
lines changed

src/material-experimental/mdc-chips/BUILD.bazel

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ ng_module(
2929
"@npm//@angular/common",
3030
"@npm//@angular/core",
3131
"@npm//@angular/forms",
32-
"@npm//@material/chips",
3332
],
3433
)
3534

@@ -91,7 +90,6 @@ ng_test_library(
9190
"@npm//@angular/common",
9291
"@npm//@angular/forms",
9392
"@npm//@angular/platform-browser",
94-
"@npm//@material/chips",
9593
"@npm//rxjs",
9694
],
9795
)

src/material-experimental/mdc-chips/chip-action.ts

Lines changed: 46 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -6,147 +6,92 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9-
import {
10-
AfterViewInit,
11-
ChangeDetectorRef,
12-
Directive,
13-
ElementRef,
14-
Inject,
15-
Input,
16-
OnChanges,
17-
OnDestroy,
18-
SimpleChanges,
19-
} from '@angular/core';
20-
import {DOCUMENT} from '@angular/common';
21-
import {
22-
MDCChipActionAdapter,
23-
MDCChipActionFoundation,
24-
MDCChipActionType,
25-
MDCChipPrimaryActionFoundation,
26-
} from '@material/chips';
27-
import {emitCustomEvent} from './emit-event';
28-
import {
29-
CanDisable,
30-
HasTabIndex,
31-
mixinDisabled,
32-
mixinTabIndex,
33-
} from '@angular/material-experimental/mdc-core';
9+
import {BooleanInput, coerceBooleanProperty} from '@angular/cdk/coercion';
10+
import {ENTER, SPACE} from '@angular/cdk/keycodes';
11+
import {Directive, ElementRef, Inject, Input} from '@angular/core';
12+
import {HasTabIndex, mixinTabIndex} from '@angular/material-experimental/mdc-core';
13+
import {MAT_CHIP} from './tokens';
3414

35-
const _MatChipActionMixinBase = mixinTabIndex(mixinDisabled(class {}), -1);
15+
abstract class _MatChipActionBase {
16+
abstract disabled: boolean;
17+
}
18+
19+
const _MatChipActionMixinBase = mixinTabIndex(_MatChipActionBase, -1);
3620

3721
/**
38-
* Interactive element within a chip.
22+
* Section within a chip.
3923
* @docs-private
4024
*/
4125
@Directive({
4226
selector: '[matChipAction]',
4327
inputs: ['disabled', 'tabIndex'],
4428
host: {
4529
'class': 'mdc-evolution-chip__action mat-mdc-chip-action',
46-
'[class.mdc-evolution-chip__action--primary]': `_getFoundation().actionType() === ${MDCChipActionType.PRIMARY}`,
30+
'[class.mdc-evolution-chip__action--primary]': '_isPrimary',
4731
// Note that while our actions are interactive, we have to add the `--presentational` class,
4832
// in order to avoid some super-specific `:hover` styles from MDC.
49-
'[class.mdc-evolution-chip__action--presentational]': `_getFoundation().actionType() === ${MDCChipActionType.PRIMARY}`,
50-
'[class.mdc-evolution-chip__action--trailing]': `_getFoundation().actionType() === ${MDCChipActionType.TRAILING}`,
33+
'[class.mdc-evolution-chip__action--presentational]': '_isPrimary',
34+
'[class.mdc-evolution-chip__action--trailing]': '!_isPrimary',
5135
'[attr.tabindex]': '(disabled || !isInteractive) ? null : tabIndex',
5236
'[attr.disabled]': "disabled ? '' : null",
5337
'[attr.aria-disabled]': 'disabled',
5438
'(click)': '_handleClick($event)',
5539
'(keydown)': '_handleKeydown($event)',
5640
},
5741
})
58-
export class MatChipAction
59-
extends _MatChipActionMixinBase
60-
implements AfterViewInit, OnDestroy, CanDisable, HasTabIndex, OnChanges
61-
{
62-
private _document: Document;
63-
private _foundation: MDCChipActionFoundation;
64-
private _adapter: MDCChipActionAdapter = {
65-
focus: () => this.focus(),
66-
getAttribute: (name: string) => this._elementRef.nativeElement.getAttribute(name),
67-
setAttribute: (name: string, value: string) => {
68-
// MDC tries to update the tabindex directly in the DOM when navigating using the keyboard
69-
// which overrides our own handling. If we detect such a case, assign it to the same property
70-
// as the Angular binding in order to maintain consistency.
71-
if (name === 'tabindex') {
72-
this._updateTabindex(parseInt(value));
73-
} else {
74-
this._elementRef.nativeElement.setAttribute(name, value);
75-
}
76-
},
77-
removeAttribute: (name: string) => {
78-
if (name !== 'tabindex') {
79-
this._elementRef.nativeElement.removeAttribute(name);
80-
}
81-
},
82-
getElementID: () => this._elementRef.nativeElement.id,
83-
emitEvent: <T>(eventName: string, data: T) => {
84-
emitCustomEvent<T>(this._elementRef.nativeElement, this._document, eventName, data, true);
85-
},
86-
};
87-
42+
export class MatChipAction extends _MatChipActionMixinBase implements HasTabIndex {
8843
/** Whether the action is interactive. */
8944
@Input() isInteractive = true;
9045

91-
_handleClick(event: MouseEvent) {
92-
// Usually these events can't happen while the chip is disabled since the browser won't
93-
// allow them which is what MDC seems to rely on, however the event can be faked in tests.
94-
if (!this.disabled && this.isInteractive) {
95-
this._foundation.handleClick();
96-
event.preventDefault();
97-
}
98-
}
46+
/** Whether this is the primary action in the chip. */
47+
_isPrimary = true;
9948

100-
_handleKeydown(event: KeyboardEvent) {
101-
// Usually these events can't happen while the chip is disabled since the browser won't
102-
// allow them which is what MDC seems to rely on, however the event can be faked in tests.
103-
if (!this.disabled && this.isInteractive) {
104-
this._foundation.handleKeydown(event);
105-
}
49+
/** Whether the action is disabled. */
50+
@Input()
51+
get disabled(): boolean {
52+
return this._disabled || this._parentChip.disabled;
10653
}
107-
108-
protected _createFoundation(adapter: MDCChipActionAdapter): MDCChipActionFoundation {
109-
return new MDCChipPrimaryActionFoundation(adapter);
54+
set disabled(value: BooleanInput) {
55+
this._disabled = coerceBooleanProperty(value);
11056
}
57+
private _disabled = false;
11158

11259
constructor(
113-
public _elementRef: ElementRef,
114-
@Inject(DOCUMENT) _document: any,
115-
private _changeDetectorRef: ChangeDetectorRef,
60+
public _elementRef: ElementRef<HTMLElement>,
61+
@Inject(MAT_CHIP)
62+
protected _parentChip: {
63+
_handlePrimaryActionInteraction(): void;
64+
remove(): void;
65+
disabled: boolean;
66+
},
11667
) {
11768
super();
118-
this._foundation = this._createFoundation(this._adapter);
11969

12070
if (_elementRef.nativeElement.nodeName === 'BUTTON') {
12171
_elementRef.nativeElement.setAttribute('type', 'button');
12272
}
12373
}
12474

125-
ngAfterViewInit() {
126-
this._foundation.init();
127-
this._foundation.setDisabled(this.disabled);
128-
}
129-
130-
ngOnChanges(changes: SimpleChanges) {
131-
if (changes['disabled']) {
132-
this._foundation.setDisabled(this.disabled);
133-
}
134-
}
135-
136-
ngOnDestroy() {
137-
this._foundation.destroy();
138-
}
139-
14075
focus() {
14176
this._elementRef.nativeElement.focus();
14277
}
14378

144-
_getFoundation() {
145-
return this._foundation;
79+
_handleClick(event: MouseEvent) {
80+
if (!this.disabled && this.isInteractive && this._isPrimary) {
81+
event.preventDefault();
82+
this._parentChip._handlePrimaryActionInteraction();
83+
}
14684
}
14785

148-
_updateTabindex(value: number) {
149-
this.tabIndex = value;
150-
this._changeDetectorRef.markForCheck();
86+
_handleKeydown(event: KeyboardEvent) {
87+
if (
88+
(event.keyCode === ENTER || event.keyCode === SPACE) &&
89+
!this.disabled &&
90+
this.isInteractive &&
91+
this._isPrimary
92+
) {
93+
event.preventDefault();
94+
this._parentChip._handlePrimaryActionInteraction();
95+
}
15196
}
15297
}

src/material-experimental/mdc-chips/chip-default-options.ts

Lines changed: 0 additions & 20 deletions
This file was deleted.

src/material-experimental/mdc-chips/chip-grid.spec.ts

Lines changed: 22 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
dispatchFakeEvent,
1717
dispatchKeyboardEvent,
1818
MockNgZone,
19+
patchElementFocus,
1920
typeInElement,
2021
} from '@angular/cdk/testing/private';
2122
import {
@@ -34,7 +35,6 @@ import {MatFormFieldModule} from '@angular/material-experimental/mdc-form-field'
3435
import {MatInputModule} from '@angular/material-experimental/mdc-input';
3536
import {By} from '@angular/platform-browser';
3637
import {BrowserAnimationsModule, NoopAnimationsModule} from '@angular/platform-browser/animations';
37-
import {MDCChipAnimation} from '@material/chips';
3838
import {MatChipEvent, MatChipGrid, MatChipInputEvent, MatChipRow, MatChipsModule} from './index';
3939

4040
describe('MDC-based MatChipGrid', () => {
@@ -199,6 +199,7 @@ describe('MDC-based MatChipGrid', () => {
199199
// Destroy the middle item
200200
testComponent.chips.splice(2, 1);
201201
fixture.detectChanges();
202+
flush();
202203

203204
// Should not have focus
204205
expect(chipGridNativeElement.contains(document.activeElement)).toBe(false);
@@ -208,6 +209,7 @@ describe('MDC-based MatChipGrid', () => {
208209
testComponent.chips = [0];
209210

210211
spyOn(chipGridInstance, 'focus');
212+
patchElementFocus(chips.last.primaryAction!._elementRef.nativeElement);
211213
chips.last.focus();
212214

213215
testComponent.chips.pop();
@@ -216,27 +218,22 @@ describe('MDC-based MatChipGrid', () => {
216218
expect(chipGridInstance.focus).toHaveBeenCalled();
217219
});
218220

219-
it(
220-
'should move focus to the last chip when the focused chip was deleted inside a ' +
221-
'component with animations',
222-
fakeAsync(() => {
223-
fixture.destroy();
224-
TestBed.resetTestingModule();
225-
226-
fixture = createComponent(StandardChipGridWithAnimations, BrowserAnimationsModule);
221+
it('should move focus to the last chip when the focused chip was deleted inside a component with animations', fakeAsync(() => {
222+
fixture.destroy();
223+
TestBed.resetTestingModule();
227224

228-
chips.last.focus();
229-
fixture.detectChanges();
225+
fixture = createComponent(StandardChipGridWithAnimations, BrowserAnimationsModule);
230226

231-
expect(document.activeElement).toBe(primaryActions[primaryActions.length - 1]);
227+
patchElementFocus(chips.last.primaryAction!._elementRef.nativeElement);
228+
chips.last.focus();
229+
fixture.detectChanges();
232230

233-
dispatchKeyboardEvent(chips.last._elementRef.nativeElement, 'keydown', BACKSPACE);
234-
fixture.detectChanges();
235-
tick(500);
231+
dispatchKeyboardEvent(chips.last._elementRef.nativeElement, 'keydown', BACKSPACE);
232+
fixture.detectChanges();
233+
tick(500);
236234

237-
expect(document.activeElement).toBe(primaryActions[primaryActions.length - 2]);
238-
}),
239-
);
235+
expect(document.activeElement).toBe(primaryActions[primaryActions.length - 2]);
236+
}));
240237
});
241238

242239
it('should have a focus indicator', () => {
@@ -394,6 +391,7 @@ describe('MDC-based MatChipGrid', () => {
394391
expect(document.activeElement).toBe(primaryActions[1]);
395392

396393
directionality.value = 'rtl';
394+
directionality.change.next('rtl');
397395
fixture.detectChanges();
398396

399397
dispatchKeyboardEvent(primaryActions[1], 'keydown', RIGHT_ARROW);
@@ -562,14 +560,7 @@ describe('MDC-based MatChipGrid', () => {
562560
// associated chip remove element.
563561
trailingActions[2].click();
564562
fixture.detectChanges();
565-
(chip as any)._handleAnimationend({
566-
animationName: MDCChipAnimation.EXIT,
567-
target: chip._elementRef.nativeElement,
568-
});
569563
flush();
570-
(chip as any)._handleTransitionend({target: chip._elementRef.nativeElement});
571-
flush();
572-
fixture.detectChanges();
573564

574565
expect(document.activeElement).toBe(primaryActions[3]);
575566
}));
@@ -589,7 +580,6 @@ describe('MDC-based MatChipGrid', () => {
589580
.map(chip => chip.nativeElement);
590581

591582
nativeChipGrid = fixture.debugElement.query(By.css('mat-chip-grid'))!.nativeElement;
592-
593583
nativeInput = fixture.nativeElement.querySelector('input');
594584
});
595585

@@ -730,18 +720,21 @@ describe('MDC-based MatChipGrid', () => {
730720

731721
it('should blur the form field when the active chip is blurred', fakeAsync(() => {
732722
const formField: HTMLElement = fixture.nativeElement.querySelector('.mat-mdc-form-field');
723+
const firstAction = nativeChips[0].querySelector('.mat-mdc-chip-action') as HTMLElement;
733724

734-
dispatchFakeEvent(nativeChips[0], 'focusin');
725+
patchElementFocus(firstAction);
726+
firstAction.focus();
735727
fixture.detectChanges();
736728

737729
expect(formField.classList).toContain('mat-focused');
738730

739-
dispatchFakeEvent(nativeChips[0], 'focusout');
731+
firstAction.blur();
740732
fixture.detectChanges();
741-
tick();
742733
fixture.detectChanges();
743734
zone.simulateZoneExit();
744735
fixture.detectChanges();
736+
flush();
737+
745738
expect(formField.classList).not.toContain('mat-focused');
746739
}));
747740

0 commit comments

Comments
 (0)