[DSC-516] Change truncable components in order to show more/less button

This commit is contained in:
Sufiyan Shaikh
2022-04-26 17:28:54 +05:30
parent c538bbbe46
commit abe1d5c6c7
9 changed files with 207 additions and 34 deletions

View File

@@ -7,13 +7,11 @@
class="lead"
[innerHTML]="firstMetadataValue('organization.legalName')"></span>
<span class="text-muted">
<ds-truncatable-part [id]="dso.id" [minLines]="3">
<span *ngIf="dso.allMetadata(['dc.description']).length > 0"
class="item-list-org-unit-description">
<ds-truncatable-part [id]="dso.id" [minLines]="3"><span
[innerHTML]="firstMetadataValue('dc.description')"></span>
</ds-truncatable-part>
</span>
</ds-truncatable-part>
<span *ngIf="dso.allMetadata(['dc.description']).length > 0"
class="item-list-org-unit-description">
<ds-truncatable-part [id]="dso.id" [minLines]="3"><span
[innerHTML]="firstMetadataValue('dc.description')"></span>
</ds-truncatable-part>
</span>
</span>
</ds-truncatable>

View File

@@ -1,5 +1,11 @@
<div class="clamp-{{background}}-{{lines}} min-{{minLines}} {{type}} {{fixedHeight ? 'fixedHeight' : ''}}">
<div class="content dont-break-out">
<div class="content dont-break-out" id="dontBreakContent">
<ng-content></ng-content>
</div>
<label for="dontBreakContent" role="button" id="expandButton">
<a dsDragClick (actualClick)="toggle()">
<i class="fas fa-angle-down"></i> {{ 'item.truncatable-part.show-more' | translate }}</a>
</label>
<a id="collapseButton" dsDragClick (actualClick)="toggle()" *ngIf="expand && expandable">
<i class="fas fa-angle-up"></i> {{ 'item.truncatable-part.show-less' | translate }}</a>
</div>

View File

@@ -0,0 +1,10 @@
#dontBreakContent:not(.truncated) ~ label{
display: none;
}
a {
color: #207698 !important;
text-decoration: none !important;
background-color: transparent !important;
cursor: pointer;
}

View File

