diff --git a/Kitodo/src/main/webapp/WEB-INF/resources/css/kitodo.css b/Kitodo/src/main/webapp/WEB-INF/resources/css/kitodo.css index 751405b6e79..ad558a59e50 100644 --- a/Kitodo/src/main/webapp/WEB-INF/resources/css/kitodo.css +++ b/Kitodo/src/main/webapp/WEB-INF/resources/css/kitodo.css @@ -3165,6 +3165,11 @@ Column content overflow: hidden; } +#metadataAccordion\:metadata\:metadataTable_data tr.focusedRow, +div[id$='metadataTable'].ui-treetable tr.focusedRow { + background-color: var(--trans-blue-highlight); +} + #imagePreviewForm { -webkit-touch-callout: none; /* iOS Safari */ -webkit-user-select: none; /* Safari */ diff --git a/Kitodo/src/main/webapp/WEB-INF/resources/css/pattern-library.css b/Kitodo/src/main/webapp/WEB-INF/resources/css/pattern-library.css index 05c69589095..93fcb720613 100644 --- a/Kitodo/src/main/webapp/WEB-INF/resources/css/pattern-library.css +++ b/Kitodo/src/main/webapp/WEB-INF/resources/css/pattern-library.css @@ -25,6 +25,7 @@ --medium-gray: #999; --orange: #f94a15; --trans-blue: #f2fbff; + --trans-blue-highlight: #dbecf6; --pure-white: #fff; --error-background: #f2dede; diff --git a/Kitodo/src/main/webapp/WEB-INF/resources/js/metadata_table.js b/Kitodo/src/main/webapp/WEB-INF/resources/js/metadata_table.js new file mode 100644 index 00000000000..62b4ff4c945 --- /dev/null +++ b/Kitodo/src/main/webapp/WEB-INF/resources/js/metadata_table.js @@ -0,0 +1,85 @@ +/** + * (c) Kitodo. Key to digital objects e. V. + * + * This file is part of the Kitodo project. + * + * It is licensed under GNU General Public License version 3 or later. + * + * For the full copyright and license information, please read the + * GPL3-License.txt file that was distributed with this source code. + */ + +/** + * This class allows to focus the most recently added metadata row of a metadata table. + * + * It requires each row to be annotated with a data attribute containing its metadata type. + * When adding a new metadata row, the last row of the same metadata type is focused. + */ +class FocusMetadataRow { + + /** + * Contains the row key (rk data attribute) of the metadata group whose add button (+) + * was last clicked by the user. + */ + #lastSelectedRowKey; + + constructor() { + this.#lastSelectedRowKey = "root"; + } + + /** + * Retrieves the recently added metadata type (e.g. TitleDocMain or LABEL) from the + * "addMetadata" dialog. Requires "widgetVar" declaration on PrimeFaces element. + */ + #getSelectedMetadataType() { + return PF('metadataTypeSelection').getSelectedValue(); + } + + /** + * Remove all highlights. + */ + #removeHighlights() { + $("div[id$='metadataTable'] tr.focusedRow").removeClass("focusedRow"); + } + + /** + * Remembers the metadata group whose add button (+) was clicked by the user. + * Is used to determine which metadata row needs to be focused if there are multiple + * groups with the same metadata type (e.g. ContributingPerson). + * + * @param {String} rowId the id of the metadata row whose add button (+) was clicked by the user + */ + remember(rowId) { + this.#removeHighlights(); + // extract data attribute "rk" (row key?), which references the current row (tree node) + let rowKey = $(document.getElementById(rowId)).closest("tr").data("rk"); + // remember the current row key if available + this.#lastSelectedRowKey = typeof rowKey !== 'undefined' ? rowKey : "root"; + } + + /** + * Focus the row that was added by a user. + */ + focus() { + this.#removeHighlights(); + + // find last metadata row matching currently selected metadata type + let row = $( + "div[id$='metadataTable'] " + // metadata table selector + "tr[data-prk='" + this.#lastSelectedRowKey + "'] " + // remembered metadata group + "label[data-metadataid='" + this.#getSelectedMetadataType() + "']" // selected metadata type + ).last().closest("tr"); + + // if found, focus row + if (row.length === 1) { + row.addClass("focusedRow"); + row.get(0).scrollIntoView(); + row.find("input:enabled:visible,textarea:enabled:visible").first().focus(); + } + } + +} + +// register class with global metadataTable namespace +var metadataTable = metadataTable || {}; +metadataTable.focusMetadataRow = new FocusMetadataRow(); diff --git a/Kitodo/src/main/webapp/WEB-INF/templates/includes/metadataEditor/dialogs/addMetadata.xhtml b/Kitodo/src/main/webapp/WEB-INF/templates/includes/metadataEditor/dialogs/addMetadata.xhtml index 87db0759c53..51f1cdf221f 100644 --- a/Kitodo/src/main/webapp/WEB-INF/templates/includes/metadataEditor/dialogs/addMetadata.xhtml +++ b/Kitodo/src/main/webapp/WEB-INF/templates/includes/metadataEditor/dialogs/addMetadata.xhtml @@ -31,6 +31,7 @@ styleClass="dialogFieldWrapper">
@@ -42,7 +43,7 @@ + oncomplete="PF('addMetadataDialog').show();metadataTable.focusMetadataRow.remember(this.source);"> diff --git a/Kitodo/src/main/webapp/WEB-INF/templates/includes/metadataTreeTable.xhtml b/Kitodo/src/main/webapp/WEB-INF/templates/includes/metadataTreeTable.xhtml index 2a28ccb1d41..19ed607f80a 100644 --- a/Kitodo/src/main/webapp/WEB-INF/templates/includes/metadataTreeTable.xhtml +++ b/Kitodo/src/main/webapp/WEB-INF/templates/includes/metadataTreeTable.xhtml @@ -15,7 +15,8 @@ xmlns:h="http://xmlns.jcp.org/jsf/html" xmlns:ui="http://xmlns.jcp.org/jsf/facelets" xmlns:f="http://xmlns.jcp.org/jsf/core" - xmlns:p="http://primefaces.org/ui"> + xmlns:p="http://primefaces.org/ui" + xmlns:a="http://xmlns.jcp.org/jsf/passthrough"> diff --git a/Kitodo/src/main/webapp/WEB-INF/templates/includes/processFromTemplate/dataEdit.xhtml b/Kitodo/src/main/webapp/WEB-INF/templates/includes/processFromTemplate/dataEdit.xhtml index b0015ef9639..5f27134438e 100644 --- a/Kitodo/src/main/webapp/WEB-INF/templates/includes/processFromTemplate/dataEdit.xhtml +++ b/Kitodo/src/main/webapp/WEB-INF/templates/includes/processFromTemplate/dataEdit.xhtml @@ -164,7 +164,7 @@ update="addMetadataDialog" disabled="#{not CreateProcessForm.addMetadataDialog.metadataAddableToStructureElement()}" action="#{CreateProcessForm.addMetadataDialog.prepareAddableMetadataForStructure()}" - oncomplete="PF('addMetadataDialog').show();"> + oncomplete="PF('addMetadataDialog').show();metadataTable.focusMetadataRow.remember(this.source);">
diff --git a/Kitodo/src/main/webapp/WEB-INF/templates/includes/processFromTemplate/dialogs/addMetadata.xhtml b/Kitodo/src/main/webapp/WEB-INF/templates/includes/processFromTemplate/dialogs/addMetadata.xhtml index 2216c5df01a..0365bf7ca32 100644 --- a/Kitodo/src/main/webapp/WEB-INF/templates/includes/processFromTemplate/dialogs/addMetadata.xhtml +++ b/Kitodo/src/main/webapp/WEB-INF/templates/includes/processFromTemplate/dialogs/addMetadata.xhtml @@ -31,6 +31,7 @@ styleClass="dialogFieldWrapper">
@@ -43,7 +44,7 @@ action="#{CreateProcessForm.processMetadata.addMetadataEntry}" process="addMetadataDialog" update="editForm:processFromTemplateTabView:metadataTable editForm:processFromTemplateTabView:addMetadataButtonWrapper" - oncomplete="PF('addMetadataDialog').hide();" + oncomplete="PF('addMetadataDialog').hide();metadataTable.focusMetadataRow.focus();" value="#{msgs.apply}" styleClass="primary right"/> + PrimeFaces.widget.VerticalTree.prototype.bindKeyEvents = function () { // Prevent PrimeFaces' default keyboard event handling for VerticalTrees. diff --git a/Kitodo/src/main/webapp/pages/processFromTemplate.xhtml b/Kitodo/src/main/webapp/pages/processFromTemplate.xhtml index 4298b9eea14..e55c2f2c62f 100644 --- a/Kitodo/src/main/webapp/pages/processFromTemplate.xhtml +++ b/Kitodo/src/main/webapp/pages/processFromTemplate.xhtml @@ -137,6 +137,7 @@ + diff --git a/Kitodo/src/test/java/org/kitodo/selenium/MetadataST.java b/Kitodo/src/test/java/org/kitodo/selenium/MetadataST.java index 619f9ba84f0..d875ca9c3cc 100644 --- a/Kitodo/src/test/java/org/kitodo/selenium/MetadataST.java +++ b/Kitodo/src/test/java/org/kitodo/selenium/MetadataST.java @@ -215,9 +215,14 @@ public void showPaginationByDefaultTest() throws Exception { Pages.getProcessesPage().goTo().editMetadata(MockDatabase.MEDIA_RENAMING_TEST_PROCESS_TITLE); assertFalse(Pages.getMetadataEditorPage().isPaginationPanelVisible()); Pages.getMetadataEditorPage().closeEditor(); - Pages.getUserEditPage().setPaginationToShowByDefault(); + Pages.getUserEditPage().togglePaginationToShowByDefault(); Pages.getProcessesPage().goTo().editMetadata(MockDatabase.MEDIA_RENAMING_TEST_PROCESS_TITLE); assertTrue(Pages.getMetadataEditorPage().isPaginationPanelVisible()); + // disable pagination again to prevent conflicts with other tests (when interacting with metadata table) + Pages.getMetadataEditorPage().closeEditor(); + Pages.getUserEditPage().togglePaginationToShowByDefault(); + Pages.getProcessesPage().goTo().editMetadata(MockDatabase.MEDIA_RENAMING_TEST_PROCESS_TITLE); + assertFalse(Pages.getMetadataEditorPage().isPaginationPanelVisible()); } /** @@ -496,6 +501,67 @@ public void linkPageToNextDivision() throws Exception { } /** + * Tests that a metadata row of the metadata table is highlighted as soon as a user adds a new + * row via the add metadata dialog. + */ + @Test + public void focusRecentlyAddedMetadataRowTest() throws Exception { + login("kowal"); + + // open the metadata editor + Pages.getProcessesPage().goTo().editMetadata(MockDatabase.MEDIA_RENAMING_TEST_PROCESS_TITLE); + + // wait until metadata table is shown + await().ignoreExceptions().pollDelay(100, TimeUnit.MILLISECONDS).atMost(5, TimeUnit.SECONDS).until( + () -> Browser.getDriver().findElement(By.id("metadataAccordion:metadata:metadataTable")).isDisplayed() + ); + + // verify no metadata row is focused yet + assertTrue(Browser.getDriver().findElements( + By.cssSelector("#metadataAccordion\\:metadata\\:metadataTable tr.focusedRow")).isEmpty() + ); + + // click on add metadata button + Browser.getDriver().findElement(By.id("metadataAccordion:addMetadataButton")).click(); + + // wait until dialog is visible + await().ignoreExceptions().pollDelay(100, TimeUnit.MILLISECONDS).atMost(5, TimeUnit.SECONDS).until( + () -> Browser.getDriver().findElement(By.id("addMetadataDialog")).isDisplayed() + ); + + // open select menu + Browser.getDriver().findElement(By.id("addMetadataForm:metadataTypeSelection")).click(); + + // wait until selection menu list is visible + await().ignoreExceptions().pollDelay(100, TimeUnit.MILLISECONDS).atMost(5, TimeUnit.SECONDS).until( + () -> Browser.getDriver().findElement(By.id("addMetadataForm:metadataTypeSelection_items")).isDisplayed() + ); + + // select Person as new metadata row + Browser.getDriver().findElement(By.cssSelector( + "#addMetadataForm\\:metadataTypeSelection_items li[data-label='Person'].ui-selectonemenu-item" + )).click(); + + // confirm dialog + Browser.getDriver().findElement(By.id("addMetadataForm:apply")).click(); + + // wait until dialog disappears + await().ignoreExceptions().pollDelay(100, TimeUnit.MILLISECONDS).atMost(5, TimeUnit.SECONDS).until( + () -> !Browser.getDriver().findElement(By.id("addMetadataDialog")).isDisplayed() + ); + + // verify metadata row with name "Person" is selected + assertEquals("Person:", Browser.getDriver().findElement( + By.cssSelector("#metadataAccordion\\:metadata\\:metadataTable tr.focusedRow label") + ).getText()); + + // verify accordion was scrolled down + assertTrue(0 < (Long)Browser.getDriver().executeScript( + "return document.getElementById('metadataAccordion:metadata:metadataTable').scrollTop;" + )); + } + + /* * Verifies that an image can be openend in a separate window by clicking on the corresponding * context menu item of the first logical tree node. */ diff --git a/Kitodo/src/test/java/org/kitodo/selenium/testframework/pages/UserEditPage.java b/Kitodo/src/test/java/org/kitodo/selenium/testframework/pages/UserEditPage.java index 6966b1fb403..b25471b19eb 100644 --- a/Kitodo/src/test/java/org/kitodo/selenium/testframework/pages/UserEditPage.java +++ b/Kitodo/src/test/java/org/kitodo/selenium/testframework/pages/UserEditPage.java @@ -196,7 +196,7 @@ public void changeUserSettings() throws Exception { /** * Set pagination panel to show by default in metadata editor. */ - public void setPaginationToShowByDefault() throws Exception { + public void togglePaginationToShowByDefault() throws Exception { openUserConfig(); switchToTabByIndex(TabIndex.USER_METADATA_EDITOR_SETTINGS.getIndex()); WebElement switchCheckBox = showPaginationByDefaultSwitch.findElement(By.className("ui-chkbox-box"));