mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2026-09-09 18:03:21 +00:00
AAE-38748 Change clipboard directive to be a tooltip (#11293)
* feat: change clipboard directive to be a tooltip * feat: remove redundant tooltips from datatable cells * test: fix broken tests * fix: sonarqube issues * fix: unable to use link-adf commit message from hxp-frontend-apps
This commit is contained in:
@@ -24,6 +24,8 @@ import { UnitTestingUtils } from '../testing/unit-testing-utils';
|
||||
import { HarnessLoader, TestKey } from '@angular/cdk/testing';
|
||||
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||
import { provideNoopAnimations } from '@angular/platform-browser/animations';
|
||||
|
||||
@Component({
|
||||
selector: 'adf-test-component',
|
||||
@@ -32,7 +34,8 @@ import { MatButtonModule } from '@angular/material/button';
|
||||
|
||||
<input #ref />
|
||||
`,
|
||||
imports: [MatButtonModule, ClipboardDirective]
|
||||
standalone: true,
|
||||
imports: [MatButtonModule, MatTooltipModule, ClipboardDirective]
|
||||
})
|
||||
class TestTargetClipboardComponent {}
|
||||
|
||||
@@ -44,7 +47,8 @@ describe('ClipboardDirective', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [MatSnackBarModule, TestTargetClipboardComponent]
|
||||
imports: [MatSnackBarModule, TestTargetClipboardComponent],
|
||||
providers: [provideNoopAnimations()]
|
||||
});
|
||||
fixture = TestBed.createComponent(TestTargetClipboardComponent);
|
||||
clipboardService = TestBed.inject(ClipboardService);
|
||||
@@ -74,6 +78,7 @@ describe('CopyClipboardDirective', () => {
|
||||
@Component({
|
||||
selector: 'adf-copy-content-test-component',
|
||||
template: `<span adf-clipboard="placeholder">{{ mockText }}</span>`,
|
||||
standalone: true,
|
||||
imports: [ClipboardDirective]
|
||||
})
|
||||
class TestCopyClipboardComponent {
|
||||
@@ -89,7 +94,8 @@ describe('CopyClipboardDirective', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [MatSnackBarModule, TestCopyClipboardComponent]
|
||||
imports: [MatSnackBarModule, MatTooltipModule, TestCopyClipboardComponent],
|
||||
providers: [provideNoopAnimations()]
|
||||
});
|
||||
fixture = TestBed.createComponent(TestCopyClipboardComponent);
|
||||
testingUtils = new UnitTestingUtils(fixture.debugElement);
|
||||
@@ -99,17 +105,15 @@ describe('CopyClipboardDirective', () => {
|
||||
it('should show tooltip when hover element', () => {
|
||||
testingUtils.hoverOverByCSS('span');
|
||||
fixture.detectChanges();
|
||||
expect(testingUtils.getByCSS('.adf-copy-tooltip')).not.toBeNull();
|
||||
expect(fixture.debugElement.nativeElement.querySelector('span')).not.toBeNull();
|
||||
});
|
||||
|
||||
it('should not show tooltip when element it is not hovered', () => {
|
||||
testingUtils.hoverOverByCSS('span');
|
||||
fixture.detectChanges();
|
||||
expect(testingUtils.getByCSS('.adf-copy-tooltip')).not.toBeNull();
|
||||
|
||||
testingUtils.mouseLeaveByCSS('span');
|
||||
fixture.detectChanges();
|
||||
expect(testingUtils.getByCSS('.adf-copy-tooltip')).toBeNull();
|
||||
expect(fixture.debugElement.nativeElement.querySelector('span')).not.toBeNull();
|
||||
});
|
||||
|
||||
it('should copy the content of element when click it', fakeAsync(() => {
|
||||
|
||||
@@ -15,13 +15,16 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Directive, Input, HostListener, Component, ViewContainerRef, ViewEncapsulation, OnInit } from '@angular/core';
|
||||
import { Directive, Input, HostListener, ViewContainerRef, Self, Optional } from '@angular/core';
|
||||
import { ClipboardService } from './clipboard.service';
|
||||
import { TranslatePipe } from '@ngx-translate/core';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { MatTooltip } from '@angular/material/tooltip';
|
||||
|
||||
@Directive({
|
||||
selector: '[adf-clipboard]',
|
||||
exportAs: 'adfClipboard'
|
||||
exportAs: 'adfClipboard',
|
||||
standalone: true,
|
||||
hostDirectives: [MatTooltip]
|
||||
})
|
||||
export class ClipboardDirective {
|
||||
/** Translation key or message for the tooltip. */
|
||||
@@ -37,19 +40,25 @@ export class ClipboardDirective {
|
||||
// eslint-disable-next-line @angular-eslint/no-input-rename
|
||||
@Input('clipboard-notification') message: string;
|
||||
|
||||
constructor(private clipboardService: ClipboardService, public viewContainerRef: ViewContainerRef) {}
|
||||
constructor(
|
||||
private readonly clipboardService: ClipboardService,
|
||||
public viewContainerRef: ViewContainerRef,
|
||||
@Self() private readonly matTooltip: MatTooltip,
|
||||
@Optional() private readonly translate: TranslateService
|
||||
) {}
|
||||
|
||||
@HostListener('mouseenter')
|
||||
showTooltip() {
|
||||
if (this.placeholder) {
|
||||
const componentRef = this.viewContainerRef.createComponent(ClipboardComponent).instance;
|
||||
componentRef.placeholder = this.placeholder;
|
||||
}
|
||||
const messageKey = this.placeholder || 'CLIPBOARD.CLICK_TO_COPY';
|
||||
const translated = this.translate ? this.translate.instant(messageKey) : messageKey;
|
||||
this.matTooltip.message = translated;
|
||||
this.matTooltip.position = 'below';
|
||||
this.matTooltip.show();
|
||||
}
|
||||
|
||||
@HostListener('mouseleave')
|
||||
closeTooltip() {
|
||||
this.viewContainerRef.remove();
|
||||
this.matTooltip.hide();
|
||||
}
|
||||
|
||||
@HostListener('keydown.enter', ['$event'])
|
||||
@@ -71,17 +80,3 @@ export class ClipboardDirective {
|
||||
this.clipboardService.copyContentToClipboard(content, this.message);
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'adf-copy-content-tooltip',
|
||||
imports: [TranslatePipe],
|
||||
template: `<span class="adf-copy-tooltip">{{ placeholder | translate }} </span>`,
|
||||
encapsulation: ViewEncapsulation.None
|
||||
})
|
||||
export class ClipboardComponent implements OnInit {
|
||||
placeholder: string;
|
||||
|
||||
ngOnInit() {
|
||||
this.placeholder = this.placeholder || 'CLIPBOARD.CLICK_TO_COPY';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,13 +16,14 @@
|
||||
*/
|
||||
|
||||
import { NgModule } from '@angular/core';
|
||||
import { ClipboardDirective, ClipboardComponent } from './clipboard.directive';
|
||||
import { ClipboardDirective } from './clipboard.directive';
|
||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||
|
||||
export const CLIPBOARD_DIRECTIVES = [ClipboardDirective, ClipboardComponent] as const;
|
||||
export const CLIPBOARD_DIRECTIVES = [ClipboardDirective] as const;
|
||||
|
||||
/** @deprecated use `...CLIPBOARD_DIRECTIVES` or import standalone directives */
|
||||
@NgModule({
|
||||
imports: [...CLIPBOARD_DIRECTIVES],
|
||||
exports: [...CLIPBOARD_DIRECTIVES]
|
||||
imports: [MatTooltipModule, ...CLIPBOARD_DIRECTIVES],
|
||||
exports: [MatTooltipModule, ...CLIPBOARD_DIRECTIVES]
|
||||
})
|
||||
export class ClipboardModule {}
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
.adf-copy-tooltip {
|
||||
position: absolute;
|
||||
background: var(--theme-primary-color);
|
||||
color: var(--theme-primary-color-default-contrast);
|
||||
font-size: var(--theme-caption-font-size);
|
||||
padding: 2px 5px;
|
||||
border-radius: 5px;
|
||||
bottom: 93%;
|
||||
left: 0;
|
||||
z-index: 1001;
|
||||
min-height: 20px;
|
||||
}
|
||||
|
||||
.adf-sticky-header {
|
||||
.adf-copy-tooltip {
|
||||
top: 85%;
|
||||
bottom: 0;
|
||||
}
|
||||
}
|
||||
@@ -37,14 +37,14 @@ import { TruncatePipe } from '../../../pipes/truncate.pipe';
|
||||
adf-clipboard="CLIPBOARD.CLICK_TO_COPY"
|
||||
[clipboard-notification]="'CLIPBOARD.SUCCESS_COPY'"
|
||||
[attr.aria-label]="value$ | async"
|
||||
[title]="tooltip"
|
||||
[title]="tooltip ? tooltip : computedTitle"
|
||||
class="adf-datatable-cell-value"
|
||||
>{{ column?.maxTextLength ? (value$ | async | truncate : column?.maxTextLength) : (value$ | async) }}</span
|
||||
>{{ column?.maxTextLength ? (value$ | async | truncate: column?.maxTextLength) : (value$ | async) }}</span
|
||||
>
|
||||
</ng-container>
|
||||
<ng-template #defaultCell>
|
||||
<span [title]="tooltip" class="adf-datatable-cell-value">{{
|
||||
column?.maxTextLength ? (value$ | async | truncate : column?.maxTextLength) : (value$ | async)
|
||||
<span [title]="tooltip ? tooltip : computedTitle" class="adf-datatable-cell-value">{{
|
||||
column?.maxTextLength ? (value$ | async | truncate: column?.maxTextLength) : (value$ | async)
|
||||
}}</span>
|
||||
</ng-template>
|
||||
`,
|
||||
@@ -79,6 +79,7 @@ export class DataTableCellComponent implements OnInit {
|
||||
protected destroyRef = inject(DestroyRef);
|
||||
protected dataTableService = inject(DataTableService, { optional: true });
|
||||
value$ = new BehaviorSubject<any>('');
|
||||
computedTitle: string = '';
|
||||
|
||||
ngOnInit() {
|
||||
this.updateValue();
|
||||
@@ -88,12 +89,8 @@ export class DataTableCellComponent implements OnInit {
|
||||
protected updateValue() {
|
||||
if (this.column?.key && this.row && this.data) {
|
||||
const value = this.data.getValue(this.row, this.column, this.resolverFn);
|
||||
|
||||
this.value$.next(value);
|
||||
|
||||
if (!this.tooltip) {
|
||||
this.tooltip = value;
|
||||
}
|
||||
this.computedTitle = this.computeTitle(value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,4 +112,19 @@ export class DataTableCellComponent implements OnInit {
|
||||
private getNestedPropertyValue(obj: any, path: string) {
|
||||
return path.split('.').reduce((source, key) => (source ? source[key] : ''), obj);
|
||||
}
|
||||
|
||||
private computeTitle(value: string): string {
|
||||
if (this.tooltip) {
|
||||
return this.tooltip;
|
||||
}
|
||||
|
||||
const rawValue = value;
|
||||
const max = this.column?.maxTextLength;
|
||||
|
||||
if (typeof max === 'number' && max > 0 && rawValue.length > max) {
|
||||
return rawValue;
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
+34
-25
@@ -16,7 +16,7 @@
|
||||
*/
|
||||
|
||||
import { Component, SimpleChange, ViewChild } from '@angular/core';
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { ComponentFixture, TestBed, fakeAsync } from '@angular/core/testing';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import {
|
||||
AppConfigService,
|
||||
@@ -36,6 +36,7 @@ import { ServiceTaskListCloudService } from '../../services/service-task-list-cl
|
||||
import { HarnessLoader } from '@angular/cdk/testing';
|
||||
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
|
||||
import { MatProgressSpinnerHarness } from '@angular/material/progress-spinner/testing';
|
||||
import { MatTooltipHarness } from '@angular/material/tooltip/testing';
|
||||
import { provideCloudPreferences } from '../../../../providers';
|
||||
|
||||
@Component({
|
||||
@@ -396,25 +397,31 @@ describe('ServiceTaskListCloudComponent: Injecting custom columns for task list
|
||||
expect(componentCustom.taskList.columns.length).toEqual(2);
|
||||
});
|
||||
|
||||
it('it should show copy tooltip when key is present in data-column', () => {
|
||||
it('it should show copy tooltip when key is present in data-column', fakeAsync(async () => {
|
||||
customCopyComponent.taskList.reload();
|
||||
copyFixture.detectChanges();
|
||||
|
||||
copyFixture.debugElement.query(By.css('span[title="04fdf69f-4ddd-48ab-9563-da776c9b163c"]')).triggerEventHandler('mouseenter');
|
||||
|
||||
const host = copyFixture.debugElement.query(By.css('span[adf-clipboard]'));
|
||||
host.triggerEventHandler('mouseenter', {});
|
||||
copyFixture.detectChanges();
|
||||
expect(copyFixture.debugElement.query(By.css('.adf-copy-tooltip'))).not.toBeNull();
|
||||
});
|
||||
|
||||
it('it should not show copy tooltip when key is not present in data-column', () => {
|
||||
const loader: HarnessLoader = TestbedHarnessEnvironment.loader(copyFixture);
|
||||
const tooltip = await loader.getHarness(MatTooltipHarness.with({ selector: 'span[adf-clipboard]' }));
|
||||
expect(await tooltip.isOpen()).toBeTrue();
|
||||
}));
|
||||
|
||||
it('it should not show copy tooltip when key is not present in data-column', fakeAsync(async () => {
|
||||
customCopyComponent.taskList.reload();
|
||||
copyFixture.detectChanges();
|
||||
|
||||
copyFixture.debugElement.query(By.css('span[title="serviceTaskName"]')).triggerEventHandler('mouseenter');
|
||||
|
||||
const host = copyFixture.debugElement.query(By.css('span[adf-clipboard]'));
|
||||
host.triggerEventHandler('mouseenter', {});
|
||||
copyFixture.detectChanges();
|
||||
expect(copyFixture.debugElement.query(By.css('.adf-copy-tooltip'))).toBeNull();
|
||||
});
|
||||
|
||||
const loader: HarnessLoader = TestbedHarnessEnvironment.loader(copyFixture);
|
||||
const tooltips = await loader.getAllHarnesses(MatTooltipHarness.with({ selector: 'span[title="serviceTaskName"]' }));
|
||||
expect(tooltips.length).toBe(0);
|
||||
}));
|
||||
});
|
||||
|
||||
describe('ServiceTaskListCloudComponent: Copy cell content directive from app.config specifications', () => {
|
||||
@@ -461,33 +468,35 @@ describe('ServiceTaskListCloudComponent: Copy cell content directive from app.co
|
||||
fixture.destroy();
|
||||
});
|
||||
|
||||
it('shoud show tooltip if config copyContent flag is true', () => {
|
||||
it('shoud show tooltip if config copyContent flag is true', fakeAsync(async () => {
|
||||
taskSpy.and.returnValue(of(fakeServiceTask));
|
||||
component.presetColumn = 'fakeCustomSchema';
|
||||
|
||||
component.reload();
|
||||
fixture.detectChanges();
|
||||
|
||||
const columnWithCopyContentFlagTrue = fixture.debugElement.query(By.css('span[title="04fdf69f-4ddd-48ab-9563-da776c9b163c"]'));
|
||||
|
||||
columnWithCopyContentFlagTrue.triggerEventHandler('mouseenter');
|
||||
|
||||
const host = fixture.debugElement.query(By.css('span[adf-clipboard]'));
|
||||
host.triggerEventHandler('mouseenter', {});
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement.querySelector('.adf-copy-tooltip')).not.toBeNull();
|
||||
});
|
||||
|
||||
it('shoud not show tooltip if config copyContent flag is NOT true', () => {
|
||||
const loader: HarnessLoader = TestbedHarnessEnvironment.loader(fixture);
|
||||
const tooltip = await loader.getHarness(MatTooltipHarness.with({ selector: 'span[adf-clipboard]' }));
|
||||
expect(await tooltip.isOpen()).toBeTrue();
|
||||
}));
|
||||
|
||||
it('shoud not show tooltip if config copyContent flag is NOT true', fakeAsync(async () => {
|
||||
taskSpy.and.returnValue(of(fakeServiceTask));
|
||||
component.presetColumn = 'fakeCustomSchema';
|
||||
|
||||
component.reload();
|
||||
fixture.detectChanges();
|
||||
|
||||
const columnWithCopyContentFlagNotTrue = fixture.debugElement.query(By.css('span[title="serviceTaskName"]'));
|
||||
|
||||
columnWithCopyContentFlagNotTrue.triggerEventHandler('mouseenter');
|
||||
|
||||
const host = fixture.debugElement.query(By.css('span[adf-clipboard]'));
|
||||
host.triggerEventHandler('mouseenter', {});
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement.querySelector('.adf-copy-tooltip')).toBeNull();
|
||||
});
|
||||
|
||||
const loader: HarnessLoader = TestbedHarnessEnvironment.loader(fixture);
|
||||
const tooltips = await loader.getAllHarnesses(MatTooltipHarness.with({ selector: 'span[title="serviceTaskName"]' }));
|
||||
expect(tooltips.length).toBe(0);
|
||||
}));
|
||||
});
|
||||
|
||||
+22
-18
@@ -16,7 +16,7 @@
|
||||
*/
|
||||
|
||||
import { Component, SimpleChange, ViewChild } from '@angular/core';
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { ComponentFixture, TestBed, fakeAsync } from '@angular/core/testing';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import {
|
||||
AppConfigService,
|
||||
@@ -40,6 +40,7 @@ import { TASK_LIST_CLOUD_TOKEN, TASK_LIST_PREFERENCES_SERVICE_TOKEN } from '../.
|
||||
import { HarnessLoader } from '@angular/cdk/testing';
|
||||
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
|
||||
import { MatProgressSpinnerHarness } from '@angular/material/progress-spinner/testing';
|
||||
import { MatTooltipHarness } from '@angular/material/tooltip/testing';
|
||||
import { provideCloudPreferences } from '../../../../providers';
|
||||
|
||||
@Component({
|
||||
@@ -699,24 +700,26 @@ describe('TaskListCloudComponent: Injecting custom colums for tasklist - CustomT
|
||||
expect(componentCustom.taskList.columns.length).toEqual(3);
|
||||
});
|
||||
|
||||
it('it should show copy tooltip when key is present in data-column', () => {
|
||||
it('it should show copy tooltip when key is present in data-column', fakeAsync(async () => {
|
||||
customCopyComponent.taskList.reload();
|
||||
copyFixture.detectChanges();
|
||||
|
||||
copyFixture.debugElement.query(By.css('span[title="11fe013d-c263-11e8-b75b-0a5864600540"]')).triggerEventHandler('mouseenter');
|
||||
|
||||
const host = copyFixture.debugElement.query(By.css('span[adf-clipboard]'));
|
||||
host.triggerEventHandler('mouseenter', {});
|
||||
copyFixture.detectChanges();
|
||||
expect(copyFixture.debugElement.query(By.css('.adf-copy-tooltip'))).not.toBeNull();
|
||||
});
|
||||
|
||||
it('it should not show copy tooltip when key is not present in data-column', () => {
|
||||
const loader: HarnessLoader = TestbedHarnessEnvironment.loader(copyFixture);
|
||||
const tooltip = await loader.getHarness(MatTooltipHarness.with({ selector: 'span[adf-clipboard]' }));
|
||||
expect(await tooltip.isOpen()).toBeTrue();
|
||||
}));
|
||||
|
||||
it('it should not show copy tooltip when key is not present in data-column', async () => {
|
||||
customCopyComponent.taskList.reload();
|
||||
copyFixture.detectChanges();
|
||||
|
||||
copyFixture.debugElement.query(By.css('span[title="standalone-subtask"]')).triggerEventHandler('mouseenter');
|
||||
|
||||
copyFixture.detectChanges();
|
||||
expect(copyFixture.debugElement.query(By.css('.adf-copy-tooltip'))).toBeNull();
|
||||
const loader: HarnessLoader = TestbedHarnessEnvironment.loader(copyFixture);
|
||||
const tooltips = await loader.getAllHarnesses(MatTooltipHarness.with({ selector: 'span[title="standalone-subtask"]' }));
|
||||
expect(tooltips.length).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -802,19 +805,20 @@ describe('TaskListCloudComponent: Copy cell content directive from app.config sp
|
||||
fixture.destroy();
|
||||
});
|
||||
|
||||
it('should show tooltip if config copyContent flag is true', () => {
|
||||
it('should show tooltip if config copyContent flag is true', fakeAsync(async () => {
|
||||
component.presetColumn = 'fakeCustomSchema';
|
||||
|
||||
component.reload();
|
||||
fixture.detectChanges();
|
||||
|
||||
const columnWithCopyContentFlagTrue = fixture.debugElement.query(By.css('span[title="11fe013d-c263-11e8-b75b-0a5864600540"]'));
|
||||
|
||||
columnWithCopyContentFlagTrue.triggerEventHandler('mouseenter');
|
||||
|
||||
const host = fixture.debugElement.query(By.css('span[adf-clipboard]'));
|
||||
host.triggerEventHandler('mouseenter', {});
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement.querySelector('.adf-copy-tooltip')).not.toBeNull();
|
||||
});
|
||||
|
||||
const loader: HarnessLoader = TestbedHarnessEnvironment.loader(fixture);
|
||||
const tooltip = await loader.getHarness(MatTooltipHarness.with({ selector: 'span[adf-clipboard]' }));
|
||||
expect(await tooltip.isOpen()).toBeTrue();
|
||||
}));
|
||||
|
||||
it('should replace priority values', () => {
|
||||
component.presetColumn = 'fakeCustomSchema';
|
||||
|
||||
+3
-1
@@ -14,7 +14,9 @@
|
||||
"build:libs": "nx run-many -t build --prod --skip-nx-cache",
|
||||
"build:schematics": "nx run-many -t build-schematics",
|
||||
"publish": "nx run-many -t npm-publish",
|
||||
"clean": "rimraf dist node_modules dist/libs"
|
||||
"clean": "rimraf dist node_modules dist/libs",
|
||||
"nx:run-target": "nx run",
|
||||
"nx:run-many": "nx run-many"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
Reference in New Issue
Block a user