Repository: OpenRefine/OpenRefine (java)

Issue:
Regression: matching a single cell to a reconciliation candidate resets the grid to the first page
In 3.8.1 (and the current master branch), matching a single cell to a reconciliation candidate resets the grid to the first page.
Before, matching a single cell to a candidate (with the button with a single check-mark) did not do that - only matching a cell to multiple candidates (with the double check-mark) did.

### To Reproduce
Steps to reproduce the behavior:
1. First, reconcile a column
2. Then, move to a different page in the grid
3. Finally, match a single cell to a reconciliation candidate



### Current Results

The cell is matched and the grid view is reset to the first page.

### Expected Behavior

The position in the grid should be preserved.

### Versions
 - OpenRefine: 3.8.1, master


### Additional context

Reported by @arthurpsmith on Mastodon: https://fediscience.org/@apsmith/112672042744786060.

This will be fixed by #6546, but because this is a pretty bad bug, it might be worth investigating if we can include a smaller fix to restore the 3.7 behaviour in 3.8. Given the scale of the changes in #6546 I'd be reluctant to port it as such to 3.8.


Pull request description:
Fix preservation of pagination when matching a single cell
Fixes #6695.

Changes proposed in this pull request:
- revert #6239 which forces the re-fetching and re-rendering of the entire grid
- add missing `cellUI` parameter to enable in-place rendering of the updated cell after a single match action
- add a Cypress regression test

I would like to backport this to 3.8.

