mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2026-09-09 18:03:21 +00:00
[ACS-8468] Add copy icon functionality (#12079)
* [ACS-9106] Make the descendants false iin data column component * [ACS-8468] Add copy icon * [ACS-8468] Remove change from data column * [ACS-8468] Use if instead of *ngIf * [ACS-8468] Revert to double click functionality * [ACS-8468] Make copy button toggable and make it icon * [ACS-8468] Add unit test for copy icons * [ACS-8468] Add unit test for mat registry * [ACS-8468] Remove deprecated component * [ACS-8468] Align the styling with dropdown menu * [ACS-8468] Fix sonar cloud issue * [ACS-8468] Remove deprecated component * [ACS-8468] Remove svg icon and use material icon * [ACS-8468] Fix styling and remove mat-form-field-icon-suffix selector * [ACS-8468] Make copy to clipboard icon property true * [ACS-8468] Add unit tests and remove unused class * [ACS-8468] Add unitTestingUtils * [ACS-8468] use with args instead of callFake * [ACS-8468] remove jasmine.spy
This commit is contained in:
@@ -1471,6 +1471,10 @@
|
||||
"description": "Copy property to the clipboard on double click",
|
||||
"type": "boolean"
|
||||
},
|
||||
"display-copy-to-clipboard-icon": {
|
||||
"description": "Displays the copy to clipboard icon",
|
||||
"type": "boolean"
|
||||
},
|
||||
"selectFilterLimit": {
|
||||
"description": "Shows a filter if list options exceed a specified number. Default value 5",
|
||||
"type": "number"
|
||||
|
||||
+16
@@ -87,6 +87,22 @@
|
||||
<span class="adf-error-text">{{ 'FORM.FIELD.VALIDATOR.INVALID_DATE_FORMAT' | translate }}</span>
|
||||
</mat-error>
|
||||
}
|
||||
@if (displayCopyToClipboardIcon && !isEditable) {
|
||||
<button
|
||||
matSuffix
|
||||
type="button"
|
||||
class="adf-copy-to-clipboard-button"
|
||||
(click)="copyToClipboard(property.displayValue, $event)"
|
||||
[attr.aria-label]="'CORE.METADATA.ACTIONS.COPY_TO_CLIPBOARD' | translate"
|
||||
[attr.data-automation-id]="'card-dateitem-copy-to-clipboard-' + property.key"
|
||||
>
|
||||
<mat-icon
|
||||
adf-icon="content_copy"
|
||||
class="adf-copy-to-clipboard-icon"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</button>
|
||||
}
|
||||
</mat-form-field>
|
||||
} @else {
|
||||
<mat-form-field class="adf-property-field adf-dateitem-editable" [floatLabel]="property.default ? 'always' : null">
|
||||
|
||||
+27
@@ -64,4 +64,31 @@
|
||||
border-bottom: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.adf-copy-to-clipboard-button {
|
||||
border: none;
|
||||
background: none;
|
||||
cursor: pointer;
|
||||
opacity: 0;
|
||||
|
||||
.adf-copy-to-clipboard-icon {
|
||||
width: 30px;
|
||||
height: 20px;
|
||||
font-size: 20px;
|
||||
line-height: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&:focus-within {
|
||||
.adf-copy-to-clipboard-button {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@media (hover: none) {
|
||||
.adf-copy-to-clipboard-button {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+41
@@ -225,6 +225,47 @@ describe('CardViewDateItemComponent', () => {
|
||||
expect(clipboardService.copyContentToClipboard).toHaveBeenCalledWith('Jul 10, 2017', 'CORE.METADATA.ACCESSIBILITY.COPY_TO_CLIPBOARD_MESSAGE');
|
||||
});
|
||||
|
||||
it('should copy value to clipboard when clicking the copy icon', () => {
|
||||
const clipboardService = TestBed.inject(ClipboardService);
|
||||
spyOn(clipboardService, 'copyContentToClipboard');
|
||||
|
||||
component.editable = false;
|
||||
component.displayCopyToClipboardIcon = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
testingUtils.clickByDataAutomationId('card-dateitem-copy-to-clipboard-' + component.property.key);
|
||||
|
||||
fixture.detectChanges();
|
||||
expect(clipboardService.copyContentToClipboard).toHaveBeenCalledWith('Jul 10, 2017', 'CORE.METADATA.ACCESSIBILITY.COPY_TO_CLIPBOARD_MESSAGE');
|
||||
});
|
||||
|
||||
it('should render the copy icon by default', () => {
|
||||
component.editable = false;
|
||||
fixture.detectChanges();
|
||||
|
||||
const copyIcon = testingUtils.getByDataAutomationId('card-dateitem-copy-to-clipboard-' + component.property.key);
|
||||
expect(copyIcon).not.toBeNull();
|
||||
});
|
||||
|
||||
it('should NOT render the copy icon when displayCopyToClipboardIcon is false', () => {
|
||||
component.editable = false;
|
||||
component.displayCopyToClipboardIcon = false;
|
||||
fixture.detectChanges();
|
||||
|
||||
const copyIcon = testingUtils.getByDataAutomationId('card-dateitem-copy-to-clipboard-' + component.property.key);
|
||||
expect(copyIcon).toBeNull();
|
||||
});
|
||||
|
||||
it('should NOT render the copy icon when the item is editable', () => {
|
||||
component.editable = true;
|
||||
component.property.editable = true;
|
||||
component.displayCopyToClipboardIcon = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
const copyIcon = testingUtils.getByDataAutomationId('card-dateitem-copy-to-clipboard-' + component.property.key);
|
||||
expect(copyIcon).toBeNull();
|
||||
});
|
||||
|
||||
describe('clear icon', () => {
|
||||
it('should render the clear icon in case of displayClearAction:true', () => {
|
||||
component.editable = true;
|
||||
|
||||
+9
-2
@@ -86,6 +86,12 @@ export class CardViewDateItemComponent extends BaseCardView<CardViewDateItemMode
|
||||
@Input()
|
||||
displayClearAction = true;
|
||||
|
||||
@Input()
|
||||
copyToClipboardAction = true;
|
||||
|
||||
@Input()
|
||||
displayCopyToClipboardIcon = true;
|
||||
|
||||
@ViewChild('datetimePicker')
|
||||
public datepicker: MatDatetimepickerComponent<any>;
|
||||
|
||||
@@ -203,11 +209,12 @@ export class CardViewDateItemComponent extends BaseCardView<CardViewDateItemMode
|
||||
this.property.default = null;
|
||||
}
|
||||
|
||||
copyToClipboard(valueToCopy: string | string[]) {
|
||||
if (typeof valueToCopy === 'string') {
|
||||
copyToClipboard(valueToCopy: string | string[], event?: MouseEvent) {
|
||||
if (typeof valueToCopy === 'string' && (this.copyToClipboardAction || this.displayCopyToClipboardIcon)) {
|
||||
const clipboardMessage = this.translateService.instant('CORE.METADATA.ACCESSIBILITY.COPY_TO_CLIPBOARD_MESSAGE');
|
||||
this.clipboardService.copyContentToClipboard(valueToCopy, clipboardMessage);
|
||||
}
|
||||
(event?.currentTarget as HTMLElement)?.blur();
|
||||
}
|
||||
|
||||
addDateToList(event: MatDatetimepickerInputEvent<Date>) {
|
||||
|
||||
+4
-1
@@ -110,6 +110,7 @@ describe('CardViewItemDispatcherComponent', () => {
|
||||
const expectedCustomInput = 1;
|
||||
const expectedDisplayNoneOption = false;
|
||||
const expectedDisplayClearAction = false;
|
||||
const expectedDisplayCopyToClipboardIcon = true;
|
||||
|
||||
component.ngOnChanges({
|
||||
editable: new SimpleChange(true, expectedEditable, false),
|
||||
@@ -117,7 +118,8 @@ describe('CardViewItemDispatcherComponent', () => {
|
||||
property: new SimpleChange(null, expectedProperty, false),
|
||||
customInput: new SimpleChange(0, expectedCustomInput, false),
|
||||
displayNoneOption: new SimpleChange(true, expectedDisplayNoneOption, false),
|
||||
displayClearAction: new SimpleChange(true, expectedDisplayClearAction, false)
|
||||
displayClearAction: new SimpleChange(true, expectedDisplayClearAction, false),
|
||||
displayCopyToClipboardIcon: new SimpleChange(false, expectedDisplayCopyToClipboardIcon, true)
|
||||
});
|
||||
|
||||
const shinyCustomElementItemComponent = testingUtils.getByCSS('whatever-you-want-to-have').componentInstance;
|
||||
@@ -127,6 +129,7 @@ describe('CardViewItemDispatcherComponent', () => {
|
||||
expect(shinyCustomElementItemComponent.customInput).toBe(expectedCustomInput);
|
||||
expect(shinyCustomElementItemComponent.displayNoneOption).toBe(expectedDisplayNoneOption);
|
||||
expect(shinyCustomElementItemComponent.displayClearAction).toBe(expectedDisplayClearAction);
|
||||
expect(shinyCustomElementItemComponent.displayCopyToClipboardIcon).toBe(expectedDisplayCopyToClipboardIcon);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
+4
@@ -45,6 +45,9 @@ export class CardViewItemDispatcherComponent implements OnChanges {
|
||||
@Input()
|
||||
copyToClipboardAction: boolean = true;
|
||||
|
||||
@Input()
|
||||
displayCopyToClipboardIcon = true;
|
||||
|
||||
@Input()
|
||||
useChipsForMultiValueProperty: boolean = true;
|
||||
|
||||
@@ -100,6 +103,7 @@ export class CardViewItemDispatcherComponent implements OnChanges {
|
||||
this.componentReference.instance.displayNoneOption = this.displayNoneOption;
|
||||
this.componentReference.instance.displayClearAction = this.displayClearAction;
|
||||
this.componentReference.instance.copyToClipboardAction = this.copyToClipboardAction;
|
||||
this.componentReference.instance.displayCopyToClipboardIcon = this.displayCopyToClipboardIcon;
|
||||
this.componentReference.instance.useChipsForMultiValueProperty = this.useChipsForMultiValueProperty;
|
||||
this.componentReference.instance.multiValueSeparator = this.multiValueSeparator;
|
||||
}
|
||||
|
||||
+18
-1
@@ -29,7 +29,8 @@
|
||||
'adf-property-value-editable': editable,
|
||||
'adf-property-readonly-value': isReadonlyProperty || !editable,
|
||||
'adf-property-value-has-error': isEditable && hasErrors,
|
||||
'adf-property-value-not-editable': !editable
|
||||
'adf-property-value-not-editable': !editable,
|
||||
'adf-property-value-has-icon-suffix': displayCopyToClipboardIcon && !isEditable
|
||||
}"
|
||||
title="{{ property.label | translate }}"
|
||||
[placeholder]="property.default"
|
||||
@@ -58,6 +59,22 @@
|
||||
[attr.data-automation-id]="'card-textitem-value-' + property.key"
|
||||
>
|
||||
</textarea>
|
||||
@if (displayCopyToClipboardIcon && !isEditable) {
|
||||
<button
|
||||
matSuffix
|
||||
type="button"
|
||||
class="adf-textitem-action adf-copy-to-clipboard-button"
|
||||
(click)="copyToClipboard(property.displayValue, $event)"
|
||||
[attr.aria-label]="'CORE.METADATA.ACTIONS.COPY_TO_CLIPBOARD' | translate"
|
||||
[attr.data-automation-id]="'card-textitem-copy-to-clipboard-' + property.key"
|
||||
>
|
||||
<mat-icon
|
||||
adf-icon="content_copy"
|
||||
class="adf-copy-to-clipboard-icon"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</button>
|
||||
}
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
|
||||
+37
@@ -59,6 +59,7 @@
|
||||
}
|
||||
|
||||
.adf-property-value-has-icon-suffix {
|
||||
box-sizing: border-box;
|
||||
padding-right: 34px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
@@ -68,4 +69,40 @@
|
||||
|
||||
.adf-card-textitem-field-container {
|
||||
padding-top: 5px;
|
||||
|
||||
.adf-card-textitem-field .adf-copy-to-clipboard-button {
|
||||
border: none;
|
||||
background: none;
|
||||
cursor: pointer;
|
||||
opacity: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.adf-copy-to-clipboard-icon {
|
||||
width: 30px;
|
||||
height: 20px;
|
||||
position: relative;
|
||||
right: 5px;
|
||||
bottom: 6px;
|
||||
font-size: 20px;
|
||||
line-height: 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&:focus-within {
|
||||
.adf-card-textitem-field .adf-copy-to-clipboard-button {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@media (hover: none) {
|
||||
.adf-card-textitem-field .adf-copy-to-clipboard-button {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+46
@@ -597,6 +597,52 @@ describe('CardViewTextItemComponent', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('should copy value to clipboard when clicking the copy icon', () => {
|
||||
const clipboardService = TestBed.inject(ClipboardService);
|
||||
spyOn(clipboardService, 'copyContentToClipboard');
|
||||
|
||||
component.property.value = 'myValueToCopy';
|
||||
component.editable = false;
|
||||
component.displayCopyToClipboardIcon = true;
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
testingUtils.clickByDataAutomationId('card-textitem-copy-to-clipboard-' + component.property.key);
|
||||
|
||||
fixture.detectChanges();
|
||||
expect(clipboardService.copyContentToClipboard).toHaveBeenCalledWith(
|
||||
'myValueToCopy',
|
||||
'CORE.METADATA.ACCESSIBILITY.COPY_TO_CLIPBOARD_MESSAGE'
|
||||
);
|
||||
});
|
||||
|
||||
it('should render the copy icon by default', () => {
|
||||
component.editable = false;
|
||||
fixture.detectChanges();
|
||||
|
||||
const copyIcon = testingUtils.getByDataAutomationId('card-textitem-copy-to-clipboard-' + component.property.key);
|
||||
expect(copyIcon).not.toBeNull();
|
||||
});
|
||||
|
||||
it('should NOT render the copy icon when displayCopyToClipboardIcon is false', () => {
|
||||
component.editable = false;
|
||||
component.displayCopyToClipboardIcon = false;
|
||||
fixture.detectChanges();
|
||||
|
||||
const copyIcon = testingUtils.getByDataAutomationId('card-textitem-copy-to-clipboard-' + component.property.key);
|
||||
expect(copyIcon).toBeNull();
|
||||
});
|
||||
|
||||
it('should NOT render the copy icon when the item is editable', () => {
|
||||
component.editable = true;
|
||||
component.property.editable = true;
|
||||
component.displayCopyToClipboardIcon = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
const copyIcon = testingUtils.getByDataAutomationId('card-textitem-copy-to-clipboard-' + component.property.key);
|
||||
expect(copyIcon).toBeNull();
|
||||
});
|
||||
|
||||
it('should input be disabled if item it NOT editable', async () => {
|
||||
component.editable = false;
|
||||
component.property.clickable = true;
|
||||
|
||||
+6
-2
@@ -71,6 +71,9 @@ export class CardViewTextItemComponent extends BaseCardView<CardViewTextItemMode
|
||||
@Input()
|
||||
copyToClipboardAction = true;
|
||||
|
||||
@Input()
|
||||
displayCopyToClipboardIcon = true;
|
||||
|
||||
@Input()
|
||||
useChipsForMultiValueProperty = true;
|
||||
|
||||
@@ -218,11 +221,12 @@ export class CardViewTextItemComponent extends BaseCardView<CardViewTextItemMode
|
||||
this.update();
|
||||
}
|
||||
|
||||
copyToClipboard(valueToCopy: string) {
|
||||
if (this.copyToClipboardAction) {
|
||||
copyToClipboard(valueToCopy: string, event?: MouseEvent) {
|
||||
if (this.copyToClipboardAction || this.displayCopyToClipboardIcon) {
|
||||
const clipboardMessage = this.translateService.instant('CORE.METADATA.ACCESSIBILITY.COPY_TO_CLIPBOARD_MESSAGE');
|
||||
this.clipboardService.copyContentToClipboard(valueToCopy, clipboardMessage);
|
||||
}
|
||||
(event?.currentTarget as HTMLElement)?.blur();
|
||||
}
|
||||
|
||||
undoText(event: KeyboardEvent) {
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
[displayNoneOption]="property['displayNoneOption'] !== undefined ? property['displayNoneOption'] : displayNoneOption"
|
||||
[displayClearAction]="displayClearAction"
|
||||
[copyToClipboardAction]="copyToClipboardAction"
|
||||
[displayCopyToClipboardIcon]="displayCopyToClipboardIcon"
|
||||
[useChipsForMultiValueProperty]="useChipsForMultiValueProperty"
|
||||
[multiValueSeparator]="multiValueSeparator" />
|
||||
</div>
|
||||
|
||||
@@ -19,6 +19,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { CardViewDateItemModel } from '../../models/card-view-dateitem.model';
|
||||
import { CardViewTextItemModel } from '../../models/card-view-textitem.model';
|
||||
import { CardViewComponent } from './card-view.component';
|
||||
import { CardViewItemDispatcherComponent } from '../card-view-item-dispatcher/card-view-item-dispatcher.component';
|
||||
import { CardViewSelectItemModel } from '../../models/card-view-selectitem.model';
|
||||
import { of } from 'rxjs';
|
||||
import { CardViewSelectItemOption } from '../../interfaces/card-view-selectitem-properties.interface';
|
||||
@@ -81,6 +82,20 @@ describe('CardViewComponent', () => {
|
||||
expect(testingUtils.getByDataAutomationId('datepicker-some-key')).not.toBeNull('Datepicker should be in DOM');
|
||||
});
|
||||
|
||||
it('should pass through displayCopyToClipboardIcon property to the item dispatcher', () => {
|
||||
component.displayCopyToClipboardIcon = false;
|
||||
component.properties = [new CardViewTextItemModel({ label: 'My label', value: 'My value', key: 'some key' })];
|
||||
fixture.detectChanges();
|
||||
|
||||
const dispatcher = testingUtils.getByDirective(CardViewItemDispatcherComponent).componentInstance;
|
||||
expect(dispatcher.displayCopyToClipboardIcon).toBeFalse();
|
||||
|
||||
component.displayCopyToClipboardIcon = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(dispatcher.displayCopyToClipboardIcon).toBeTrue();
|
||||
});
|
||||
|
||||
it('should render the date in the correct format', async () => {
|
||||
component.properties = [
|
||||
new CardViewDateItemModel({
|
||||
|
||||
@@ -53,6 +53,10 @@ export class CardViewComponent {
|
||||
@Input()
|
||||
copyToClipboardAction: boolean = true;
|
||||
|
||||
/** Toggles whether or not to display the copy to clipboard icon. */
|
||||
@Input()
|
||||
displayCopyToClipboardIcon: boolean = true;
|
||||
|
||||
/** Toggles whether or not to enable chips for multivalued properties. */
|
||||
@Input()
|
||||
useChipsForMultiValueProperty: boolean = true;
|
||||
|
||||
@@ -77,6 +77,14 @@ export const cardViewArgTypes: ArgTypes = {
|
||||
defaultValue: { summary: 'true' }
|
||||
}
|
||||
},
|
||||
displayCopyToClipboardIcon: {
|
||||
control: 'boolean',
|
||||
description: 'Display copy to clipboard icon',
|
||||
table: {
|
||||
type: { summary: 'boolean' },
|
||||
defaultValue: { summary: 'true' }
|
||||
}
|
||||
},
|
||||
useChipsForMultiValueProperty: {
|
||||
control: 'boolean',
|
||||
description: 'Split text for chips using defined separator',
|
||||
@@ -104,6 +112,7 @@ export const cardViewDefaultArgs: Record<string, unknown> = {
|
||||
displayNoneOption: true,
|
||||
displayClearAction: true,
|
||||
copyToClipboardAction: true,
|
||||
displayCopyToClipboardIcon: true,
|
||||
useChipsForMultiValueProperty: true,
|
||||
multiValueSeparator: ', '
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user