Skip to content
22 changes: 22 additions & 0 deletions projects/components/src/description/description.component.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
:host {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this doable without :host? we use it in some places, but it's a bad practice and we should try to get rid of it where possible (since the parent is generally responsible for styling the host, and will have greater precedence here)

Copy link
Contributor Author

@DmitiryPotychkin DmitiryPotychkin Jan 29, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm try to do it without :host but for some reason style min-width: 0 for .description not affected parent component. Originally this min-width: 0 added for fix such case as on screenshots.

Without min-width: 0 for host
Screenshot from 2021-01-29 03-04-54
Add min-width: 0
Screenshot from 2021-01-29 03-04-43

anyway, I still work on this component and will try to figure way to fix this without using :host.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's usually alternatives, but always the easiest to find. Don't spend too much time on it - if we need to use :host so be it.

min-width: 0;
}

.description {
display: flex;
}

.description-button {
cursor: pointer;
text-decoration: underline;
white-space: nowrap;
&-more {
margin-left: 5px;
}
}

.truncated-text {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { createHostFactory, Spectator } from '@ngneat/spectator/jest';
import { DescriptionComponent } from './description.component';

describe('Description Component', () => {
let spectator: Spectator<DescriptionComponent>;

const createHost = createHostFactory<DescriptionComponent>({
component: DescriptionComponent,
shallow: true
});

test('should render the description', () => {
spectator = createHost('<ht-description [description] = "description"> </ht-description>', {
hostProps: {
description: 'Description text'
}
});

expect(spectator.query('.description')).toExist();
expect(spectator.query('.description-text')).toHaveText('Description text');
});

test('should show full description text if pressed button show more', () => {
spectator = createHost('<ht-description [description] = "description"> </ht-description>', {
hostProps: {
description: 'Description text'
}
});

expect(spectator.component.isDescriptionTextToggled).toBeFalsy();
spectator.component.toggleDescriptionText();
expect(spectator.component.isDescriptionTextToggled).toBeTruthy();
});
});
81 changes: 81 additions & 0 deletions projects/components/src/description/description.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import {
AfterViewInit,
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
ElementRef,
Input,
OnChanges,
ViewChild
} from '@angular/core';

@Component({
selector: 'ht-description',
styleUrls: ['./description.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<div class="description" (htLayoutChange)="this.remeasure()" #eventDescriptionContainer>
<div
class="description-text"
[ngClass]="{ 'truncated-text': !isDescriptionTextToggled }"
data-sensitive-pii
#eventDescriptionText
>
{{ description }}
<span
*ngIf="isDescriptionTruncated && isDescriptionTextToggled"
(click)="toggleDescriptionText()"
class="description-button"
>show less</span
>
</div>
<div
class="description-button description-button-more"
*ngIf="isDescriptionTruncated && !isDescriptionTextToggled"
(click)="toggleDescriptionText()"
>
show more
</div>
</div>
`
})
export class DescriptionComponent implements OnChanges, AfterViewInit {
public isInitialized: boolean = false;
public isDescriptionTextToggled: boolean = false;
public isDescriptionTruncated!: boolean;

@ViewChild('eventDescriptionText', { read: ElementRef })
public readonly eventDescriptionText!: ElementRef;

@ViewChild('eventDescriptionContainer', { read: ElementRef })
public readonly eventDescriptionContainer!: ElementRef;

@Input()
public description!: string;

public constructor(private readonly cdRef: ChangeDetectorRef) {}

public ngOnChanges(): void {
if (this.isInitialized) {
this.remeasure();
}
}

public ngAfterViewInit(): void {
this.isInitialized = true;
this.remeasure();
}

public toggleDescriptionText(): void {
this.isDescriptionTextToggled = !this.isDescriptionTextToggled;
}

public remeasure(): void {
this.isDescriptionTextToggled = false;
this.cdRef.detectChanges();

this.isDescriptionTruncated =
this.eventDescriptionContainer.nativeElement.offsetWidth < this.eventDescriptionText.nativeElement.scrollWidth;
this.cdRef.detectChanges();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only run once, at the end of the remeasure method.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It will cause bug, value of isDescriptionTruncated will be wrong as we try to get this.eventDescriptionText.nativeElement.scrollWidth before it truncated and DOM changed.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm - that's one of the weird parts about using detectChanges explicitly instead of markForCheck (which just invalidates this node in the change detection tree to be picked up by angular's general change detection, which should ensure that the changes settle. Changing that here may be better - could you try that out, and if it's not working we'll go with this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I try to use markForCheck method but unfortunately it not work correctly

}
}
11 changes: 11 additions & 0 deletions projects/components/src/description/description.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { LayoutChangeModule } from '../layout/layout-change.module';
import { DescriptionComponent } from './description.component';

@NgModule({
imports: [CommonModule, LayoutChangeModule],
declarations: [DescriptionComponent],
exports: [DescriptionComponent]
})
export class DescriptionModule {}
4 changes: 4 additions & 0 deletions projects/components/src/public-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -304,3 +304,7 @@ export * from './confirmation/confirmation.service';
// Notification
export * from './notification/notification.service';
export * from './notification/notification.module';

// Description
export * from './description/description.component';
export * from './description/description.module';