Compare commits

...
Author SHA1 Message Date
Anukriti Singh 68f7d40fc7 [ACS-5551] add missing methods 2023-10-13 18:24:38 +05:30
Anukriti Singh 45ca40cd5f [ACS-5551] design modify 2023-10-13 16:29:42 +05:30
Anukriti Singh 9a93b74bf3 [ACS-5551] color updated 2023-10-13 16:29:42 +05:30
pkundu 355958e75d scroll issue fixed 2023-10-13 16:29:42 +05:30
Anukriti Singh 4feef96818 [ACS-5551] margin issue fixed 2023-10-13 16:29:42 +05:30
Anukriti Singh 3c61188ccb [ACCS-5551] unit test added 2023-10-13 16:29:41 +05:30
Anukriti Singh 2c0b64d8b1 [ACS-5551] design updated 2023-10-13 16:29:41 +05:30
pkundu 7e88ec93c0 Fixed ACS-6110 2023-10-13 16:29:41 +05:30
Anukriti Singh b1b4675835 [ACS-5551] margin adjusted 2023-10-13 16:29:41 +05:30
pkundu 422c0341f7 fixed scrollbar issue 2023-10-13 16:29:41 +05:30
Anukriti Singh 145bc65223 [ACS-5551] remove extra space 2023-10-13 16:29:41 +05:30
Anukriti Singh 59e5d3898d [ACS-5551] code updated 2023-10-13 16:29:41 +05:30
Anukriti Singh f74450bc02 [ACS-5551] content-metadata updated 2023-10-13 16:29:41 +05:30
Anukriti Singh 5f0ef77035 [ACS-5551] modified changes 2023-10-13 16:29:41 +05:30
Anukriti Singh a8d1b23859 [ACS-5551] modified the changes 2023-10-13 16:29:41 +05:30
Anukriti Singh 515ae12a23 [ACS-5551] updated checks for non -editable field 2023-10-13 16:29:16 +05:30
Anukriti Singh a294880506 [ACS-5551]minor change 2023-10-13 16:29:16 +05:30
Anukriti Singh 4ed5d39083 [ACS-5551] minor changes 2023-10-13 16:29:16 +05:30
Anukriti Singh 4500e25062 [ACS-5551] property panel design 2023-10-13 16:29:16 +05:30
Yasa-Nataliya cb6462bd59 [ACS-5645]Implemented changes as per the review comments 2023-10-13 16:29:16 +05:30
Yasa-Nataliya 85b4b6294b [ACS-5645]removed unwanted code 2023-10-13 16:29:16 +05:30
Yasa-Nataliya eceef0de20 minor fixes 2023-10-13 16:29:16 +05:30
Yasa-Nataliya c142ca1a30 [ACS-5725]fixed failing e2es 2023-10-13 16:29:16 +05:30
Yasa-Nataliya 3e314e74ad metadata e2e fix 2023-10-13 16:29:16 +05:30
Yasa-Nataliya adb323037e [ACS-5645]Added edit functionality for each panel and updated test cases 2023-10-13 16:29:16 +05:30
Yasa-Nataliya caeeec1382 [ACS-5645] added group panel lock changes 2023-10-13 16:29:16 +05:30
Yasa-Nataliya 52b7d321e7 [ACS-5645] Modified the changes 2023-10-13 16:29:16 +05:30
Yasa-Nataliya 70b54213fd minor fixes 2023-10-13 16:29:16 +05:30
Yasa-Nataliya b44ff8aa0c [ACS-5645]Added edit functionality for each panel and updated test cases 2023-10-13 16:29:16 +05:30
Yasa-Nataliya 251b325747 [ACS-5645] added group panel lock changes 2023-10-13 16:29:15 +05:30
Yasa-Nataliya b9a7425047 [ACS-5645] Modified the changes 2023-10-13 16:29:15 +05:30
Yasa-Nataliya e569260e2c [ACS-5645] fixed lock-file bug 2023-10-13 16:29:15 +05:30
Yasa-Nataliya ba863a4704 [ACS-5645]linting fixes 2023-10-13 16:29:15 +05:30
Yasa-Nataliya 8139b0388c [ACS-5645]Implemented the changes as per review comments 2023-10-13 16:29:15 +05:30
Yasa-Nataliya def3943940 [ACS-5645]Implemented changes as per the review comments 2023-10-13 16:29:15 +05:30
Yasa-Nataliya b4b995889f [ACS-5645]removed unwanted code 2023-10-13 16:28:58 +05:30
Yasa-Nataliya 7750bcb9d5 [ACS-5645]code modification 2023-10-13 16:28:58 +05:30
Yasa-Nataliya 92fb997bb0 [ACS-5645]Added edit functionality for each panel and updated test cases 2023-10-13 16:28:58 +05:30
Yasa-Nataliya ea1ae97144 [ACS-5645] renamed the methods 2023-10-13 16:28:58 +05:30
Yasa-Nataliya 7e4ad3c6d9 [ACS-5645] Added missing translation key 2023-10-13 16:28:27 +05:30
Yasa-Nataliya baf2a7ce1f [ACS-5645] fixed sonarcloud issue 2023-10-13 16:28:27 +05:30
Yasa-Nataliya a56aff1b00 [ACS-5645] Removed unwanted code 2023-10-13 16:28:27 +05:30
Yasa-Nataliya b7a71218fa [ACS-5645] Lint fixes 2023-10-13 16:28:27 +05:30
Yasa-Nataliya af552aa0ce [ACS-5645] Lint fixes 2023-10-13 16:28:27 +05:30
Yasa-Nataliya ee71ce5cbb [ACS-5645] Lint fixes 2023-10-13 16:28:27 +05:30
Yasa-Nataliya 2b0ead0077 [ACS-5645] modified the changes 2023-10-13 16:27:31 +05:30
Yasa-Nataliya ec2219fd9b [ACS-5645] added focus 2023-10-13 16:27:31 +05:30
Yasa-Nataliya 498bc52df5 [ACS-5645] Implemented the review comments 2023-10-13 16:27:31 +05:30
Yasa-Nataliya b1f6ce331c [ACS-5645] lint fixes 2023-10-13 16:27:31 +05:30
Yasa-Nataliya d404d67f9b [ACS-5645] Implemented changes as per review comments 2023-10-13 16:27:31 +05:30
Yasa-Nataliya 47199de899 [ACS-5645] updated the documentation 2023-10-13 16:27:31 +05:30
Yasa-Nataliya 483faa06df [ACS-5645] updated the documentation 2023-10-13 16:27:31 +05:30
Yasa-Nataliya 5d1272b9d2 [ACS-5645] updated the documentation 2023-10-13 16:27:31 +05:30
Yasa-Nataliya 52acd0a216 [ACS-5645] added test cases for tags component 2023-10-13 16:27:31 +05:30
Yasa-Nataliya a5c4fb9ecd [ACS-5645] Resolved sonarcloud issue 2023-10-13 16:27:31 +05:30
Yasa-Nataliya 2223e46979 [ACS-5645] added group panel lock changes 2023-10-13 16:23:19 +05:30
Yasa-Nataliya 4b76e5beb9 [ACS-5645]Modified the changes 2023-10-13 16:23:19 +05:30
Yasa-Nataliya b1da556451 [ACS-5645] Implemented the changes as per the review comments 2023-10-13 16:22:28 +05:30
Yasa-Nataliya 9723f9f480 [ACS-5645] Implemented the changes as per the review comments 2023-10-13 16:21:45 +05:30
Yasa-Nataliya aa6b527a5d [ACS-5645]Modified the changes 2023-10-13 16:21:45 +05:30
Yasa-Nataliya 6969f76f1d [ACS-5645] Modified the changes 2023-10-13 16:21:45 +05:30
Yasa-Nataliya 1c48220b4c [ACS-5645] added tooltips for save and cancel icons 2023-10-13 16:20:27 +05:30
Yasa-Nataliya 46b6e1c44b [ACS-5645] fixed lock-file bug 2023-10-13 16:20:27 +05:30
Yasa-Nataliya 24cbdb1ae9 [ACS-5645]modified changes 2023-10-13 16:20:27 +05:30
Yasa-Nataliya 2ac50ad3cc [ACS-5645] Renamed the theme 2023-10-13 16:20:27 +05:30
Yasa-Nataliya e1d866a3ab [ACS-5645]e2e fixes 2023-10-13 16:20:26 +05:30
Yasa-Nataliya 787889f9d4 [ACS-5645]fixes for e2e 2023-10-13 16:20:26 +05:30
Yasa-Nataliya dc9693a4ad [ACS-5645]fixes for e2e 2023-10-13 16:20:26 +05:30
Yasa-Nataliya c5bee87508 [ACS-5645]Added translation 2023-10-13 16:20:26 +05:30
Yasa-Nataliya 4f14cd64bb [ACS-5645]clean up 2023-10-13 16:20:26 +05:30
Yasa-Nataliya 8a8718b6a4 [ACS-5645] e2e fixes 2023-10-13 16:20:26 +05:30
Yasa-Nataliya 1bea34b940 [ACS-5645]fixes for lint 2023-10-13 16:20:26 +05:30
Yasa-Nataliya 10774c064c [ACS-5645]linting fixes 2023-10-13 16:20:26 +05:30
Yasa-Nataliya 85a644dcc8 [ACS-5645] Removed unused code 2023-10-13 16:20:26 +05:30
Yasa-Nataliya 2c4e815161 [ACS-5645]Implemented the changes as per review comments 2023-10-13 16:20:26 +05:30
Yasa-Nataliya ac9e8dd6db [ACS-5540]lint fixes 2023-10-13 16:20:26 +05:30
Yasa-Nataliya ff78d6495d [ACS-5645]fixes linting 2023-10-13 16:20:26 +05:30
Yasa-Nataliya 1a5a23184e [ACS-5645]rename the function 2023-10-13 16:20:26 +05:30
Yasa-Nataliya 7147ba492f [ACS-5645]fixed errors 2023-10-13 16:20:26 +05:30
Yasa-Nataliya 95d8d66c75 [ACS-5645]fixed sonarcloud issue 2023-10-13 16:20:26 +05:30
Yasa-Nataliya 33b32b5409 [ACS-5645]linting fixes 2023-10-13 16:20:25 +05:30
Yasa-Nataliya e8e9531141 [ACS-5645]Implemented the changes as per the review comments 2023-10-13 16:20:25 +05:30
Yasa-Nataliya 08f406c2c1 [ACS-5645]modified changes 2023-10-13 16:20:25 +05:30
Yasa-Nataliya e412f395af [ACS-5645]aligned input 2023-10-13 16:20:25 +05:30
Yasa-Nataliya 1f35ddd033 [ACS-5645]modified the change 2023-10-13 16:20:25 +05:30
Yasa-Nataliya e8394d8648 [ACS-5645] removed unwanted code 2023-10-13 16:20:25 +05:30
Yasa-Nataliya 99fd7519fb [ACS-5645]minor fixes 2023-10-13 16:20:25 +05:30
Yasa-Nataliya 001d0160d8 linting fixes 2023-10-13 16:20:25 +05:30
Yasa-Nataliya 66af36351b [ACS-5645]Implemented changes as per the review comments 2023-10-13 16:20:25 +05:30
Yasa-Nataliya 5812988710 [ACS-5645]removed unwanted code 2023-10-13 16:19:58 +05:30
Yasa-Nataliya a03aba0a2a [ACS-5645]removed unwanted space 2023-10-13 16:19:58 +05:30
Yasa-Nataliya 71bb551deb [ACS-5645]modified the changes 2023-10-13 16:19:58 +05:30
Yasa-Nataliya bdb7b11ecd [ACS-5645]removed unwanted code 2023-10-13 16:19:57 +05:30
Yasa-Nataliya c005237498 [ACS-5645]code modification 2023-10-13 16:19:57 +05:30
Yasa-Nataliya c629c1b3a4 minor fixes 2023-10-13 16:19:34 +05:30
Yasa-Nataliya d1307c3cfc minor fixes 2023-10-13 16:19:34 +05:30
Yasa-Nataliya 367a10c529 minor fixes 2023-10-13 16:19:34 +05:30
Yasa-Nataliya b0124f801c added unit test cases for new functionality 2023-10-13 16:19:34 +05:30
Yasa-Nataliya 15d5d90ddc [ACS-5725]fixed failing e2es 2023-10-13 16:19:34 +05:30
Yasa-Nataliya 00e143f721 metadata e2e fix 2023-10-13 16:19:03 +05:30
Yasa-Nataliya de94af8f36 [ACS-5645]Added edit functionality for each panel and updated test cases 2023-10-13 16:19:03 +05:30
54 changed files with 1415 additions and 832 deletions
@@ -56,12 +56,6 @@ Displays and edits metadata related to a node.
| editable | `boolean` | | (optional) This flag toggles editable of content. |
| customPanels | [`ContentMetadataCustomPanel`](../interfaces/content-metadata-custom-panel.interface.md)`[]` | | (optional) List of custom metadata panels to be displayed as [`Dynamic components`](../../extensions/components/dynamic.component.md). |
### Events
| Name | Type | Description |
|----------------|-----------------------------------------------------------------------|---------------------------------------------------|
| editableChange | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<boolean>` | Emitted when content's editable state is changed. |
## Details
The component shows metadata related to a given node. It uses the
@@ -444,3 +438,4 @@ When the list of values is too long, the options selection panel will be enhance
## Custom metadata panels
If there is a need to display some custom node properties that require additional UI or data to be fetched you can define custom metadata panels for the metadata component. After creating custom component to be displayed it has to be registered in [Extension Service](../../extensions/services/extension.service.md). Both `panelTitle` and `component` properties have to be defined.
![Chips for multi value properties](../../docassets/images/adf-search-select.png)
@@ -37,3 +37,4 @@ Allows to create multiple tags. That component contains input and two lists. Top
| existingTagsPanelVisibilityChange | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<boolean>` | Emitted when bottom list is showing or hiding. |
| tagNameControlVisibleChange | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<boolean>` | Emitted when input is showing or hiding. |
| tagsChange | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<string[]>` | Emitted when tags in top list are changed. |
Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB

