Skip to content

Commit d6a37c9

Browse files
authoredJul 30, 2024··
feat(design)!: change daffSkeletonMixin to a directive (#2923)
BREAKING CHANGE: daffSkeletonMixin has been removed in favor of DaffSkeletonableDirective. Update usage by using the hostDirective feature.
1 parent ae6cb7e commit d6a37c9

File tree

11 files changed

+126
-156
lines changed

11 files changed

+126
-156
lines changed
 

Diff for: ‎libs/design/image/src/image/image.component.spec.ts

+5-2
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,11 @@ describe('@daffodil/design/image | DaffImageComponent', () => {
8282
expect(component.height).toEqual(100);
8383
});
8484

85-
it('should be able to take `skeleton` as an input', () => {
86-
expect(component.skeleton).toEqual(wrapper.skeleton);
85+
it('should take skeleton as an input', () => {
86+
wrapper.skeleton = true;
87+
fixture.detectChanges();
88+
89+
expect(de.nativeElement.classList.contains('daff-skeleton')).toEqual(true);
8790
});
8891

8992
it('should throw an error when src is invalid', () => {

Diff for: ‎libs/design/image/src/image/image.component.ts

+7-26
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,12 @@ import {
44
Input,
55
EventEmitter,
66
OnInit,
7-
ElementRef,
8-
Renderer2,
97
Output,
108
HostBinding,
119
} from '@angular/core';
1210
import { DomSanitizer } from '@angular/platform-browser';
1311

14-
import {
15-
daffSkeletonableMixin,
16-
DaffSkeletonable,
17-
} from '@daffodil/design';
12+
import { DaffSkeletonableDirective } from '@daffodil/design';
1813
import { daffThumbnailCompatToken } from '@daffodil/design/media-gallery';
1914

2015
const validateProperty = (object: Record<string, any>, prop: string) => {
@@ -38,15 +33,6 @@ const validateProperties = (object: Record<string, any>, props: string[]) => {
3833
}
3934
};
4035

41-
/**
42-
* An _elementRef is needed for the GolfGhostable mixin
43-
*/
44-
class DaffImageBase {
45-
constructor(public _elementRef: ElementRef, public _renderer: Renderer2) { }
46-
}
47-
48-
const _daffImageBase = daffSkeletonableMixin(DaffImageBase);
49-
5036
@Component({
5137
selector: 'daff-image',
5238
templateUrl: './image.component.html',
@@ -58,11 +44,12 @@ const _daffImageBase = daffSkeletonableMixin(DaffImageBase);
5844
provide: daffThumbnailCompatToken, useExisting: DaffImageComponent,
5945
},
6046
],
61-
// todo(damienwebdev): remove once decorators hit stage 3 - https://github.com/microsoft/TypeScript/issues/7342
62-
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
63-
inputs: ['skeleton'],
47+
hostDirectives: [{
48+
directive: DaffSkeletonableDirective,
49+
inputs: ['skeleton'],
50+
}],
6451
})
65-
export class DaffImageComponent extends _daffImageBase implements OnInit, DaffSkeletonable {
52+
export class DaffImageComponent implements OnInit {
6653

6754
private _src: string;
6855

@@ -119,13 +106,7 @@ export class DaffImageComponent extends _daffImageBase implements OnInit, DaffSk
119106
validateProperties(this, ['src', 'alt', 'width', 'height']);
120107
}
121108

122-
constructor(
123-
private sanitizer: DomSanitizer,
124-
private elementRef: ElementRef,
125-
private renderer: Renderer2,
126-
) {
127-
super(elementRef, renderer);
128-
}
109+
constructor(private sanitizer: DomSanitizer) {}
129110

130111
/**
131112
* @docs-private

Diff for: ‎libs/design/media-gallery/src/media-gallery/media-gallery.component.spec.ts

+5-2
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,11 @@ describe('@daffodil/design/media-gallery | DaffMediaGalleryComponent', () => {
7878
expect(component.name).toEqual(stubName);
7979
});
8080

81-
it('should take a skeleton as input', () => {
82-
expect(component.skeleton).toEqual(wrapper.skeleton);
81+
it('should take skeleton as an input', () => {
82+
wrapper.skeleton = true;
83+
fixture.detectChanges();
84+
85+
expect(de.nativeElement.classList.contains('daff-skeleton')).toEqual(true);
8386
});
8487

8588
it('should remove the gallery from the registry when the gallery is destroyed', () => {

Diff for: ‎libs/design/media-gallery/src/media-gallery/media-gallery.component.ts

+11-24
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,11 @@ import {
55
Input,
66
OnInit,
77
OnDestroy,
8-
ElementRef,
9-
Renderer2,
108
} from '@angular/core';
119

1210
import {
13-
daffSkeletonableMixin,
14-
DaffSkeletonable,
1511
DaffArticleEncapsulatedDirective,
12+
DaffSkeletonableDirective,
1613
} from '@daffodil/design';
1714

1815
import { DaffMediaGalleryRegistration } from '../helpers/media-gallery-registration.interface';
@@ -21,15 +18,6 @@ import { DaffMediaGalleryRegistry } from '../registry/media-gallery.registry';
2118

2219
let uniqueGalleryId = 0;
2320

24-
/**
25-
* An _elementRef and an instance of renderer2 are needed for the link set mixins
26-
*/
27-
class DaffMediaGalleryBase {
28-
constructor(public _elementRef: ElementRef, public _renderer: Renderer2) {}
29-
}
30-
31-
const _daffMediaGalleryBase = daffSkeletonableMixin((DaffMediaGalleryBase));
32-
3321
@Component({
3422
selector: 'daff-media-gallery',
3523
templateUrl: './media-gallery.component.html',
@@ -42,11 +30,15 @@ const _daffMediaGalleryBase = daffSkeletonableMixin((DaffMediaGalleryBase));
4230
// todo(damienwebdev): remove once decorators hit stage 3 - https://github.com/microsoft/TypeScript/issues/7342
4331
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
4432
inputs: ['skeleton'],
45-
hostDirectives: [{
46-
directive: DaffArticleEncapsulatedDirective,
47-
}],
33+
hostDirectives: [
34+
{ directive: DaffArticleEncapsulatedDirective },
35+
{
36+
directive: DaffSkeletonableDirective,
37+
inputs: ['skeleton'],
38+
},
39+
],
4840
})
49-
export class DaffMediaGalleryComponent extends _daffMediaGalleryBase implements DaffMediaGalleryRegistration, DaffSkeletonable, OnInit, OnDestroy {
41+
export class DaffMediaGalleryComponent implements DaffMediaGalleryRegistration, OnInit, OnDestroy {
5042
/**
5143
* Adds a class for styling the media gallery
5244
*/
@@ -57,13 +49,8 @@ export class DaffMediaGalleryComponent extends _daffMediaGalleryBase implements
5749
*/
5850
@Input() name = `${uniqueGalleryId}`;
5951

60-
constructor(
61-
private elementRef: ElementRef,
62-
private renderer: Renderer2,
63-
private registry: DaffMediaGalleryRegistry,
64-
) {
65-
super(elementRef, renderer);
66-
uniqueGalleryId++;
52+
constructor(private registry: DaffMediaGalleryRegistry) {
53+
uniqueGalleryId++;
6754
}
6855

6956
ngOnInit() {

Diff for: ‎libs/design/scss/state/skeleton/_mixins.scss

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828

2929
&::before {
3030
animation-name: loading;
31-
animation-duration: 1500ms;
31+
animation-duration: 1000ms;
3232
animation-timing-function: linear;
3333
animation-iteration-count: infinite;
3434
animation-direction: alternate;

Diff for: ‎libs/design/src/core/skeletonable/public_api.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
export { DaffSkeletonable } from './skeletonable';
2-
export { daffSkeletonableMixin } from './skeletonable-mixin';
2+
export { DaffSkeletonableDirective } from './skeletonable.directive';

Diff for: ‎libs/design/src/core/skeletonable/skeletonable-mixin.ts

-45
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import {
2+
Component,
3+
DebugElement,
4+
} from '@angular/core';
5+
import {
6+
waitForAsync,
7+
ComponentFixture,
8+
TestBed,
9+
} from '@angular/core/testing';
10+
import { By } from '@angular/platform-browser';
11+
12+
import { DaffSkeletonableDirective } from './skeletonable.directive';
13+
14+
@Component({
15+
template: `
16+
<div daffSkeletonable
17+
[skeleton]="skeleton">
18+
</div>`,
19+
})
20+
21+
class WrapperComponent {
22+
skeleton: boolean;
23+
}
24+
25+
describe('@daffodil/design | DaffSkeletonableDirective', () => {
26+
let wrapper: WrapperComponent;
27+
let de: DebugElement;
28+
let fixture: ComponentFixture<WrapperComponent>;
29+
let directive: DaffSkeletonableDirective;
30+
31+
beforeEach(waitForAsync(() => {
32+
TestBed.configureTestingModule({
33+
declarations: [
34+
WrapperComponent,
35+
],
36+
imports: [
37+
DaffSkeletonableDirective,
38+
],
39+
})
40+
.compileComponents();
41+
}));
42+
43+
beforeEach(() => {
44+
fixture = TestBed.createComponent(WrapperComponent);
45+
wrapper = fixture.componentInstance;
46+
de = fixture.debugElement.query(By.css('[daffSkeletonable]'));
47+
directive = de.injector.get(DaffSkeletonableDirective);
48+
fixture.detectChanges();
49+
});
50+
51+
it('should create', () => {
52+
expect(wrapper).toBeTruthy();
53+
expect(directive).toBeTruthy();
54+
});
55+
56+
it('should take skeleton as an input', () => {
57+
expect(directive.skeleton).toEqual(wrapper.skeleton);
58+
});
59+
60+
it('should add a class of "daff-skeleton" to the host element when skeleton is true', () => {
61+
wrapper.skeleton = true;
62+
fixture.detectChanges();
63+
64+
expect(de.classes).toEqual(jasmine.objectContaining({
65+
'daff-skeleton': true,
66+
}));
67+
});
68+
69+
it('should not add a class of "daff-skeleton" to the host element when skeleton is false', () => {
70+
expect(de.classes['daff-skeleton']).toBeUndefined();
71+
});
72+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import {
2+
Directive,
3+
HostBinding,
4+
Input,
5+
} from '@angular/core';
6+
7+
/**
8+
* The `DaffSkeletonableDirective` allows a component to display a skeleton loading
9+
* state by conditionally applying a CSS class. This is useful for indicating to
10+
* users that content is loading or being processed. This directive can be used to
11+
* apply a skeleton loading state to any component by toggling the `skeleton`
12+
* input property. When `skeleton` is `true`, the `daff-skeleton` CSS class
13+
* is applied, which should style the component to look like a loading placeholder.
14+
*
15+
* The styles for the`daff-skeleton` class should be defined component's
16+
* stylesheets to display the loading state as desired.
17+
*/
18+
@Directive({
19+
selector: '[daffSkeletonable]',
20+
standalone: true,
21+
})
22+
export class DaffSkeletonableDirective {
23+
@Input() @HostBinding('class.daff-skeleton') skeleton = false;
24+
}

Diff for: ‎libs/design/src/core/skeletonable/skeletonable.spec.ts

-54
This file was deleted.

Diff for: ‎libs/design/src/core/skeletonable/skeletonable.ts

-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
/**
22
* An interface for giving a component the ability to display a skeleton/loading UI.
3-
* In order to be skeletonable, our class must implement this property.
43
*/
54
export interface DaffSkeletonable {
65
skeleton: boolean;

0 commit comments

Comments
 (0)
Please sign in to comment.