Skip to content

Commit 7aed80d

Browse files
Add ht-description component (#543)
* feat(description): create description component * feat(description): add text truncation recalculate on resize * fix(description): use cahngedetctionref * fix(description): linting * fix(description): resize event * fix(description): linting Co-authored-by: Dmitriy Potychkin <dmitriy.traceable.ai> Co-authored-by: Anand Tiwary <52081890+anandtiwary@users.noreply.github.com>
1 parent 6175c39 commit 7aed80d

File tree

5 files changed

+152
-0
lines changed

5 files changed

+152
-0
lines changed
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
:host {
2+
min-width: 0;
3+
}
4+
5+
.description {
6+
display: flex;
7+
}
8+
9+
.description-button {
10+
cursor: pointer;
11+
text-decoration: underline;
12+
white-space: nowrap;
13+
&-more {
14+
margin-left: 5px;
15+
}
16+
}
17+
18+
.truncated-text {
19+
overflow: hidden;
20+
text-overflow: ellipsis;
21+
white-space: nowrap;
22+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { createHostFactory, Spectator } from '@ngneat/spectator/jest';
2+
import { DescriptionComponent } from './description.component';
3+
4+
describe('Description Component', () => {
5+
let spectator: Spectator<DescriptionComponent>;
6+
7+
const createHost = createHostFactory<DescriptionComponent>({
8+
component: DescriptionComponent,
9+
shallow: true
10+
});
11+
12+
test('should render the description', () => {
13+
spectator = createHost('<ht-description [description] = "description"> </ht-description>', {
14+
hostProps: {
15+
description: 'Description text'
16+
}
17+
});
18+
19+
expect(spectator.query('.description')).toExist();
20+
expect(spectator.query('.description-text')).toHaveText('Description text');
21+
});
22+
23+
test('should show full description text if pressed button show more', () => {
24+
spectator = createHost('<ht-description [description] = "description"> </ht-description>', {
25+
hostProps: {
26+
description: 'Description text'
27+
}
28+
});
29+
30+
expect(spectator.component.isDescriptionTextToggled).toBeFalsy();
31+
spectator.component.toggleDescriptionText();
32+
expect(spectator.component.isDescriptionTextToggled).toBeTruthy();
33+
});
34+
});
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import {
2+
AfterViewInit,
3+
ChangeDetectionStrategy,
4+
ChangeDetectorRef,
5+
Component,
6+
ElementRef,
7+
Input,
8+
OnChanges,
9+
ViewChild
10+
} from '@angular/core';
11+
12+
@Component({
13+
selector: 'ht-description',
14+
styleUrls: ['./description.component.scss'],
15+
changeDetection: ChangeDetectionStrategy.OnPush,
16+
template: `
17+
<div class="description" (htLayoutChange)="this.remeasure()" #eventDescriptionContainer>
18+
<div
19+
class="description-text"
20+
[ngClass]="{ 'truncated-text': !isDescriptionTextToggled }"
21+
data-sensitive-pii
22+
#eventDescriptionText
23+
>
24+
{{ description }}
25+
<span
26+
*ngIf="isDescriptionTruncated && isDescriptionTextToggled"
27+
(click)="toggleDescriptionText()"
28+
class="description-button"
29+
>show less</span
30+
>
31+
</div>
32+
<div
33+
class="description-button description-button-more"
34+
*ngIf="isDescriptionTruncated && !isDescriptionTextToggled"
35+
(click)="toggleDescriptionText()"
36+
>
37+
show more
38+
</div>
39+
</div>
40+
`
41+
})
42+
export class DescriptionComponent implements OnChanges, AfterViewInit {
43+
public isInitialized: boolean = false;
44+
public isDescriptionTextToggled: boolean = false;
45+
public isDescriptionTruncated!: boolean;
46+
47+
@ViewChild('eventDescriptionText', { read: ElementRef })
48+
public readonly eventDescriptionText!: ElementRef;
49+
50+
@ViewChild('eventDescriptionContainer', { read: ElementRef })
51+
public readonly eventDescriptionContainer!: ElementRef;
52+
53+
@Input()
54+
public description!: string;
55+
56+
public constructor(private readonly cdRef: ChangeDetectorRef) {}
57+
58+
public ngOnChanges(): void {
59+
if (this.isInitialized) {
60+
this.remeasure();
61+
}
62+
}
63+
64+
public ngAfterViewInit(): void {
65+
this.isInitialized = true;
66+
this.remeasure();
67+
}
68+
69+
public toggleDescriptionText(): void {
70+
this.isDescriptionTextToggled = !this.isDescriptionTextToggled;
71+
}
72+
73+
public remeasure(): void {
74+
this.isDescriptionTextToggled = false;
75+
this.cdRef.detectChanges();
76+
77+
this.isDescriptionTruncated =
78+
this.eventDescriptionContainer.nativeElement.offsetWidth < this.eventDescriptionText.nativeElement.scrollWidth;
79+
this.cdRef.detectChanges();
80+
}
81+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { CommonModule } from '@angular/common';
2+
import { NgModule } from '@angular/core';
3+
import { LayoutChangeModule } from '../layout/layout-change.module';
4+
import { DescriptionComponent } from './description.component';
5+
6+
@NgModule({
7+
imports: [CommonModule, LayoutChangeModule],
8+
declarations: [DescriptionComponent],
9+
exports: [DescriptionComponent]
10+
})
11+
export class DescriptionModule {}

projects/components/src/public-api.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,3 +304,7 @@ export * from './confirmation/confirmation.service';
304304
// Notification
305305
export * from './notification/notification.service';
306306
export * from './notification/notification.module';
307+
308+
// Description
309+
export * from './description/description.component';
310+
export * from './description/description.module';

0 commit comments

Comments
 (0)