+20 -2
View File
@@ -98,12 +98,30 @@ Now that the `my-preset` configuration is defined, let's use it in a view of the
</adf-content-metadata-card>
```
### Properties
| Name | Type | Default value | Description |
| ---- | ---- | ------------- | ----------- |
| readOnly | `boolean` | false | (optional) This flag sets the metadata in read only mode preventing changes. |
| editable | `boolean` | | (optional) This flag toggles editable of content. |
| editableTags | `boolean` | | (optional) This flag toggles editable of tags content. |
| editableCategories | `boolean` | | (optional) This flag toggles editable of categories content. |
### Events
| Name | Type | Description |
|----------------|-----------------------------------------------------------------------|---------------------------------------------------|
| editableChange | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<boolean>` | Emitted when content's editable state is changed. |
| editableTagsChange | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<boolean>` | Emitted when content's editableTags state is changed. |
| editableCategoriesChange | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<boolean>` | Emitted when content's editableCategories state is changed. |
| groupChange | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<boolean>` | Emitted when content's group state is changed. |
### Viewing the result
After saving the html file, open the ADF app in a browser and dive into the `Personal Files > Sites > swsdp` folder of the Alfresco's repository. Once there, select the `documentLibrary` folder (one click only) and click on the view details icon (the `i` on the top right). Scrolling down the metadata tab on the right, click on the `More information` item at the bottom. Once clicked, you will see two different groups: `Properties` (already there by default) and `This is my preset`. Click on `This is my preset` to show the properties related.
After saving the html file, open the ADF app in a browser and dive into the `Personal Files > Sites > swsdp` folder of the Alfresco's repository. Once there, select the `documentLibrary` folder (one click only) and click on the view details icon (the `menu_open icon` on the top right). Scrolling down the metadata tab on the right. You will see different panels: `Properties` (already there by default), `Tags`, `Categories` and `grouped properties`.
In the following screenshot you can see how the result should look:
![content_metadata_preset](../docassets/images/content_metadata_preset.png)
![UI_Property_Panel](../docassets/images/UI_property_panel.png)
To check it out, double click on the `documentLibrary` folder and select (with one click) the `Presentations` folder. You should see the `This is my preset` group disappear from the metadata panel, because the node doesn't have the `st:siteContainer` aspect.
@@ -339,8 +339,8 @@ describe('Permissions Component', () => {
await BrowserActions.closeMenuAndDialogs();
await contentList.checkActionMenuIsNotDisplayed();
await contentServicesPage.metadataContent('RoleEditor' + fileModel.name);
await metadataViewPage.editIconIsDisplayed();
await metadataViewPage.editIconClick();
await metadataViewPage.isEditGeneralIconDisplayed();
await metadataViewPage.clickEditIconGeneral();
await metadataViewPage.editPropertyIconIsDisplayed('properties.cm:title');
await metadataViewPage.enterPropertyText('properties.cm:title', 'newTitle1');
await expect(await metadataViewPage.getPropertyText('properties.cm:title')).toEqual('newTitle1');
@@ -360,8 +360,8 @@ describe('Permissions Component', () => {
await BrowserActions.closeMenuAndDialogs();
await contentList.checkActionMenuIsNotDisplayed();
await contentServicesPage.metadataContent('RoleCollaborator' + fileModel.name);
await metadataViewPage.editIconIsDisplayed();
await metadataViewPage.editIconClick();
await metadataViewPage.isEditGeneralIconDisplayed();
await metadataViewPage.clickEditIconGeneral();
await metadataViewPage.editPropertyIconIsDisplayed('properties.cm:title');
await metadataViewPage.enterPropertyText('properties.cm:title', 'newTitle2');
await expect(await metadataViewPage.getPropertyText('properties.cm:title')).toEqual('newTitle2');
@@ -382,8 +382,8 @@ describe('Permissions Component', () => {
await viewerPage.clickCloseButton();
await contentList.waitForTableBody();
await contentServicesPage.metadataContent('RoleCoordinator' + fileModel.name);
await metadataViewPage.editIconIsDisplayed();
await metadataViewPage.editIconClick();
await metadataViewPage.isEditGeneralIconDisplayed();
await metadataViewPage.clickEditIconGeneral();
await metadataViewPage.editPropertyIconIsDisplayed('properties.cm:title');
await metadataViewPage.enterPropertyText('properties.cm:title', 'newTitle3');
await expect(await metadataViewPage.getPropertyText('properties.cm:title')).toEqual('newTitle3');
@@ -122,8 +122,6 @@ describe('Aspect oriented config', () => {
await viewerPage.clickInfoButton();
await viewerPage.checkInfoSideBarIsDisplayed();
await metadataViewPage.clickOnPropertiesTab();
await metadataViewPage.informationButtonIsDisplayed();
await metadataViewPage.clickOnInformationButton();
await metadataViewPage.clickMetadataGroup('IMAGE');
await metadataViewPage.checkPropertyIsVisible('properties.exif:pixelXDimension', 'textitem');
@@ -152,8 +150,6 @@ describe('Aspect oriented config', () => {
await viewerPage.clickInfoButton();
await viewerPage.checkInfoSideBarIsDisplayed();
await metadataViewPage.clickOnPropertiesTab();
await metadataViewPage.informationButtonIsDisplayed();
await metadataViewPage.clickOnInformationButton();
await metadataViewPage.checkMetadataGroupIsPresent('EXIF');
await metadataViewPage.checkMetadataGroupIsPresent('properties');
@@ -170,8 +166,6 @@ describe('Aspect oriented config', () => {
await viewerPage.clickInfoButton();
await viewerPage.checkInfoSideBarIsDisplayed();
await metadataViewPage.clickOnPropertiesTab();
await metadataViewPage.informationButtonIsDisplayed();
await metadataViewPage.clickOnInformationButton();
await metadataViewPage.checkMetadataGroupIsPresent('EXIF');
await metadataViewPage.checkMetadataGroupIsPresent('properties');
@@ -191,8 +185,6 @@ describe('Aspect oriented config', () => {
await viewerPage.clickInfoButton();
await viewerPage.checkInfoSideBarIsDisplayed();
await metadataViewPage.clickOnPropertiesTab();
await metadataViewPage.informationButtonIsDisplayed();
await metadataViewPage.clickOnInformationButton();
await metadataViewPage.checkMetadataGroupIsPresent('EXIF');
await metadataViewPage.checkMetadataGroupIsPresent('properties');
@@ -232,9 +224,6 @@ describe('Aspect oriented config', () => {
await viewerPage.checkInfoSideBarIsDisplayed();
await metadataViewPage.clickOnPropertiesTab();
await metadataViewPage.informationButtonIsDisplayed();
await metadataViewPage.clickOnInformationButton();
await metadataViewPage.checkMetadataGroupIsPresent('GROUP-TITLE1-TRANSLATION-KEY');
await metadataViewPage.checkMetadataGroupIsPresent('GROUP-TITLE2-TRANSLATION-KEY');
@@ -262,10 +251,6 @@ describe('Aspect oriented config', () => {
await CheckboxPage.check(metadataViewPage.presetSwitch);
await metadataViewPage.enterPresetText('custom-preset');
await metadataViewPage.informationButtonIsDisplayed();
await metadataViewPage.clickOnInformationButton();
await metadataViewPage.checkMetadataGroupIsPresent('properties');
await metadataViewPage.checkMetadataGroupIsPresent('Versionable');
});
@@ -286,9 +271,6 @@ describe('Aspect oriented config', () => {
await viewerPage.checkInfoSideBarIsDisplayed();
await metadataViewPage.clickOnPropertiesTab();
await metadataViewPage.informationButtonIsDisplayed();
await metadataViewPage.clickOnInformationButton();
await metadataViewPage.checkMetadataGroupIsNotPresent(emptyAspectName);
});
@@ -308,11 +290,6 @@ describe('Aspect oriented config', () => {
await viewerPage.checkInfoSideBarIsDisplayed();
await metadataViewPage.clickOnPropertiesTab();
await metadataViewPage.informationButtonIsDisplayed();
await metadataViewPage.clickOnInformationButton();
await metadataViewPage.checkMetadataGroupIsNotPresent(aspectName);
await metadataViewPage.editIconClick();
await metadataViewPage.checkMetadataGroupIsPresent(aspectName);
@@ -114,16 +114,16 @@ describe('content type', () => {
await viewerPage.clickInfoButton();
await viewerPage.checkInfoSideBarIsDisplayed();
await metadataViewPage.clickOnPropertiesTab();
await metadataViewPage.editIconIsDisplayed();
await metadataViewPage.isEditGeneralIconDisplayed();
await expect(await viewerPage.getActiveTab()).toEqual('PROPERTIES');
const defaultType = (await metadataViewPage.hasContentType('Content')) || (await metadataViewPage.hasContentType('cm:content'));
await expect(defaultType).toBe(true, 'Content type not found');
await metadataViewPage.editIconClick();
await metadataViewPage.clickEditIconGeneral();
await expect(await metadataViewPage.changeContentType(type.title)).toBe(true, 'Failed to update node type.');
await metadataViewPage.clickSaveMetadata();
await metadataViewPage.clickSaveGeneralMetadata();
await metadataViewPage.checkConfirmDialogDisplayed();
await metadataViewPage.applyNodeProperties();
@@ -139,7 +139,7 @@ describe('content type', () => {
await viewerPage.clickInfoButton();
await viewerPage.checkInfoSideBarIsDisplayed();
await metadataViewPage.clickOnPropertiesTab();
await metadataViewPage.editIconIsDisplayed();
await metadataViewPage.isEditGeneralIconDisplayed();
await expect(await viewerPage.getActiveTab()).toEqual('PROPERTIES');
const customType = (await metadataViewPage.hasContentType(type.title)) || (await metadataViewPage.hasContentType(`${model.namespacePrefix}:${type.name}`));
@@ -155,16 +155,16 @@ describe('content type', () => {
await viewerPage.clickInfoButton();
await viewerPage.checkInfoSideBarIsDisplayed();
await metadataViewPage.clickOnPropertiesTab();
await metadataViewPage.editIconIsDisplayed();
await metadataViewPage.isEditGeneralIconDisplayed();
await expect(await viewerPage.getActiveTab()).toEqual('PROPERTIES');
let defaultType = (await metadataViewPage.hasContentType('Content')) || (await metadataViewPage.hasContentType('cm:content'));
await expect(defaultType).toBe(true, 'Content type not found');
await metadataViewPage.editIconClick();
await metadataViewPage.clickEditIconGeneral();
await expect(await metadataViewPage.changeContentType(type.title)).toBe(true, 'Failed to update node type.');
await metadataViewPage.clickSaveMetadata();
await metadataViewPage.clickSaveGeneralMetadata();
await metadataViewPage.checkConfirmDialogDisplayed();
await metadataViewPage.cancelNodeProperties();
@@ -178,7 +178,7 @@ describe('content type', () => {
await viewerPage.clickInfoButton();
await viewerPage.checkInfoSideBarIsDisplayed();
await metadataViewPage.clickOnPropertiesTab();
await metadataViewPage.editIconIsDisplayed();
await metadataViewPage.isEditGeneralIconDisplayed();
await expect(await viewerPage.getActiveTab()).toEqual('PROPERTIES');
defaultType = (await metadataViewPage.hasContentType('Content')) || (await metadataViewPage.hasContentType('cm:content'));
@@ -38,10 +38,6 @@ describe('permissions', () => {
COMMENTS_TAB: 'COMMENTS',
PROPERTY_TAB: 'PROPERTIES',
DEFAULT_ASPECT: 'Properties',
MORE_INFO_BUTTON: 'More information',
LESS_INFO_BUTTON: 'Less information',
ARROW_DOWN: 'keyboard_arrow_down',
ARROW_UP: 'keyboard_arrow_up',
EDIT_BUTTON_TOOLTIP: 'Edit'
};
@@ -132,7 +128,6 @@ describe('permissions', () => {
await expect(await viewerPage.getActiveTab()).toEqual(METADATA.PROPERTY_TAB);
await metadataViewPage.clickOnInformationButton();
await metadataViewPage.clickMetadataGroup('EXIF');
@@ -152,7 +147,6 @@ describe('permissions', () => {
await expect(await viewerPage.getActiveTab()).toEqual(METADATA.PROPERTY_TAB);
await metadataViewPage.clickOnInformationButton();
await metadataViewPage.clickMetadataGroup('EXIF');
@@ -37,10 +37,6 @@ describe('CardView Component - properties', () => {
COMMENTS_TAB: 'COMMENTS',
PROPERTY_TAB: 'PROPERTIES',
DEFAULT_ASPECT: 'Properties',
MORE_INFO_BUTTON: 'More information',
LESS_INFO_BUTTON: 'Less information',
ARROW_DOWN: 'keyboard_arrow_down',
ARROW_UP: 'keyboard_arrow_up',
EDIT_BUTTON_TOOLTIP: 'Edit'
};
@@ -90,7 +86,6 @@ describe('CardView Component - properties', () => {
await expect(await viewerPage.getActiveTab()).toEqual(METADATA.PROPERTY_TAB);
await metadataViewPage.clickOnInformationButton();
await metadataViewPage.clickMetadataGroup('EXIF');
@@ -108,11 +103,9 @@ describe('CardView Component - properties', () => {
await viewerPage.clickInfoButton();
await viewerPage.checkInfoSideBarIsDisplayed();
await metadataViewPage.clickOnPropertiesTab();
await metadataViewPage.editIconIsDisplayed();
await metadataViewPage.isEditGeneralIconDisplayed();
await CheckboxPage.check(metadataViewPage.readonlySwitch);
await metadataViewPage.editIconIsNotDisplayed();
});
it('[C268965] Should multi property allow expand multi accordion at the same time when set', async () => {
@@ -121,15 +114,9 @@ describe('CardView Component - properties', () => {
await viewerPage.checkInfoSideBarIsDisplayed();
await metadataViewPage.clickOnPropertiesTab();
await metadataViewPage.clickOnInformationButton();
await metadataViewPage.checkMetadataGroupIsNotExpand('EXIF');
await metadataViewPage.checkMetadataGroupIsNotExpand('properties');
await metadataViewPage.clickMetadataGroup('properties');
await metadataViewPage.checkMetadataGroupIsNotExpand('EXIF');
await metadataViewPage.checkMetadataGroupIsExpand('properties');
await metadataViewPage.checkMetadataGroupIsNotExpand('EXIF');
await metadataViewPage.clickMetadataGroup('EXIF');
@@ -168,11 +155,7 @@ describe('CardView Component - properties', () => {
await viewerPage.checkInfoSideBarIsDisplayed();
await metadataViewPage.clickOnPropertiesTab();
await metadataViewPage.informationButtonIsDisplayed();
await CheckboxPage.uncheck(metadataViewPage.defaultPropertiesSwitch);
await metadataViewPage.informationButtonIsNotDisplayed();
});
it('[C307975] Should be able to choose which aspect to show expanded in the info-drawer', async () => {
@@ -39,11 +39,7 @@ describe('Metadata component', () => {
TITLE: 'Details',
COMMENTS_TAB: 'COMMENTS',
PROPERTY_TAB: 'PROPERTIES',
DEFAULT_ASPECT: 'Properties',
MORE_INFO_BUTTON: 'More information',
LESS_INFO_BUTTON: 'Less information',
ARROW_DOWN: 'keyboard_arrow_down',
ARROW_UP: 'keyboard_arrow_up',
DEFAULT_ASPECT: 'General info',
EDIT_BUTTON_TOOLTIP: 'Edit'
};
@@ -120,26 +116,12 @@ describe('Metadata component', () => {
await expect(await metadataViewPage.getSize()).toEqual(pngFileModel.getContent().getSizeInBytes());
await metadataViewPage.editIconIsDisplayed();
await metadataViewPage.informationButtonIsDisplayed();
await expect(await metadataViewPage.getInformationButtonText()).toEqual(METADATA.MORE_INFO_BUTTON);
await expect(await metadataViewPage.getInformationIconText()).toEqual(METADATA.ARROW_DOWN);
});
it('[C272769] Should be possible to display more details when clicking on More Information button', async () => {
await viewerPage.clickInfoButton();
await viewerPage.checkInfoSideBarIsDisplayed();
await metadataViewPage.clickOnPropertiesTab();
await metadataViewPage.informationButtonIsDisplayed();
await metadataViewPage.clickOnInformationButton();
await expect(await metadataViewPage.getInformationButtonText()).toEqual(METADATA.LESS_INFO_BUTTON);
await expect(await metadataViewPage.getInformationIconText()).toEqual(METADATA.ARROW_UP);
});
it('[C270952] Should be possible to open/close properties using info icon', async () => {
await viewerPage.clickInfoButton();
await viewerPage.checkInfoSideBarIsDisplayed();
await metadataViewPage.clickOnPropertiesTab();
await metadataViewPage.informationButtonIsDisplayed();
await viewerPage.clickInfoButton();
await viewerPage.checkInfoSideBarIsNotDisplayed();
await viewerPage.clickInfoButton();
@@ -154,31 +136,24 @@ describe('Metadata component', () => {
await viewerPage.clickInfoButton();
await viewerPage.checkInfoSideBarIsDisplayed();
await metadataViewPage.clickOnPropertiesTab();
await metadataViewPage.editIconIsDisplayed();
await metadataViewPage.isEditGeneralIconDisplayed();
await expect(await viewerPage.getActiveTab()).toEqual(METADATA.PROPERTY_TAB);
await metadataViewPage.editIconClick();
await metadataViewPage.editPropertyIconIsDisplayed('properties.cm:name');
await metadataViewPage.editPropertyIconIsDisplayed('properties.cm:title');
await metadataViewPage.editPropertyIconIsDisplayed('properties.cm:description');
await expect(await metadataViewPage.getPropertyIconTooltip('properties.cm:name')).toEqual('Edit');
await expect(await metadataViewPage.getPropertyIconTooltip('properties.cm:title')).toEqual('Edit');
await expect(await metadataViewPage.getPropertyIconTooltip('properties.cm:description')).toEqual('Edit');
await metadataViewPage.clickEditIconGeneral();
await metadataViewPage.enterPropertyText('properties.cm:name', 'exampleText');
await metadataViewPage.clickResetMetadata();
await expect(await metadataViewPage.getPropertyText('properties.cm:name')).toEqual(browser.params.resources.Files.ADF_DOCUMENTS.PNG.file_name);
await metadataViewPage.clickEditIconGeneral();
await metadataViewPage.enterPropertyText('properties.cm:name', 'exampleText.png');
await metadataViewPage.enterPropertyText('properties.cm:title', 'example title');
await metadataViewPage.enterDescriptionText('example description');
await metadataViewPage.clickSaveGeneralMetadata();
await expect(await metadataViewPage.getPropertyText('properties.cm:name')).toEqual('exampleText.png');
await expect(await metadataViewPage.getPropertyText('properties.cm:title')).toEqual('example title');
await expect(await metadataViewPage.getPropertyText('properties.cm:description')).toEqual('example description');
await metadataViewPage.clickSaveMetadata();
await viewerPage.clickCloseButton();
await contentServicesPage.waitForTableBody();
@@ -187,16 +162,15 @@ describe('Metadata component', () => {
await viewerPage.clickInfoButton();
await viewerPage.checkInfoSideBarIsDisplayed();
await metadataViewPage.clickOnPropertiesTab();
await metadataViewPage.editIconIsDisplayed();
await metadataViewPage.isEditGeneralIconDisplayed();
await expect(await metadataViewPage.getPropertyText('properties.cm:name')).toEqual('exampleText.png');
await expect(await metadataViewPage.getPropertyText('properties.cm:title')).toEqual('example title');
await expect(await metadataViewPage.getPropertyText('properties.cm:description')).toEqual('example description');
await metadataViewPage.editIconClick();
await metadataViewPage.clickEditIconGeneral();
await metadataViewPage.enterPropertyText('properties.cm:name', browser.params.resources.Files.ADF_DOCUMENTS.PNG.file_name);
await expect(await metadataViewPage.getPropertyText('properties.cm:name')).toEqual(browser.params.resources.Files.ADF_DOCUMENTS.PNG.file_name);
await metadataViewPage.clickSaveMetadata();
await metadataViewPage.clickSaveGeneralMetadata();
});
it('[C260181] Should be possible edit all the metadata aspect', async () => {
@@ -207,8 +181,6 @@ describe('Metadata component', () => {
await expect(await viewerPage.getActiveTab()).toEqual(METADATA.PROPERTY_TAB);
await metadataViewPage.clickOnInformationButton();
await metadataViewPage.clickMetadataGroup('EXIF');
await metadataViewPage.editIconClick();
@@ -250,18 +222,20 @@ describe('Metadata component', () => {
it('[C261158] Should be possible edit the metadata When the node is a Folder', async () => {
await contentServicesPage.metadataContent(folderName);
await metadataViewPage.editIconClick();
await metadataViewPage.clickEditIconGeneral();
await metadataViewPage.enterPropertyText('properties.cm:name', 'newnameFolder');
await metadataViewPage.clickResetButton();
await expect(await metadataViewPage.getPropertyText('properties.cm:name')).toEqual(folderName);
await metadataViewPage.clickEditIconGeneral();
await metadataViewPage.enterPropertyText('properties.cm:name', 'newnameFolder');
await metadataViewPage.clickSaveMetadata();
await metadataViewPage.clickSaveGeneralMetadata();
await expect(await metadataViewPage.getPropertyText('properties.cm:name')).toEqual('newnameFolder');
await metadataViewPage.clickEditIconGeneral();
await metadataViewPage.enterPropertyText('properties.cm:name', folderName);
await metadataViewPage.clickSaveMetadata();
await metadataViewPage.clickSaveGeneralMetadata();
await expect(await metadataViewPage.getPropertyText('properties.cm:name')).toEqual(folderName);
});
});
@@ -274,14 +248,14 @@ describe('Metadata component', () => {
await viewerPage.clickInfoButton();
await viewerPage.checkInfoSideBarIsDisplayed();
await metadataViewPage.clickOnPropertiesTab();
await metadataViewPage.editIconIsDisplayed();
await metadataViewPage.isEditGeneralIconDisplayed();
await expect(await viewerPage.getActiveTab()).toEqual(METADATA.PROPERTY_TAB);
await metadataViewPage.editIconClick();
await metadataViewPage.clickEditIconGeneral();
await metadataViewPage.enterDescriptionText('check author example description');
await metadataViewPage.clickSaveMetadata();
await metadataViewPage.clickSaveGeneralMetadata();
await expect(await metadataViewPage.getPropertyText('properties.cm:description')).toEqual('check author example description');
await navigationBarPage.clickLogoutButton();
+13 -29
View File
@@ -33,9 +33,7 @@ export class MetadataViewPage {
author = $(`[data-automation-id='card-textitem-value-properties.cm:author']`);
titleProperty = $(`span[data-automation-id='card-textitem-value-properties.cm:title'] span`);
editIcon = $(`button[data-automation-id='meta-data-card-toggle-edit']`);
informationButton = $(`button[data-automation-id='meta-data-card-toggle-expand']`);
informationSpan = $(`span[data-automation-id='meta-data-card-toggle-expand-label']`);
informationIcon = $(`span[data-automation-id='meta-data-card-toggle-expand-label'] ~ mat-icon`);
editIconGeneral = $(`button[data-automation-id='meta-data-general-info-edit']`);
displayEmptySwitch = $(`#adf-metadata-empty`);
readonlySwitch = $(`#adf-metadata-readonly`);
multiSwitch = $(`#adf-metadata-multi`);
@@ -45,6 +43,7 @@ export class MetadataViewPage {
displayAspect = $(`input[data-placeholder='Display Aspect']`);
applyAspect = element(by.cssContainingText(`button span.mat-button-wrapper`, 'Apply Aspect'));
saveMetadataButton = $(`[data-automation-id='save-metadata']`);
saveGeneralMetadataButton = $(`[data-automation-id='save-general-info-metadata']`);
resetMetadataButton = $(`[data-automation-id='reset-metadata']`);
private getMetadataGroupLocator = async (groupName: string): Promise<ElementFinder> =>
@@ -108,28 +107,16 @@ export class MetadataViewPage {
await BrowserVisibility.waitUntilElementIsNotVisible(this.editIcon);
}
async isEditGeneralIconDisplayed(): Promise<void> {
await BrowserVisibility.waitUntilElementIsVisible(this.editIconGeneral);
}
async editIconClick(): Promise<void> {
await BrowserActions.clickExecuteScript('button[data-automation-id="meta-data-card-toggle-edit"]');
}
async informationButtonIsDisplayed(): Promise<void> {
await BrowserVisibility.waitUntilElementIsClickable(this.informationButton);
}
async informationButtonIsNotDisplayed(): Promise<void> {
await BrowserVisibility.waitUntilElementIsNotVisible(this.informationButton);
}
async clickOnInformationButton(): Promise<void> {
await BrowserActions.click(this.informationButton);
}
async getInformationButtonText(): Promise<string> {
return BrowserActions.getText(this.informationSpan);
}
async getInformationIconText(): Promise<string> {
return BrowserActions.getText(this.informationIcon);
async clickEditIconGeneral(): Promise<void> {
await BrowserActions.click(this.editIconGeneral);
}
async clickOnPropertiesTab(): Promise<void> {
@@ -180,11 +167,6 @@ export class MetadataViewPage {
return BrowserActions.getInputValue(textField);
}
async getPropertyIconTooltip(propertyName: string): Promise<string> {
const editPropertyIcon = $('[data-automation-id="header-' + propertyName + '"] .adf-textitem-edit-icon');
return BrowserActions.getAttribute(editPropertyIcon, 'title');
}
async clickMetadataGroup(groupName: string): Promise<void> {
const group = await this.getMetadataGroupLocator(groupName);
await BrowserActions.click(group);
@@ -211,9 +193,7 @@ export class MetadataViewPage {
}
async getMetadataGroupTitle(groupName: string): Promise<string> {
const group = $(
'mat-expansion-panel[data-automation-id="adf-metadata-group-' + groupName + '"] > mat-expansion-panel-header > span > mat-panel-title'
);
const group = $('mat-expansion-panel[data-automation-id="adf-metadata-group-' + groupName + '"] > mat-expansion-panel-header > span > div > mat-panel-title');
return BrowserActions.getText(group);
}
@@ -316,4 +296,8 @@ export class MetadataViewPage {
async clickResetMetadata(): Promise<void> {
await BrowserActions.click(this.resetMetadataButton);
}
async clickSaveGeneralMetadata(): Promise<void> {
await BrowserActions.click(this.saveGeneralMetadataButton);
}
}
@@ -446,9 +446,9 @@ describe('Content Services Viewer', () => {
await viewerPage.checkInfoSideBarIsDisplayed();
await viewerPage.clickOnTab('Properties');
await viewerPage.checkTabIsActive('Properties');
await metadataViewPage.editIconClick();
await metadataViewPage.clickEditIconGeneral();
await metadataViewPage.enterPropertyText('properties.cm:name', newName);
await metadataViewPage.clickSaveMetadata();
await metadataViewPage.clickSaveGeneralMetadata();
await viewerPage.clickCloseButton();
}
});
@@ -1,10 +1,19 @@
<div class="adf-categories-management">
<p *ngIf="!categories.length && !categoryNameControlVisible"
class="adf-no-categories-message">
{{ noCategoriesMsg | translate }}
</p>
<div class="adf-categories-list"
[class.adf-categories-list-fixed]="!categoryNameControlVisible">
<div *ngIf="isNameCategoryVisible"
class="adf-category-name-field">
<mat-form-field appearance="fill">
<input #categoryNameInput
matInput
autocomplete="off"
[formControl]="categoryNameControl"
(keyup.enter)="addCategory()"
placeholder="{{'CATEGORIES_MANAGEMENT.INPUT_PLACEHOLDER' | translate }}"
adf-auto-focus
/>
<mat-error *ngIf="categoryNameControl.invalid">{{ categoryNameErrorMessageKey | translate }}</mat-error>
</mat-form-field>
</div>
<div class="adf-categories-list" [class.adf-categories-list-fixed]="!categoryNameControlVisible">
<span
*ngFor="let category of categories"
[class.adf-categories-padded]="!isCRUDMode"
@@ -21,33 +30,9 @@
</button>
</span>
</div>
<div *ngIf="((!categoryNameControlVisible && categories.length)) || categoryNameControlVisible"
[hidden]="!categoryNameControlVisible"
class="adf-category-name-field">
<mat-form-field>
<mat-icon matPrefix>search</mat-icon>
<mat-label id="adf-category-name-input-label">
{{ 'CATEGORIES_MANAGEMENT.NAME' | translate }}
</mat-label>
<input
#categoryNameInput
matInput
autocomplete="off"
[formControl]="categoryNameControl"
(keyup.enter)="addCategory()"
aria-labelledby="adf-category-name-input-label"
adf-auto-focus
/>
<mat-error [hidden]="!categoryNameControl.invalid">{{ categoryNameErrorMessageKey | translate }}</mat-error>
</mat-form-field>
<button
mat-icon-button
[class.adf-btn-padded]="!isCRUDMode"
(click)="hideNameInput()"
[attr.title]="'CATEGORIES_MANAGEMENT.HIDE_INPUT' | translate">
<mat-icon>remove</mat-icon>
</button>
</div>
<p *ngIf="isCategoryEmpty" class="adf-no-categories-message">
{{ noCategoriesMsg | translate }}
</p>
</div>
<div class="adf-existing-categories-panel" *ngIf="existingCategoriesPanelVisible">
<ng-container *ngIf="isCRUDMode && (!existingCategoriesLoading || existingCategories)">
@@ -1,8 +1,14 @@
.adf-categories-management {
padding-top: 12px;
.adf-category-name-field {
display: flex;
justify-content: space-between;
width: 100%;
background: var(--theme-metadata-buttons-background-color);
height: 32px;
border-radius: 12px;
align-items: center;
mat-form-field {
width: 100%;
@@ -11,6 +17,20 @@
.adf-btn-padded {
margin-right: -14px;
}
.mat-form-field-underline {
display: none;
}
.mat-form-field-appearance-fill {
.mat-form-field-flex {
background: none;
}
.mat-form-field-infix {
padding: 0;
}
}
}
.adf-assigned-categories {
@@ -31,11 +51,15 @@
[hidden] {
visibility: hidden;
}
.adf-no-categories-message {
margin-top: 28.5px;
margin-bottom: 0;
height: 30px;
}
}
.adf-categories-list {
padding-bottom: 10px;
.mat-list-base .mat-list-item,
.mat-list-base .mat-list-option {
display: flex;
@@ -220,44 +220,33 @@ describe('CategoriesManagementComponent', () => {
component.categoryNameControlVisible = true;
fixture.detectChanges();
});
it('should be hidden initially', () => {
component.categoryNameControlVisible = false;
fixture.detectChanges();
const categoryControl: HTMLDivElement = fixture.debugElement.query(By.css('.adf-category-name-field')).nativeElement;
expect(categoryControl.hidden).toBeTrue();
});
it('should be visible when categoryNameControlVisible is true', () => {
const categoryControl = fixture.debugElement.query(By.css('.adf-category-name-field'));
expect(categoryControl).toBeTruthy();
});
it('should have correct label and hide button', () => {
const categoryControlLabel = fixture.debugElement.query(By.css('#adf-category-name-input-label')).nativeElement;
const categoryControlHideBtn: HTMLButtonElement = fixture.debugElement.query(By.css('.adf-category-name-field button')).nativeElement;
expect(categoryControlHideBtn).toBeTruthy();
expect(categoryControlHideBtn.attributes.getNamedItem('title').textContent.trim()).toBe('CATEGORIES_MANAGEMENT.HIDE_INPUT');
expect(categoryControlLabel.textContent.trim()).toBe('CATEGORIES_MANAGEMENT.NAME');
it('should return true when categoryNameControlVisible is true', () => {
component.categoryNameControlVisible = true;
const result = component.isNameCategoryVisible;
expect(result).toBeTrue();
});
it('should hide and clear category control and existing categories panel on clicking hide button', fakeAsync(() => {
typeCategory('test');
const categoryControlHideBtn: HTMLButtonElement = fixture.debugElement.query(By.css('.adf-category-name-field button')).nativeElement;
const controlVisibilityChangeSpy = spyOn(component.categoryNameControlVisibleChange, 'emit').and.callThrough();
categoryControlHideBtn.click();
fixture.detectChanges();
it('should return false when categoryNameControlVisible is false and categories length is 0', () => {
component.categoryNameControlVisible = false;
component.categories = [];
const result = component.isNameCategoryVisible;
expect(result).toBeFalse();
});
});
const categoryControl: HTMLDivElement = fixture.debugElement.query(By.css('.adf-category-name-field')).nativeElement;
expect(categoryControl.hidden).toBeTrue();
expect(component.categoryNameControlVisible).toBeFalse();
expect(component.existingCategoriesPanelVisible).toBeFalse();
expect(controlVisibilityChangeSpy).toHaveBeenCalledOnceWith(false);
component.categoryNameControlVisible = true;
fixture.detectChanges();
tick(100);
expect(getCategoryControlInput().value).toBe('');
}));
describe('isCategoryEmpty', () => {
it('should return true when categories empty', () => {
component.categories = [];
component.categoryNameControlVisible = false;
const result = component.isCategoryEmpty;
expect(result).toBeTrue();
});
});
describe('Spinner', () => {
@@ -476,13 +465,8 @@ describe('CategoriesManagementComponent', () => {
expect(categoriesChangeSpy).toHaveBeenCalledOnceWith(component.categories);
}));
it('should clear and hide input after category is created', fakeAsync(() => {
const controlVisibilityChangeSpy = spyOn(component.categoryNameControlVisibleChange, 'emit');
it('should clear input after category is created', fakeAsync(() => {
createCategory('test');
const categoryControl: HTMLDivElement = fixture.debugElement.query(By.css('.adf-category-name-field')).nativeElement;
expect(categoryControl.hidden).toBeTrue();
expect(controlVisibilityChangeSpy).toHaveBeenCalledOnceWith(false);
expect(getExistingCategoriesList()).toEqual([]);
expect(component.categoryNameControl.value).toBe('');
expect(component.categoryNameControl.untouched).toBeTrue();
@@ -177,10 +177,24 @@ export class CategoriesManagementComponent implements OnInit, OnDestroy {
this.cancelExistingCategoriesLoading$.complete();
}
/*
* Returns `true` if categoryNameControlVisible is true, otherwise `false`
*/
get isNameCategoryVisible(): boolean {
return (!this.categoryNameControlVisible && this.categories?.length > 0) || this.categoryNameControlVisible;
}
get categoryNameControl(): FormControl<string> {
return this._categoryNameControl;
}
/*
* Returns `true` if categories empty, otherwise `false`
*/
get isCategoryEmpty(): boolean {
return this.categories?.length === 0 && !this.categoryNameControlVisible;
}
get existingCategories(): Category[] {
return this._existingCategories;
}
@@ -205,16 +219,6 @@ export class CategoriesManagementComponent implements OnInit, OnDestroy {
return this.managementMode === CategoriesManagementMode.CRUD;
}
/**
* Hides and emits categoryNameControl and hides existing categories panel.
*/
hideNameInput() {
this.categoryNameControlVisible = false;
this.categoryNameControlVisibleChange.emit(false);
this._existingCategoriesPanelVisible = false;
this.clearCategoryNameInput();
}
/**
* Adds category that has been typed to a categoryNameControl and hides it afterwards.
*/
@@ -223,7 +227,6 @@ export class CategoriesManagementComponent implements OnInit, OnDestroy {
const newCatName = this.categoryNameControl.value.trim();
const newCat = new Category({ id: newCatName, name: newCatName });
this.categories.push(newCat);
this.hideNameInput();
this.clearCategoryNameInput();
this._existingCategories = null;
this.categoriesChange.emit(this.categories);
@@ -24,24 +24,6 @@
data-automation-id="meta-data-card-edit-aspect">
<mat-icon>menu</mat-icon>
</button>
<button *ngIf="!readOnly && hasAllowableOperations()"
mat-icon-button
(click)="toggleEdit()"
[attr.title]="'CORE.METADATA.ACTIONS.EDIT' | translate"
[attr.aria-label]="'CORE.METADATA.ACCESSIBILITY.EDIT' | translate"
data-automation-id="meta-data-card-toggle-edit">
<mat-icon>mode_edit</mat-icon>
</button>
</div>
<button *ngIf="displayDefaultProperties" mat-button (click)="toggleExpanded()" data-automation-id="meta-data-card-toggle-expand">
<ng-container *ngIf="!expanded">
<span data-automation-id="meta-data-card-toggle-expand-label">{{ 'ADF_VIEWER.SIDEBAR.METADATA.MORE_INFORMATION' | translate }}</span>
<mat-icon>keyboard_arrow_down</mat-icon>
</ng-container>
<ng-container *ngIf="expanded">
<span data-automation-id="meta-data-card-toggle-expand-label">{{ 'ADF_VIEWER.SIDEBAR.METADATA.LESS_INFORMATION' | translate }}</span>
<mat-icon>keyboard_arrow_up</mat-icon>
</ng-container>
</button>
</mat-card-footer>
</mat-card>
@@ -147,55 +147,6 @@ describe('ContentMetadataCardComponent', () => {
expect(contentMetadataComponent).toBeNull();
});
it('should toggle editable by clicking on the button', () => {
component.editable = true;
component.node.allowableOperations = [AllowableOperationsEnum.UPDATE];
fixture.detectChanges();
getToggleEditButton().triggerEventHandler('click', {});
fixture.detectChanges();
expect(component.editable).toBe(false);
});
it('should emit editableChange by clicking on toggle edit button', () => {
component.node.allowableOperations = [AllowableOperationsEnum.UPDATE];
fixture.detectChanges();
spyOn(component.editableChange, 'emit');
getToggleEditButton().nativeElement.click();
expect(component.editableChange.emit).toHaveBeenCalledWith(true);
});
it('should toggle expanded by clicking on the button', () => {
component.expanded = true;
fixture.detectChanges();
const button = fixture.debugElement.query(By.css('[data-automation-id="meta-data-card-toggle-expand"]'));
button.triggerEventHandler('click', {});
fixture.detectChanges();
expect(component.expanded).toBe(false);
});
it('should have the proper text on button while collapsed', () => {
component.expanded = false;
fixture.detectChanges();
const buttonLabel = fixture.debugElement.query(By.css('[data-automation-id="meta-data-card-toggle-expand-label"]'));
expect(buttonLabel.nativeElement.innerText.trim()).toBe('ADF_VIEWER.SIDEBAR.METADATA.MORE_INFORMATION');
});
it('should have the proper text on button while collapsed', () => {
component.expanded = true;
fixture.detectChanges();
const buttonLabel = fixture.debugElement.query(By.css('[data-automation-id="meta-data-card-toggle-expand-label"]'));
expect(buttonLabel.nativeElement.innerText.trim()).toBe('ADF_VIEWER.SIDEBAR.METADATA.LESS_INFORMATION');
});
it('should hide the edit button in readOnly is true', () => {
component.readOnly = true;
fixture.detectChanges();
@@ -211,14 +162,6 @@ describe('ContentMetadataCardComponent', () => {
expect(getToggleEditButton()).toBeNull();
});
it('should show the edit button if node does has `update` permissions', () => {
component.readOnly = false;
component.node.allowableOperations = [AllowableOperationsEnum.UPDATE];
fixture.detectChanges();
expect(getToggleEditButton()).not.toBeNull();
});
it('should expand the card when custom display aspect is valid', () => {
expect(component.expanded).toBeFalsy();
@@ -15,7 +15,7 @@
* limitations under the License.
*/
import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges, ViewEncapsulation } from '@angular/core';
import { Component, Input, OnChanges, SimpleChanges, ViewEncapsulation } from '@angular/core';
import { Node } from '@alfresco/js-api';
import { NodeAspectService } from '../../../aspect-list/services/node-aspect.service';
import { ContentMetadataCustomPanel, PresetConfig } from '../../interfaces/content-metadata.interfaces';
@@ -84,10 +84,6 @@ export class ContentMetadataCardComponent implements OnChanges {
@Input()
customPanels: ContentMetadataCustomPanel[];
/** Emitted when content's editable state is changed. */
@Output()
editableChange = new EventEmitter<boolean>();
private _displayDefaultProperties: boolean = true;
/**
@@ -125,15 +121,6 @@ export class ContentMetadataCardComponent implements OnChanges {
this.expanded = !this._displayDefaultProperties;
}
toggleEdit(): void {
this.editable = !this.editable;
this.editableChange.emit(this.editable);
}
toggleExpanded(): void {
this.expanded = !this.expanded;
}
hasAllowableOperations() {
return this.contentService.hasAllowableOperations(this.node, AllowableOperationsEnum.UPDATE);
}
@@ -0,0 +1,24 @@
/*!
* @license
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// eslint-disable-next-line no-shadow
export enum ButtonType {
GeneralInfo = 'generalInfo',
Tags = 'tags',
Categories = 'categories',
Group = 'group'
}
@@ -3,12 +3,48 @@
[multi]="multi">
<mat-expansion-panel *ngIf="displayDefaultProperties"
[expanded]="canExpandProperties()"
[attr.data-automation-id]="'adf-metadata-group-properties'">
[attr.data-automation-id]="'adf-metadata-group-properties'"
hideToggle
(opened)="toggleGeneralInfoPanel()"
(closed)="toggleGeneralInfoPanel()">
<mat-expansion-panel-header>
<mat-panel-title class="adf-metadata-properties-title">
{{ 'CORE.METADATA.BASIC.HEADER' | translate }}
</mat-panel-title>
<div class="adf-metadata-properties-panel-content">
<mat-icon>
{{ isGeneralInfoPanelVisible ? 'expand_more' : 'chevron_right'}}
</mat-icon>
<mat-panel-title class="adf-metadata-properties-title">
{{ 'CORE.METADATA.BASIC.HEADER' | translate }}
</mat-panel-title>
</div>
<button *ngIf="hasToggleEdit"
mat-icon-button
(click)="toggleEdit($event, group, buttonType.GeneralInfo)"
[attr.title]="'CORE.METADATA.ACTIONS.EDIT' | translate"
[attr.aria-label]="'CORE.METADATA.ACCESSIBILITY.EDIT' | translate"
data-automation-id="meta-data-general-info-edit"
class="adf-edit-icon-buttons">
<mat-icon>mode_edit</mat-icon>
</button>
<div *ngIf="editable"
class="adf-metadata-action-buttons">
<button mat-icon-button
[attr.title]="'CORE.METADATA.ACTIONS.CANCEL' | translate"
(click)="cancelChanges(buttonType.GeneralInfo, $event, group)"
data-automation-id="reset-metadata"
class="adf-metadata-action-buttons-clear">
<mat-icon>clear</mat-icon>
</button>
<button mat-icon-button
[attr.title]="'CORE.METADATA.ACTIONS.SAVE' | translate"
(click)="saveChanges(buttonType.GeneralInfo, $event, group)"
color="primary"
data-automation-id="save-general-info-metadata"
[disabled]="!hasMetadataChanged">
<mat-icon>check</mat-icon>
</button>
</div>
</mat-expansion-panel-header>
<mat-divider class="adf-mat-divider"></mat-divider>
<adf-card-view
(keydown)="keyDown($event)"
[properties]="basicProperties$ | async"
@@ -20,63 +56,130 @@
</adf-card-view>
</mat-expansion-panel>
<ng-container *ngIf="displayTags">
<mat-expansion-panel *ngIf="!editable">
<mat-expansion-panel
(opened)="toggleTagsPanel(true)"
(closed)="toggleTagsPanel(false)"
hideToggle
[expanded]="isTagPanelVisible">
<mat-expansion-panel-header>
<mat-panel-title>{{ 'METADATA.BASIC.TAGS' | translate }}</mat-panel-title>
<div class="adf-metadata-properties-panel-content">
<mat-icon>
{{ isTagPanelVisible ? 'expand_more' : 'chevron_right'}}
</mat-icon>
<mat-panel-title class="adf-metadata-properties-title">
{{ 'METADATA.BASIC.TAGS' | translate }}
</mat-panel-title>
</div>
<div class="adf-tags-buttons">
<button *ngIf="hasTagsToggleEdit"
mat-icon-button
(click)="toggleEdit($event, group, buttonType.Tags)"
[attr.title]="'CORE.METADATA.ACTIONS.EDIT' | translate"
[attr.aria-label]="'CORE.METADATA.ACCESSIBILITY.EDIT' | translate"
data-automation-id="showing-tag-input-button"
class="adf-edit-icon-buttons">
<mat-icon>mode_edit</mat-icon>
</button>
</div>
<div *ngIf="editableTags" class="adf-metadata-action-buttons">
<button mat-icon-button
[attr.title]="'CORE.METADATA.ACTIONS.CANCEL' | translate"
(click)="cancelChanges(buttonType.Tags, $event, group)"
data-automation-id="reset-tags-metadata"
class="adf-metadata-action-buttons-clear">
<mat-icon>clear</mat-icon>
</button>
<button mat-icon-button
[attr.title]="'CORE.METADATA.ACTIONS.SAVE' | translate"
(click)="saveChanges(buttonType.Tags, $event, group)"
color="primary"
data-automation-id="save-tags-metadata"
[disabled]="!hasMetadataChanged">
<mat-icon>check</mat-icon>
</button>
</div>
</mat-expansion-panel-header>
<p *ngFor="let tag of tags" class="adf-metadata-properties-tag">{{ tag }}</p>
</mat-expansion-panel>
<div
*ngIf="editable"
class="adf-metadata-properties-tags">
<div class="adf-metadata-properties-tags-title">
<p>{{ 'METADATA.BASIC.TAGS' | translate }}</p>
<button
data-automation-id="showing-tag-input-button"
mat-icon-button
[attr.title]="'METADATA.BASIC.ADD_TAG_TOOLTIP' | translate"
(click)="tagNameControlVisible = true"
[hidden]="tagNameControlVisible || saving">
<mat-icon>add</mat-icon>
</button>
<mat-divider class="adf-mat-divider"></mat-divider>
<div
*ngIf="!editableTags"
class="adf-metadata-properties-tags">
<span *ngFor="let tag of tags" class="adf-metadata-properties-tag">{{ tag }}</span>
</div>
<adf-tags-creator
[(tagNameControlVisible)]="tagNameControlVisible"
(tagsChange)="storeTagsToAssign($event)"
[mode]="tagsCreatorMode"
[tags]="assignedTags"
[disabledTagsRemoving]="saving">
</adf-tags-creator>
</div>
<div *ngIf="isTagsEmpty()" class="adf-metadata-no-item-added">
{{ 'METADATA.BASIC.NO_TAGS_ADDED' | translate }}
</div>
<div *ngIf="editableTags" class="adf-metadata-properties-tags">
<adf-tags-creator
[(tagNameControlVisible)]="tagNameControlVisible"
(tagsChange)="storeTagsToAssign($event)"
[mode]="tagsCreatorMode"
[tags]="assignedTags"
[disabledTagsRemoving]="saving">
</adf-tags-creator>
</div>
</mat-expansion-panel>
</ng-container>
<ng-container *ngIf="displayCategories">
<mat-expansion-panel *ngIf="!editable">
<mat-expansion-panel
(opened)="handleCategoriesPanelState(true)"
(closed)="handleCategoriesPanelState(false)"
hideToggle
[expanded]="isCategoriesPanelVisible">
<mat-expansion-panel-header>
<mat-panel-title>{{ 'CATEGORIES_MANAGEMENT.CATEGORIES_TITLE' | translate }}</mat-panel-title>
<div class="adf-metadata-properties-panel-content">
<mat-icon>
{{ isCategoriesPanelVisible ? 'expand_more' : 'chevron_right'}}</mat-icon>
<mat-panel-title class="adf-metadata-properties-title">
{{ 'CATEGORIES_MANAGEMENT.CATEGORIES_TITLE' | translate }}
</mat-panel-title>
</div>
<div class="adf-metadata-categories-title">
<button *ngIf="hasCategoriesToggleEdit"
mat-icon-button
(click)="toggleEdit($event, group, buttonType.Categories)"
[attr.title]="'CORE.METADATA.ACTIONS.EDIT' | translate"
[attr.aria-label]="'CORE.METADATA.ACCESSIBILITY.EDIT' | translate"
data-automation-id="meta-data-categories-edit"
class="adf-edit-icon-buttons">
<mat-icon>mode_edit</mat-icon>
</button>
</div>
<div *ngIf="editableCategories" class="adf-metadata-action-buttons">
<button mat-icon-button
[attr.title]="'CORE.METADATA.ACTIONS.CANCEL' | translate"
(click)="cancelChanges(buttonType.Categories, $event, group)"
data-automation-id="reset-metadata"
class="adf-metadata-action-buttons-clear">
<mat-icon>clear</mat-icon>
</button>
<button mat-icon-button
[attr.title]="'CORE.METADATA.ACTIONS.SAVE' | translate"
(click)="saveChanges(buttonType.Categories, $event, group)"
color="primary"
data-automation-id="save-categories-metadata"
[disabled]="!hasMetadataChanged">
<mat-icon>check</mat-icon>
</button>
</div>
</mat-expansion-panel-header>
<p *ngFor="let category of categories" class="adf-metadata-categories">{{ category.name }}</p>
</mat-expansion-panel>
<div *ngIf="editable"
class="adf-metadata-categories-header">
<div class="adf-metadata-categories-title">
<p>{{ 'CATEGORIES_MANAGEMENT.CATEGORIES_TITLE' | translate }}</p>
<button
mat-icon-button
[attr.title]="'CATEGORIES_MANAGEMENT.ASSIGN_CATEGORIES' | translate"
[hidden]="categoryControlVisible || saving"
(click)="categoryControlVisible = true">
<mat-icon>add</mat-icon>
</button>
<mat-divider class="adf-mat-divider"></mat-divider>
<div *ngIf="!editableCategories">
<p *ngFor="let category of categories" class="adf-metadata-categories">{{ category.name }}</p>
</div>
<adf-categories-management
[(categoryNameControlVisible)]="categoryControlVisible"
[disableRemoval]="saving"
[categories]="categories"
[managementMode]="categoriesManagementMode"
[classifiableChanged]="classifiableChanged"
(categoriesChange)="storeCategoriesToAssign($event)">
</adf-categories-management>
</div>
<div *ngIf="isCategoryEmpty()" class="adf-metadata-no-item-added">
{{ 'CATEGORIES_MANAGEMENT.NO_CATEGORIES_ADDED' | translate }}
</div>
<div *ngIf="editableCategories" class="adf-metadata-categories-header">
<adf-categories-management
[(categoryNameControlVisible)]="categoryControlVisible"
[disableRemoval]="saving"
[categories]="categories"
[managementMode]="categoriesManagementMode"
[classifiableChanged]="classifiableChanged"
(categoriesChange)="storeCategoriesToAssign($event)">
</adf-categories-management>
</div>
</mat-expansion-panel>
</ng-container>
<mat-expansion-panel *ngFor="let customPanel of customPanels" [expanded]="canExpandTheCard(customPanel.panelTitle)">
<mat-expansion-panel-header>
@@ -84,7 +187,6 @@
</mat-expansion-panel-header>
<adf-dynamic-component [id]="customPanel.component" [data]="{ node }"></adf-dynamic-component>
</mat-expansion-panel>
<ng-container *ngIf="expanded">
<ng-container *ngIf="groupedProperties$ | async; else loading; let groupedProperties">
<div *ngFor="let group of groupedProperties; let first = first;"
class="adf-metadata-grouped-properties-container">
@@ -92,15 +194,51 @@
[attr.data-automation-id]="'adf-metadata-group-' + group.title"
[expanded]="canExpandTheCard(group.title) || !displayDefaultProperties && first">
<mat-expansion-panel-header>
<mat-panel-title>
{{ group.title | translate }}
</mat-panel-title>
<div class="adf-metadata-properties-panel-content">
<mat-icon>
{{ group.expanded ? 'expand_more' : 'chevron_right'}}
</mat-icon>
<mat-panel-title class="adf-metadata-properties-title" matTooltip="{{ group.title | translate }}">
<p class="adf-metadata-properties-group-title">
{{ group.title | translate }}
</p>
</mat-panel-title>
</div>
<button *ngIf="hasGroupToggleEdit(group)"
mat-icon-button
[attr.title]="'CORE.METADATA.ACTIONS.EDIT' | translate"
[attr.aria-label]="'CORE.METADATA.ACCESSIBILITY.EDIT' | translate"
data-automation-id="meta-data-card-toggle-edit"
class="adf-edit-icon-buttons"
(click)="toggleEdit($event, group, buttonType.Group)">
<mat-icon>mode_edit</mat-icon>
</button>
<div class="adf-metadata-action-buttons" *ngIf="group.editable">
<button mat-icon-button
[attr.title]="'CORE.METADATA.ACTIONS.CANCEL' | translate"
(click)="cancelChanges(buttonType.Group, $event, group)"
data-automation-id="reset-metadata"
class="adf-metadata-action-buttons-clear">
<mat-icon>clear</mat-icon>
</button>
<button mat-icon-button
[attr.title]="'CORE.METADATA.ACTIONS.SAVE' | translate"
(click)="saveChanges(buttonType.Group, $event, group)"
color="primary"
data-automation-id="save-metadata"
[disabled]="!hasMetadataChanged">
<mat-icon>check</mat-icon>
</button>
</div>
</mat-expansion-panel-header>
<mat-divider class="adf-mat-divider"></mat-divider>
<div *ngIf="!showGroup(group) && !group.editable" class="adf-metadata-no-item-added">
{{ 'METADATA.BASIC.NO_ITEMS_MESSAGE' | translate: { groupTitle: group.title } }}
</div>
<adf-card-view
(keydown)="keyDown($event)"
[properties]="group.properties"
[editable]="editable"
[editable]="group.editable"
[displayEmpty]="displayEmpty"
[copyToClipboardAction]="copyToClipboardAction"
[useChipsForMultiValueProperty]="useChipsForMultiValueProperty"
@@ -108,31 +246,11 @@
[displayLabelForChips]="true">
</adf-card-view>
</mat-expansion-panel>
</div>
</ng-container>
<ng-template #loading>
<mat-progress-bar mode="indeterminate" [attr.aria-label]="'DATA_LOADING' | translate">
</mat-progress-bar>
</ng-template>
</ng-container>
</mat-accordion>
<div class="adf-metadata-action-buttons"
*ngIf="editable">
<button mat-button
(click)="cancelChanges()"
data-automation-id="reset-metadata"
[disabled]="!hasMetadataChanged">
{{ 'CORE.METADATA.ACTIONS.CANCEL' | translate }}
</button>
<button mat-raised-button
(click)="saveChanges()"
color="primary"
data-automation-id="save-metadata"
[disabled]="!hasMetadataChanged">
{{ 'CORE.METADATA.ACTIONS.SAVE' | translate }}
</button>
</div>
</div>
@@ -1,56 +1,92 @@
$panel-properties-height: 56px !default;
.adf {
&-metadata-properties {
.mat-expansion-panel-header.mat-expanded:hover,
.mat-expansion-panel-header.mat-expanded:focus {
background: var(--adf-theme-background-hover-color);
}
mat-expansion-panel-header {
height: 64px;
height: $panel-properties-height;
padding: 0 12px;
border-radius: 12px;
&.mat-expanded {
height: $panel-properties-height;
}
.mat-content {
display: contents;
}
.adf-metadata-properties-title {
font-weight: normal;
font-weight: 700;
font-size: 15px;
padding-left: 12px;
}
.adf-metadata-properties-group-title {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
width: 80px;
}
}
.mat-expansion-panel:not([class*='mat-elevation-z']) {
box-shadow: none;
border: 1px solid var(--theme-metadata-property-panel-border-color);
border-radius: 12px;
margin: 12px;
}
.adf-mat-divider {
margin-left: -24px;
margin-right: -24px;
}
.adf-edit-icon-buttons {
color: var(--adf-theme-foreground-text-color-054);
}
.mat-content.mat-content-hide-toggle {
margin-right: 0;
}
.adf-metadata-properties-panel-content {
display: flex;
align-items: center;
flex: 1;
}
.adf-metadata-properties-tag {
height: 40px;
display: flex;
height: 32px;
display: inline-flex;
align-items: center;
margin-top: -14px;
margin-bottom: 1em;
border-radius: 16px;
width: fit-content;
background: var(--theme-metadata-buttons-background-color);
margin-top: 12px;
padding: 6px 12px;
justify-content: center;
margin-left: 8px;
}
&:first-of-type {
margin-top: 5px;
}
.adf-metadata-no-item-added {
word-break: break-all;
font-size: 15px;
padding-top: 16px;
text-align: center;
}
&-tags {
padding: 0 10px 0 26px;
&-title {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 15px;
height: 68px;
height: $panel-properties-height;
}
adf-tags-creator {
margin-top: 19px;
.adf-tags-creation {
padding-right: 0;
padding-left: 12px;
.adf-tag {
margin-top: -14px;
}
}
&.adf-creator-with-existing-tags-panel {
@@ -67,20 +103,23 @@
&-metadata-action-buttons {
display: flex;
justify-content: space-evenly;
margin: 10px;
&-clear {
color: var(--theme-metadata-action-button-clear-color);
}
}
&-metadata-categories-header {
display: flex;
flex-direction: column;
padding: 0 24px;
padding-right: 24px;
.adf-metadata-categories-title {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 15px;
height: 64px;
height: $panel-properties-height;
button {
margin-right: -14px;
@@ -92,3 +131,19 @@
}
}
}
.adf-info-drawer-layout-content .adf-content-metadata-card .adf-metadata-properties .mat-expansion-panel {
border: 1px solid var(--theme-metadata-property-panel-border-color);
border-radius: 12px;
margin-bottom: 12px;
}
.adf-content-metadata-card {
.mat-card {
box-shadow: none;
}
}
.adf-metadata-grouped-properties-container .mat-content.mat-content-hide-toggle {
align-items: center;
}
@@ -21,7 +21,7 @@ import { By } from '@angular/platform-browser';
import { Category, CategoryPaging, ClassesApi, Node, Tag, TagBody, TagEntry, TagPaging, TagPagingList } from '@alfresco/js-api';
import { ContentMetadataComponent } from './content-metadata.component';
import { ContentMetadataService } from '../../services/content-metadata.service';
import { AppConfigService, CardViewBaseItemModel, CardViewComponent, LogService, UpdateNotification } from '@alfresco/adf-core';
import { AppConfigService, CardViewBaseItemModel, CardViewComponent, LogService, NotificationService, UpdateNotification } from '@alfresco/adf-core';
import { NodesApiService } from '../../../common/services/nodes-api.service';
import { EMPTY, of, throwError } from 'rxjs';
import { ContentTestingModule } from '../../../testing/content.testing.module';
@@ -31,6 +31,7 @@ import { CardViewContentUpdateService } from '../../../common/services/card-view
import { PropertyGroup } from '../../interfaces/property-group.interface';
import { PropertyDescriptorsService } from '../../services/property-descriptors.service';
import {
CardViewGroup,
CategoriesManagementComponent,
CategoriesManagementMode,
CategoryService,
@@ -38,6 +39,7 @@ import {
TagsCreatorMode,
TagService
} from '@alfresco/adf-content-services';
import { ButtonType } from './button-type.enum';
describe('ContentMetadataComponent', () => {
let component: ContentMetadataComponent;
@@ -49,6 +51,8 @@ describe('ContentMetadataComponent', () => {
let folderNode: Node;
let tagService: TagService;
let categoryService: CategoryService;
let getClassSpy: jasmine.Spy;
let notificationService: NotificationService;
const preset = 'custom-preset';
@@ -71,26 +75,40 @@ describe('ContentMetadataComponent', () => {
const category2 = new Category({ id: 'test2', name: 'testCat2' });
const categoryPagingResponse: CategoryPaging = { list: { pagination: {}, entries: [{ entry: category1 }, { entry: category2 }] } };
const findTagElements = (): DebugElement[] => fixture.debugElement.queryAll(By.css('.adf-metadata-properties-tag'));
const findTagElements = (): DebugElement[] => fixture.debugElement.queryAll(By.css('.adf-metadata-properties .adf-metadata-properties-tag'));
const findCancelButton = (): HTMLButtonElement => fixture.debugElement.query(By.css('[data-automation-id=reset-metadata]')).nativeElement;
const findCancelTagsButton = (): HTMLButtonElement =>
fixture.debugElement.query(By.css('[data-automation-id=reset-tags-metadata]')).nativeElement;
const clickOnCancel = () => {
findCancelButton().click();
fixture.detectChanges();
};
const findSaveButton = (): HTMLButtonElement => fixture.debugElement.query(By.css('[data-automation-id=save-metadata]')).nativeElement;
const findSaveGeneralInfoButton = (): HTMLButtonElement =>
fixture.debugElement.query(By.css('[data-automation-id=save-general-info-metadata]')).nativeElement;
const findSaveTagsButton = (): HTMLButtonElement => fixture.debugElement.query(By.css('[data-automation-id=save-tags-metadata]')).nativeElement;
const findSaveCategoriesButton = (): HTMLButtonElement =>
fixture.debugElement.query(By.css('[data-automation-id=save-categories-metadata]')).nativeElement;
const clickOnSave = () => {
findSaveButton().click();
const clickOnGeneralInfoSave = () => {
findSaveGeneralInfoButton().click();
fixture.detectChanges();
};
const clickOnTagsSave = () => {
findSaveTagsButton().click();
fixture.detectChanges();
};
const findTagsCreator = (): TagsCreatorComponent => fixture.debugElement.query(By.directive(TagsCreatorComponent))?.componentInstance;
const findShowingTagInputButton = (): HTMLButtonElement =>
fixture.debugElement.query(By.css('[data-automation-id=showing-tag-input-button]')).nativeElement;
const findShowingTagInputButton = (): HTMLButtonElement => fixture.debugElement.query(By.css('.adf-tags-buttons')).nativeElement;
const getToggleEditButton = () => fixture.debugElement.query(By.css('[data-automation-id="meta-data-general-info-edit"]'));
const getTagsToggleEditButton = () => fixture.debugElement.query(By.css('[data-automation-id="showing-tag-input-button"]'));
const getCategoriesToggleEditButton = () => fixture.debugElement.query(By.css('[data-automation-id="meta-data-categories-edit"]'));
const getGroupToggleEditButton = () => fixture.debugElement.query(By.css('[data-automation-id="meta-data-card-toggle-edit"]'));
/**
* Get metadata categories
@@ -116,7 +134,7 @@ describe('ContentMetadataComponent', () => {
* @returns native element
*/
function getAssignCategoriesBtn(): HTMLButtonElement {
return fixture.debugElement.query(By.css('.adf-metadata-categories-title button')).nativeElement;
return fixture.debugElement.query(By.css('.adf-metadata-categories-title')).nativeElement;
}
/**
@@ -135,8 +153,8 @@ describe('ContentMetadataComponent', () => {
fixture.detectChanges();
await fixture.whenStable();
clickOnSave();
const event = new MouseEvent('click');
component.saveChanges(ButtonType.Group, event);
await fixture.whenStable();
}
@@ -175,6 +193,9 @@ describe('ContentMetadataComponent', () => {
nodesApiService = TestBed.inject(NodesApiService);
tagService = TestBed.inject(TagService);
categoryService = TestBed.inject(CategoryService);
notificationService = TestBed.inject(NotificationService);
const propertyDescriptorsService = TestBed.inject(PropertyDescriptorsService);
const classesApi = propertyDescriptorsService['classesApi'];
node = {
id: 'node-id',
@@ -197,6 +218,7 @@ describe('ContentMetadataComponent', () => {
component.node = node;
component.preset = preset;
spyOn(contentMetadataService, 'getContentTypeProperty').and.returnValue(of([]));
getClassSpy = spyOn(classesApi, 'getClass');
fixture.detectChanges();
});
@@ -272,7 +294,7 @@ describe('ContentMetadataComponent', () => {
}));
it('should call removeTag and assignTagsToNode on TagService on save click', fakeAsync(() => {
component.editable = true;
component.editableTags = true;
component.displayTags = true;
const property = { key: 'properties.property-key', value: 'original-value' } as CardViewBaseItemModel;
const expectedNode = { ...node, name: 'some-modified-value' };
@@ -284,14 +306,13 @@ describe('ContentMetadataComponent', () => {
spyOn(tagService, 'assignTagsToNode').and.returnValue(EMPTY);
const tagName1 = tagPaging.list.entries[0].entry.tag;
const tagName2 = 'New tag 3';
updateService.update(property, 'updated-value');
tick(600);
fixture.detectChanges();
findTagsCreator().tagsChange.emit([tagName1, tagName2]);
clickOnSave();
fixture.detectChanges();
tick(600);
clickOnTagsSave();
tick(100);
const tag1 = new TagBody();
tag1.tag = tagName1;
const tag2 = new TagBody();
@@ -300,8 +321,8 @@ describe('ContentMetadataComponent', () => {
expect(tagService.assignTagsToNode).toHaveBeenCalledWith(node.id, [tag1, tag2]);
}));
it('should call getTagsByNodeId on TagService on save click', fakeAsync(() => {
component.editable = true;
it('should call getTagsByNodeId on TagService on save click', () => {
component.editableTags = true;
component.displayTags = true;
const property = { key: 'properties.property-key', value: 'original-value' } as CardViewBaseItemModel;
const expectedNode = { ...node, name: 'some-modified-value' };
@@ -313,15 +334,15 @@ describe('ContentMetadataComponent', () => {
spyOn(tagService, 'assignTagsToNode').and.returnValue(of({}));
updateService.update(property, 'updated-value');
tick(600);
fixture.detectChanges();
findTagsCreator().tagsChange.emit([tagPaging.list.entries[0].entry.tag, 'New tag 3']);
getTagsByNodeIdSpy.calls.reset();
clickOnSave();
const mockEvent = new MouseEvent('click');
component.saveChanges(ButtonType.Tags, mockEvent);
expect(tagService.getTagsByNodeId).toHaveBeenCalledWith(node.id);
}));
});
it('should throw error on unsuccessful save', fakeAsync(() => {
const logService: LogService = TestBed.inject(LogService);
@@ -340,7 +361,7 @@ describe('ContentMetadataComponent', () => {
spyOn(nodesApiService, 'updateNode').and.returnValue(throwError(new Error('My bad')));
fixture.detectChanges();
fixture.whenStable().then(() => clickOnSave());
fixture.whenStable().then(() => clickOnGeneralInfoSave());
discardPeriodicTasks();
flush();
}));
@@ -357,7 +378,7 @@ describe('ContentMetadataComponent', () => {
fixture.detectChanges();
tick(100);
clickOnSave();
clickOnGeneralInfoSave();
tick(100);
expect(component.node).toEqual(expectedNode);
@@ -367,7 +388,7 @@ describe('ContentMetadataComponent', () => {
}));
it('should call removeTag and assignTagsToNode on TagService after confirming confirmation dialog when content type is changed', fakeAsync(() => {
component.editable = true;
component.editableTags = true;
component.displayTags = true;
const property = { key: 'nodeType', value: 'ft:sbiruli' } as CardViewBaseItemModel;
const expectedNode = { ...node, nodeType: 'ft:sbiruli' };
@@ -388,7 +409,7 @@ describe('ContentMetadataComponent', () => {
findTagsCreator().tagsChange.emit([tagName1, tagName2]);
tick(100);
fixture.detectChanges();
clickOnSave();
clickOnTagsSave();
tick(100);
const tag1 = new TagBody();
@@ -412,7 +433,7 @@ describe('ContentMetadataComponent', () => {
fixture.detectChanges();
tick(100);
clickOnSave();
clickOnGeneralInfoSave();
tick(100);
expect(component.node).toEqual(expectedNode);
@@ -420,6 +441,182 @@ describe('ContentMetadataComponent', () => {
}));
});
describe('toggleEdit', () => {
let mockEvent: MouseEvent;
let showErrorSpy: jasmine.Spy;
const mockGroup: CardViewGroup = {
editable: false,
expanded: false,
title: '',
properties: []
};
beforeEach(() => {
mockEvent = new MouseEvent('click');
component.editableGroup = mockGroup;
showErrorSpy = spyOn(notificationService, 'showError').and.stub();
});
it('should toggle General Info editing mode', () => {
component.editable = false;
component.toggleEdit(mockEvent, mockGroup, ButtonType.GeneralInfo);
expect(component.editableTags).toBe(false);
expect(component.editableCategories).toBe(false);
expect(component.editableGroup.editable).toBe(false);
});
it('should toggle Tags editing mode', () => {
component.editableTags = false;
component.toggleEdit(mockEvent, mockGroup, ButtonType.Tags);
expect(component.isTagPanelVisible).toBe(component.editableTags);
expect(component.tagNameControlVisible).toBe(true);
expect(component.editableCategories).toBe(false);
expect(component.editableGroup.editable).toBe(false);
});
it('should toggle Categories editing mode', () => {
component.editableCategories = false;
component.toggleEdit(mockEvent, mockGroup, ButtonType.Categories);
expect(component.isCategoriesPanelVisible).toBe(component.editableCategories);
expect(component.categoryControlVisible).toBe(true);
expect(component.editableTags).toBe(false);
expect(component.editableGroup.editable).toBe(false);
});
it('should toggle Group editing mode', () => {
component.toggleEdit(mockEvent, mockGroup, ButtonType.Group);
expect(component.editable).toBe(false);
expect(component.editableGroup).toBe(mockGroup.editable ? mockGroup : null);
if (mockGroup.editable) {
expect(mockGroup.expanded).toBe(true);
}
});
it('should show Snackbar when Editing Panel is Active', () => {
spyOn(component, 'isEditingPanel').and.returnValue(true);
component.toggleEdit(mockEvent, mockGroup, ButtonType.GeneralInfo);
expect(component.isEditingPanel).toHaveBeenCalled();
expect(showErrorSpy).toHaveBeenCalledWith('METADATA.BASIC.SAVE_OR_DISCARD_CHANGES');
});
});
describe('toggleEditMode', () => {
it('should toggle general editable', () => {
component.editable = false;
component.toggleEditMode(ButtonType.GeneralInfo);
expect(component.editable).toBe(true);
});
it('should toggle tags editable', () => {
component.editableTags = false;
component.toggleEditMode(ButtonType.Tags);
expect(component.editableTags).toBe(true);
});
it('should toggle categories editable', () => {
component.editableCategories = false;
component.toggleEditMode(ButtonType.Categories);
expect(component.editableCategories).toBe(true);
});
it('should toggle group editable', () => {
const group: CardViewGroup = {
editable: false,
expanded: false,
title: '',
properties: []
};
component.editableGroup = null;
component.toggleEditMode(ButtonType.Group, group);
expect(group.editable).toBe(true);
});
});
;
describe('Permission', () => {
it('should hide the general info edit button if node does not have `update` permissions', () => {
component.readOnly = false;
component.node.allowableOperations = null;
fixture.detectChanges();
expect(getToggleEditButton()).toBeNull();
});
it('should hide the tags edit button if node does not have `update` permissions', () => {
component.readOnly = false;
component.node.allowableOperations = null;
fixture.detectChanges();
expect(getTagsToggleEditButton()).toBeNull();
});
it('should hide the categories edit button if node does not have `update` permissions', () => {
component.readOnly = false;
component.node.allowableOperations = null;
fixture.detectChanges();
expect(getCategoriesToggleEditButton()).toBeNull();
});
it('should hide the groups edit button if node does not have `update` permissions', () => {
component.readOnly = false;
component.node.allowableOperations = null;
fixture.detectChanges();
expect(getGroupToggleEditButton()).toBeNull();
});
});
describe('hasToggleEdit', () => {
it('should return true when editable is false, readOnly is false, and hasAllowableOperations is true', () => {
component.editable = false;
component.readOnly = false;
component.hasAllowableOperations = true;
const result = component.hasToggleEdit;
expect(result).toBe(true);
});
it('should return false when editable is true', () => {
component.editable = true;
component.readOnly = false;
component.hasAllowableOperations = true;
const result = component.hasToggleEdit;
expect(result).toBe(false);
});
});
describe('hasTagsToggleEdit', () => {
it('should have hasTagsToggleEdit property as expected', () => {
component.editableTags = false;
component.readOnly = false;
component.hasAllowableOperations = true;
fixture.detectChanges();
expect(component.hasTagsToggleEdit).toBe(true);
});
});
describe('hasGroupToggleEdit', () => {
it('should return true when group is not editable, not read-only, and has allowable operations', () => {
component.readOnly = false;
component.hasAllowableOperations = true;
const group: CardViewGroup = {
title: 'Group Title',
properties: [],
expanded: true,
editable: false
};
const result = component.hasGroupToggleEdit(group);
expect(result).toBe(true);
});
});
describe('hasCategoriesToggleEdit', () => {
it('should have hasCategoriesToggleEdit property as expected', () => {
component.editableCategories = false;
component.readOnly = false;
component.hasAllowableOperations = true;
expect(component.hasCategoriesToggleEdit).toBe(true);
});
});
describe('Reseting', () => {
it('should reset properties on reset click', async () => {
component.changedProperties = { properties: { 'property-key': 'updated-value' } };
@@ -562,9 +759,7 @@ describe('ContentMetadataComponent', () => {
});
it('should hide card views group when the grouped properties are empty', async () => {
component.expanded = true;
spyOn(contentMetadataService, 'getGroupedProperties').and.returnValue(of([{ properties: [] } as any]));
spyOn(contentMetadataService, 'getGroupedProperties').and.stub();
component.ngOnChanges({ node: new SimpleChange(node, expectedNode, false) });
@@ -577,21 +772,7 @@ describe('ContentMetadataComponent', () => {
it('should display card views group when there is at least one property that is not empty', async () => {
component.expanded = true;
const cardViewGroup = {
title: 'Group 1',
properties: [
{
data: null,
default: null,
displayValue: 'DefaultName',
icon: '',
key: 'properties.cm:default',
label: 'To'
}
]
};
spyOn(contentMetadataService, 'getGroupedProperties').and.returnValue(of([{ properties: [cardViewGroup] } as any]));
spyOn(contentMetadataService, 'getGroupedProperties').and.stub();
component.ngOnChanges({ node: new SimpleChange(node, expectedNode, false) });
@@ -725,7 +906,7 @@ describe('ContentMetadataComponent', () => {
'cm:versionable': '*'
});
spyOn(classesApi, 'getClass').and.returnValue(Promise.resolve(verResponse));
getClassSpy.and.returnValue(Promise.resolve(verResponse));
component.ngOnChanges({ node: new SimpleChange(node, expectedNode, false) });
fixture.detectChanges();
@@ -745,7 +926,7 @@ describe('ContentMetadataComponent', () => {
'cm:versionable': '*'
});
spyOn(classesApi, 'getClass').and.returnValue(Promise.resolve(verResponse));
getClassSpy.and.returnValue(Promise.resolve(verResponse));
component.ngOnChanges({ node: new SimpleChange(node, expectedNode, false) });
fixture.detectChanges();
@@ -765,7 +946,7 @@ describe('ContentMetadataComponent', () => {
exclude: 'cm:versionable'
});
spyOn(classesApi, 'getClass').and.returnValue(Promise.resolve(verResponse));
getClassSpy.and.returnValue(Promise.resolve(verResponse));
component.ngOnChanges({ node: new SimpleChange(node, expectedNode, false) });
fixture.detectChanges();
@@ -786,7 +967,7 @@ describe('ContentMetadataComponent', () => {
'cm:versionable': '*'
});
spyOn(classesApi, 'getClass').and.returnValue(Promise.resolve(verResponse));
getClassSpy.and.returnValue(Promise.resolve(verResponse));
component.ngOnChanges({ node: new SimpleChange(node, expectedNode, false) });
fixture.detectChanges();
@@ -806,7 +987,7 @@ describe('ContentMetadataComponent', () => {
exclude: ['cm:versionable', 'cm:auditable']
});
spyOn(classesApi, 'getClass').and.returnValue(Promise.resolve(verResponse));
getClassSpy.and.returnValue(Promise.resolve(verResponse));
component.ngOnChanges({ node: new SimpleChange(node, expectedNode, false) });
fixture.detectChanges();
@@ -830,7 +1011,7 @@ describe('ContentMetadataComponent', () => {
'exif:exif': ['exif:pixelXDimension', 'exif:pixelYDimension']
});
spyOn(classesApi, 'getClass').and.returnValue(Promise.resolve(exifResponse));
getClassSpy.and.returnValue(Promise.resolve(exifResponse));
component.ngOnChanges({ node: new SimpleChange(node, expectedNode, false) });
fixture.detectChanges();
@@ -864,7 +1045,7 @@ describe('ContentMetadataComponent', () => {
'exif:exif': ['exif:pixelXDimension', 'exif:pixelYDimension']
});
spyOn(classesApi, 'getClass').and.returnValue(Promise.resolve(exifResponse));
getClassSpy.and.returnValue(Promise.resolve(exifResponse));
component.ngOnChanges({ node: new SimpleChange(node, expectedNode, false) });
fixture.detectChanges();
@@ -892,16 +1073,10 @@ describe('ContentMetadataComponent', () => {
component.displayAspect = 'EXIF';
component.expanded = true;
component.displayEmpty = true;
fixture.detectChanges();
await fixture.whenStable();
let defaultProp = queryDom(fixture);
let exifProp = queryDom(fixture, 'EXIF');
let customProp = queryDom(fixture, 'CUSTOM');
expect(defaultProp.componentInstance.expanded).toBeFalsy();
expect(exifProp.componentInstance.expanded).toBeTruthy();
expect(customProp.componentInstance.expanded).toBeFalsy();
component.displayAspect = 'CUSTOM';
@@ -909,23 +1084,14 @@ describe('ContentMetadataComponent', () => {
await fixture.whenStable();
defaultProp = queryDom(fixture);
exifProp = queryDom(fixture, 'EXIF');
customProp = queryDom(fixture, 'CUSTOM');
expect(defaultProp.componentInstance.expanded).toBeFalsy();
expect(exifProp.componentInstance.expanded).toBeFalsy();
expect(customProp.componentInstance.expanded).toBeTruthy();
component.displayAspect = 'Properties';
fixture.detectChanges();
await fixture.whenStable();
defaultProp = queryDom(fixture);
exifProp = queryDom(fixture, 'EXIF');
customProp = queryDom(fixture, 'CUSTOM');
expect(defaultProp.componentInstance.expanded).toBeTruthy();
expect(exifProp.componentInstance.expanded).toBeFalsy();
expect(customProp.componentInstance.expanded).toBeFalsy();
});
it('should not expand anything if input is wrong', async () => {
@@ -938,10 +1104,8 @@ describe('ContentMetadataComponent', () => {
const defaultProp = queryDom(fixture);
const exifProp = queryDom(fixture, 'EXIF');
const customProp = queryDom(fixture, 'CUSTOM');
expect(defaultProp.componentInstance.expanded).toBeFalsy();
expect(exifProp.componentInstance.expanded).toBeFalsy();
expect(customProp.componentInstance.expanded).toBeFalsy();
});
});
@@ -1053,7 +1217,7 @@ describe('ContentMetadataComponent', () => {
});
it('should render tags after loading tags after clicking on Cancel button', fakeAsync(() => {
component.editable = true;
component.editableTags = true;
fixture.detectChanges();
TestBed.inject(CardViewContentUpdateService).itemUpdated$.next({
changed: {}
@@ -1061,9 +1225,8 @@ describe('ContentMetadataComponent', () => {
tick(500);
fixture.detectChanges();
spyOn(tagService, 'getTagsByNodeId').and.returnValue(of(tagPaging));
clickOnCancel();
component.editable = false;
findCancelTagsButton().click();
component.editableTags = false;
fixture.detectChanges();
const tagElements = findTagElements();
expect(tagElements).toHaveSize(2);
@@ -1077,7 +1240,7 @@ describe('ContentMetadataComponent', () => {
component.ngOnInit();
fixture.detectChanges();
component.editable = true;
component.editableTags = true;
fixture.detectChanges();
expect(findTagElements()).toHaveSize(0);
});
@@ -1087,7 +1250,7 @@ describe('ContentMetadataComponent', () => {
let tagsCreator: TagsCreatorComponent;
beforeEach(() => {
component.editable = true;
component.editableTags = true;
component.displayTags = true;
fixture.detectChanges();
tagsCreator = findTagsCreator();
@@ -1097,12 +1260,6 @@ describe('ContentMetadataComponent', () => {
expect(tagsCreator.tagNameControlVisible).toBeFalse();
});
it('should hide showing tag input button after emitting tagNameControlVisibleChange event with true', () => {
tagsCreator.tagNameControlVisibleChange.emit(true);
fixture.detectChanges();
expect(findShowingTagInputButton().hasAttribute('hidden')).toBeTrue();
});
it('should show showing tag input button after emitting tagNameControlVisibleChange event with false', fakeAsync(() => {
tagsCreator.tagNameControlVisibleChange.emit(true);
fixture.detectChanges();
@@ -1120,28 +1277,20 @@ describe('ContentMetadataComponent', () => {
it('should enable cancel button after emitting tagsChange event', () => {
tagsCreator.tagsChange.emit(['New tag 1', 'New tag 2', 'New tag 3']);
fixture.detectChanges();
expect(findCancelButton().disabled).toBeFalse();
expect(findCancelTagsButton().disabled).toBeFalse();
});
it('should enable save button after emitting tagsChange event', () => {
tagsCreator.tagsChange.emit(['New tag 1', 'New tag 2', 'New tag 3']);
fixture.detectChanges();
expect(findSaveButton().disabled).toBeFalse();
expect(findSaveTagsButton().disabled).toBeFalse();
});
it('should have assigned false to disabledTagsRemoving', () => {
expect(tagsCreator.disabledTagsRemoving).toBeFalse();
});
it('should have assigned true to disabledTagsRemoving after clicking on update button', () => {
tagsCreator.tagsChange.emit([]);
fixture.detectChanges();
clickOnSave();
expect(tagsCreator.disabledTagsRemoving).toBeTrue();
});
it('should have assigned false to disabledTagsRemoving if forkJoin fails', fakeAsync(() => {
it('should have assigned false to disabledTagsRemoving if forkJoin fails', () => {
const property = { key: 'properties.property-key', value: 'original-value' } as CardViewBaseItemModel;
const expectedNode = { ...node, name: 'some-modified-value' };
spyOn(nodesApiService, 'updateNode').and.returnValue(of(expectedNode));
@@ -1154,22 +1303,12 @@ describe('ContentMetadataComponent', () => {
const tagName2 = 'New tag 3';
updateService.update(property, 'updated-value');
tick(600);
fixture.detectChanges();
tagsCreator.tagsChange.emit([tagName1, tagName2]);
clickOnSave();
clickOnTagsSave();
expect(tagsCreator.disabledTagsRemoving).toBeFalse();
}));
it('should have assigned false to tagNameControlVisible after clicking on update button', () => {
tagsCreator.tagNameControlVisibleChange.emit(true);
tagsCreator.tagsChange.emit([]);
fixture.detectChanges();
clickOnSave();
expect(tagsCreator.tagNameControlVisible).toBeFalse();
});
describe('Setting tags', () => {
@@ -1199,7 +1338,7 @@ describe('ContentMetadataComponent', () => {
});
it('should show tags creator if editable is true and displayTags is true', () => {
component.editable = true;
component.editableTags = true;
component.displayTags = true;
fixture.detectChanges();
expect(findTagsCreator()).toBeDefined();
@@ -1263,7 +1402,7 @@ describe('ContentMetadataComponent', () => {
});
it('should render categories after discard changes button is clicked', fakeAsync(() => {
component.editable = true;
component.editableCategories = true;
fixture.detectChanges();
TestBed.inject(CardViewContentUpdateService).itemUpdated$.next({
changed: {}
@@ -1285,7 +1424,7 @@ describe('ContentMetadataComponent', () => {
}));
it('should be hidden when editable is true', () => {
component.editable = true;
component.editableCategories = true;
fixture.detectChanges();
expect(getCategories().length).toBe(0);
});
@@ -1295,7 +1434,7 @@ describe('ContentMetadataComponent', () => {
let categoriesManagementComponent: CategoriesManagementComponent;
beforeEach(() => {
component.editable = true;
component.editableCategories = true;
component.displayCategories = true;
component.node.aspectNames.push('generalclassifiable');
spyOn(categoryService, 'getCategoryLinksForNode').and.returnValue(of(categoryPagingResponse));
@@ -1307,12 +1446,6 @@ describe('ContentMetadataComponent', () => {
expect(categoriesManagementComponent.categoryNameControlVisible).toBeFalse();
});
it('should hide assign categories button when categoryNameControlVisible changes to true', () => {
categoriesManagementComponent.categoryNameControlVisibleChange.emit(true);
fixture.detectChanges();
expect(getAssignCategoriesBtn().hasAttribute('hidden')).toBeTrue();
});
it('should show assign categories button when categoryNameControlVisible changes to false', fakeAsync(() => {
categoriesManagementComponent.categoryNameControlVisibleChange.emit(true);
fixture.detectChanges();
@@ -1341,22 +1474,14 @@ describe('ContentMetadataComponent', () => {
categoriesManagementComponent.categoriesChange.emit([category1, category2]);
fixture.detectChanges();
expect(findCancelButton().disabled).toBeFalse();
expect(findSaveButton().disabled).toBeFalse();
expect(findSaveCategoriesButton().disabled).toBeFalse();
});
it('should not disable removal initially', () => {
expect(categoriesManagementComponent.disableRemoval).toBeFalse();
});
it('should disable removal on saving', () => {
categoriesManagementComponent.categoriesChange.emit([]);
fixture.detectChanges();
clickOnSave();
expect(categoriesManagementComponent.disableRemoval).toBeTrue();
});
it('should not disable removal if forkJoin fails', fakeAsync(() => {
it('should not disable removal if forkJoin fails', () => {
const property = { key: 'properties.property-key', value: 'original-value' } as CardViewBaseItemModel;
const expectedNode = { ...node, name: 'some-modified-value' };
spyOn(nodesApiService, 'updateNode').and.returnValue(of(expectedNode));
@@ -1367,24 +1492,12 @@ describe('ContentMetadataComponent', () => {
spyOn(categoryService, 'linkNodeToCategory').and.returnValue(throwError({}));
updateService.update(property, 'updated-value');
tick(600);
fixture.detectChanges();
categoriesManagementComponent.categoriesChange.emit([category1, category2]);
clickOnSave();
findSaveCategoriesButton();
expect(categoriesManagementComponent.disableRemoval).toBeFalse();
discardPeriodicTasks();
flush();
}));
it('should set categoryNameControlVisible to false after saving', () => {
categoriesManagementComponent.categoryNameControlVisibleChange.emit(true);
categoriesManagementComponent.categoriesChange.emit([]);
fixture.detectChanges();
clickOnSave();
expect(categoriesManagementComponent.categoryNameControlVisible).toBeFalse();
});
describe('Setting categories', () => {
@@ -15,23 +15,27 @@
* limitations under the License.
*/
import { Component, Input, OnChanges, OnDestroy, OnInit, SimpleChanges, ViewEncapsulation } from '@angular/core';
import {
Category,
CategoryEntry,
CategoryLinkBody,
CategoryPaging,
Node,
TagBody,
TagEntry,
TagPaging
} from '@alfresco/js-api';
ChangeDetectorRef,
Component,
EventEmitter,
Input,
OnChanges,
OnDestroy,
OnInit,
Output,
SimpleChanges,
ViewChild,
ViewEncapsulation
} from '@angular/core';
import { Category, CategoryEntry, CategoryLinkBody, CategoryPaging, Node, TagBody, TagEntry, TagPaging } from '@alfresco/js-api';
import { forkJoin, Observable, of, Subject, zip } from 'rxjs';
import {
AppConfigService,
CardViewBaseItemModel,
CardViewItem,
LogService,
NotificationService,
TranslationService,
UpdateNotification
} from '@alfresco/adf-core';
@@ -44,6 +48,10 @@ import { TagsCreatorMode } from '../../../tag/tags-creator/tags-creator-mode';
import { TagService } from '../../../tag/services/tag.service';
import { CategoryService } from '../../../category/services/category.service';
import { CategoriesManagementMode } from '../../../category/categories-management/categories-management-mode';
import { MatExpansionPanel } from '@angular/material/expansion';
import { ButtonType } from './button-type.enum';
import { AllowableOperationsEnum } from '../../../common/models/allowable-operations.enum';
import { ContentService } from '../../../common/services/content.service';
const DEFAULT_SEPARATOR = ', ';
@@ -55,6 +63,8 @@ const DEFAULT_SEPARATOR = ', ';
encapsulation: ViewEncapsulation.None
})
export class ContentMetadataComponent implements OnChanges, OnInit, OnDestroy {
@ViewChild(MatExpansionPanel)
panel: MatExpansionPanel;
protected onDestroy$ = new Subject<boolean>();
/** (required) The node entity to fetch metadata about */
@@ -119,6 +129,73 @@ export class ContentMetadataComponent implements OnChanges, OnInit, OnDestroy {
@Input()
customPanels: ContentMetadataCustomPanel[] = [];
/**
* (optional) This flag sets the metadata in read-only mode,
* preventing changes.
*/
@Input()
readOnly = false;
/**
* Emitted when content's editable state is changed.
*
* @event editableChange
* @type {EventEmitter<boolean>}
*/
@Output()
editableChange = new EventEmitter<boolean>();
/**
* Emitted when content's editableTags state is changed.
*
* @event editableTagsChange
* @type {EventEmitter<boolean>}
*/
@Output()
editableTagsChange = new EventEmitter<boolean>();
/**
* Emitted when content's editableCategories state is changed.
*
* @event editableCategoriesChange
* @type {EventEmitter<boolean>}
*/
@Output()
editableCategoriesChange = new EventEmitter<boolean>();
/**
* Emitted when content's group state is changed.
*
* @event groupChange
* @type {EventEmitter<CardViewGroup>}
*/
@Output()
groupChange = new EventEmitter<CardViewGroup>();
/**
* (optional) This flag toggles editable of categories content.
*
* @type {boolean}
*/
@Input()
editableCategories = false;
/**
* (optional) This flag toggles editable of tags content.
*
* @type {boolean}
*/
@Input()
editableTags = false;
/**
* Group content state
*
* @type {CardViewGroup}
*/
@Input()
group: CardViewGroup;
private _assignedTags: string[] = [];
private assignedTagsEntries: TagEntry[] = [];
private _editable = false;
@@ -140,6 +217,12 @@ export class ContentMetadataComponent implements OnChanges, OnInit, OnDestroy {
categoriesManagementMode = CategoriesManagementMode.ASSIGN;
categoryControlVisible = false;
classifiableChanged = this.classifiableChangedSubject.asObservable();
isGeneralInfoPanelVisible: boolean;
isTagPanelVisible: boolean;
isCategoriesPanelVisible: boolean;
hasAllowableOperations = false;
editableGroup: CardViewGroup;
buttonType = ButtonType;
constructor(
private contentMetadataService: ContentMetadataService,
@@ -149,7 +232,10 @@ export class ContentMetadataComponent implements OnChanges, OnInit, OnDestroy {
private translationService: TranslationService,
private appConfig: AppConfigService,
private tagService: TagService,
private categoryService: CategoryService
private categoryService: CategoryService,
private cdr: ChangeDetectorRef,
private contentService: ContentService,
private notificationService: NotificationService
) {
this.copyToClipboardAction = this.appConfig.get<boolean>('content-metadata.copy-to-clipboard-action');
this.multiValueSeparator = this.appConfig.get<string>('content-metadata.multi-value-pipe-separator') || DEFAULT_SEPARATOR;
@@ -158,23 +244,19 @@ export class ContentMetadataComponent implements OnChanges, OnInit, OnDestroy {
ngOnInit() {
this.cardViewContentUpdateService.itemUpdated$
.pipe(
debounceTime(500),
takeUntil(this.onDestroy$))
.subscribe(
(updatedNode: UpdateNotification) => {
this.hasMetadataChanged = true;
this.targetProperty = updatedNode.target;
this.updateChanges(updatedNode.changed);
}
);
.pipe(debounceTime(500), takeUntil(this.onDestroy$))
.subscribe((updatedNode: UpdateNotification) => {
this.hasMetadataChanged = true;
this.targetProperty = updatedNode.target;
this.updateChanges(updatedNode.changed);
});
this.cardViewContentUpdateService.updatedAspect$.pipe(
debounceTime(500),
takeUntil(this.onDestroy$))
this.cardViewContentUpdateService.updatedAspect$
.pipe(debounceTime(500), takeUntil(this.onDestroy$))
.subscribe((node) => this.loadProperties(node));
this.loadProperties(this.node);
this.hasAllowableOperations = this.contentService.hasAllowableOperations(this.node, AllowableOperationsEnum.UPDATE);
}
get assignedTags(): string[] {
@@ -200,8 +282,7 @@ export class ContentMetadataComponent implements OnChanges, OnInit, OnDestroy {
try {
statusCode = JSON.parse(error.message).error.statusCode;
} catch {
}
} catch {}
let message = `METADATA.ERRORS.${statusCode}`;
@@ -240,10 +321,15 @@ export class ContentMetadataComponent implements OnChanges, OnInit, OnDestroy {
}
/**
* Called after clicking save button. It confirms all changes done for metadata and hides both category and tag name controls.
* Before clicking on that button they are not saved.
* Called after clicking the save button. It confirms all changes done for metadata and hides both category and tag name controls.
*
* @param buttonType - The type of button clicked.
* @param event - The click event.
* @param group - The group associated with the action.
*/
saveChanges() {
saveChanges(buttonType: ButtonType, event: MouseEvent, group?: CardViewGroup) {
event.stopPropagation();
this.toggleEditMode(buttonType, group);
this._saving = true;
this.tagNameControlVisible = false;
this.categoryControlVisible = false;
@@ -256,6 +342,27 @@ export class ContentMetadataComponent implements OnChanges, OnInit, OnDestroy {
}
}
toggleEditMode(buttonType: ButtonType, group?: CardViewGroup) {
switch (buttonType) {
case ButtonType.GeneralInfo:
this.editable = !this.editable;
break;
case ButtonType.Tags:
this.editableTags = !this.editableTags;
break;
case ButtonType.Categories:
this.editableCategories = !this.editableCategories;
break;
case ButtonType.Group:
if (group) {
group.editable = !group.editable;
}
break;
default:
break;
}
}
/**
* Register all tags which should be assigned to node. Please note that they are just in "register" state and are not yet saved
* until button for saving data is clicked. Calling that function causes that save button is enabled.
@@ -285,11 +392,119 @@ export class ContentMetadataComponent implements OnChanges, OnInit, OnDestroy {
this.categoryControlVisible = false;
}
cancelChanges() {
cancelChanges(buttonType: ButtonType, event: MouseEvent, group?: CardViewGroup) {
event.stopPropagation();
this.toggleEditMode(buttonType, group);
this.revertChanges();
this.loadProperties(this.node);
}
// Returns the editing state of the panel
isEditingPanel(): boolean {
return (
(this.editable && this.hasMetadataChanged) ||
(this.editableTags && this.hasMetadataChanged) ||
(this.editableCategories && this.hasMetadataChanged) ||
(this.editableGroup?.editable && this.hasMetadataChanged)
);
}
toggleEdit(event: MouseEvent, group: CardViewGroup, buttonType: ButtonType): void {
event.stopPropagation();
if (this.isEditingPanel()) {
this.notificationService.showError('METADATA.BASIC.SAVE_OR_DISCARD_CHANGES');
return;
}
if (this.editableGroup && this.editableGroup !== group) {
this.editableGroup.editable = false;
}
switch (buttonType) {
case ButtonType.GeneralInfo:
this.editable = !this.editable;
this.editableChange.emit(this.editable);
this.panel.open();
group.editable = false;
break;
case ButtonType.Tags:
this.editableTags = !this.editableTags;
this.editableTagsChange.emit(this.editableTags);
this.isTagPanelVisible = this.editableTags;
this.tagNameControlVisible = true;
group.editable = false;
break;
case ButtonType.Categories:
this.editableCategories = !this.editableCategories;
this.editableCategoriesChange.emit(this.editableCategories);
this.isCategoriesPanelVisible = this.editableCategories;
this.categoryControlVisible = true;
group.editable = false;
break;
case ButtonType.Group:
group.editable = !group.editable;
this.groupChange.emit(group);
this.editableGroup = group.editable ? group : null;
if (group.editable) {
group.expanded = true;
}
break;
default:
break;
}
if (buttonType !== ButtonType.GeneralInfo) {
this.editable = false;
}
if (buttonType !== ButtonType.Tags) {
this.editableTags = false;
}
if (buttonType !== ButtonType.Categories) {
this.editableCategories = false;
}
}
toggleGeneralInfoPanel() {
this.isGeneralInfoPanelVisible = !this.isGeneralInfoPanelVisible;
this.cdr.detectChanges();
}
toggleTagsPanel(tagPanelState: boolean) {
this.isTagPanelVisible = tagPanelState;
this.cdr.detectChanges();
}
handleCategoriesPanelState(isCategoriesPanelVisible: boolean) {
this.isCategoriesPanelVisible = isCategoriesPanelVisible;
this.cdr.detectChanges();
}
isTagsEmpty(): boolean {
return this.tags?.length === 0 && !this.editableTags;
}
isCategoryEmpty(): boolean {
return this.categories?.length === 0 && !this.editableCategories;
}
get hasToggleEdit(): boolean {
return !this.editable && !this.readOnly && this.hasAllowableOperations;
}
get hasTagsToggleEdit(): boolean {
return !this.editableTags && !this.readOnly && this.hasAllowableOperations;
}
get hasCategoriesToggleEdit(): boolean {
return !this.editableCategories && !this.readOnly && this.hasAllowableOperations;
}
hasGroupToggleEdit(group: CardViewGroup): boolean {
return !group.editable && !this.readOnly && this.hasAllowableOperations;
}
showGroup(group: CardViewGroup): boolean {
const properties = group.properties.filter((property) => !this.isEmpty(property.displayValue));
@@ -315,13 +530,15 @@ export class ContentMetadataComponent implements OnChanges, OnInit, OnDestroy {
updatedNode: this.nodesApiService.updateNode(this.node.id, this.changedProperties),
...(this.displayTags ? this.saveTags() : {}),
...(this.displayCategories ? this.saveCategories() : {})
}).pipe(
catchError((err) => {
this.cardViewContentUpdateService.updateElement(this.targetProperty);
this.handleUpdateError(err);
this._saving = false;
return of(null);
}))
})
.pipe(
catchError((err) => {
this.cardViewContentUpdateService.updateElement(this.targetProperty);
this.handleUpdateError(err);
this._saving = false;
return of(null);
})
)
.subscribe((result: any) => {
if (result) {
this.updateUndefinedNodeProperties(result.updatedNode);
@@ -335,9 +552,9 @@ export class ContentMetadataComponent implements OnChanges, OnInit, OnDestroy {
this.loadTagsForNode(this.node.id);
}
if (this.displayCategories && !!result.LinkingCategories) {
this.assignedCategories = result.LinkingCategories.list ?
result.LinkingCategories.list.entries.map((entry: CategoryEntry) => entry.entry) :
[result.LinkingCategories.entry];
this.assignedCategories = result.LinkingCategories.list
? result.LinkingCategories.list.entries.map((entry: CategoryEntry) => entry.entry)
: [result.LinkingCategories.entry];
}
}
this._saving = false;
@@ -374,11 +591,14 @@ export class ContentMetadataComponent implements OnChanges, OnInit, OnDestroy {
private getProperties(node: Node) {
const properties$ = this.contentMetadataService.getBasicProperties(node);
const contentTypeProperty$ = this.contentMetadataService.getContentTypeProperty(node);
return zip(properties$, contentTypeProperty$)
.pipe(map(([properties, contentTypeProperty]) => {
const filteredProperties = contentTypeProperty.filter((property) => properties.findIndex((baseProperty) => baseProperty.key === property.key) === -1);
return zip(properties$, contentTypeProperty$).pipe(
map(([properties, contentTypeProperty]) => {
const filteredProperties = contentTypeProperty.filter(
(property) => properties.findIndex((baseProperty) => baseProperty.key === property.key) === -1
);
return [...properties, ...filteredProperties];
}));
})
);
}
private isEmpty(value: any): boolean {
@@ -434,11 +654,14 @@ export class ContentMetadataComponent implements OnChanges, OnInit, OnDestroy {
}
});
if (this.tags.length) {
observables.tagsAssigning = this.tagService.assignTagsToNode(this.node.id, this.tags.map((tag) => {
const tagBody = new TagBody();
tagBody.tag = tag;
return tagBody;
}));
observables.tagsAssigning = this.tagService.assignTagsToNode(
this.node.id,
this.tags.map((tag) => {
const tagBody = new TagBody();
tagBody.tag = tag;
return tagBody;
})
);
}
}
return observables;
@@ -49,7 +49,9 @@ export const mockGroupProperties = [
clickCallBack: null,
displayValue: 400
}
]
],
editable: true,
expanded: true
},
{
title: 'CUSTOM',
@@ -69,6 +71,8 @@ export const mockGroupProperties = [
clickCallBack: null,
displayValue: 400
}
]
],
editable: true,
expanded: true
}
];
@@ -20,4 +20,6 @@ import { CardViewItem } from '@alfresco/adf-core';
export interface CardViewGroup {
title: string;
properties: CardViewItem[];
editable?: boolean;
expanded?: boolean;
}
+7 -1
View File
@@ -149,6 +149,7 @@
"TITLE": "Create Tags",
"CREATE_TAG": "Create: {{tag}}",
"NAME": "Name",
"INPUT_PLACEHOLDER": "Add a tag to these items",
"ERRORS": {
"EXISTING_TAG": "Tag already exists",
"ALREADY_ADDED_TAG": "Tag is already added",
@@ -179,6 +180,7 @@
"NAME": "Category name",
"LOADING": "Loading",
"HIDE_INPUT": "Hide input",
"NO_CATEGORIES_ADDED": "There are currently no categories added",
"ERRORS": {
"NOT_FOUND": "Categories not found",
"REQUIRED": "Category name is required",
@@ -490,7 +492,11 @@
"MODIFIED_DATE": "Modified Date",
"CONTENT_TYPE": "Content Type",
"TAGS": "Tags",
"ADD_TAG_TOOLTIP": "Add tag"
"ADD_TAG_TOOLTIP": "Add tag",
"HEADER_TITLE": "General info",
"NO_TAGS_ADDED": "There are currently no tags added",
"NO_ITEMS_MESSAGE": "There are currently no {{ groupTitle }} added",
"SAVE_OR_DISCARD_CHANGES": "Save or discard changes to continue"
},
"CONTENT_TYPE": {
"DIALOG" :{
@@ -1,54 +1,38 @@
<div class="adf-tags-creation">
<div *ngIf="isNameTagsVisible" class="adf-tag-name-field">
<mat-form-field class="adf-tag-search-field" appearance="fill">
<input #tagNameInput
matInput
autocomplete="off"
[formControl]="tagNameControl"
(keyup.enter)="addTag()"
adf-auto-focus
placeholder="{{'TAG.TAGS_CREATOR.INPUT_PLACEHOLDER' | translate}}"
/>
<mat-error *ngIf="tagNameControl.invalid">{{ tagNameErrorMessageKey | translate }}</mat-error>
</mat-form-field >
</div>
<p
class="adf-no-tags-message"
*ngIf="!tags.length && !tagNameControlVisible">
*ngIf="isTagsEmpty">
{{ 'TAG.TAGS_CREATOR.NO_TAGS_CREATED' | translate }}
</p>
<div
class="adf-tags-list"
[class.adf-tags-list-fixed]="!tagNameControlVisible"
#tagsList>
<p
*ngFor="let tag of tags"
class="adf-tag adf-label-with-icon-button">
<span *ngFor="let tag of tags" class="adf-tag adf-label-with-icon-button">
{{ tag }}
<button
data-automation-id="remove-tag-button"
mat-icon-button
(click)="removeTag(tag)"
[attr.title]="'TAG.TAGS_CREATOR.TOOLTIPS.DELETE_TAG' | translate"
[disabled]="disabledTagsRemoving">
<mat-icon>remove</mat-icon>
[disabled]="disabledTagsRemoving"
class="adf-remove-tag">
<mat-icon>close</mat-icon>
</button>
</p>
</div>
<div
class="adf-tag-name-field"
*ngIf="(!tagNameControlVisible && tags.length) || tagNameControlVisible"
[hidden]="!tagNameControlVisible">
<mat-form-field *ngIf="tagNameControlVisible">
<mat-icon matPrefix>search</mat-icon>
<mat-label id="adf-tag-name-input-label">
{{ 'TAG.TAGS_CREATOR.NAME' | translate }}
</mat-label>
<input
#tagNameInput
matInput
autocomplete="off"
[formControl]="tagNameControl"
(keyup.enter)="addTag()"
aria-labelledby="adf-tag-name-input-label"
adf-auto-focus
/>
<mat-error [hidden]="!tagNameControl.invalid">{{ tagNameErrorMessageKey | translate }}</mat-error>
</mat-form-field>
<button
data-automation-id="hide-tag-name-input-button"
mat-icon-button
(click)="hideNameInput()"
[attr.title]="'TAG.TAGS_CREATOR.TOOLTIPS.HIDE_INPUT' | translate">
<mat-icon>remove</mat-icon>
</button>
</span>
</div>
</div>
<div
@@ -1,15 +1,25 @@
adf-tags-creator {
display: block;
margin-left: -24px;
.adf-label-with-icon-button {
display: flex;
justify-content: space-between;
background: var(--theme-metadata-buttons-background-color);
width: fit-content;
height: 32px;
display: inline-flex;
align-items: center;
justify-content: center;
border-radius: 16px;
padding: 6px 0;
padding-left: 12px;
.adf-remove-tag {
line-height: 24px;
height: 24px;
transform: scale(0.7);
}
}
.adf-no-tags-message {
margin-left: 9px;
margin-top: 28.5px;
margin-bottom: 0;
height: 30px;
@@ -27,13 +37,37 @@ adf-tags-creator {
padding-right: 3px;
font-size: 14px;
}
.mat-form-field-appearance-fill .mat-form-field-flex {
background: none;
}
.mat-form-field-appearance-fill .mat-form-field-infix {
padding: 0;
}
}
.adf-tag-search-field {
background: var(--theme-metadata-buttons-background-color);
height: 32px;
border-radius: 12px;
align-items: center;
display: flex;
.mat-form-field-underline {
display: none;
}
.mat-form-field-wrapper {
width: 100%;
}
}
.adf-create-tag-label {
color: var(--theme-primary-color);
cursor: pointer;
margin-top: -1px;
padding-left: 27px;
padding-left: 12px;
overflow-wrap: anywhere;
display: inline-block;
padding-right: 12px;
@@ -42,25 +76,21 @@ adf-tags-creator {
}
.adf-tags-list {
padding-left: 10px;
padding-right: 0;
overflow: auto;
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.adf-tag {
margin-top: 0;
margin-top: 8px;
overflow-wrap: anywhere;
& + .adf-tag {
margin-top: -14px;
margin-top: 8px;
}
}
.adf-tags-creation {
padding-left: 27px;
padding-right: 22px;
}
.adf-existing-tags-panel {
border-top-left-radius: 6px;
border-top-right-radius: 6px;
@@ -75,7 +105,7 @@ adf-tags-creator {
}
.adf-tags-list {
padding-left: 18px;
padding-left: 12px;
margin-top: -2px;
padding-right: 0;
display: flex;
@@ -94,7 +124,7 @@ adf-tags-creator {
}
.mat-list-item-content-reverse {
padding: 0 6px;
padding: 0;
.mat-pseudo-checkbox {
display: none;
@@ -113,14 +113,6 @@ describe('TagsCreatorComponent', () => {
return elements.map(el => el.nativeElement);
}
/**
* Click at the hide name input button
*/
function clickAtHideNameInputButton() {
fixture.debugElement.query(By.css(`[data-automation-id="hide-tag-name-input-button"]`)).nativeElement.click();
fixture.detectChanges();
}
/**
* Get newly added tags
*
@@ -311,24 +303,6 @@ describe('TagsCreatorComponent', () => {
expect(tagNameField.query(By.directive(MatFormField))).toBeTruthy();
});
it('should be hidden and cleared after clicking button for hiding input', fakeAsync(() => {
component.tagNameControlVisible = true;
typeTag('test');
fixture.detectChanges();
tick(100);
clickAtHideNameInputButton();
const tagNameField = fixture.debugElement.query(By.css(tagNameFieldSelector));
expect(tagNameField).toBeFalsy();
component.tagNameControlVisible = true;
fixture.detectChanges();
tick(100);
expect(getNameInput().value).toBe('');
}));
it('should input be autofocused', fakeAsync(() => {
component.tagNameControlVisible = true;
fixture.detectChanges();
@@ -341,7 +315,6 @@ describe('TagsCreatorComponent', () => {
fixture.detectChanges();
tick(100);
clickAtHideNameInputButton();
component.tagNameControlVisible = true;
fixture.detectChanges();
tick(100);
@@ -349,24 +322,29 @@ describe('TagsCreatorComponent', () => {
expect(getNameInput()).toBe(document.activeElement as HTMLInputElement);
}));
it('should be hidden and cleared on discard changes', fakeAsync(() => {
component.tagNameControlVisible = true;
component.tags = ['Passed tag 1', 'Passed tag 2'];
typeTag('test');
fixture.detectChanges();
tick(100);
expect(getNameInput().value).toBe('test');
describe('isNameTagsVisible', () => {
it('should return true when tagNameControlVisible is true', () => {
component.tagNameControlVisible = true;
const result = component.isNameTagsVisible;
expect(result).toBeTrue();
});
component.tagNameControlVisible = false;
fixture.detectChanges();
tick(100);
expect(getNameInput()).toBeFalsy();
it('should return false when tagNameControlVisible is false and tags length is 0', () => {
component.tagNameControlVisible = false;
component.tags = [];
const result = component.isNameTagsVisible;
expect(result).toBeFalse();
});
});
component.tagNameControlVisible = true;
fixture.detectChanges();
tick(100);
expect(getNameInput().value).toBe('');
}));
describe('isTagsEmpty', () => {
it('should return true when tags empty', () => {
component.tags = [];
component.tagNameControlVisible = false;
const result = component.isTagsEmpty;
expect(result).toBeTrue();
});
});
describe('Errors', () => {
/**
@@ -487,14 +465,6 @@ describe('TagsCreatorComponent', () => {
expect(getPanel()).toBeTruthy();
});
it('should not be visible when something has been typed and input has been hidden', fakeAsync(() => {
typeTag('some tag');
clickAtHideNameInputButton();
expect(getPanel()).toBeFalsy();
}));
it('should have correct label when mode is Create and Assign', fakeAsync(() => {
component.mode = TagsCreatorMode.CREATE_AND_ASSIGN;
@@ -104,7 +104,7 @@ export class TagsCreatorComponent implements OnInit, OnDestroy {
if (tagNameControlVisible) {
this._existingTagsPanelVisible = true;
setTimeout(() => {
this.tagNameInputElement.nativeElement.scrollIntoView();
this.tagNameInputElement?.nativeElement?.scrollIntoView();
});
} else {
this._existingTagsPanelVisible = false;
@@ -219,6 +219,20 @@ export class TagsCreatorComponent implements OnInit, OnDestroy {
return this._tagNameControl;
}
/*
* Returns `true` if tagNameControlVisible is true, otherwise `false`
*/
get isNameTagsVisible(): boolean {
return (!this.tagNameControlVisible && this.tags?.length > 0) || this.tagNameControlVisible;
}
/*
* Returns `true` if tags empty, otherwise `false`
*/
get isTagsEmpty(): boolean {
return this.tags.length === 0 && !this.tagNameControlVisible;
}
get existingTags(): TagEntry[] {
return this._existingTags;
}
@@ -243,17 +257,6 @@ export class TagsCreatorComponent implements OnInit, OnDestroy {
return this._existingTagsPanelVisible;
}
/**
* Hide input for typing name for new tag or for searching. When input is hidden then panel of existing tags is hidden as well.
*/
hideNameInput(): void {
this.tagNameControlVisible = false;
this._existingTagsPanelVisible = false;
this.existingTagsPanelVisibilityChange.emit(this.existingTagsPanelVisible);
this.tagNameControlVisibleChange.emit(this.tagNameControlVisible);
this.clearTagNameInput();
}
/**
* Add tags to top list using value which is set in input. Adding tag is not allowed when value in input is invalid
* or if user is still typing what means that validation for input is not called yet.
@@ -261,7 +264,6 @@ export class TagsCreatorComponent implements OnInit, OnDestroy {
addTag(): void {
if (!this._typing && !this.tagNameControl.invalid) {
this.tags.push(this.tagNameControl.value.trim());
this.hideNameInput();
this.clearTagNameInput();
this.checkScrollbarVisibility();
this.tagsChange.emit(this.tags);
@@ -45,6 +45,7 @@ import { CardViewKeyValuePairsItemComponent } from './components/card-view-keyva
import { CardViewSelectItemComponent } from './components/card-view-selectitem/card-view-selectitem.component';
import { CardViewArrayItemComponent } from './components/card-view-arrayitem/card-view-arrayitem.component';
import { SelectFilterInputComponent } from './components/card-view-selectitem/select-filter-input/select-filter-input.component';
import { MatDividerModule } from '@angular/material/divider';
@NgModule({
imports: [
@@ -66,7 +67,8 @@ import { SelectFilterInputComponent } from './components/card-view-selectitem/se
MatDatetimepickerModule,
MatNativeDatetimeModule,
MatSlideToggleModule,
MatTooltipModule
MatTooltipModule,
MatDividerModule
],
declarations: [
CardViewComponent,
@@ -1,10 +1,12 @@
<label class="adf-property-label"
[attr.data-automation-id]="'card-dateitem-label-' + property.key"
[ngClass]="{'adf-property-label-not-editable' : isNonEditableField}"
*ngIf="showProperty() || isEditable()"
[attr.for]="'card-view-dateitem-' + property.key">
{{ property.label | translate }}
</label>
<div class="adf-property-value adf-property-value-padding-top">
<div class="adf-property-value adf-property-value-padding-top"
[ngClass]="{'adf-property-dateitem-edit-mode' : editable, 'adf-property-label-not-editable' : isNonEditableField, 'adf-empty-field' : !showProperty()}">
<span *ngIf="!isEditable() && !property.multivalued"
[attr.data-automation-id]="'card-' + property.type + '-value-' + property.key">
<span *ngIf="showProperty()"
@@ -98,3 +100,4 @@
</mat-form-field>
</div>
</div>
<mat-divider *ngIf="showDivider"></mat-divider>
@@ -11,6 +11,23 @@
float: right;
}
&-property-label {
color: var(--theme-metadata-property-panel-text-color);
display: flex;
padding: 6px 0;
line-height: 20px;
}
&-property-dateitem-edit-mode {
background-color: var(--theme-metadata-buttons-background-color);
border-radius: 6px;
padding-left: 12px;
.mat-select-value {
color: var(--theme-metadata-property-panel-text-color);
}
}
&-dateitem-chip-list-container.adf-property-field {
margin-bottom: -7px !important;
border-bottom: 0;
@@ -232,6 +232,45 @@ describe('CardViewDateItemComponent', () => {
expect(clipboardService.copyContentToClipboard).toHaveBeenCalledWith('Jul 10, 2017', 'CORE.METADATA.ACCESSIBILITY.COPY_TO_CLIPBOARD_MESSAGE');
});
it('should not show divider when editable is true', () => {
component.editable = true;
spyOn(component, 'showProperty').and.returnValue(true); // Mock showProperty result
const result = component.showDivider;
expect(result).toBe(false);
});
it('should not show divider when showProperty returns false', () => {
component.editable = false;
spyOn(component, 'showProperty').and.returnValue(false);
const result = component.showDivider;
expect(result).toBe(false);
});
it('should show divider when editable is false', () => {
component.editable = false;
spyOn(component, 'showProperty').and.returnValue(true);
const result = component.showDivider;
expect(result).toBe(true);
});
it('should return true for isNonEditableField when editable is true and property.editable is false', () => {
component.editable = true;
component.property.editable = false;
const result = component.isNonEditableField;
expect(result).toBe(true);
});
it('should return false for isNonEditableField when editable is false and property.editable is true', () => {
component.editable = false;
component.property.editable = true;
const result = component.isNonEditableField;
expect(result).toBe(false);
});
describe('clear icon', () => {
it('should render the clear icon in case of displayClearAction:true', () => {
component.editable = true;
@@ -154,4 +154,12 @@ export class CardViewDateItemComponent extends BaseCardView<CardViewDateItemMode
update() {
this.cardViewUpdateService.update({ ...this.property } as CardViewDateItemModel, this.property.value);
}
get showDivider(): boolean {
return !this.editable && this.showProperty();
}
get isNonEditableField(): boolean {
return this.editable && !this.property.editable;
}
}
@@ -2,6 +2,7 @@
<div
[attr.data-automation-id]="'card-select-label-' + property.key"
class="adf-property-label"
[ngClass]="{'adf-property-label-not-editable': isNonEditableField, 'adf-property-editable-property' : isEditable()}"
>{{ property.label | translate }}</div>
<div class="adf-property-field">
<div
@@ -15,6 +16,7 @@
<mat-select
[(value)]="value"
panelClass="adf-select-filter"
[ngClass]="{'adf-property-select-edit-mode' : isEditable(),'adf-property-normal-mode':!isEditable(),'adf-property-label-not-editable' : isNonEditableField}"
(selectionChange)="onChange($event)"
data-automation-class="select-box"
[aria-label]="property.label | translate"
@@ -2,14 +2,12 @@
width: 100%;
}
.adf-select-item-padding {
padding-top: 6px;
padding-bottom: 20px;
.adf-select-item-padding-editable {
padding: 6px 0;
}
.adf-select-item-padding-editable {
padding-top: 6px;
padding-bottom: 6px;
.adf-property-editable-property {
color: var(--theme-metadata-property-panel-title-color);
}
.adf-select-filter-input {
@@ -251,4 +251,22 @@ describe('CardViewSelectItemComponent', () => {
expect(filterInput).not.toBe(null);
});
});
it('should return true for isNonEditableField when editable is true and property.editable is false', () => {
component.editable = true;
component.property.editable = false;
const result = component.isNonEditableField;
expect(result).toBe(true);
});
it('should return false for isNonEditableField when editable is false and property.editable is true', () => {
component.editable = false;
component.property.editable = true;
const result = component.isNonEditableField;
expect(result).toBe(false);
});
});
@@ -109,4 +109,8 @@ export class CardViewSelectItemComponent extends BaseCardView<CardViewSelectItem
private get optionsLimit(): number {
return this.appConfig.get<number>('content-metadata.selectFilterLimit', CardViewSelectItemComponent.HIDE_FILTER_LIMIT);
}
get isNonEditableField(): boolean {
return this.editable && !this.property.editable;
}
}
@@ -1,15 +1,17 @@
<div [ngSwitch]="templateType">
<div *ngSwitchDefault>
<mat-form-field class="adf-property-field adf-card-textitem-field"
[ngClass]="{ 'adf-property-read-only': !isEditable, 'adf-property-field-has-error mat-form-field-invalid': isEditable && hasErrors }"
[floatLabel]="'never'"
[ngClass]="{ 'adf-property-read-only': !isEditable, 'adf-property-editable-property': isEditable, 'adf-property-field-has-error mat-form-field-invalid': isEditable && hasErrors }"
[floatLabel]="'always'"
appearance="standard">
<mat-label *ngIf="showProperty || isEditable" [attr.data-automation-id]="'card-textitem-label-' + property.key" class="adf-property-label">
<mat-label *ngIf="showProperty || isEditable" [attr.data-automation-id]="'card-textitem-label-' + property.key" class="adf-property-label"
[ngClass]="{'adf-property-label-not-editable' : isNonEditableField}">
{{ property.label | translate }}
</mat-label>
<input matInput
*ngIf="!property.multiline"
class="adf-property-value"
[ngClass]="{'adf-property-edit-mode' : editable,'adf-property-normal-mode':!editable, 'adf-property-label-not-editable' : isNonEditableField}"
title="{{property.label | translate }}"
[placeholder]="property.default"
[attr.aria-label]="property.label | translate"
@@ -27,34 +29,21 @@
[cdkAutosizeMaxRows]="1"
[cdkAutosizeMaxRows]="5"
class="adf-property-value"
[ngClass]="{'adf-property-edit-mode' : editable,'adf-property-normal-mode':!editable, 'adf-property-label-not-editable' : isNonEditableField}"
[placeholder]="property.default"
[attr.aria-label]="property.label | translate"
[formControl]="textInput"
[attr.data-automation-id]="'card-textitem-value-' + property.key">
</textarea>
<button
*ngIf="isEditable"
matSuffix
class="adf-textitem-clear-icon"
[attr.aria-label]="'CORE.METADATA.ACTIONS.CLEAR' | translate"
(click)="clearValue()">
<mat-icon>cancel</mat-icon>
</button>
<button
*ngIf="isEditable"
matSuffix
class="adf-textitem-edit-icon"
[attr.title]="'CORE.METADATA.ACTIONS.EDIT' | translate">
<mat-icon>mode_edit</mat-icon>
</button>
</mat-form-field>
<mat-divider *ngIf="showProperty && !editable"></mat-divider>
</div>
<div *ngSwitchCase="'chipsTemplate'"
class="adf-property-field adf-textitem-chip-list-container">
<mat-label *ngIf="showLabelForChips" [attr.data-automation-id]="'card-textitem-label-' + property.key" class="adf-property-label">
<mat-label *ngIf="showLabelForChips" [attr.data-automation-id]="'card-textitem-label-' + property.key" class="adf-property-label"
[ngClass]="{'adf-property-label-not-editable' : isNonEditableField}">
{{ property.label | translate }}
</mat-label>
<mat-chip-list #chipList
@@ -74,6 +63,7 @@
[floatLabel]="'never'">
<input matInput
class="adf-property-value"
[ngClass]="{'adf-property-edit-mode' : editable,'adf-property-normal-mode':!editable, 'adf-property-label-not-editable' : isNonEditableField}"
title="{{property.label | translate }}"
[placeholder]="editedValue ? '' : property.default | translate"
[attr.aria-label]="property.label | translate"
@@ -81,9 +71,8 @@
[matChipInputAddOnBlur]="true"
(matChipInputTokenEnd)="addValueToList($event)"
[attr.data-automation-id]="'card-textitem-editchipinput-' + property.key">
<mat-icon matSuffix
class="adf-textitem-edit-icon">mode_edit</mat-icon>
</mat-form-field>
<mat-divider *ngIf="showDivider"></mat-divider>
</div>
<div *ngSwitchCase="'clickableTemplate'"
@@ -94,14 +83,15 @@
(click)="clicked()">
<mat-form-field class="adf-property-field adf-card-textitem-field" appearance="standard"
[floatLabel]="'never'">
<mat-label *ngIf="showProperty || isEditable" [attr.data-automation-id]="'card-textitem-label-' + property.key" class="adf-property-label">
<mat-label *ngIf="showProperty || isEditable" [attr.data-automation-id]="'card-textitem-label-' + property.key" class="adf-property-label"
[ngClass]="{'adf-property-label-not-editable' : isNonEditableField}">
{{ property.label | translate }}
</mat-label>
<input matInput
[type]=property.inputType
class="adf-property-value"
title="{{property.label | translate }}"
[ngClass]="{ 'adf-textitem-clickable-value': !isEditable }"
[ngClass]="{'adf-textitem-clickable-value': !isEditable,'adf-property-edit-mode' : editable,'adf-property-normal-mode':!editable, 'adf-property-label-not-editable' : isNonEditableField}"
[placeholder]="property.default"
[attr.aria-label]="property.label | translate"
[(ngModel)]="editedValue"
@@ -1,4 +1,8 @@
.adf {
&-property-field .mat-form-field-underline {
display: none;
}
&-textitem-edit-icon.mat-icon {
font-size: var(--theme-subheading-2-font-size);
width: 16px;
@@ -185,24 +185,6 @@ describe('CardViewTextItemComponent', () => {
});
it('should render the edit icon in case of editable:true', () => {
component.editable = true;
component.property.editable = true;
fixture.detectChanges();
const editIcon = fixture.debugElement.query(By.css('.adf-textitem-edit-icon'));
expect(editIcon).not.toBeNull('Edit icon should be shown');
});
it('should NOT render the edit icon in case of editable:false', async () => {
component.editable = false;
fixture.detectChanges();
await fixture.whenStable();
const editIcon = fixture.debugElement.query(By.css('.adf-textitem-edit-icon'));
expect(editIcon).toBeNull('Edit icon should NOT be shown');
});
it('should NOT render the picker and toggle in case of editable:true but (general) editable:false', async () => {
component.editable = false;
component.property.editable = true;
@@ -314,6 +296,51 @@ describe('CardViewTextItemComponent', () => {
const labelElement = fixture.debugElement.query(By.css(`.adf-property-label`));
expect(labelElement).toBeNull();
});
it('should not show divider when editable is true', () => {
component.editable = true;
spyOnProperty(component, 'showProperty', 'get').and.returnValue(true);
const result = component.showDivider;
expect(result).toBe(false);
});
it('should not show divider when showProperty returns false', () => {
component.editable = false;
spyOnProperty(component, 'showProperty', 'get').and.returnValue(false);
const result = component.showDivider;
expect(result).toBe(false);
});
it('should show divider when editable is false', () => {
component.editable = false;
spyOnProperty(component, 'showProperty', 'get').and.returnValue(true);
const result = component.showDivider;
expect(result).toBe(true);
});
it('should return true for isNonEditableField when editable is true and property.editable is false', () => {
component.editable = true;
component.property.editable = false;
const result = component.isNonEditableField;
expect(result).toBe(true);
});
it('should return false for isNonEditableField when editable is false and property.editable is true', () => {
component.editable = false;
component.property.editable = true;
const result = component.isNonEditableField;
expect(result).toBe(false);
});
});
describe('clickable', () => {
@@ -493,26 +520,6 @@ describe('CardViewTextItemComponent', () => {
expect(clipboardService.copyContentToClipboard).toHaveBeenCalledWith('myValueToCopy', 'CORE.METADATA.ACCESSIBILITY.COPY_TO_CLIPBOARD_MESSAGE');
});
it('should clear value when clear value icon is clicked', async () => {
spyOn(component, 'update');
component.property.value = 'testValue';
component.property.icon = 'FAKE_ICON';
component.property.clickable = true;
component.property.editable = true;
component.editable = true;
component.property.isValid = () => true;
fixture.detectChanges();
await fixture.whenStable();
fixture.detectChanges();
const clickEl = fixture.debugElement.query(By.css(`.adf-textitem-clear-icon`));
clickEl.triggerEventHandler('click', new MouseEvent('click'));
fixture.detectChanges();
const elementValue = fixture.debugElement.query(By.css(`[data-automation-id="card-textitem-value-${component.property.key}"]`));
expect(elementValue.nativeElement.textContent).toEqual('');
expect(component.update).toHaveBeenCalled();
});
});
describe('Update', () => {
@@ -232,4 +232,12 @@ export class CardViewTextItemComponent extends BaseCardView<CardViewTextItemMode
get showLabelForChips(): boolean {
return this.displayLabelForChips;
}
get showDivider(): boolean {
return !this.editable && this.showProperty;
}
get isNonEditableField(): boolean {
return this.editable && !this.property.editable;
}
}
@@ -6,23 +6,99 @@
border-radius: var(--adf-card-view-border-radius);
.adf-property {
margin-bottom: 20px;
.adf-property-value-padding-top {
margin-top: 6px;
display: flex;
flex-direction: column;
padding: 6px 0;
line-height: 20px;
}
.adf-empty-field {
padding: 0;
}
.adf-property-label-not-editable {
color: var(--theme-metadata-property-panel-label-color);
.mat-tooltip-trigger {
padding-left: 12px;
}
}
.adf-property-editable-property {
.adf-property-label {
color: var(--theme-metadata-property-panel-title-color);
}
}
.adf-property-field {
width: 100%;
margin-bottom: -25px;
.mat-input-element {
text-overflow: ellipsis;
}
.mat-form-field-infix {
display: flex;
border-top-width: 0;
padding: 0;
}
.mat-form-field-flex {
padding-top: 0;
}
.mat-form-field-wrapper {
padding-bottom: 0;
}
.mat-form-field-label {
margin-top: 6px;
padding: 6px 0;
justify-content: center;
display: flex;
flex-direction: column;
height: 20px;
transform: translateY(-1.34373em) scale(1);
}
.mat-form-field-label-wrapper {
padding-top: 0;
top: -1px;
}
.mat-input-element.adf-property-normal-mode {
color: var(--theme-metadata-property-panel-title-color);
margin-top: 32px;
padding: 6px 0;
line-height: 20px;
}
.mat-input-element.adf-property-edit-mode {
padding: 6px 0 6px 12px;
margin-top: 32px;
line-height: 20px;
background-color: var(--theme-metadata-buttons-background-color);
color: var(--theme-metadata-property-panel-text-color);
border-radius: 6px;
}
.mat-input-element.adf-property-label-not-editable {
color: var(--theme-metadata-property-panel-label-color);
}
.mat-select.adf-property-select-edit-mode {
padding: 6px 0 6px 12px;
margin-top: 0;
line-height: 20px;
background-color: var(--theme-metadata-buttons-background-color);
border-radius: 6px;
.mat-select-min-line {
color: var(--theme-metadata-property-panel-text-color);
}
}
}
}
}
+1 -5
View File
@@ -193,7 +193,7 @@
},
"METADATA": {
"BASIC": {
"HEADER": "Properties",
"HEADER": "General info",
"NAME": "Name",
"TITLE": "Title",
"DESCRIPTION": "Description",
@@ -401,10 +401,6 @@
"SIDEBAR": {
"THUMBNAILS": {
"PAGE": "Page {{ pageNum }}"
},
"METADATA": {
"MORE_INFORMATION": "More information",
"LESS_INFORMATION": "Less information"
}
},
"PDF_DIALOG": {
@@ -1,5 +1,6 @@
<div *ngIf="showHeader" class="adf-info-drawer-layout-header">
<div class="adf-info-drawer-layout-header-title">
<ng-content select="[info-drawer-node-icon]"></ng-content>
<ng-content select="[info-drawer-title]"></ng-content>
</div>
<div class="adf-info-drawer-layout-header-buttons">
@@ -14,30 +14,34 @@ $adf-info-drawer-icon-size-half: 24px !default;
overflow: auto;
width: 100%;
background-color: var(--theme-background-color);
background-color: var(--theme-card-background-color);
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.27);
& .mat-tab-labels {
padding: 0 32px;
}
& .mat-tab-label {
font-weight: bold;
height: 32px;
text-align: left;
color: var(--theme-accent-color);
text-transform: uppercase;
opacity: 1;
letter-spacing: 0.25px;
padding: 0;
margin: 0;
&-active {
color: var(--theme-primary-color);
}
}
&-header {
padding: 13px 0 0 23px;
padding: 8px 0 0 24px;
display: flex;
justify-content: space-between;
align-items: center;
justify-content: space-between;
height: 56px;
margin-bottom: 40px;
&-buttons {
padding-right: 18px;
mat-icon {
cursor: pointer;
@@ -45,18 +49,20 @@ $adf-info-drawer-icon-size-half: 24px !default;
}
&-title {
width: 197px;
height: 32px;
display: flex;
font-size: $adf-info-drawer-layout-title-font-size;
line-height: 1.6;
letter-spacing: -0.5px;
line-height: 28px;
letter-spacing: 0.15px;
text-align: left;
align-items: start;
color: var(--adf-theme-foreground-text-color-054);
& > div {
width: 195px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
color: var(--theme-metadata-property-panel-title-color);
}
}
}
@@ -1,4 +1,6 @@
<adf-info-drawer-layout [showHeader]="showHeader">
<img class="adf-info-drawer-icon" *ngIf="nodeIcon" src="{{ nodeIcon }}" info-drawer-node-icon>
<div role="heading" aria-level="1" *ngIf="title" info-drawer-title>{{ title | translate }}</div>
<ng-content *ngIf="!title" info-drawer-title select="[info-drawer-title]"></ng-content>
@@ -1,12 +1,23 @@
.adf {
&-info-drawer {
display: block;
.adf-metadata-properties-panel {
display: block;
margin: 0;
}
.mat-tab-label {
min-width: 0;
}
& &-layout {
.adf-info-drawer-icon {
display: inline-block;
vertical-align: middle;
margin-right: 10px;
}
&-content {
padding: 0;
@@ -38,16 +49,16 @@
}
.mat-ink-bar {
height: 4px;
height: 2px;
}
.mat-tab-body {
padding: 10px;
overflow-y: hidden;
}
.mat-tab-body-content {
/* stylelint-disable */
overflow: initial;
overflow: auto;
/* stylelint-enable */
}
}
@@ -87,18 +87,19 @@ describe('InfoDrawerComponent', () => {
@Component({
template: `
<adf-info-drawer [selectedIndex]="tabIndex" title="Fake Title Custom">
<adf-info-drawer-tab label="Tab1">
</adf-info-drawer-tab>
<adf-info-drawer-tab label="Tab2">
</adf-info-drawer-tab>
<adf-info-drawer-tab label="Tab3" icon="tab-icon">
</adf-info-drawer-tab>
</adf-info-drawer>
`
<adf-info-drawer [selectedIndex]="tabIndex" [nodeIcon]="nodeIcon" title="Fake Title Custom">
<adf-info-drawer-tab label="Tab1">
</adf-info-drawer-tab>
<adf-info-drawer-tab label="Tab2">
</adf-info-drawer-tab>
<adf-info-drawer-tab label="Tab3" icon="tab-icon">
</adf-info-drawer-tab>
</adf-info-drawer>
`
})
class CustomInfoDrawerComponent extends InfoDrawerComponent {
tabIndex: number;
nodeIcon: string;
}
describe('Custom InfoDrawer', () => {
@@ -139,7 +140,7 @@ describe('Custom InfoDrawer', () => {
fixture.detectChanges();
const tab: any = fixture.debugElement.queryAll(By.css('.mat-tab-label-active'));
expect(tab.length).toBe(1);
expect(tab[0].nativeElement.innerText).toContain('TAB1');
expect(tab[0].nativeElement.innerText).toContain('Tab1');
});
it('should select the tab 2 (index 1)', () => {
@@ -147,26 +148,36 @@ describe('Custom InfoDrawer', () => {
fixture.detectChanges();
const tab: any = fixture.debugElement.queryAll(By.css('.mat-tab-label-active'));
expect(tab.length).toBe(1);
expect(tab[0].nativeElement.innerText).toContain('TAB2');
expect(tab[0].nativeElement.innerText).toContain('Tab2');
});
it('should render a tab with icon', () => {
component.tabIndex = 2;
fixture.detectChanges();
const tab: any = fixture.debugElement.queryAll(By.css('.mat-tab-label-active'));
expect(tab[0].nativeElement.innerText).not.toBe('TAB3');
expect(tab[0].nativeElement.innerText).not.toBe('Tab3');
expect(tab[0].nativeElement.innerText).toContain('tab-icon');
});
it('should render a icon with title', () => {
component.nodeIcon = '/assets/images/ft_ic_miscellaneous.svg';
fixture.detectChanges();
const icon = fixture.debugElement.queryAll(By.css('[info-drawer-node-icon]'));
const srcAttribute = icon[0].nativeElement.getAttribute('src');
expect(icon.length).toBe(1);
expect(srcAttribute).toContain('/assets/images/ft_ic_miscellaneous.svg');
});
});
@Component({
template: `
<adf-info-drawer [showHeader]="showHeader" title="Fake Visibility Info Drawer Title">
<adf-info-drawer [showHeader]="showHeader" [nodeIcon]="nodeIcon" title="Fake Visibility Info Drawer Title">
</adf-info-drawer>
`
`
})
class VisibilityInfoDrawerComponent extends InfoDrawerComponent {
showHeader: boolean;
nodeIcon: string;
}
describe('Header visibility InfoDrawer', () => {
@@ -189,18 +200,24 @@ describe('Header visibility InfoDrawer', () => {
});
it('should show info drawer header by default', () => {
component.nodeIcon = '/assets/images/ft_ic_miscellaneous.svg';
fixture.detectChanges();
const title: any = fixture.debugElement.queryAll(By.css('[info-drawer-title]'));
const icon = fixture.debugElement.queryAll(By.css('[info-drawer-node-icon]'));
const srcAttribute = icon[0].nativeElement.getAttribute('src');
expect(title.length).toBe(1);
expect(icon.length).toBe(1);
expect(srcAttribute).toContain('/assets/images/ft_ic_miscellaneous.svg');
expect(title[0].nativeElement.innerText).toBe('Fake Visibility Info Drawer Title');
expect(component.showHeader).toEqual(true);
});
it('should not show info drawer header when showHeader is false', () => {
fixture.detectChanges();
it('should not show info drawer header with icon when showHeader is false', () => {
component.showHeader = false;
fixture.detectChanges();
const title: any = fixture.debugElement.queryAll(By.css('[info-drawer-title]'));
const icon = fixture.debugElement.queryAll(By.css('[info-drawer-node-icon]'));
expect(title.length).toBe(0);
expect(icon.length).toBe(0);
});
});
@@ -47,6 +47,9 @@ export class InfoDrawerComponent {
@Input()
title: string|null = null;
@Input()
nodeIcon: string | null = null;
/** The selected index tab. */
@Input()
selectedIndex: number = 0;
@@ -45,6 +45,9 @@
--adf-identity-user-info-line-height: $adf-ref-line-height,
--adf-identity-user-info-font-size: var(--theme-adf-picture-1-font-size),
--adf-user-info-container-margin-right: $adf-ref-margin-right,
--theme-metadata-property-panel-border-color: $metadata-property-panel-border-color,
--theme-metadata-buttons-background-color: $metadata-buttons-background-color,
--theme-metadata-action-button-clear-color: $metadata-action-button-clear-color
);
// propagates SCSS variables into the CSS variables scope
@@ -27,3 +27,6 @@ $adf-ref-height: 40px;
$adf-ref-width: 40px;
$adf-ref-line-height: 40px;
$adf-ref-margin-right: 8px;
$metadata-property-panel-border-color: rgba(0, 0, 0, 0.12);
$metadata-buttons-background-color: rgba(33, 33, 33, 0.05);
$metadata-action-button-clear-color: #212328b2;