Patch:
diff --git a/main/src/com/google/refine/importers/ImportingParserBase.java b/main/src/com/google/refine/importers/ImportingParserBase.java
index 06b09ff37..50eaf78fa 100644
--- a/main/src/com/google/refine/importers/ImportingParserBase.java
+++ b/main/src/com/google/refine/importers/ImportingParserBase.java
@@ -117,6 +117,7 @@ abstract public class ImportingParserBase implements ImportingParser {
             final MultiFileReadingProgress progress) throws IOException {
         final File file = ImportingUtilities.getFile(job, fileRecord);
         final String fileSource = ImportingUtilities.getFileSource(fileRecord);
+        final String fileName = ImportingUtilities.getFileName(fileRecord);
         final String archiveFileName = ImportingUtilities.getArchiveFileName(fileRecord);
         int filenameColumnIndex = -1;
         int archiveColumnIndex = -1;
@@ -136,7 +137,7 @@ abstract public class ImportingParserBase implements ImportingParser {
                 }
 
                 if (useInputStream) {
-                    parseOneFile(project, metadata, job, fileSource, inputStream, limit, options, exceptions);
+                    parseOneFile(project, metadata, job, fileName, inputStream, limit, options, exceptions);
                 } else {
                     // Although this is called "common" encoding, it may represent the user's override of the encoding
                     String commonEncoding = JSONUtilities.getString(options, "encoding", null);
@@ -147,7 +148,7 @@ abstract public class ImportingParserBase implements ImportingParser {
                     Reader reader = ImportingUtilities.getReaderFromStream(
                             inputStream, fileRecord, commonEncoding);
 
-                    parseOneFile(project, metadata, job, fileSource, reader, limit, options, exceptions);
+                    parseOneFile(project, metadata, job, fileName, reader, limit, options, exceptions);
                 }
 
                 // Fill in filename and archive name column for all rows added from this file
diff --git a/main/src/com/google/refine/importing/ImportingUtilities.java b/main/src/com/google/refine/importing/ImportingUtilities.java
index 97882320d..6209f9a7b 100644
--- a/main/src/com/google/refine/importing/ImportingUtilities.java
+++ b/main/src/com/google/refine/importing/ImportingUtilities.java
@@ -597,6 +597,13 @@ public class ImportingUtilities {
                 JSONUtilities.getString(fileRecord, "fileName", "unknown"));
     }
 
+    static public String getFileName(ObjectNode fileRecord) {
+        return JSONUtilities.getString(
+                fileRecord,
+                "fileName",
+                "unknown");
+    }
+
     static public String getArchiveFileName(ObjectNode fileRecord) {
         return JSONUtilities.getString(
                 fileRecord,
diff --git a/main/webapp/modules/core/scripts/views/data-table/cell-renderers/recon-renderer.js b/main/webapp/modules/core/scripts/views/data-table/cell-renderers/recon-renderer.js
index 8735bbb14..87b210369 100644
--- a/main/webapp/modules/core/scripts/views/data-table/cell-renderers/recon-renderer.js
+++ b/main/webapp/modules/core/scripts/views/data-table/cell-renderers/recon-renderer.js
@@ -122,7 +122,7 @@ class ReconCellRenderer {
               a.attr("href", encodeURI(service.view.url.replace("{{id}}", candidate.id)));
             }
 
-            self.previewOnHover(service, candidate, liSpan.parent(), liSpan, true, rowIndex, cellIndex, cell);
+            self.previewOnHover(service, candidate, liSpan.parent(), liSpan, true, rowIndex, cellIndex, cell, cellUI);
 
             var score;
             if (candidate.score < 1) {
@@ -411,7 +411,7 @@ class ReconCellRenderer {
       command,
       params,
       bodyParams,
-      { cellsChanged: true, columnStatsChanged: columnStatsChanged },
+      { columnStatsChanged: columnStatsChanged },
       {
         onDone: function(o) {
           if (o.cell.r) {
diff --git a/pom.xml b/pom.xml
index b102e5af8..677a8d97d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -118,7 +118,7 @@
     <maven-dependency-plugin.version>3.7.1</maven-dependency-plugin.version>
     <jacoco-maven-plugin.version>0.8.12</jacoco-maven-plugin.version>
     <surefire.version>3.3.0</surefire.version>
-    <maven-jar-plugin.version>3.4.1</maven-jar-plugin.version>
+    <maven-jar-plugin.version>3.4.2</maven-jar-plugin.version>
     <maven-clean-plugin.version>3.4.0</maven-clean-plugin.version>
     <git-commit-id-plugin.version>4.9.10</git-commit-id-plugin.version>
     <maven-deploy-plugin.version>3.1.2</maven-deploy-plugin.version>


Test patch:
diff --git a/main/tests/cypress/cypress/e2e/project/grid/column/reconcile/actions/cell-buttons.cy.js b/main/tests/cypress/cypress/e2e/project/grid/column/reconcile/actions/cell-buttons.cy.js
index 1ac0b9e1d..7ee8cfef5 100644
--- a/main/tests/cypress/cypress/e2e/project/grid/column/reconcile/actions/cell-buttons.cy.js
+++ b/main/tests/cypress/cypress/e2e/project/grid/column/reconcile/actions/cell-buttons.cy.js
@@ -51,6 +51,30 @@ describe('In-cell reconciliation buttons', () => {
         //confirming the no. of candidates after we click on see less
         cy.getCell(0, 'species').find('.data-table-recon-candidate:visible').should('have.length', 4);
     });
+
+    it('Matching a single cell preserves pagination', () => {
+        cy.visitOpenRefine();
+        cy.navigateTo('Import project');
+        cy.get('.grid-layout').should('to.contain', 'Locate an existing Refine project file');
+        cy.get('#project-tar-file-input').attachFile('reconciled-project-no-automatch.zip')
+        cy.get('#import-project-button').click();
+
+        // Set a smaller page number to make sure there are multiple pages
+        cy.get('.viewPanel-pagingControls-page:nth-of-type(1)').click();
+        // Go to the next page
+        cy.get('.viewpanel-paging .action').first().click();
+        // Check that we have a single row in view (second page)
+        cy.get('.data-table tbody tr').should('have.length', 1);
+
+        // Match the first recon candidate in the cell
+        cy.get('.data-table-recon-match').first().click();
+
+        // Make sure the cell is matched (no recon candidates are visible anymore)
+        cy.get('.data-table-recon-match').should('not.exist');
+        // We are still in the second page
+        cy.get('.data-table tbody tr').should('have.length', 1);
+    });
+
 });
 
 
diff --git a/main/tests/cypress/package.json b/main/tests/cypress/package.json
index d7f898e78..acbaf6cf8 100644
--- a/main/tests/cypress/package.json
+++ b/main/tests/cypress/package.json
@@ -20,7 +20,7 @@
         "cypress-file-upload": "^5.0.8"
     },
     "devDependencies": {
-        "eslint": "^9.5.0",
+        "eslint": "^9.6.0",
         "eslint-config-google": "^0.14.0",
         "eslint-config-prettier": "^9.1.0",
         "eslint-plugin-cypress": "^3.3.0",
diff --git a/main/tests/cypress/yarn.lock b/main/tests/cypress/yarn.lock
index c494915dd..07014a9b7 100644
--- a/main/tests/cypress/yarn.lock
+++ b/main/tests/cypress/yarn.lock
@@ -51,14 +51,14 @@
   resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.6.2.tgz#1816b5f6948029c5eaacb0703b850ee0cb37d8f8"
   integrity sha512-pPTNuaAG3QMH+buKyBIGJs3g/S5y0caxw0ygM3YyE6yJFySwiGGSzA+mM3KJ8QQvzeLh3blwgSonkFjgQdxzMw==
 
-"@eslint/config-array@^0.16.0":
-  version "0.16.0"
-  resolved "https://registry.yarnpkg.com/@eslint/config-array/-/config-array-0.16.0.tgz#bb3364fc39ee84ec3a62abdc4b8d988d99dfd706"
-  integrity sha512-/jmuSd74i4Czf1XXn7wGRWZCuyaUZ330NH1Bek0Pplatt4Sy1S5haN21SCLLdbeKslQ+S0wEJ+++v5YibSi+Lg==
+"@eslint/config-array@^0.17.0":
+  version "0.17.0"
+  resolved "https://registry.yarnpkg.com/@eslint/config-array/-/config-array-0.17.0.tgz#ff305e1ee618a00e6e5d0485454c8d92d94a860d"
+  integrity sha512-A68TBu6/1mHHuc5YJL0U0VVeGNiklLAL6rRmhTCP2B5XjWLMnrX+HkO+IAXyHvks5cyyY1jjK5ITPQ1HGS2EVA==
   dependencies:
     "@eslint/object-schema" "^2.1.4"
     debug "^4.3.1"
-    minimatch "^3.0.5"
+    minimatch "^3.1.2"
 
 "@eslint/eslintrc@^3.1.0":
   version "3.1.0"
@@ -75,10 +75,10 @@
     minimatch "^3.1.2"
     strip-json-comments "^3.1.1"
 
-"@eslint/js@9.5.0":
-  version "9.5.0"
-  resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.5.0.tgz#0e9c24a670b8a5c86bff97b40be13d8d8f238045"
-  integrity sha512-A7+AOT2ICkodvtsWnxZP4Xxk3NbZ3VMHd8oihydLRGrJgqqdEz1qSeEgXYyT/Cu8h1TWWsQRejIx48mtjZ5y1w==
+"@eslint/js@9.6.0":
+  version "9.6.0"
+  resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.6.0.tgz#5b0cb058cc13d9c92d4e561d3538807fa5127c95"
+  integrity sha512-D9B0/3vNg44ZeWbYMpBoXqNP4j6eQD5vNwIlGAuFRRzK/WtT/jvDQW3Bi9kkf3PMDMlM7Yi+73VLUsn5bJcl8A==
 
 "@eslint/object-schema@^2.1.4":
   version "2.1.4"
@@ -148,10 +148,10 @@ acorn-jsx@^5.3.2:
   resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937"
   integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==
 
-acorn@^8.11.3:
-  version "8.11.3"
-  resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.11.3.tgz#71e0b14e13a4ec160724b38fb7b0f233b1b81d7a"
-  integrity sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==
+acorn@^8.12.0:
+  version "8.12.0"
+  resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.12.0.tgz#1627bfa2e058148036133b8d9b51a700663c294c"
+  integrity sha512-RTvkC4w+KNXrM39/lWCUaG0IbRkWdCv7W/IOW9oU6SawyxulvkQy5HQPVTKxEjczcUvapcrw3cFx/60VN/NRNw==
 
 aggregate-error@^3.0.0:
   version "3.1.0"
@@ -592,16 +592,16 @@ eslint-visitor-keys@^4.0.0:
   resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz#e3adc021aa038a2a8e0b2f8b0ce8f66b9483b1fb"
   integrity sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==
 
-eslint@^9.5.0:
-  version "9.5.0"
-  resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.5.0.tgz#11856034b94a9e1a02cfcc7e96a9f0956963cd2f"
-  integrity sha512-+NAOZFrW/jFTS3dASCGBxX1pkFD0/fsO+hfAkJ4TyYKwgsXZbqzrw+seCYFCcPCYXvnD67tAnglU7GQTz6kcVw==
+eslint@^9.6.0:
+  version "9.6.0"
+  resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.6.0.tgz#9f54373afa15e1ba356656a8d96233182027fb49"
+  integrity sha512-ElQkdLMEEqQNM9Njff+2Y4q2afHk7JpkPvrd7Xh7xefwgQynqPxwf55J7di9+MEibWUGdNjFF9ITG9Pck5M84w==
   dependencies:
     "@eslint-community/eslint-utils" "^4.2.0"
     "@eslint-community/regexpp" "^4.6.1"
-    "@eslint/config-array" "^0.16.0"
+    "@eslint/config-array" "^0.17.0"
     "@eslint/eslintrc" "^3.1.0"
-    "@eslint/js" "9.5.0"
+    "@eslint/js" "9.6.0"
     "@humanwhocodes/module-importer" "^1.0.1"
     "@humanwhocodes/retry" "^0.3.0"
     "@nodelib/fs.walk" "^1.2.8"
@@ -612,7 +612,7 @@ eslint@^9.5.0:
     escape-string-regexp "^4.0.0"
     eslint-scope "^8.0.1"
     eslint-visitor-keys "^4.0.0"
-    espree "^10.0.1"
+    espree "^10.1.0"
     esquery "^1.5.0"
     esutils "^2.0.2"
     fast-deep-equal "^3.1.3"
@@ -632,12 +632,12 @@ eslint@^9.5.0:
     strip-ansi "^6.0.1"
     text-table "^0.2.0"
 
-espree@^10.0.1:
-  version "10.0.1"
-  resolved "https://registry.yarnpkg.com/espree/-/espree-10.0.1.tgz#600e60404157412751ba4a6f3a2ee1a42433139f"
-  integrity sha512-MWkrWZbJsL2UwnjxTX3gG8FneachS/Mwg7tdGXce011sJd5b0JG54vat5KHnfSBODZ3Wvzd2WnjxyzsRoVv+ww==
+espree@^10.0.1, espree@^10.1.0:
+  version "10.1.0"
+  resolved "https://registry.yarnpkg.com/espree/-/espree-10.1.0.tgz#8788dae611574c0f070691f522e4116c5a11fc56"
+  integrity sha512-M1M6CpiE6ffoigIOWYO9UDP8TMUw9kqb21tf+08IgDYjCsOvCuDt4jQcZmoYxx+w7zlKw9/N0KXfto+I8/FrXA==
   dependencies:
-    acorn "^8.11.3"
+    acorn "^8.12.0"
     acorn-jsx "^5.3.2"
     eslint-visitor-keys "^4.0.0"
 
@@ -1186,7 +1186,7 @@ mimic-fn@^2.1.0:
   resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz"
   integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==
 
-minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.2:
+minimatch@^3.0.4, minimatch@^3.1.2:
   version "3.1.2"
   resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
   integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
diff --git a/main/tests/server/src/com/google/refine/importing/ImportingUtilitiesTests.java b/main/tests/server/src/com/google/refine/importing/ImportingUtilitiesTests.java
index 8e6851236..faca5a4a3 100644
--- a/main/tests/server/src/com/google/refine/importing/ImportingUtilitiesTests.java
+++ b/main/tests/server/src/com/google/refine/importing/ImportingUtilitiesTests.java
@@ -41,6 +41,7 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.net.URISyntaxException;
+import java.net.URL;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
@@ -564,4 +565,14 @@ public class ImportingUtilitiesTests extends ImporterTest {
             }
         }
     }
+
+    @Test
+    public void testGetFileName() {
+        ObjectNode fileRecord = ParsingUtilities.mapper.createObjectNode();
+        String fileName = "aFileName";
+
+        JSONUtilities.safePut(fileRecord, "fileName", fileName);
+
+        assertEquals(fileName, ImportingUtilities.getFileName(fileRecord));
+    }
 }