@@ -4,10 +4,16 @@ import { TruncatablePartComponent } from './truncatable-part.component';
import { TruncatableService } from '../truncatable.service';
import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { TranslateLoader, TranslateModule, TranslateService } from '@ngx-translate/core';
import { getMockTranslateService } from '../../mocks/translate.service.mock';
import { TranslateLoaderMock } from '../../mocks/translate-loader.mock';
import { mockTruncatableService } from '../../mocks/mock-trucatable.service';
import { By } from '@angular/platform-browser';
describe('TruncatablePartComponent', () => {
let comp: TruncatablePartComponent;
let fixture: ComponentFixture<TruncatablePartComponent>;
let translateService: TranslateService;
const id1 = '123';
const id2 = '456';
@@ -22,8 +28,16 @@ describe('TruncatablePartComponent', () => {
}
};
beforeEach(waitForAsync(() => {
translateService = getMockTranslateService();
TestBed.configureTestingModule({
imports: [NoopAnimationsModule],
imports: [NoopAnimationsModule,
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useClass: TranslateLoaderMock
}
}),
],
declarations: [TruncatablePartComponent],
providers: [
{ provide: TruncatableService, useValue: truncatableServiceStub },
@@ -52,6 +66,11 @@ describe('TruncatablePartComponent', () => {
it('lines should equal minlines', () => {
expect((comp as any).lines).toEqual(comp.minLines.toString());
});
it('collapseButton should be hidden', () => {
const a = fixture.debugElement.query(By.css('#collapseButton'));
expect(a).toBeNull();
});
});
describe('When the item is expanded', () => {
@@ -72,5 +91,62 @@ describe('TruncatablePartComponent', () => {
fixture.detectChanges();
expect((comp as any).lines).toEqual('none');
});
it('collapseButton should be shown', () => {
(comp as any).setLines();
(comp as any).expandable = true;
fixture.detectChanges();
const a = fixture.debugElement.query(By.css('#collapseButton'));
expect(a).not.toBeNull();
});
});
});
describe('TruncatablePartComponent', () => {
let comp: TruncatablePartComponent;
let fixture: ComponentFixture<TruncatablePartComponent>;
let translateService: TranslateService;
const identifier = '1234567890';
let truncatableService;
beforeEach(waitForAsync(() => {
translateService = getMockTranslateService();
TestBed.configureTestingModule({
imports: [
NoopAnimationsModule,
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useClass: TranslateLoaderMock
}
}),
],
declarations: [TruncatablePartComponent],
providers: [
{ provide: TruncatableService, useValue: mockTruncatableService },
],
schemas: [NO_ERRORS_SCHEMA]
}).overrideComponent(TruncatablePartComponent, {
set: { changeDetection: ChangeDetectionStrategy.Default }
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(TruncatablePartComponent);
comp = fixture.componentInstance; // TruncatablePartComponent test instance
comp.id = identifier;
fixture.detectChanges();
truncatableService = (comp as any).service;
});
describe('When toggle is called', () => {
beforeEach(() => {
spyOn(truncatableService, 'toggle');
comp.toggle();
});
it('should call toggle on the TruncatableService', () => {
expect(truncatableService.toggle).toHaveBeenCalledWith(identifier);
});
});
});

View File

@@ -1,6 +1,8 @@
import { Component, Input, OnDestroy, OnInit } from '@angular/core';
import { Component, Inject, Input, OnDestroy, OnInit, PLATFORM_ID } from '@angular/core';
import { TruncatableService } from '../truncatable.service';
import { hasValue } from '../../empty.util';
import { DOCUMENT, isPlatformBrowser } from '@angular/common';
import { NativeWindowRef, NativeWindowService } from 'src/app/core/services/window.service';
@Component({
selector: 'ds-truncatable-part',
@@ -49,8 +51,34 @@ export class TruncatablePartComponent implements OnInit, OnDestroy {
* Subscription to unsubscribe from
*/
private sub;
/**
* store variable used for local to expand collapse
*/
expand = false;
/**
* variable to check if expandable
*/
expandable = false;
/**
* variable to check if it is a browser
*/
isBrowser: boolean;
/**
* variable which save get observer
*/
observer;
/**
* variable to save content to be observed
*/
observedContent;
public constructor(private service: TruncatableService) {
public constructor(
private service: TruncatableService,
@Inject(DOCUMENT) private document: any,
@Inject(NativeWindowService) private _window: NativeWindowRef,
@Inject(PLATFORM_ID) platformId: object
) {
this.isBrowser = isPlatformBrowser(platformId);
}
/**
@@ -67,12 +95,70 @@ export class TruncatablePartComponent implements OnInit, OnDestroy {
this.sub = this.service.isCollapsed(this.id).subscribe((collapsed: boolean) => {
if (collapsed) {
this.lines = this.minLines.toString();
this.expand = false;
} else {
this.lines = this.maxLines < 0 ? 'none' : this.maxLines.toString();
this.expand = true;
}
});
}
ngAfterContentChecked() {
if (this.isBrowser) {
if (this.observer && this.observedContent) {
this.toUnobserve();
}
this.toObserve();
}
}
/**
* Function to get data to be observed
*/
toObserve() {
this.observedContent = this.document.querySelectorAll('#dontBreakContent');
this.observer = new (this._window.nativeWindow as any).ResizeObserver(entries => {
// tslint:disable-next-line:prefer-const
for (let entry of entries) {
if (!entry.target.classList.contains('notruncatable')) {
if (entry.target.scrollHeight > entry.contentRect.height) {
if (entry.target.children.length > 0) {
if (entry.target.children[0].offsetHeight > entry.contentRect.height) {
entry.target.classList.add('truncated');
} else {
entry.target.classList.remove('truncated');
}
} else {
entry.target.classList.add('truncated');
}
} else {
entry.target.classList.remove('truncated');
}
}
}
});
this.observedContent.forEach(p => {
this.observer.observe(p);
});
}
/**
* Function to remove data which is observed
*/
toUnobserve() {
this.observedContent.forEach(p => {
this.observer.unobserve(p);
});
}
/**
* Expands the truncatable when it's collapsed, collapses it when it's expanded
*/
public toggle() {
this.service.toggle(this.id);
this.expandable = !this.expandable;
}
/**
* Unsubscribe from the subscription
*/
@@ -80,5 +166,8 @@ export class TruncatablePartComponent implements OnInit, OnDestroy {
if (hasValue(this.sub)) {
this.sub.unsubscribe();
}
if (this.isBrowser) {
this.toUnobserve();
}
}
}

View File

@@ -1,3 +1,3 @@
<div dsDragClick (actualClick)="toggle()" (mouseenter)="hoverExpand()" (mouseleave)="hoverCollapse()">
<div (mouseenter)="hoverExpand()" (mouseleave)="hoverCollapse()">
<ng-content></ng-content>
</div>

View File

@@ -70,15 +70,4 @@ describe('TruncatableComponent', () => {
});
});
describe('When toggle is called', () => {
beforeEach(() => {
spyOn(truncatableService, 'toggle');
comp.toggle();
});
it('should call toggle on the TruncatableService', () => {
expect(truncatableService.toggle).toHaveBeenCalledWith(identifier);
});
});
});

View File

@@ -1,6 +1,4 @@
import {
Component, Input
} from '@angular/core';
import { AfterViewChecked, Component, ElementRef, Input, OnInit } from '@angular/core';
import { TruncatableService } from './truncatable.service';
@Component({
@@ -13,7 +11,7 @@ import { TruncatableService } from './truncatable.service';
/**
* Component that represents a section with one or more truncatable parts that all listen to this state
*/
export class TruncatableComponent {
export class TruncatableComponent implements OnInit, AfterViewChecked {
/**
* Is true when all truncatable parts in this truncatable should be expanded on loading
*/
@@ -29,7 +27,7 @@ export class TruncatableComponent {
*/
@Input() onHover = false;
public constructor(private service: TruncatableService) {
public constructor(private service: TruncatableService, private el: ElementRef,) {
}
/**
@@ -61,11 +59,14 @@ export class TruncatableComponent {
}
}
/**
* Expands the truncatable when it's collapsed, collapses it when it's expanded
*/
public toggle() {
this.service.toggle(this.id);
ngAfterViewChecked() {
const truncatedElements = this.el.nativeElement.querySelectorAll('.truncated');
if (truncatedElements?.length > 1) {
for (let i = 0; i < (truncatedElements.length - 1); i++) {
truncatedElements[i].classList.remove('truncated');
truncatedElements[i].classList.add('notruncatable');
}
}
}
}

View File

@@ -2056,6 +2056,10 @@
"item.search.title": "Item Search",
"item.truncatable-part.show-more": "Show more",
"item.truncatable-part.show-less": "Collapse",
"item.page.abstract": "Abstract",