Fix firefox ability to load file, add firefox tests back (#1380)

## Launch Checklist

This PR fixes the problem with firefox not able to load a file when
clicking the open file button.
It does so by removing a very old dependency: `react-file-reader-input`
It uses the "regular" `<label><a/><input type="file" /><label>`
approach.
It adds tests for both firefox and chrome although I'm not sure the
firefox tests will stay...

 - [x] Briefly describe the changes in this PR.
 - [x] Write tests for all new functionality.
 - [x] Add an entry to `CHANGELOG.md` under the `## main` section.
This commit is contained in:
Harel M
2025-09-15 08:15:00 +03:00
committed by GitHub
parent 5312d61598
commit b42afd0027
7 changed files with 36 additions and 34 deletions

View File

@@ -16,6 +16,7 @@
- Fix modal close button possition
- Fixed an issue with the generation of tranlations
- Fix missing spec info when clicking next to a property
- Fix Firefox open file that stopped working due to react upgrade
- _...Add new stuff here..._
## 3.0.0

View File

@@ -26,6 +26,22 @@ export default class MaputnikCypressHelper {
this.helper.when.wait(200);
this.helper.get.elementByTestId(element).realMouseUp();
},
openFileByFixture: (fixture: string, buttonTestId: string, inputTestId: string) => {
cy.window().then((win) => {
const file = {
text: cy.stub().resolves(cy.fixture(fixture).then(JSON.stringify)),
};
const fileHandle = {
getFile: cy.stub().resolves(file),
};
if (!win.showOpenFilePicker) {
this.helper.get.elementByTestId(inputTestId).selectFile("cypress/fixtures/" + fixture, { force: true });
} else {
cy.stub(win, "showOpenFilePicker").resolves([fileHandle]);
this.helper.get.elementByTestId(buttonTestId).click();
}
});
},
...this.helper.when,
};

View File

