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(design)!: change daffSkeletonMixin to a directive #2923

Merged
merged 1 commit into from
Jul 30, 2024
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions libs/design/image/src/image/image.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,11 @@ describe('@daffodil/design/image | DaffImageComponent', () => {
expect(component.height).toEqual(100);
});

it('should be able to take `skeleton` as an input', () => {
expect(component.skeleton).toEqual(wrapper.skeleton);
it('should take skeleton as an input', () => {
wrapper.skeleton = true;
fixture.detectChanges();

expect(de.nativeElement.classList.contains('daff-skeleton')).toEqual(true);
});

it('should throw an error when src is invalid', () => {
Expand Down
33 changes: 7 additions & 26 deletions libs/design/image/src/image/image.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,12 @@ import {
Input,
EventEmitter,
OnInit,
ElementRef,
Renderer2,
Output,
HostBinding,
} from '@angular/core';
import { DomSanitizer } from '@angular/platform-browser';

import {
daffSkeletonableMixin,
DaffSkeletonable,
} from '@daffodil/design';
import { DaffSkeletonableDirective } from '@daffodil/design';
import { daffThumbnailCompatToken } from '@daffodil/design/media-gallery';

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

/**
* An _elementRef is needed for the GolfGhostable mixin
*/
class DaffImageBase {
constructor(public _elementRef: ElementRef, public _renderer: Renderer2) { }
}

const _daffImageBase = daffSkeletonableMixin(DaffImageBase);

@Component({
selector: 'daff-image',
templateUrl: './image.component.html',
Expand All @@ -58,11 +44,12 @@ const _daffImageBase = daffSkeletonableMixin(DaffImageBase);
provide: daffThumbnailCompatToken, useExisting: DaffImageComponent,
},
],
// todo(damienwebdev): remove once decorators hit stage 3 - https://github.com/microsoft/TypeScript/issues/7342
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
inputs: ['skeleton'],
hostDirectives: [{
directive: DaffSkeletonableDirective,
inputs: ['skeleton'],
}],
})
export class DaffImageComponent extends _daffImageBase implements OnInit, DaffSkeletonable {
export class DaffImageComponent implements OnInit {

private _src: string;

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

constructor(
private sanitizer: DomSanitizer,
private elementRef: ElementRef,
private renderer: Renderer2,
) {
super(elementRef, renderer);
}
constructor(private sanitizer: DomSanitizer) {}

/**
* @docs-private
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,11 @@ describe('@daffodil/design/media-gallery | DaffMediaGalleryComponent', () => {
expect(component.name).toEqual(stubName);
});

it('should take a skeleton as input', () => {
expect(component.skeleton).toEqual(wrapper.skeleton);
it('should take skeleton as an input', () => {
wrapper.skeleton = true;
fixture.detectChanges();

expect(de.nativeElement.classList.contains('daff-skeleton')).toEqual(true);
});

it('should remove the gallery from the registry when the gallery is destroyed', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,11 @@ import {
Input,
OnInit,
OnDestroy,
ElementRef,
Renderer2,
} from '@angular/core';

import {
daffSkeletonableMixin,
DaffSkeletonable,
DaffArticleEncapsulatedDirective,
DaffSkeletonableDirective,
} from '@daffodil/design';

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

let uniqueGalleryId = 0;

/**
* An _elementRef and an instance of renderer2 are needed for the link set mixins
*/
class DaffMediaGalleryBase {
constructor(public _elementRef: ElementRef, public _renderer: Renderer2) {}
}

const _daffMediaGalleryBase = daffSkeletonableMixin((DaffMediaGalleryBase));

@Component({
selector: 'daff-media-gallery',
templateUrl: './media-gallery.component.html',
Expand All @@ -42,11 +30,15 @@ const _daffMediaGalleryBase = daffSkeletonableMixin((DaffMediaGalleryBase));
// todo(damienwebdev): remove once decorators hit stage 3 - https://github.com/microsoft/TypeScript/issues/7342
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
inputs: ['skeleton'],
hostDirectives: [{
directive: DaffArticleEncapsulatedDirective,
}],
hostDirectives: [
{ directive: DaffArticleEncapsulatedDirective },
{
directive: DaffSkeletonableDirective,
inputs: ['skeleton'],
},
],
})
export class DaffMediaGalleryComponent extends _daffMediaGalleryBase implements DaffMediaGalleryRegistration, DaffSkeletonable, OnInit, OnDestroy {
export class DaffMediaGalleryComponent implements DaffMediaGalleryRegistration, OnInit, OnDestroy {
/**
* Adds a class for styling the media gallery
*/
Expand All @@ -57,13 +49,8 @@ export class DaffMediaGalleryComponent extends _daffMediaGalleryBase implements
*/
@Input() name = `${uniqueGalleryId}`;

constructor(
private elementRef: ElementRef,
private renderer: Renderer2,
private registry: DaffMediaGalleryRegistry,
) {
super(elementRef, renderer);
uniqueGalleryId++;
constructor(private registry: DaffMediaGalleryRegistry) {
uniqueGalleryId++;
}

ngOnInit() {
Expand Down
2 changes: 1 addition & 1 deletion libs/design/scss/state/skeleton/_mixins.scss
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@

&::before {
animation-name: loading;
animation-duration: 1500ms;
animation-duration: 1000ms;
animation-timing-function: linear;
animation-iteration-count: infinite;
animation-direction: alternate;
Expand Down
2 changes: 1 addition & 1 deletion libs/design/src/core/skeletonable/public_api.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export { DaffSkeletonable } from './skeletonable';
export { daffSkeletonableMixin } from './skeletonable-mixin';
export { DaffSkeletonableDirective } from './skeletonable.directive';
45 changes: 0 additions & 45 deletions libs/design/src/core/skeletonable/skeletonable-mixin.ts

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import {
Component,
DebugElement,
} from '@angular/core';
import {
waitForAsync,
ComponentFixture,
TestBed,
} from '@angular/core/testing';
import { By } from '@angular/platform-browser';

import { DaffSkeletonableDirective } from './skeletonable.directive';

@Component({
template: `
<div daffSkeletonable
[skeleton]="skeleton">
</div>`,
})

class WrapperComponent {
skeleton: boolean;
}

describe('@daffodil/design | DaffSkeletonableDirective', () => {
let wrapper: WrapperComponent;
let de: DebugElement;
let fixture: ComponentFixture<WrapperComponent>;
let directive: DaffSkeletonableDirective;

beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [
WrapperComponent,
],
imports: [
DaffSkeletonableDirective,
],
})
.compileComponents();
}));

beforeEach(() => {
fixture = TestBed.createComponent(WrapperComponent);
wrapper = fixture.componentInstance;
de = fixture.debugElement.query(By.css('[daffSkeletonable]'));
directive = de.injector.get(DaffSkeletonableDirective);
fixture.detectChanges();
});

it('should create', () => {
expect(wrapper).toBeTruthy();
expect(directive).toBeTruthy();
});

it('should take skeleton as an input', () => {
expect(directive.skeleton).toEqual(wrapper.skeleton);
});

it('should add a class of "daff-skeleton" to the host element when skeleton is true', () => {
wrapper.skeleton = true;
fixture.detectChanges();

expect(de.classes).toEqual(jasmine.objectContaining({
'daff-skeleton': true,
}));
});

it('should not add a class of "daff-skeleton" to the host element when skeleton is false', () => {
expect(de.classes['daff-skeleton']).toBeUndefined();
});
});
24 changes: 24 additions & 0 deletions libs/design/src/core/skeletonable/skeletonable.directive.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import {
Directive,
HostBinding,
Input,
} from '@angular/core';

/**
* The `DaffSkeletonableDirective` allows a component to display a skeleton loading
* state by conditionally applying a CSS class. This is useful for indicating to
* users that content is loading or being processed. This directive can be used to
* apply a skeleton loading state to any component by toggling the `skeleton`
* input property. When `skeleton` is `true`, the `daff-skeleton` CSS class
* is applied, which should style the component to look like a loading placeholder.
*
* The styles for the`daff-skeleton` class should be defined component's
* stylesheets to display the loading state as desired.
*/
@Directive({
selector: '[daffSkeletonable]',
standalone: true,
})
export class DaffSkeletonableDirective {
@Input() @HostBinding('class.daff-skeleton') skeleton = false;
}
54 changes: 0 additions & 54 deletions libs/design/src/core/skeletonable/skeletonable.spec.ts

This file was deleted.

1 change: 0 additions & 1 deletion libs/design/src/core/skeletonable/skeletonable.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
/**
* An interface for giving a component the ability to display a skeleton/loading UI.
* In order to be skeletonable, our class must implement this property.
*/
export interface DaffSkeletonable {
skeleton: boolean;
Expand Down
Loading