Skip to content
Merged
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
- Changed `SkulptRunner.jsx` implementation of hiding elements to use `display: none` rather than `block-size: 0` (#1219)
- Enabled `hyphens: auto` globally (with exceptions) to prevent overflow with longer words (#1215)
- Removed fixed size from `ProjectBar` to prevent overflow when text wraps (#1221)
- Added missing translation strings (#1222)

## Changed

Expand Down
7 changes: 5 additions & 2 deletions src/components/Button/Button.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { Link } from "react-router-dom";
import classNames from "classnames";

import "../../assets/stylesheets/Button.scss";
import { useTranslation } from "react-i18next";

const Button = (props) => {
const {
Expand All @@ -26,6 +27,8 @@ const Button = (props) => {
buttonIconPosition = "left",
} = props;

const { t } = useTranslation();

const buttonClass = classNames("btn", className, {
"btn--svg-only": !buttonText,
});
Expand All @@ -40,11 +43,11 @@ const Button = (props) => {
message: confirmText,
buttons: [
{
label: "Yes",
label: t("button.yes"),
onClick: () => onClickHandler(e),
},
{
label: "No",
label: t("button.no"),
},
],
});
Expand Down
2 changes: 1 addition & 1 deletion src/components/Editor/EditorInput/EditorInput.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ const EditorInput = () => {
{!["main.py", "index.html"].includes(fileName) ? (
<Button
className="btn--tertiary react-tabs__tab-close-btn"
label="close"
label={t("editorPanel.close")}
onClickHandler={(e) => closeFileTab(e, fileName)}
ButtonIcon={() => <CloseIcon scaleFactor={0.85} />}
/>
Expand Down
52 changes: 26 additions & 26 deletions src/components/Editor/ImageUploadButton/ImageUploadButton.jsx
Comment thread
loiswells97 marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -10,26 +10,13 @@ import Button from "../../Button/Button";
import NameErrorMessage from "../ErrorMessage/NameErrorMessage";
import store from "../../../app/store";
import ApiCallHandler from "../../../utils/apiCallHandler";
import { useTranslation } from "react-i18next";
import { allowedExtensionsString } from "../../../utils/allowedExtensionsString";

const allowedExtensions = {
Comment thread
loiswells97 marked this conversation as resolved.
python: ["jpg", "jpeg", "png", "gif"],
};

const allowedExtensionsString = (projectType) => {
const extensionsList = allowedExtensions[projectType];
if (extensionsList.length === 1) {
return `'.${extensionsList[0]}'`;
} else {
return (
`'.` +
extensionsList.slice(0, -1).join(`', '.`) +
`' or '.` +
extensionsList[extensionsList.length - 1] +
`'`
);
}
};

const ImageUploadButton = ({ reactAppApiEndpoint }) => {
const [modalIsOpen, setIsOpen] = useState(false);
const [files, setFiles] = useState([]);
Expand All @@ -41,6 +28,7 @@ const ImageUploadButton = ({ reactAppApiEndpoint }) => {
const projectImages = useSelector((state) => state.editor.project.image_list);
const imageNames = projectImages.map((image) => `${image.filename}`);
const user = useSelector((state) => state.auth.user);
const { t } = useTranslation();

const closeModal = () => {
setFiles([]);
Expand All @@ -62,19 +50,27 @@ const ImageUploadButton = ({ reactAppApiEndpoint }) => {
imageNames.includes(fileName) ||
files.filter((file) => file.name === fileName).length > 1
) {
dispatch(setNameError("Image names must be unique."));
dispatch(
setNameError(t("imageUploadButton.errors.imageNameNotUnique")),
);
return false;
} else if (isValidFileName(fileName, files)) {
return true;
} else if (!allowedExtensions[projectType].includes(extension)) {
dispatch(
setNameError(
`Image names must end in ${allowedExtensionsString(projectType)}.`,
t("errors.invalidImageExtension", {
extensions: allowedExtensionsString(
projectType,
t,
allowedExtensions,
),
}),
),
);
return false;
} else {
dispatch(setNameError("Error"));
dispatch(setNameError("imageUploadButton.error"));
return false;
}
});
Expand Down Expand Up @@ -119,7 +115,7 @@ const ImageUploadButton = ({ reactAppApiEndpoint }) => {
return (
<>
<Button
buttonText="Upload Image"
buttonText={t("imageUploadButton.uploadImage")}
onClickHandler={showModal}
className="proj-image-upload-button"
/>
Expand All @@ -128,10 +124,10 @@ const ImageUploadButton = ({ reactAppApiEndpoint }) => {
isOpen={modalIsOpen}
onRequestClose={closeModal}
style={customStyles}
contentLabel="Upload Image"
contentLabel={t("imageUploadButton.uploadImage")}
appElement={document.getElementById("root") || undefined}
>
<h2>Upload an image</h2>
<h2>{t("imageUploadButton.uploadAnImage")}</h2>

<NameErrorMessage />
<Dropzone
Expand All @@ -143,9 +139,7 @@ const ImageUploadButton = ({ reactAppApiEndpoint }) => {
<section>
<div {...getRootProps()} className="dropzone-area">
<input {...getInputProps()} />
<p className="dropzone-info">
Drag and drop images here, or click to select images from file
</p>
<p className="dropzone-info">{t("imageUploadButton.info")}</p>
{files.map((file, i) => (
<p key={i}>{file.name}</p>
))}
Expand All @@ -154,8 +148,14 @@ const ImageUploadButton = ({ reactAppApiEndpoint }) => {
)}
</Dropzone>
<div className="modal-footer">
<Button buttonText="Cancel" onClickHandler={closeModal} />
<Button buttonText="Save" onClickHandler={saveImages} />
<Button
buttonText={t("imageUploadButton.cancel")}
onClickHandler={closeModal}
/>
<Button
buttonText={t("imageUploadButton.save")}
onClickHandler={saveImages}
/>
</div>
</Modal>
</>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,20 +44,20 @@ describe("When user logged in and owns project", () => {
</div>
</Provider>,
));
button = queryByText(/Upload Image/);
button = queryByText("imageUploadButton.uploadImage");
});

test("Modal opens when Image Upload button clicked", () => {
fireEvent.click(button);
const dropzone = queryByText(/Drag and drop/);
const dropzone = queryByText("imageUploadButton.info");
expect(dropzone).not.toBeNull();
});

test("Modal closes when cancel button clicked", () => {
fireEvent.click(button);
const cancelButton = queryByText(/Cancel/);
const cancelButton = queryByText("imageUploadButton.cancel");
fireEvent.click(cancelButton);
const dropzone = queryByText(/Drag and drop/);
const dropzone = queryByText("imageUploadButton.info");
expect(dropzone).toBeNull();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,19 @@ import React from "react";
import Button from "../../Button/Button";
import { useDispatch } from "react-redux";
import { addFilePanel } from "../EditorSlice";
import { useTranslation } from "react-i18next";

const NewInputPanelButton = () => {
const dispatch = useDispatch();
const { t } = useTranslation();

const openNewPanel = () => {
dispatch(addFilePanel());
};
return (
<Button
className={"btn--primary"}
buttonText="Add another panel"
buttonText={t("newInputPanelButton.buttonText")}
onClickHandler={openNewPanel}
/>
);
Expand Down
2 changes: 1 addition & 1 deletion src/components/Modals/ErrorModal.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ const ErrorModal = ({ errorType, additionalOnClose }) => {
onRequestClose={closeModal}
className="modal-content"
overlayClassName="modal-overlay"
contentLabel="Error"
contentLabel={t("modal.error.error")}
parentSelector={() =>
document.querySelector("#app") ||
document.querySelector("editor-wc").shadowRoot.querySelector("#wc")
Expand Down
10 changes: 10 additions & 0 deletions src/utils/allowedExtensionsString.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export const allowedExtensionsString = (projectType, t, allowedExtensions) => {
const extensionsList = allowedExtensions[projectType];
if (extensionsList.length === 1) {
return `'.${extensionsList[0]}'`;
} else {
return `'.${extensionsList.slice(0, -1).join(`', '.`)}' ${t(
"common.or",
)} '.${extensionsList[extensionsList.length - 1]}'`;
}
};
Comment thread
loiswells97 marked this conversation as resolved.
18 changes: 6 additions & 12 deletions src/utils/componentNameValidation.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { setNameError } from "../redux/EditorSlice";
import { allowedExtensionsString } from "./allowedExtensionsString";

const allowedExtensions = {
Comment thread
loiswells97 marked this conversation as resolved.
python: ["py", "csv", "txt"],
Expand All @@ -7,17 +8,6 @@ const allowedExtensions = {

const reservedFileNames = ["INSTRUCTIONS.md"];

const allowedExtensionsString = (projectType, t) => {
const extensionsList = allowedExtensions[projectType];
if (extensionsList.length === 1) {
return `'.${extensionsList[0]}'`;
} else {
return `'.${extensionsList.slice(0, -1).join(`', '.`)}' ${t(
"filePanel.errors.or",
)} '.${extensionsList[extensionsList.length - 1]}'`;
}
};

const isValidFileName = (fileName, projectType, componentNames) => {
const extension = fileName.split(".").slice(1).join(".");
if (
Expand Down Expand Up @@ -59,7 +49,11 @@ export const validateFileName = (
dispatch(
setNameError(
t("filePanel.errors.unsupportedExtension", {
allowedExtensions: allowedExtensionsString(projectType, t),
allowedExtensions: allowedExtensionsString(
projectType,
t,
allowedExtensions,
),
}),
),
);
Expand Down
52 changes: 50 additions & 2 deletions src/utils/i18n.js
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ i18n
modal: {
close: "Close",
error: {
error: "Error",
heading: "An error has occurred",
externalLink: {
message:
Expand All @@ -129,6 +130,7 @@ i18n
characterLimitExplanation:
"Files in the editor are limited to {{maxCharacters}} characters",
viewOnly: "View only",
close: "close",
},
filePanel: {
errors: {
Expand All @@ -137,7 +139,6 @@ i18n
containsSpaces: "File names must not contain spaces.",
generalError: "Error",
notUnique: "File names must be unique.",
or: "or",
unsupportedExtension:
"File names must end in {{allowedExtensions}}.",
},
Expand Down Expand Up @@ -416,13 +417,37 @@ i18n
loading: "Loading",
failed: "Load failed",
},
imageUploadButton: {
uploadImage: "Upload Image",
uploadAnImage: "Upload an image",
info: "Drag and drop images here, or click to select images from file",
cancel: "Cancel",
save: "Save",
or: "or",
errors: {
error: "Error",
imageNameNotUnique: "Image names must be unique.",
invalidImageExtension: "Image names must end in {{extensions}}.",
},
},
newInputPanelButton: {
buttonText: "Add another panel",
},
button: {
yes: "Yes",
no: "No",
},
common: {
or: "or",
},
},
},
"xx-XX": {
translation: {
modal: {
close: "Schließen",
error: {
error: "Fehler",
heading: "Ein Fehler ist aufgetreten",
externalLink: {
message:
Expand All @@ -436,6 +461,7 @@ i18n
characterLimitExplanation:
"Dateien im Editor sind auf {{maxCharacters}} Zeichen begrenzt",
viewOnly: "Nur Ansicht",
close: "schließen",
},
filePanel: {
errors: {
Expand All @@ -444,7 +470,6 @@ i18n
containsSpaces: "Dateinamen dürfen keine Leerzeichen enthalten.",
generalError: "Fehler",
notUnique: "Dateinamen müssen eindeutig sein.",
or: "oder",
unsupportedExtension:
"Dateinamen müssen mit {{allowedExtensions}} enden.",
},
Expand Down Expand Up @@ -724,6 +749,29 @@ i18n
loading: "Wird geladen",
failed: "Laden fehlgeschlagen",
},
imageUploadButton: {
uploadImage: "Bild hochladen",
uploadAnImage: "Laden Sie ein Bild hoch",
info: "Ziehen Sie Bilder hierher, oder klicken Sie, um Bilder aus der Datei auszuwählen",
cancel: "Stornieren",
save: "Speichern",
errors: {
error: "Fehler",
imageNameNotUnique: "Bildnamen müssen eindeutig sein.",
invalidImageExtension:
"Bildnamen müssen enden auf {{extensions}}.",
},
},
newInputPanelButton: {
buttonText: "Fügen Sie ein weiteres Panel hinzu",
},
button: {
yes: "Ja",
no: "Nein",
},
common: {
or: "oder",
},
},
},
},
Expand Down
Loading