mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2026-09-16 18:13:06 +00:00
Compare commits
101
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
68f7d40fc7 | ||
|
|
45ca40cd5f | ||
|
|
9a93b74bf3 | ||
|
|
355958e75d | ||
|
|
4feef96818 | ||
|
|
3c61188ccb | ||
|
|
2c0b64d8b1 | ||
|
|
7e88ec93c0 | ||
|
|
b1b4675835 | ||
|
|
422c0341f7 | ||
|
|
145bc65223 | ||
|
|
59e5d3898d | ||
|
|
f74450bc02 | ||
|
|
5f0ef77035 | ||
|
|
a8d1b23859 | ||
|
|
515ae12a23 | ||
|
|
a294880506 | ||
|
|
4ed5d39083 | ||
|
|
4500e25062 | ||
|
|
cb6462bd59 | ||
|
|
85b4b6294b | ||
|
|
eceef0de20 | ||
|
|
c142ca1a30 | ||
|
|
3e314e74ad | ||
|
|
adb323037e | ||
|
|
caeeec1382 | ||
|
|
52b7d321e7 | ||
|
|
70b54213fd | ||
|
|
b44ff8aa0c | ||
|
|
251b325747 | ||
|
|
b9a7425047 | ||
|
|
e569260e2c | ||
|
|
ba863a4704 | ||
|
|
8139b0388c | ||
|
|
def3943940 | ||
|
|
b4b995889f | ||
|
|
7750bcb9d5 | ||
|
|
92fb997bb0 | ||
|
|
ea1ae97144 | ||
|
|
7e4ad3c6d9 | ||
|
|
baf2a7ce1f | ||
|
|
a56aff1b00 | ||
|
|
b7a71218fa | ||
|
|
af552aa0ce | ||
|
|
ee71ce5cbb | ||
|
|
2b0ead0077 | ||
|
|
ec2219fd9b | ||
|
|
498bc52df5 | ||
|
|
b1f6ce331c | ||
|
|
d404d67f9b | ||
|
|
47199de899 | ||
|
|
483faa06df | ||
|
|
5d1272b9d2 | ||
|
|
52acd0a216 | ||
|
|
a5c4fb9ecd | ||
|
|
2223e46979 | ||
|
|
4b76e5beb9 | ||
|
|
b1da556451 | ||
|
|
9723f9f480 | ||
|
|
aa6b527a5d | ||
|
|
6969f76f1d | ||
|
|
1c48220b4c | ||
|
|
46b6e1c44b | ||
|
|
24cbdb1ae9 | ||
|
|
2ac50ad3cc | ||
|
|
e1d866a3ab | ||
|
|
787889f9d4 | ||
|
|
dc9693a4ad | ||
|
|
c5bee87508 | ||
|
|
4f14cd64bb | ||
|
|
8a8718b6a4 | ||
|
|
1bea34b940 | ||
|
|
10774c064c | ||
|
|
85a644dcc8 | ||
|
|
2c4e815161 | ||
|
|
ac9e8dd6db | ||
|
|
ff78d6495d | ||
|
|
1a5a23184e | ||
|
|
7147ba492f | ||
|
|
95d8d66c75 | ||
|
|
33b32b5409 | ||
|
|
e8e9531141 | ||
|
|
08f406c2c1 | ||
|
|
e412f395af | ||
|
|
1f35ddd033 | ||
|
|
e8394d8648 | ||
|
|
99fd7519fb | ||
|
|
001d0160d8 | ||
|
|
66af36351b | ||
|
|
5812988710 | ||
|
|
a03aba0a2a | ||
|
|
71bb551deb | ||
|
|
bdb7b11ecd | ||
|
|
c005237498 | ||
|
|
c629c1b3a4 | ||
|
|
d1307c3cfc | ||
|
|
367a10c529 | ||
|
|
b0124f801c | ||
|
|
15d5d90ddc | ||
|
|
00e143f721 | ||
|
|
de94af8f36 |
@@ -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.
|
||||

|
||||
|
||||
@@ -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 |
@@ -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:
|
||||
|
||||

|
||||

|
||||
|
||||
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();
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
});
|
||||
|
||||
+18
-33
@@ -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)">
|
||||
|
||||
+26
-2
@@ -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;
|
||||
|
||||
+19
-35
@@ -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();
|
||||
|
||||
+14
-11
@@ -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);
|
||||
|
||||
-18
@@ -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>
|
||||
|
||||
-57
@@ -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();
|
||||
|
||||
|
||||
+1
-14
@@ -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);
|
||||
}
|
||||
|
||||
+24
@@ -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'
|
||||
}
|
||||
+197
-79
@@ -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>
|
||||
|
||||
+82
-27
@@ -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;
|
||||
}
|
||||
|
||||
+248
-135
@@ -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', () => {
|
||||
|
||||
+272
-49
@@ -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;
|
||||
|
||||
+6
-2
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
+4
-1
@@ -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>
|
||||
|
||||
+17
@@ -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;
|
||||
|
||||
+39
@@ -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;
|
||||
|
||||
+8
@@ -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
@@ -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"
|
||||
|
||||
+4
-6
@@ -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 {
|
||||
|
||||
+18
@@ -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);
|
||||
});
|
||||
});
|
||||
|
||||
+4
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
+14
-24
@@ -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"
|
||||
|
||||
+4
@@ -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;
|
||||
|
||||
+45
-38
@@ -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', () => {
|
||||
|
||||
+8
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user