diff --git a/demo-shell/src/app/components/file-view/file-view.component.html b/demo-shell/src/app/components/file-view/file-view.component.html index 1fab5fc9ae5..936ed69521d 100644 --- a/demo-shell/src/app/components/file-view/file-view.component.html +++ b/demo-shell/src/app/components/file-view/file-view.component.html @@ -29,20 +29,17 @@ - - - + +

- - + [displayEmpty]="displayEmptyMetadata" + [displayTags]="true" + [displayCategories]="true"> + diff --git a/docs/content-services/components/content-metadata-card.component.md b/docs/content-services/components/content-metadata-card.component.md index e9257a9d1e2..bf545485364 100644 --- a/docs/content-services/components/content-metadata-card.component.md +++ b/docs/content-services/components/content-metadata-card.component.md @@ -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)`` | Emitted when content's editable state is changed. | - ## Details The component shows metadata related to a given node. It uses the @@ -444,3 +438,4 @@ When the list of values is too long, the options selection panel will be enhance ## Custom metadata panels If there is a need to display some custom node properties that require additional UI or data to be fetched you can define custom metadata panels for the metadata component. After creating custom component to be displayed it has to be registered in [Extension Service](../../extensions/services/extension.service.md). Both `panelTitle` and `component` properties have to be defined. +![Chips for multi value properties](../../docassets/images/adf-search-select.png) diff --git a/docs/content-services/components/tags-creator.component.md b/docs/content-services/components/tags-creator.component.md index 30d59198332..d4303b954a0 100644 --- a/docs/content-services/components/tags-creator.component.md +++ b/docs/content-services/components/tags-creator.component.md @@ -13,7 +13,7 @@ Allows to create multiple tags. That component contains input and two lists. Top ```html @@ -35,5 +35,4 @@ Allows to create multiple tags. That component contains input and two lists. Top | Name | Type | Description | | ---- | ---- | ----------- | | existingTagsPanelVisibilityChange | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`` | Emitted when bottom list is showing or hiding. | -| tagNameControlVisibleChange | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`` | Emitted when input is showing or hiding. | | tagsChange | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`` | Emitted when tags in top list are changed. | diff --git a/docs/docassets/images/UI_property_panel.png b/docs/docassets/images/UI_property_panel.png new file mode 100644 index 00000000000..7c236b27c88 Binary files /dev/null and b/docs/docassets/images/UI_property_panel.png differ diff --git a/docs/tutorials/content-metadata-component.md b/docs/tutorials/content-metadata-component.md index 2f77230ba40..1243e911e2e 100644 --- a/docs/tutorials/content-metadata-component.md +++ b/docs/tutorials/content-metadata-component.md @@ -98,12 +98,18 @@ Now that the `my-preset` configuration is defined, let's use it in a view of the ``` +### Properties + +| Name | Type | Default value | Description | +| ---- | ---- | ------------- | ----------- | +| readOnly | `boolean` | false | (optional) This flag sets the metadata in read only mode preventing changes. | + ### Viewing the result -After saving the html file, open the ADF app in a browser and dive into the `Personal Files > Sites > swsdp` folder of the Alfresco's repository. Once there, select the `documentLibrary` folder (one click only) and click on the view details icon (the `i` on the top right). Scrolling down the metadata tab on the right, click on the `More information` item at the bottom. Once clicked, you will see two different groups: `Properties` (already there by default) and `This is my preset`. Click on `This is my preset` to show the properties related. +After saving the html file, open the ADF app in a browser and dive into the `Personal Files > Sites > swsdp` folder of the Alfresco's repository. Once there, select the `documentLibrary` folder (one click only) and click on the view details icon (the `menu_open icon` on the top right). Scrolling down the metadata tab on the right. You will see different panels: `Properties` (already there by default), `Tags`, `Categories` and `grouped properties`. In the following screenshot you can see how the result should look: -![content_metadata_preset](../docassets/images/content_metadata_preset.png) +![UI_Property_Panel](../docassets/images/UI_property_panel.png) To check it out, double click on the `documentLibrary` folder and select (with one click) the `Presentations` folder. You should see the `This is my preset` group disappear from the metadata panel, because the node doesn't have the `st:siteContainer` aspect. diff --git a/e2e/content-services/components/viewer-content-services-component.e2e.ts b/e2e/content-services/components/viewer-content-services-component.e2e.ts index f38cdb292c4..13c314891ed 100644 --- a/e2e/content-services/components/viewer-content-services-component.e2e.ts +++ b/e2e/content-services/components/viewer-content-services-component.e2e.ts @@ -426,9 +426,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(); } }); diff --git a/e2e/content-services/metadata/metadata-content-type.e2e.ts b/e2e/content-services/metadata/metadata-content-type.e2e.ts index c3647fc851b..29c84300e4a 100644 --- a/e2e/content-services/metadata/metadata-content-type.e2e.ts +++ b/e2e/content-services/metadata/metadata-content-type.e2e.ts @@ -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'); + 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,9 +139,9 @@ 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'); + await expect(await viewerPage.getActiveTab()).toEqual('Properties'); const customType = (await metadataViewPage.hasContentType(type.title)) || (await metadataViewPage.hasContentType(`${model.namespacePrefix}:${type.name}`)); await expect(customType).toBe(true, 'Custom type not found'); await expect(await metadataViewPage.getPropertyText(`properties.${model.namespacePrefix}:${property.name}`)).toContain(property.defaultValue); @@ -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'); + 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,9 +178,9 @@ 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'); + await expect(await viewerPage.getActiveTab()).toEqual('Properties'); defaultType = (await metadataViewPage.hasContentType('Content')) || (await metadataViewPage.hasContentType('cm:content')); await expect(defaultType).toBe(true, 'Content type not found'); await viewerPage.clickCloseButton(); diff --git a/e2e/content-services/metadata/metadata-permissions.e2e.ts b/e2e/content-services/metadata/metadata-permissions.e2e.ts index 6884b1b8b02..fcbcb506b85 100644 --- a/e2e/content-services/metadata/metadata-permissions.e2e.ts +++ b/e2e/content-services/metadata/metadata-permissions.e2e.ts @@ -15,14 +15,7 @@ * limitations under the License. */ -import { createApiService, - LoginPage, - StringUtil, - UploadActions, - UserModel, - UsersActions, - ViewerPage -} from '@alfresco/adf-testing'; +import { createApiService, LoginPage, StringUtil, UploadActions, UserModel, UsersActions, ViewerPage } from '@alfresco/adf-testing'; import { MetadataViewPage } from '../../core/pages/metadata-view.page'; import { NavigationBarPage } from '../../core/pages/navigation-bar.page'; import { FileModel } from '../../models/ACS/file.model'; @@ -31,20 +24,6 @@ import CONSTANTS = require('../../util/constants'); import { SitesApi } from '@alfresco/js-api'; describe('permissions', () => { - - const METADATA = { - DATA_FORMAT: 'mmm dd yyyy', - 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', - EDIT_BUTTON_TOOLTIP: 'Edit' - }; - const loginPage = new LoginPage(); const viewerPage = new ViewerPage(); const metadataViewPage = new MetadataViewPage(); @@ -130,9 +109,7 @@ describe('permissions', () => { await metadataViewPage.clickOnPropertiesTab(); await metadataViewPage.editIconIsDisplayed(); - await expect(await viewerPage.getActiveTab()).toEqual(METADATA.PROPERTY_TAB); - - await metadataViewPage.clickOnInformationButton(); + await expect(await viewerPage.getActiveTab()).toEqual('Properties'); await metadataViewPage.clickMetadataGroup('EXIF'); @@ -150,9 +127,7 @@ describe('permissions', () => { await metadataViewPage.clickOnPropertiesTab(); await metadataViewPage.editIconIsDisplayed(); - await expect(await viewerPage.getActiveTab()).toEqual(METADATA.PROPERTY_TAB); - - await metadataViewPage.clickOnInformationButton(); + await expect(await viewerPage.getActiveTab()).toEqual('Properties'); await metadataViewPage.clickMetadataGroup('EXIF'); diff --git a/e2e/content-services/metadata/metadata-properties.e2e.ts b/e2e/content-services/metadata/metadata-properties.e2e.ts index 502e7721a8b..629cd937203 100644 --- a/e2e/content-services/metadata/metadata-properties.e2e.ts +++ b/e2e/content-services/metadata/metadata-properties.e2e.ts @@ -34,13 +34,9 @@ describe('CardView Component - properties', () => { const METADATA = { DATA_FORMAT: 'mmm dd yyyy', TITLE: 'Details', - COMMENTS_TAB: 'COMMENTS', - PROPERTY_TAB: '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 () => { diff --git a/e2e/content-services/metadata/metadata-smoke-tests.e2e.ts b/e2e/content-services/metadata/metadata-smoke-tests.e2e.ts index a8586adaf69..ff66f84ecd1 100644 --- a/e2e/content-services/metadata/metadata-smoke-tests.e2e.ts +++ b/e2e/content-services/metadata/metadata-smoke-tests.e2e.ts @@ -36,14 +36,9 @@ describe('Metadata component', () => { const METADATA = { DATA_FORMAT: 'PP', 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', - EDIT_BUTTON_TOOLTIP: 'Edit' + COMMENTS_TAB: 'Comments', + PROPERTY_TAB: 'Properties', + DEFAULT_ASPECT: 'General info' }; const loginPage = new LoginPage(); @@ -103,7 +98,7 @@ describe('Metadata component', () => { await contentServicesPage.waitForTableBody(); }); - it("[C245652] Should be possible to display a file's properties", async () => { + it('[C245652] Should be possible to display a file properties', async () => { await viewerPage.clickInfoButton(); await viewerPage.checkInfoSideBarIsDisplayed(); await metadataViewPage.clickOnPropertiesTab(); @@ -129,36 +124,12 @@ describe('Metadata component', () => { expect(modifiedDate).toEqual(format(new Date(pngFileModel.createdAt), METADATA.DATA_FORMAT), pngFileModel.createdAt); expect(mimeTypeName).toEqual(pngFileModel.getContent().mimeTypeName); expect(size).toEqual(pngFileModel.getContent().getSizeInBytes()); - - await metadataViewPage.editIconIsDisplayed(); - await metadataViewPage.informationButtonIsDisplayed(); - - const informationButtonText = await metadataViewPage.getInformationButtonText(); - const informationIconText = await metadataViewPage.getInformationIconText(); - - expect(informationButtonText).toEqual(METADATA.MORE_INFO_BUTTON); - expect(informationIconText).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(); - - const informationButtonText = await metadataViewPage.getInformationButtonText(); - const informationIconText = await metadataViewPage.getInformationIconText(); - - expect(informationButtonText).toEqual(METADATA.LESS_INFO_BUTTON); - expect(informationIconText).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(); @@ -166,25 +137,17 @@ describe('Metadata component', () => { await expect(await viewerPage.getActiveTab()).toEqual(METADATA.COMMENTS_TAB); await metadataViewPage.clickOnPropertiesTab(); await expect(await viewerPage.getActiveTab()).toEqual(METADATA.PROPERTY_TAB); - await expect(await metadataViewPage.getEditIconTooltip()).toEqual(METADATA.EDIT_BUTTON_TOOLTIP); }); it('[C245654] Should be possible edit the basic Metadata Info of a Document', async () => { 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(); @@ -192,14 +155,15 @@ describe('Metadata component', () => { 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(); @@ -208,32 +172,26 @@ 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 () => { await viewerPage.clickInfoButton(); await viewerPage.checkInfoSideBarIsDisplayed(); await metadataViewPage.clickOnPropertiesTab(); - await metadataViewPage.editIconIsDisplayed(); await expect(await viewerPage.getActiveTab()).toEqual(METADATA.PROPERTY_TAB); - - await metadataViewPage.clickOnInformationButton(); - await metadataViewPage.clickMetadataGroup('EXIF'); - await metadataViewPage.editIconClick(); await metadataViewPage.enterPropertyText('properties.exif:software', 'test custom text software'); @@ -255,14 +213,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(); diff --git a/e2e/core/pages/metadata-view.page.ts b/e2e/core/pages/metadata-view.page.ts index 03c67b4724b..8815076cbe8 100644 --- a/e2e/core/pages/metadata-view.page.ts +++ b/e2e/core/pages/metadata-view.page.ts @@ -32,9 +32,7 @@ export class MetadataViewPage { description = $(`span[data-automation-id='card-textitem-value-properties.cm:description']`); author = $(`[data-automation-id='card-textitem-value-properties.cm:author']`); 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`); @@ -43,6 +41,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 => @@ -102,28 +101,17 @@ export class MetadataViewPage { await BrowserVisibility.waitUntilElementIsNotVisible(this.editIcon); } - async editIconClick(): Promise { - await BrowserActions.clickExecuteScript('button[data-automation-id="meta-data-card-toggle-edit"]'); - } - - async informationButtonIsDisplayed(): Promise { - await BrowserVisibility.waitUntilElementIsClickable(this.informationButton); - } - - async informationButtonIsNotDisplayed(): Promise { - await BrowserVisibility.waitUntilElementIsNotVisible(this.informationButton); - } - - async clickOnInformationButton(): Promise { - await BrowserActions.click(this.informationButton); + async isEditGeneralIconDisplayed(): Promise { + await BrowserVisibility.waitUntilElementIsVisible(this.editIconGeneral); } - async getInformationButtonText(): Promise { - return BrowserActions.getText(this.informationSpan); + async editIconClick(): Promise { + await BrowserActions.clickExecuteScript('button[data-automation-id="meta-data-card-toggle-edit"]'); } - async getInformationIconText(): Promise { - return BrowserActions.getText(this.informationIcon); + async clickEditIconGeneral(): Promise { + await BrowserVisibility.waitUntilElementIsVisible(this.editIconGeneral); + await BrowserActions.click(this.editIconGeneral); } async clickOnPropertiesTab(): Promise { @@ -137,11 +125,6 @@ export class MetadataViewPage { return BrowserActions.getAttribute(this.editIcon, 'title'); } - async editPropertyIconIsDisplayed(propertyName: string) { - const editPropertyIcon = $('[data-automation-id="header-' + propertyName + '"] .adf-textitem-edit-icon'); - await BrowserVisibility.waitUntilElementIsPresent(editPropertyIcon); - } - async enterPropertyText(propertyName: string, text: string | number): Promise { const textField = $('input[data-automation-id="card-textitem-value-' + propertyName + '"]'); await BrowserActions.clearSendKeys(textField, text.toString()); @@ -161,11 +144,6 @@ export class MetadataViewPage { return BrowserActions.getInputValue(textField); } - async getPropertyIconTooltip(propertyName: string): Promise { - const editPropertyIcon = $('[data-automation-id="header-' + propertyName + '"] .adf-textitem-edit-icon'); - return BrowserActions.getAttribute(editPropertyIcon, 'title'); - } - async clickMetadataGroup(groupName: string): Promise { const group = await this.getMetadataGroupLocator(groupName); await BrowserActions.click(group); @@ -286,4 +264,8 @@ export class MetadataViewPage { async clickResetMetadata(): Promise { await BrowserActions.click(this.resetMetadataButton); } + + async clickSaveGeneralMetadata(): Promise { + await BrowserActions.click(this.saveGeneralMetadataButton); + } } diff --git a/lib/content-services/src/lib/category/categories-management/categories-management.component.html b/lib/content-services/src/lib/category/categories-management/categories-management.component.html index f0ca8efe99f..1f106b31a2b 100644 --- a/lib/content-services/src/lib/category/categories-management/categories-management.component.html +++ b/lib/content-services/src/lib/category/categories-management/categories-management.component.html @@ -1,10 +1,16 @@

-

- {{ noCategoriesMsg | translate }} -

-
+
+ + {{ categoryNameErrorMessageKey | translate }} +
+
@@ -21,33 +26,9 @@
-
- - search - - {{ 'CATEGORIES_MANAGEMENT.NAME' | translate }} - - - {{ categoryNameErrorMessageKey | translate }} - - -
+

+ {{ noCategoriesMsg | translate }} +

diff --git a/lib/content-services/src/lib/category/categories-management/categories-management.component.scss b/lib/content-services/src/lib/category/categories-management/categories-management.component.scss index 31911029be8..ffa69523b5e 100644 --- a/lib/content-services/src/lib/category/categories-management/categories-management.component.scss +++ b/lib/content-services/src/lib/category/categories-management/categories-management.component.scss @@ -1,15 +1,17 @@ .adf-categories-management { + padding-top: 12px; + .adf-category-name-field { - display: flex; justify-content: space-between; width: 100%; + color: var(--adf-metadata-property-panel-text-color); + background: var(--adf-metadata-buttons-background-color); + height: 32px; + border-radius: 12px; + align-items: center; - mat-form-field { - width: 100%; - } - - .adf-btn-padded { - margin-right: -14px; + input { + padding: 7px 8px; } } @@ -18,10 +20,6 @@ justify-content: space-between; align-items: center; word-break: break-word; - - .adf-btn-padded { - margin-right: -14px; - } } .adf-categories-padded { @@ -31,11 +29,14 @@ [hidden] { visibility: hidden; } + + .adf-no-categories-message { + 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; diff --git a/lib/content-services/src/lib/category/categories-management/categories-management.component.spec.ts b/lib/content-services/src/lib/category/categories-management/categories-management.component.spec.ts index e7e88da813a..25e41c89590 100644 --- a/lib/content-services/src/lib/category/categories-management/categories-management.component.spec.ts +++ b/lib/content-services/src/lib/category/categories-management/categories-management.component.spec.ts @@ -223,44 +223,19 @@ 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'); + describe('showEmptyCategoryMessage', () => { + it('should return true when categories empty and category in non editable state', () => { + component.categories = []; + component.categoryNameControlVisible = false; + expect(component.showEmptyCategoryMessage).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(); - - 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('Spinner', () => { @@ -472,13 +447,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(); diff --git a/lib/content-services/src/lib/category/categories-management/categories-management.component.ts b/lib/content-services/src/lib/category/categories-management/categories-management.component.ts index 51d4683fe13..e5fd1d24b5c 100644 --- a/lib/content-services/src/lib/category/categories-management/categories-management.component.ts +++ b/lib/content-services/src/lib/category/categories-management/categories-management.component.ts @@ -181,6 +181,13 @@ export class CategoriesManagementComponent implements OnInit, OnDestroy { return this._categoryNameControl; } + /* + * Returns `true` if categories empty and category panel non editable state, otherwise `false` + */ + get showEmptyCategoryMessage(): boolean { + return this.categories.length === 0 && !this.categoryNameControlVisible; + } + get existingCategories(): Category[] { return this._existingCategories; } @@ -205,16 +212,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 +220,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); diff --git a/lib/content-services/src/lib/common/services/content.service.spec.ts b/lib/content-services/src/lib/common/services/content.service.spec.ts index 74e38254125..7df05204ccf 100644 --- a/lib/content-services/src/lib/common/services/content.service.spec.ts +++ b/lib/content-services/src/lib/common/services/content.service.spec.ts @@ -129,4 +129,52 @@ describe('ContentService', () => { expect(contentService.hasPermissions(permissionNode, 'manager')).toBeTruthy(); }); }); + + describe('Node Icons', () => { + let node: Node; + + node = { + isFolder: true, + isFile: false, + createdByUser: { id: 'admin', displayName: 'Administrator' }, + modifiedAt: new Date('2017-05-24T15:08:55.640Z'), + nodeType: 'cm:content', + content: { + mimeType: 'application/rtf', + mimeTypeName: 'Rich Text Format', + sizeInBytes: 14530 + }, + createdAt: new Date('2017-05-24T15:08:55.640Z'), + modifiedByUser: { id: 'admin', displayName: 'Administrator' }, + name: 'b_txt_file.rtf', + id: 'test node 1', + aspectNames: [''] + } as Node; + + it('should resolve folder icon', () => { + expect(contentService.getNodeIcon(node)).toContain('assets/images/ft_ic_folder.svg'); + }); + + it('should resolve link folder icon', () => { + node.nodeType = 'app:folderlink'; + expect(contentService.getNodeIcon(node)).toContain('assets/images/ft_ic_folder_shortcut_link.svg'); + }); + + it('should resolve smart folder icon', () => { + node.aspectNames = ['smf:customConfigSmartFolder']; + expect(contentService.getNodeIcon(node)).toContain('assets/images/ft_ic_smart_folder.svg'); + }); + + it('should resolve file icon for content type', () => { + node.isFolder = false; + node.isFile = true; + expect(contentService.getNodeIcon(node)).toContain('assets/images/ft_ic_ms_word.svg'); + }); + + it('should resolve fallback file icon for unknown node', () => { + node.isFolder = false; + node.isFile = false; + expect(contentService.getNodeIcon(node)).toContain('assets/images/ft_ic_miscellaneous'); + }); + }); }); diff --git a/lib/content-services/src/lib/common/services/content.service.ts b/lib/content-services/src/lib/common/services/content.service.ts index 379e360bc19..d55109a38db 100644 --- a/lib/content-services/src/lib/common/services/content.service.ts +++ b/lib/content-services/src/lib/common/services/content.service.ts @@ -18,7 +18,7 @@ import { Injectable } from '@angular/core'; import { ContentApi, Node, NodeEntry } from '@alfresco/js-api'; import { Subject } from 'rxjs'; -import { AlfrescoApiService, AuthenticationService } from '@alfresco/adf-core'; +import { AlfrescoApiService, AuthenticationService, ThumbnailService } from '@alfresco/adf-core'; import { PermissionsEnum } from '../models/permissions.enum'; import { AllowableOperationsEnum } from '../models/allowable-operations.enum'; @@ -43,7 +43,7 @@ export class ContentService { return this._contentApi; } - constructor(public authService: AuthenticationService, public apiService: AlfrescoApiService) {} + constructor(public authService: AuthenticationService, public apiService: AlfrescoApiService, private thumbnailService?: ThumbnailService) {} /** * Gets a content URL for the given node. @@ -145,4 +145,48 @@ export class ContentService { return hasAllowableOperations; } + + getNodeIcon(node: Node): string { + if (node?.isFolder) { + return this.getFolderIcon(node); + } + if (node?.isFile) { + return this.thumbnailService.getMimeTypeIcon(node?.content?.mimeType); + } + return this.thumbnailService.getDefaultMimeTypeIcon(); + } + + private getFolderIcon(node: Node): string { + if (this.isSmartFolder(node)) { + return this.thumbnailService.getMimeTypeIcon('smartFolder'); + } else if (this.isRuleFolder(node)) { + return this.thumbnailService.getMimeTypeIcon('ruleFolder'); + } else if (this.isLinkFolder(node)) { + return this.thumbnailService.getMimeTypeIcon('linkFolder'); + } else { + return this.thumbnailService.getMimeTypeIcon('folder'); + } + } + + isSmartFolder(node: Node): boolean { + if (node) { + return this.hasAspect(node, 'smf:customConfigSmartFolder') || this.hasAspect(node, 'smf:systemConfigSmartFolder'); + } + return false; + } + + isRuleFolder(node: Node): boolean { + if (node) { + return this.hasAspect(node, 'rule:rules'); + } + return false; + } + + isLinkFolder(node: Node): boolean { + return node?.nodeType === 'app:folderlink'; + } + + private hasAspect(node: Node, aspectName: string): boolean { + return node?.aspectNames?.includes(aspectName); + } } diff --git a/lib/content-services/src/lib/content-metadata/components/content-metadata-card/content-metadata-card.component.html b/lib/content-services/src/lib/content-metadata/components/content-metadata-card/content-metadata-card.component.html index 3b9930e3b91..d49b5d688b3 100644 --- a/lib/content-services/src/lib/content-metadata/components/content-metadata-card/content-metadata-card.component.html +++ b/lib/content-services/src/lib/content-metadata/components/content-metadata-card/content-metadata-card.component.html @@ -5,7 +5,7 @@ [expanded]="expanded" [node]="node" [displayEmpty]="displayEmpty" - [editable]="editable" + [readOnly]="!editable" [multi]="multi" [displayAspect]="displayAspect" [preset]="preset" @@ -24,24 +24,6 @@ data-automation-id="meta-data-card-edit-aspect"> menu -
- diff --git a/lib/content-services/src/lib/content-metadata/components/content-metadata-card/content-metadata-card.component.spec.ts b/lib/content-services/src/lib/content-metadata/components/content-metadata-card/content-metadata-card.component.spec.ts index 8d8416f17a6..8cd19d3bd8f 100644 --- a/lib/content-services/src/lib/content-metadata/components/content-metadata-card/content-metadata-card.component.spec.ts +++ b/lib/content-services/src/lib/content-metadata/components/content-metadata-card/content-metadata-card.component.spec.ts @@ -114,14 +114,6 @@ describe('ContentMetadataCardComponent', () => { expect(contentMetadataComponent.displayEmpty).toBe(true); }); - it('should pass through the editable to the underlying component', () => { - component.editable = true; - fixture.detectChanges(); - const contentMetadataComponent = fixture.debugElement.query(By.directive(ContentMetadataComponent)).componentInstance; - - expect(contentMetadataComponent.editable).toBe(true); - }); - it('should pass through the multi to the underlying component', () => { component.multi = true; fixture.detectChanges(); @@ -147,55 +139,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 +154,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(); diff --git a/lib/content-services/src/lib/content-metadata/components/content-metadata-card/content-metadata-card.component.ts b/lib/content-services/src/lib/content-metadata/components/content-metadata-card/content-metadata-card.component.ts index 4522d57b672..6210dbb838e 100644 --- a/lib/content-services/src/lib/content-metadata/components/content-metadata-card/content-metadata-card.component.ts +++ b/lib/content-services/src/lib/content-metadata/components/content-metadata-card/content-metadata-card.component.ts @@ -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(); - 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); } diff --git a/lib/content-services/src/lib/content-metadata/components/content-metadata/content-metadata-header.component.ts b/lib/content-services/src/lib/content-metadata/components/content-metadata/content-metadata-header.component.ts new file mode 100644 index 00000000000..8c857e2a11c --- /dev/null +++ b/lib/content-services/src/lib/content-metadata/components/content-metadata/content-metadata-header.component.ts @@ -0,0 +1,55 @@ +/*! + * @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. + */ + +import { CommonModule } from '@angular/common'; +import { Component, Input, ViewEncapsulation } from '@angular/core'; +import { MatExpansionModule } from '@angular/material/expansion'; +import { MatIconModule } from '@angular/material/icon'; +import { TranslateModule } from '@ngx-translate/core'; + +@Component({ + standalone: true, + imports: [CommonModule, MatIconModule, MatExpansionModule, TranslateModule], + selector: 'adf-content-metadata-header', + encapsulation: ViewEncapsulation.None, + styles: [ + ` + adf-content-metadata-header { + display: flex; + align-items: center; + flex: 1; + } + + .adf-metadata-properties-title { + font-weight: 700; + font-size: 15px; + padding-left: 12px; + } + ` + ], + template: ` + + {{ expanded ? 'expand_more' : 'chevron_right' }} + + + + ` +}) +export class ContentMetadataHeaderComponent { + @Input() title: string = null; + @Input() expanded = true; +} diff --git a/lib/content-services/src/lib/content-metadata/components/content-metadata/content-metadata.component.html b/lib/content-services/src/lib/content-metadata/components/content-metadata/content-metadata.component.html index d5d41f07c36..419c68bf012 100644 --- a/lib/content-services/src/lib/content-metadata/components/content-metadata/content-metadata.component.html +++ b/lib/content-services/src/lib/content-metadata/components/content-metadata/content-metadata.component.html @@ -1,138 +1,225 @@ -