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:
@@ -417,7 +417,8 @@ Once you have enabled this feature you will be able to double click on your meta
|
|||||||
"exif:exif": [ "exif:pixelXDimension", "exif:pixelYDimension"]
|
"exif:exif": [ "exif:pixelXDimension", "exif:pixelYDimension"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"copy-to-clipboard-action": true
|
"copy-to-clipboard-action": true,
|
||||||
|
"display-copy-to-clipboard-icon": true
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -109,6 +109,7 @@ Defining properties from Typescript:
|
|||||||
| Name | Type | Default value | Description |
|
| Name | Type | Default value | Description |
|
||||||
| ---- | ---- | ------------- | ----------- |
|
| ---- | ---- | ------------- | ----------- |
|
||||||
| copyToClipboardAction | `boolean` | true | Toggles whether or not to enable copy to clipboard action. |
|
| copyToClipboardAction | `boolean` | true | Toggles whether or not to enable copy to clipboard action. |
|
||||||
|
| displayCopyToClipboardIcon | `boolean` | true | Toggles whether or not to show copy to clipboard icon. |
|
||||||
| displayClearAction | `boolean` | true | Toggles whether or not to display clear action. |
|
| displayClearAction | `boolean` | true | Toggles whether or not to display clear action. |
|
||||||
| displayEmpty | `boolean` | true | Toggles whether or not to show empty items in non-editable mode. |
|
| displayEmpty | `boolean` | true | Toggles whether or not to show empty items in non-editable mode. |
|
||||||
| displayNoneOption | `boolean` | true | Toggles whether or not to display none option. |
|
| displayNoneOption | `boolean` | true | Toggles whether or not to display none option. |
|
||||||
@@ -193,6 +194,7 @@ const textItemProperty = new CardViewTextItemModel(options);
|
|||||||
| clickable | boolean | false | Toggles whether the property responds to clicks |
|
| clickable | boolean | false | Toggles whether the property responds to clicks |
|
||||||
| clickableCallBack | function | null | Function to execute when click the element |
|
| clickableCallBack | function | null | Function to execute when click the element |
|
||||||
| copyToClipboardAction | `boolean` | true | Toggles whether or not to enable copy to clipboard action. |
|
| copyToClipboardAction | `boolean` | true | Toggles whether or not to enable copy to clipboard action. |
|
||||||
|
| displayCopyToClipboardIcon | `boolean` | true | Toggles whether or not to show copy to clipboard icon. |
|
||||||
| useChipsForMultiValueProperty | `boolean` | true | Toggles whether or not to enable chips for multivalued properties. |
|
| useChipsForMultiValueProperty | `boolean` | true | Toggles whether or not to enable chips for multivalued properties. |
|
||||||
| multiValueSeparator | `string` | ',' | String separator between multi-value property items. |
|
| multiValueSeparator | `string` | ',' | String separator between multi-value property items. |
|
||||||
| icon | string | | The material icon to show beside the item if it is clickable |
|
| icon | string | | The material icon to show beside the item if it is clickable |
|
||||||
@@ -249,6 +251,7 @@ const dateItemProperty = new CardViewDateItemModel(options);
|
|||||||
| label\* | string | | Item label |
|
| label\* | string | | Item label |
|
||||||
| value\* | any | | The original data value for the item |
|
| value\* | any | | The original data value for the item |
|
||||||
| copyToClipboardAction | `boolean` | true | Toggles whether or not to enable copy to clipboard action. |
|
| copyToClipboardAction | `boolean` | true | Toggles whether or not to enable copy to clipboard action. |
|
||||||
|
| displayCopyToClipboardIcon | `boolean` | true | Toggles whether or not to show copy to clipboard icon. |
|
||||||
| key\* | string | | Identifying key (important when editing the item) |
|
| key\* | string | | Identifying key (important when editing the item) |
|
||||||
| default | any | | The default value to display if the value is empty |
|
| default | any | | The default value to display if the value is empty |
|
||||||
| displayValue\* | any | | The value to display |
|
| displayValue\* | any | | The value to display |
|
||||||
|
|||||||
@@ -109,9 +109,10 @@ Now that the `my-preset` configuration is defined, let's use it in a view of the
|
|||||||
|
|
||||||
### Properties
|
### Properties
|
||||||
|
|
||||||
| Name | Type | Default value | Description |
|
| Name | Type | Default value | Description |
|
||||||
|----------|-----------|---------------|------------------------------------------------------------------------------|
|
|----------------------------|-----------|---------------|------------------------------------------------------------------------------|
|
||||||
| readOnly | `boolean` | false | (optional) This flag sets the metadata in read only mode preventing changes. |
|
| readOnly | `boolean` | false | (optional) This flag sets the metadata in read only mode preventing changes. |
|
||||||
|
| displayCopyToClipboardIcon | `boolean` | true | Toggles whether or not to display the copy to clipboard icon. |
|
||||||
|
|
||||||
### Viewing the result
|
### Viewing the result
|
||||||
|
|
||||||
|
|||||||
+2
@@ -51,6 +51,7 @@
|
|||||||
[editable]="!readOnly && isPanelEditing(DefaultPanels.PROPERTIES)"
|
[editable]="!readOnly && isPanelEditing(DefaultPanels.PROPERTIES)"
|
||||||
[displayEmpty]="displayEmpty"
|
[displayEmpty]="displayEmpty"
|
||||||
[copyToClipboardAction]="copyToClipboardAction"
|
[copyToClipboardAction]="copyToClipboardAction"
|
||||||
|
[displayCopyToClipboardIcon]="displayCopyToClipboardIcon"
|
||||||
[useChipsForMultiValueProperty]="useChipsForMultiValueProperty"
|
[useChipsForMultiValueProperty]="useChipsForMultiValueProperty"
|
||||||
[multiValueSeparator]="multiValueSeparator" />
|
[multiValueSeparator]="multiValueSeparator" />
|
||||||
</mat-expansion-panel>
|
</mat-expansion-panel>
|
||||||
@@ -252,6 +253,7 @@
|
|||||||
[editable]="!readOnly && group.editable && isPanelEditing(group.title)"
|
[editable]="!readOnly && group.editable && isPanelEditing(group.title)"
|
||||||
[displayEmpty]="displayEmpty"
|
[displayEmpty]="displayEmpty"
|
||||||
[copyToClipboardAction]="copyToClipboardAction"
|
[copyToClipboardAction]="copyToClipboardAction"
|
||||||
|
[displayCopyToClipboardIcon]="displayCopyToClipboardIcon"
|
||||||
[useChipsForMultiValueProperty]="useChipsForMultiValueProperty"
|
[useChipsForMultiValueProperty]="useChipsForMultiValueProperty"
|
||||||
[multiValueSeparator]="multiValueSeparator" />
|
[multiValueSeparator]="multiValueSeparator" />
|
||||||
</mat-expansion-panel>
|
</mat-expansion-panel>
|
||||||
|
|||||||
+20
@@ -50,6 +50,7 @@ describe('ContentMetadataComponent', () => {
|
|||||||
let contentMetadataService: ContentMetadataService;
|
let contentMetadataService: ContentMetadataService;
|
||||||
let updateService: CardViewContentUpdateService;
|
let updateService: CardViewContentUpdateService;
|
||||||
let nodesApiService: NodesApiService;
|
let nodesApiService: NodesApiService;
|
||||||
|
let appConfigService: AppConfigService;
|
||||||
let node: Node;
|
let node: Node;
|
||||||
let folderNode: Node;
|
let folderNode: Node;
|
||||||
let tagService: TagService;
|
let tagService: TagService;
|
||||||
@@ -212,6 +213,7 @@ describe('ContentMetadataComponent', () => {
|
|||||||
tagService = TestBed.inject(TagService);
|
tagService = TestBed.inject(TagService);
|
||||||
categoryService = TestBed.inject(CategoryService);
|
categoryService = TestBed.inject(CategoryService);
|
||||||
notificationService = TestBed.inject(NotificationService);
|
notificationService = TestBed.inject(NotificationService);
|
||||||
|
appConfigService = TestBed.inject(AppConfigService);
|
||||||
const propertyDescriptorsService = TestBed.inject(PropertyDescriptorsService);
|
const propertyDescriptorsService = TestBed.inject(PropertyDescriptorsService);
|
||||||
const classesApi = propertyDescriptorsService['classesApi'];
|
const classesApi = propertyDescriptorsService['classesApi'];
|
||||||
|
|
||||||
@@ -255,6 +257,24 @@ describe('ContentMetadataComponent', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('Copy to clipboard configuration', () => {
|
||||||
|
it('should set displayCopyToClipboardIcon to true when config value is true', () => {
|
||||||
|
const getSpy = spyOn(appConfigService, 'get').and.callThrough();
|
||||||
|
getSpy.withArgs('content-metadata.display-copy-to-clipboard-icon').and.returnValue(true);
|
||||||
|
const newFixture = TestBed.createComponent(ContentMetadataComponent);
|
||||||
|
|
||||||
|
expect(newFixture.componentInstance.displayCopyToClipboardIcon).toBeTrue();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set displayCopyToClipboardIcon to false when config value is false', () => {
|
||||||
|
const getSpy = spyOn(appConfigService, 'get').and.callThrough();
|
||||||
|
getSpy.withArgs('content-metadata.display-copy-to-clipboard-icon').and.returnValue(false);
|
||||||
|
const newFixture = TestBed.createComponent(ContentMetadataComponent);
|
||||||
|
|
||||||
|
expect(newFixture.componentInstance.displayCopyToClipboardIcon).toBeFalse();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('Folder', () => {
|
describe('Folder', () => {
|
||||||
it('should show the folder node', (done) => {
|
it('should show the folder node', (done) => {
|
||||||
component.expanded = false;
|
component.expanded = false;
|
||||||
|
|||||||
+5
@@ -131,6 +131,10 @@ export class ContentMetadataComponent implements OnChanges, OnInit {
|
|||||||
@Input()
|
@Input()
|
||||||
copyToClipboardAction: boolean = true;
|
copyToClipboardAction: boolean = true;
|
||||||
|
|
||||||
|
/** Toggles whether or not to display the copy to clipboard icon. */
|
||||||
|
@Input()
|
||||||
|
displayCopyToClipboardIcon = true;
|
||||||
|
|
||||||
/** Toggles whether or not to enable chips for multivalued properties. */
|
/** Toggles whether or not to enable chips for multivalued properties. */
|
||||||
@Input()
|
@Input()
|
||||||
useChipsForMultiValueProperty: boolean = true;
|
useChipsForMultiValueProperty: boolean = true;
|
||||||
@@ -186,6 +190,7 @@ export class ContentMetadataComponent implements OnChanges, OnInit {
|
|||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.copyToClipboardAction = this.appConfig.get<boolean>('content-metadata.copy-to-clipboard-action');
|
this.copyToClipboardAction = this.appConfig.get<boolean>('content-metadata.copy-to-clipboard-action');
|
||||||
|
this.displayCopyToClipboardIcon = this.appConfig.get<boolean>('content-metadata.display-copy-to-clipboard-icon');
|
||||||
this.multiValueSeparator = this.appConfig.get<string>('content-metadata.multi-value-pipe-separator') || DEFAULT_SEPARATOR;
|
this.multiValueSeparator = this.appConfig.get<string>('content-metadata.multi-value-pipe-separator') || DEFAULT_SEPARATOR;
|
||||||
this.useChipsForMultiValueProperty = this.appConfig.get<boolean>('content-metadata.multi-value-chips');
|
this.useChipsForMultiValueProperty = this.appConfig.get<boolean>('content-metadata.multi-value-chips');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1471,6 +1471,10 @@
|
|||||||
"description": "Copy property to the clipboard on double click",
|
"description": "Copy property to the clipboard on double click",
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
},
|
},
|
||||||
|
"display-copy-to-clipboard-icon": {
|
||||||
|
"description": "Displays the copy to clipboard icon",
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
"selectFilterLimit": {
|
"selectFilterLimit": {
|
||||||
"description": "Shows a filter if list options exceed a specified number. Default value 5",
|
"description": "Shows a filter if list options exceed a specified number. Default value 5",
|
||||||
"type": "number"
|
"type": "number"
|
||||||
|
|||||||
+16
@@ -87,6 +87,22 @@
|
|||||||
<span class="adf-error-text">{{ 'FORM.FIELD.VALIDATOR.INVALID_DATE_FORMAT' | translate }}</span>
|
<span class="adf-error-text">{{ 'FORM.FIELD.VALIDATOR.INVALID_DATE_FORMAT' | translate }}</span>
|
||||||
</mat-error>
|
</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>
|
</mat-form-field>
|
||||||
} @else {
|
} @else {
|
||||||
<mat-form-field class="adf-property-field adf-dateitem-editable" [floatLabel]="property.default ? 'always' : null">
|
<mat-form-field class="adf-property-field adf-dateitem-editable" [floatLabel]="property.default ? 'always' : null">
|
||||||
|
|||||||
+27
@@ -64,4 +64,31 @@
|
|||||||
border-bottom: 0;
|
border-bottom: 0;
|
||||||
cursor: pointer;
|
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');
|
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', () => {
|
describe('clear icon', () => {
|
||||||
it('should render the clear icon in case of displayClearAction:true', () => {
|
it('should render the clear icon in case of displayClearAction:true', () => {
|
||||||
component.editable = true;
|
component.editable = true;
|
||||||
|
|||||||
+9
-2
@@ -86,6 +86,12 @@ export class CardViewDateItemComponent extends BaseCardView<CardViewDateItemMode
|
|||||||
@Input()
|
@Input()
|
||||||
displayClearAction = true;
|
displayClearAction = true;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
copyToClipboardAction = true;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
displayCopyToClipboardIcon = true;
|
||||||
|
|
||||||
@ViewChild('datetimePicker')
|
@ViewChild('datetimePicker')
|
||||||
public datepicker: MatDatetimepickerComponent<any>;
|
public datepicker: MatDatetimepickerComponent<any>;
|
||||||
|
|
||||||
@@ -203,11 +209,12 @@ export class CardViewDateItemComponent extends BaseCardView<CardViewDateItemMode
|
|||||||
this.property.default = null;
|
this.property.default = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
copyToClipboard(valueToCopy: string | string[]) {
|
copyToClipboard(valueToCopy: string | string[], event?: MouseEvent) {
|
||||||
if (typeof valueToCopy === 'string') {
|
if (typeof valueToCopy === 'string' && (this.copyToClipboardAction || this.displayCopyToClipboardIcon)) {
|
||||||
const clipboardMessage = this.translateService.instant('CORE.METADATA.ACCESSIBILITY.COPY_TO_CLIPBOARD_MESSAGE');
|
const clipboardMessage = this.translateService.instant('CORE.METADATA.ACCESSIBILITY.COPY_TO_CLIPBOARD_MESSAGE');
|
||||||
this.clipboardService.copyContentToClipboard(valueToCopy, clipboardMessage);
|
this.clipboardService.copyContentToClipboard(valueToCopy, clipboardMessage);
|
||||||
}
|
}
|
||||||
|
(event?.currentTarget as HTMLElement)?.blur();
|
||||||
}
|
}
|
||||||
|
|
||||||
addDateToList(event: MatDatetimepickerInputEvent<Date>) {
|
addDateToList(event: MatDatetimepickerInputEvent<Date>) {
|
||||||
|
|||||||
+4
-1
@@ -110,6 +110,7 @@ describe('CardViewItemDispatcherComponent', () => {
|
|||||||
const expectedCustomInput = 1;
|
const expectedCustomInput = 1;
|
||||||
const expectedDisplayNoneOption = false;
|
const expectedDisplayNoneOption = false;
|
||||||
const expectedDisplayClearAction = false;
|
const expectedDisplayClearAction = false;
|
||||||
|
const expectedDisplayCopyToClipboardIcon = true;
|
||||||
|
|
||||||
component.ngOnChanges({
|
component.ngOnChanges({
|
||||||
editable: new SimpleChange(true, expectedEditable, false),
|
editable: new SimpleChange(true, expectedEditable, false),
|
||||||
@@ -117,7 +118,8 @@ describe('CardViewItemDispatcherComponent', () => {
|
|||||||
property: new SimpleChange(null, expectedProperty, false),
|
property: new SimpleChange(null, expectedProperty, false),
|
||||||
customInput: new SimpleChange(0, expectedCustomInput, false),
|
customInput: new SimpleChange(0, expectedCustomInput, false),
|
||||||
displayNoneOption: new SimpleChange(true, expectedDisplayNoneOption, 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;
|
const shinyCustomElementItemComponent = testingUtils.getByCSS('whatever-you-want-to-have').componentInstance;
|
||||||
@@ -127,6 +129,7 @@ describe('CardViewItemDispatcherComponent', () => {
|
|||||||
expect(shinyCustomElementItemComponent.customInput).toBe(expectedCustomInput);
|
expect(shinyCustomElementItemComponent.customInput).toBe(expectedCustomInput);
|
||||||
expect(shinyCustomElementItemComponent.displayNoneOption).toBe(expectedDisplayNoneOption);
|
expect(shinyCustomElementItemComponent.displayNoneOption).toBe(expectedDisplayNoneOption);
|
||||||
expect(shinyCustomElementItemComponent.displayClearAction).toBe(expectedDisplayClearAction);
|
expect(shinyCustomElementItemComponent.displayClearAction).toBe(expectedDisplayClearAction);
|
||||||
|
expect(shinyCustomElementItemComponent.displayCopyToClipboardIcon).toBe(expectedDisplayCopyToClipboardIcon);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
+4
@@ -45,6 +45,9 @@ export class CardViewItemDispatcherComponent implements OnChanges {
|
|||||||
@Input()
|
@Input()
|
||||||
copyToClipboardAction: boolean = true;
|
copyToClipboardAction: boolean = true;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
displayCopyToClipboardIcon = true;
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
useChipsForMultiValueProperty: boolean = true;
|
useChipsForMultiValueProperty: boolean = true;
|
||||||
|
|
||||||
@@ -100,6 +103,7 @@ export class CardViewItemDispatcherComponent implements OnChanges {
|
|||||||
this.componentReference.instance.displayNoneOption = this.displayNoneOption;
|
this.componentReference.instance.displayNoneOption = this.displayNoneOption;
|
||||||
this.componentReference.instance.displayClearAction = this.displayClearAction;
|
this.componentReference.instance.displayClearAction = this.displayClearAction;
|
||||||
this.componentReference.instance.copyToClipboardAction = this.copyToClipboardAction;
|
this.componentReference.instance.copyToClipboardAction = this.copyToClipboardAction;
|
||||||
|
this.componentReference.instance.displayCopyToClipboardIcon = this.displayCopyToClipboardIcon;
|
||||||
this.componentReference.instance.useChipsForMultiValueProperty = this.useChipsForMultiValueProperty;
|
this.componentReference.instance.useChipsForMultiValueProperty = this.useChipsForMultiValueProperty;
|
||||||
this.componentReference.instance.multiValueSeparator = this.multiValueSeparator;
|
this.componentReference.instance.multiValueSeparator = this.multiValueSeparator;
|
||||||
}
|
}
|
||||||
|
|||||||
+18
-1
@@ -29,7 +29,8 @@
|
|||||||
'adf-property-value-editable': editable,
|
'adf-property-value-editable': editable,
|
||||||
'adf-property-readonly-value': isReadonlyProperty || !editable,
|
'adf-property-readonly-value': isReadonlyProperty || !editable,
|
||||||
'adf-property-value-has-error': isEditable && hasErrors,
|
'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 }}"
|
title="{{ property.label | translate }}"
|
||||||
[placeholder]="property.default"
|
[placeholder]="property.default"
|
||||||
@@ -58,6 +59,22 @@
|
|||||||
[attr.data-automation-id]="'card-textitem-value-' + property.key"
|
[attr.data-automation-id]="'card-textitem-value-' + property.key"
|
||||||
>
|
>
|
||||||
</textarea>
|
</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>
|
</mat-form-field>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
+37
@@ -59,6 +59,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.adf-property-value-has-icon-suffix {
|
.adf-property-value-has-icon-suffix {
|
||||||
|
box-sizing: border-box;
|
||||||
padding-right: 34px;
|
padding-right: 34px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
@@ -68,4 +69,40 @@
|
|||||||
|
|
||||||
.adf-card-textitem-field-container {
|
.adf-card-textitem-field-container {
|
||||||
padding-top: 5px;
|
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 () => {
|
it('should input be disabled if item it NOT editable', async () => {
|
||||||
component.editable = false;
|
component.editable = false;
|
||||||
component.property.clickable = true;
|
component.property.clickable = true;
|
||||||
|
|||||||
+6
-2
@@ -71,6 +71,9 @@ export class CardViewTextItemComponent extends BaseCardView<CardViewTextItemMode
|
|||||||
@Input()
|
@Input()
|
||||||
copyToClipboardAction = true;
|
copyToClipboardAction = true;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
displayCopyToClipboardIcon = true;
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
useChipsForMultiValueProperty = true;
|
useChipsForMultiValueProperty = true;
|
||||||
|
|
||||||
@@ -218,11 +221,12 @@ export class CardViewTextItemComponent extends BaseCardView<CardViewTextItemMode
|
|||||||
this.update();
|
this.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
copyToClipboard(valueToCopy: string) {
|
copyToClipboard(valueToCopy: string, event?: MouseEvent) {
|
||||||
if (this.copyToClipboardAction) {
|
if (this.copyToClipboardAction || this.displayCopyToClipboardIcon) {
|
||||||
const clipboardMessage = this.translateService.instant('CORE.METADATA.ACCESSIBILITY.COPY_TO_CLIPBOARD_MESSAGE');
|
const clipboardMessage = this.translateService.instant('CORE.METADATA.ACCESSIBILITY.COPY_TO_CLIPBOARD_MESSAGE');
|
||||||
this.clipboardService.copyContentToClipboard(valueToCopy, clipboardMessage);
|
this.clipboardService.copyContentToClipboard(valueToCopy, clipboardMessage);
|
||||||
}
|
}
|
||||||
|
(event?.currentTarget as HTMLElement)?.blur();
|
||||||
}
|
}
|
||||||
|
|
||||||
undoText(event: KeyboardEvent) {
|
undoText(event: KeyboardEvent) {
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
[displayNoneOption]="property['displayNoneOption'] !== undefined ? property['displayNoneOption'] : displayNoneOption"
|
[displayNoneOption]="property['displayNoneOption'] !== undefined ? property['displayNoneOption'] : displayNoneOption"
|
||||||
[displayClearAction]="displayClearAction"
|
[displayClearAction]="displayClearAction"
|
||||||
[copyToClipboardAction]="copyToClipboardAction"
|
[copyToClipboardAction]="copyToClipboardAction"
|
||||||
|
[displayCopyToClipboardIcon]="displayCopyToClipboardIcon"
|
||||||
[useChipsForMultiValueProperty]="useChipsForMultiValueProperty"
|
[useChipsForMultiValueProperty]="useChipsForMultiValueProperty"
|
||||||
[multiValueSeparator]="multiValueSeparator" />
|
[multiValueSeparator]="multiValueSeparator" />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|||||||
import { CardViewDateItemModel } from '../../models/card-view-dateitem.model';
|
import { CardViewDateItemModel } from '../../models/card-view-dateitem.model';
|
||||||
import { CardViewTextItemModel } from '../../models/card-view-textitem.model';
|
import { CardViewTextItemModel } from '../../models/card-view-textitem.model';
|
||||||
import { CardViewComponent } from './card-view.component';
|
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 { CardViewSelectItemModel } from '../../models/card-view-selectitem.model';
|
||||||
import { of } from 'rxjs';
|
import { of } from 'rxjs';
|
||||||
import { CardViewSelectItemOption } from '../../interfaces/card-view-selectitem-properties.interface';
|
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');
|
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 () => {
|
it('should render the date in the correct format', async () => {
|
||||||
component.properties = [
|
component.properties = [
|
||||||
new CardViewDateItemModel({
|
new CardViewDateItemModel({
|
||||||
|
|||||||
@@ -53,6 +53,10 @@ export class CardViewComponent {
|
|||||||
@Input()
|
@Input()
|
||||||
copyToClipboardAction: boolean = true;
|
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. */
|
/** Toggles whether or not to enable chips for multivalued properties. */
|
||||||
@Input()
|
@Input()
|
||||||
useChipsForMultiValueProperty: boolean = true;
|
useChipsForMultiValueProperty: boolean = true;
|
||||||
|
|||||||
@@ -77,6 +77,14 @@ export const cardViewArgTypes: ArgTypes = {
|
|||||||
defaultValue: { summary: 'true' }
|
defaultValue: { summary: 'true' }
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
displayCopyToClipboardIcon: {
|
||||||
|
control: 'boolean',
|
||||||
|
description: 'Display copy to clipboard icon',
|
||||||
|
table: {
|
||||||
|
type: { summary: 'boolean' },
|
||||||
|
defaultValue: { summary: 'true' }
|
||||||
|
}
|
||||||
|
},
|
||||||
useChipsForMultiValueProperty: {
|
useChipsForMultiValueProperty: {
|
||||||
control: 'boolean',
|
control: 'boolean',
|
||||||
description: 'Split text for chips using defined separator',
|
description: 'Split text for chips using defined separator',
|
||||||
@@ -104,6 +112,7 @@ export const cardViewDefaultArgs: Record<string, unknown> = {
|
|||||||
displayNoneOption: true,
|
displayNoneOption: true,
|
||||||
displayClearAction: true,
|
displayClearAction: true,
|
||||||
copyToClipboardAction: true,
|
copyToClipboardAction: true,
|
||||||
|
displayCopyToClipboardIcon: true,
|
||||||
useChipsForMultiValueProperty: true,
|
useChipsForMultiValueProperty: true,
|
||||||
multiValueSeparator: ', '
|
multiValueSeparator: ', '
|
||||||
};
|
};
|
||||||
|
|||||||
+1
@@ -8,6 +8,7 @@
|
|||||||
[multi]="multi"
|
[multi]="multi"
|
||||||
[displayAspect]="displayAspect"
|
[displayAspect]="displayAspect"
|
||||||
[copyToClipboardAction]="copyToClipboardAction"
|
[copyToClipboardAction]="copyToClipboardAction"
|
||||||
|
[displayCopyToClipboardIcon]="displayCopyToClipboardIcon"
|
||||||
[useChipsForMultiValueProperty]="useChipsForMultiValueProperty"
|
[useChipsForMultiValueProperty]="useChipsForMultiValueProperty"
|
||||||
[displayTags]="false"
|
[displayTags]="false"
|
||||||
[displayCategories]="false"
|
[displayCategories]="false"
|
||||||
|
|||||||
+4
@@ -73,6 +73,10 @@ export class PropertiesViewerWrapperComponent implements OnInit, OnChanges {
|
|||||||
@Input()
|
@Input()
|
||||||
copyToClipboardAction: boolean;
|
copyToClipboardAction: boolean;
|
||||||
|
|
||||||
|
/** Toggles the visibility of the copy to clipboard icon */
|
||||||
|
@Input()
|
||||||
|
displayCopyToClipboardIcon: boolean;
|
||||||
|
|
||||||
/** Toggles chips for multivalued properties. */
|
/** Toggles chips for multivalued properties. */
|
||||||
@Input()
|
@Input()
|
||||||
useChipsForMultiValueProperty: boolean;
|
useChipsForMultiValueProperty: boolean;
|
||||||
|
|||||||
+1
@@ -19,6 +19,7 @@
|
|||||||
[multi]="properties?.multi !== undefined ? properties?.multi : false"
|
[multi]="properties?.multi !== undefined ? properties?.multi : false"
|
||||||
[displayAspect]="properties?.displayAspect !== undefined ? properties?.displayAspect : null"
|
[displayAspect]="properties?.displayAspect !== undefined ? properties?.displayAspect : null"
|
||||||
[copyToClipboardAction]="properties?.copyToClipboardAction !== undefined ? properties?.copyToClipboardAction : true"
|
[copyToClipboardAction]="properties?.copyToClipboardAction !== undefined ? properties?.copyToClipboardAction : true"
|
||||||
|
[displayCopyToClipboardIcon]="properties?.displayCopyToClipboardIcon ?? true"
|
||||||
[useChipsForMultiValueProperty]="properties?.useChipsForMultiValueProperty !== undefined ? properties?.useChipsForMultiValueProperty : true"
|
[useChipsForMultiValueProperty]="properties?.useChipsForMultiValueProperty !== undefined ? properties?.useChipsForMultiValueProperty : true"
|
||||||
(nodeContentLoaded)="onNodeContentLoaded($event)"
|
(nodeContentLoaded)="onNodeContentLoaded($event)"
|
||||||
/>
|
/>
|
||||||
|
|||||||
+29
-1
@@ -16,8 +16,9 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
import { FormFieldModel, FormModel, NoopTranslateModule, NoopAuthModule } from '@alfresco/adf-core';
|
import { FormFieldModel, FormModel, NoopTranslateModule, NoopAuthModule, UnitTestingUtils } from '@alfresco/adf-core';
|
||||||
import { PropertiesViewerWidgetComponent } from './properties-viewer.widget';
|
import { PropertiesViewerWidgetComponent } from './properties-viewer.widget';
|
||||||
|
import { PropertiesViewerWrapperComponent } from './properties-viewer-wrapper/properties-viewer-wrapper.component';
|
||||||
import { fakeNodeWithProperties } from '../../../mocks/attach-file-cloud-widget.mock';
|
import { fakeNodeWithProperties } from '../../../mocks/attach-file-cloud-widget.mock';
|
||||||
import { NodesApiService, BasicPropertiesService } from '@alfresco/adf-content-services';
|
import { NodesApiService, BasicPropertiesService } from '@alfresco/adf-content-services';
|
||||||
import { of } from 'rxjs';
|
import { of } from 'rxjs';
|
||||||
@@ -27,6 +28,7 @@ describe('PropertiesViewerWidgetComponent', () => {
|
|||||||
let fixture: ComponentFixture<PropertiesViewerWidgetComponent>;
|
let fixture: ComponentFixture<PropertiesViewerWidgetComponent>;
|
||||||
let element: HTMLElement;
|
let element: HTMLElement;
|
||||||
let nodesApiService: NodesApiService;
|
let nodesApiService: NodesApiService;
|
||||||
|
let testingUtils: UnitTestingUtils;
|
||||||
|
|
||||||
const fakePngAnswer: any = {
|
const fakePngAnswer: any = {
|
||||||
id: '1933',
|
id: '1933',
|
||||||
@@ -52,6 +54,7 @@ describe('PropertiesViewerWidgetComponent', () => {
|
|||||||
nodesApiService = TestBed.inject(NodesApiService);
|
nodesApiService = TestBed.inject(NodesApiService);
|
||||||
widget = fixture.componentInstance;
|
widget = fixture.componentInstance;
|
||||||
element = fixture.nativeElement;
|
element = fixture.nativeElement;
|
||||||
|
testingUtils = new UnitTestingUtils(fixture.debugElement);
|
||||||
|
|
||||||
widget.field = new FormFieldModel(new FormModel());
|
widget.field = new FormFieldModel(new FormModel());
|
||||||
spyOn(nodesApiService, 'getNode').and.returnValue(of(fakeNodeWithProperties));
|
spyOn(nodesApiService, 'getNode').and.returnValue(of(fakeNodeWithProperties));
|
||||||
@@ -97,6 +100,31 @@ describe('PropertiesViewerWidgetComponent', () => {
|
|||||||
expect(propertiesViewer).not.toBeNull();
|
expect(propertiesViewer).not.toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should default displayCopyToClipboardIcon to true when not set in options', async () => {
|
||||||
|
widget.field.value = '1234';
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
await fixture.whenStable();
|
||||||
|
|
||||||
|
const wrapper = testingUtils.getByDirective(PropertiesViewerWrapperComponent).componentInstance;
|
||||||
|
|
||||||
|
expect(wrapper.displayCopyToClipboardIcon).toBeTrue();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should pass displayCopyToClipboardIcon from options to the properties viewer wrapper', async () => {
|
||||||
|
widget.field = new FormFieldModel(new FormModel(), {
|
||||||
|
value: '1234',
|
||||||
|
params: { propertiesViewerOptions: { displayCopyToClipboardIcon: false } }
|
||||||
|
});
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
await fixture.whenStable();
|
||||||
|
|
||||||
|
const wrapper = testingUtils.getByDirective(PropertiesViewerWrapperComponent).componentInstance;
|
||||||
|
|
||||||
|
expect(wrapper.displayCopyToClipboardIcon).toBeFalse();
|
||||||
|
});
|
||||||
|
|
||||||
it('should emit the node when node content is loaded', async () => {
|
it('should emit the node when node content is loaded', async () => {
|
||||||
const nodeContentLoadedSpy = spyOn(widget.nodeContentLoaded, 'emit');
|
const nodeContentLoadedSpy = spyOn(widget.nodeContentLoaded, 'emit');
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user