@@ -109,9 +109,9 @@ export class MaputnikDriver {
this.helper.when.waitForResponse("example-style.json");
},
chooseExampleFile: () => {
this.helper.get
.bySelector("type", "file")
.selectFile("cypress/fixtures/example-style.json", { force: true });
this.helper.given.fixture("example-style.json", "example-style.json");
this.helper.when.openFileByFixture("example-style.json", "modal:open.file.button", "modal:open.file.input");
this.helper.when.wait(200);
},
setStyle: (
styleProperties: "geojson" | "raster" | "both" | "layer" | "rectangles" | "",

View File

@@ -18,10 +18,9 @@ describe("modals", () => {
then(get.elementByTestId("modal:open")).shouldNotExist();
});
it.skip("upload", () => {
// HM: I was not able to make the following choose file actually to select a file and close the modal...
it("upload", () => {
when.chooseExampleFile();
then(get.responseBody("example-style.json")).shouldEqualToStoredStyle();
then(get.fixture("example-style.json")).shouldEqualToStoredStyle();
});
describe("when click open url", () => {
@@ -334,6 +333,7 @@ describe("modals", () => {
it("add variable", () => {
when.click("global-state-add-variable");
when.wait(100);
then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
state: { key1: { default: "value" } },
});
@@ -344,6 +344,7 @@ describe("modals", () => {
when.click("global-state-add-variable");
when.click("global-state-add-variable");
when.click("global-state-add-variable");
when.wait(100);
then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
state: { key1: { default: "value" }, key2: { default: "value" }, key3: { default: "value" } },
});
@@ -354,6 +355,7 @@ describe("modals", () => {
when.click("global-state-add-variable");
when.click("global-state-add-variable");
when.click("global-state-remove-variable", 0);
when.wait(100);
then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
state: { key2: { default: "value" }, key3: { default: "value" } },
});
@@ -363,6 +365,7 @@ describe("modals", () => {
when.click("global-state-add-variable");
when.setValue("global-state-variable-key:0", "mykey");
when.typeKeys("{enter}");
when.wait(100);
then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
state: { mykey: { default: "value" } },
});
@@ -372,6 +375,7 @@ describe("modals", () => {
when.click("global-state-add-variable");
when.setValue("global-state-variable-value:0", "myvalue");
when.typeKeys("{enter}");
when.wait(100);
then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
state: { key1: { default: "myvalue" } },
});

20
package-lock.json generated
View File

@@ -51,7 +51,6 @@
"react-collapse": "^5.1.1",
"react-color": "^2.19.3",
"react-dom": "^19.1.1",
"react-file-reader-input": "^2.0.0",
"react-i18next": "^15.7.3",
"react-icons": "^5.5.0",
"react-markdown": "^10.1.0",
@@ -86,7 +85,6 @@
"@types/react-collapse": "^5.0.4",
"@types/react-color": "^3.0.13",
"@types/react-dom": "^19.1.9",
"@types/react-file-reader-input": "^2.0.4",
"@types/string-hash": "^1.1.3",
"@types/uuid": "^10.0.0",
"@types/wicg-file-system-access": "^2023.10.6",
@@ -3225,15 +3223,6 @@
"@types/react": "^19.0.0"
}
},
"node_modules/@types/react-file-reader-input": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/@types/react-file-reader-input/-/react-file-reader-input-2.0.4.tgz",
"integrity": "sha512-Jqrfn+w42j8t8Q3npMXXKPdk+reIM0UHLKVc3ykrA7q7bN3Z62SGhsClZX0+Edlqm66lcKwmDQl+WMm+Xor7Xg==",
"dev": true,
"dependencies": {
"@types/react": "*"
}
},
"node_modules/@types/reactcss": {
"version": "1.2.12",
"resolved": "https://registry.npmjs.org/@types/reactcss/-/reactcss-1.2.12.tgz",
@@ -11681,15 +11670,6 @@
"react": "^19.1.1"
}
},
"node_modules/react-file-reader-input": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/react-file-reader-input/-/react-file-reader-input-2.0.0.tgz",
"integrity": "sha512-1XgkCpwMnNQsuOIy938UCntz8Xzwt9ECwHaH3cCfIQK1SPpH+y7gCYtqEcb6Rm0hAUq7Lp9+Ljoti9zGMswYrQ==",
"peerDependencies": {
"react": "^15.0.0 || ^16.0.0",
"react-dom": "^15.0.0 || ^16.0.0"
}
},
"node_modules/react-hot-loader": {
"version": "4.13.1",
"resolved": "https://registry.npmjs.org/react-hot-loader/-/react-hot-loader-4.13.1.tgz",

View File

@@ -66,7 +66,6 @@
"react-collapse": "^5.1.1",
"react-color": "^2.19.3",
"react-dom": "^19.1.1",
"react-file-reader-input": "^2.0.0",
"react-i18next": "^15.7.3",
"react-icons": "^5.5.0",
"react-markdown": "^10.1.0",
@@ -118,7 +117,6 @@
"@types/react-collapse": "^5.0.4",
"@types/react-color": "^3.0.13",
"@types/react-dom": "^19.1.9",
"@types/react-file-reader-input": "^2.0.4",
"@types/string-hash": "^1.1.3",
"@types/uuid": "^10.0.0",
"@types/wicg-file-system-access": "^2023.10.6",

View File

@@ -1,7 +1,6 @@
import React, { type FormEvent } from "react";
import {MdFileUpload} from "react-icons/md";
import {MdAddCircleOutline} from "react-icons/md";
import FileReaderInput, { type Result } from "react-file-reader-input";
import { Trans, type WithTranslation, withTranslation } from "react-i18next";
import ModalLoading from "./ModalLoading";
@@ -171,8 +170,10 @@ class ModalOpenInternal extends React.Component<ModalOpenInternalProps, ModalOpe
// it is not guaranteed that the File System Access API is available on all
// browsers. If the function is not available, a fallback behavior is used.
onFileChanged = async (_: any, files: Result[]) => {
const [, file] = files[0];
onFileChanged = (files: FileList | null) => {
if (!files) return;
if (files.length === 0) return;
const file = files[0];
const reader = new FileReader();
this.clearError();
@@ -246,13 +247,15 @@ class ModalOpenInternal extends React.Component<ModalOpenInternalProps, ModalOpe
<div>
{typeof window.showOpenFilePicker === "function" ? (
<InputButton
data-wd-key="modal:open.file.button"
className="maputnik-big-button"
onClick={this.onOpenFile}><MdFileUpload/> {t("Open Style")}
</InputButton>
) : (
<FileReaderInput onChange={this.onFileChanged} tabIndex={-1} aria-label={t("Open Style")}>
<InputButton className="maputnik-upload-button"><MdFileUpload /> {t("Open Style")}</InputButton>
</FileReaderInput>
<label>
<a className="maputnik-button maputnik-upload-button" aria-label={t("Open Style")}><MdFileUpload /> {t("Open Style")}</a>
<input data-wd-key="modal:open.file.input" type="file" style={{ display: "none" }} onChange={(e) => this.onFileChanged(e.target.files)} />
</label>
)}
</div>
</section>