Skip to content

Commit df17887

Browse files
authored
1187: add upload button to project bar (#1387)
issue: [1187](RaspberryPiFoundation/digital-editor-issues#1187) This is a follow up to the addition of the upload button to the code editors save and download panel for scratch. This adds the upload button to the project panel ONLY for scratch projects
1 parent 61c7750 commit df17887

9 files changed

Lines changed: 364 additions & 174 deletions

File tree

AGENTS.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,11 @@ yarn exec cypress open
5656
`src/assets/stylesheets/InternalStyles.scss` (and external libs in
5757
`ExternalStyles.scss`); avoid `rem`, prefer `em` and `--scale-factor`.
5858

59+
## Translations / locale files
60+
- Only update `public/translations/en.json` when adding or changing translation keys.
61+
- Do not add or edit keys in other locale files under `public/translations/`; Crowdin sync overwrites them.
62+
- Missing keys in non-en locales are intentional until Crowdin syncs; the app falls back to `en`. Do not suggest adding a new key to other locale files when it appears only in `en.json`.
63+
5964
## Git & Commit Guidance
6065
- Write descriptive commit messages that explain why a change was made.
6166
- When applicable, include alternatives considered and why they were not

cypress/helpers/editor.js

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,28 @@
11
export const getEditorShadow = () => cy.get("editor-wc").shadow();
22

3+
export const getSidebarPanel = () =>
4+
getEditorShadow().findByTestId("sidebar__panel");
5+
36
export const openSaveAndDownloadPanel = () => {
47
getEditorShadow().findByRole("button", { name: "Download project" }).click();
5-
getEditorShadow()
8+
9+
getSidebarPanel()
610
.findByRole("heading", { name: "Save & download" })
711
.should("be.visible");
812

913
return {
1014
uploadProject: (fixturePath) => {
11-
getEditorShadow()
12-
.find(".download-panel__download-section")
13-
.findByRole("button", { name: "Upload project" })
14-
.should("be.visible");
15-
getEditorShadow()
16-
.findByTestId("upload-file-input")
17-
.selectFile(fixturePath, { force: true });
15+
getSidebarPanel().within(() => {
16+
cy.findByRole("button", { name: "Upload project" }).should(
17+
"be.visible",
18+
);
19+
cy.findByTestId("upload-file-input").selectFile(fixturePath, {
20+
force: true,
21+
});
22+
});
1823
},
1924
downloadProject: () => {
20-
getEditorShadow()
21-
.find(".download-panel__download-section")
25+
getSidebarPanel()
2226
.findByRole("button", { name: "Download project" })
2327
.click();
2428
},

public/translations/en.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@
7474
},
7575
"header": {
7676
"download": "Download",
77+
"upload": "Upload",
7778
"downloadFileNameDefault": "my {{project_type}} project",
7879
"editorLogoAltText": "Editor logo",
7980
"projects": "Your projects",

src/components/Editor/Project/Project.jsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import "../../../assets/stylesheets/Project.scss";
1010
import Output from "../Output/Output";
1111
import { showSavedMessage } from "../../../utils/Notifications";
1212
import ProjectBar from "../../ProjectBar/ProjectBar";
13+
import ScratchProjectBar from "../../ProjectBar/ScratchProjectBar";
1314
import Sidebar from "../../Menus/Sidebar/Sidebar";
1415
import EditorInput from "../EditorInput/EditorInput";
1516
import ResizableWithHandle from "../../../utils/ResizableWithHandle";
@@ -76,7 +77,12 @@ const Project = (props) => {
7677
/>
7778
)}
7879
<div className="project-wrapper" ref={containerRef}>
79-
{withProjectbar && <ProjectBar nameEditable={nameEditable} />}
80+
{withProjectbar &&
81+
(isCodeEditorScratchProject ? (
82+
<ScratchProjectBar nameEditable={nameEditable} />
83+
) : (
84+
<ProjectBar nameEditable={nameEditable} />
85+
))}
8086
{!loading && !isCodeEditorScratchProject && (
8187
<div className="proj-editor-wrapper">
8288
<ResizableWithHandle

src/components/Editor/Project/Project.test.js

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,56 @@ test("Renders sidebar with correct options if withSidebar is true", () => {
6767
expect(screen.queryByTitle("sidebar.settings")).toBeInTheDocument();
6868
});
6969

70+
describe("Project bar selection", () => {
71+
test("renders default project bar for non-scratch projects", () => {
72+
const middlewares = [];
73+
const mockStore = configureStore(middlewares);
74+
const initialState = {
75+
editor: {
76+
project: { components: [], project_type: "python" },
77+
loading: "success",
78+
openFiles: [[]],
79+
focussedFileIndices: [0],
80+
},
81+
auth: {},
82+
};
83+
const store = mockStore(initialState);
84+
render(
85+
<Provider store={store}>
86+
<MemoryRouter>
87+
<Project />
88+
</MemoryRouter>
89+
</Provider>,
90+
);
91+
expect(screen.getByTestId("default-project-bar")).toBeInTheDocument();
92+
expect(screen.queryByTestId("scratch-project-bar")).not.toBeInTheDocument();
93+
});
94+
95+
test("renders scratch project bar for code_editor_scratch projects", () => {
96+
const middlewares = [];
97+
const mockStore = configureStore(middlewares);
98+
const initialState = {
99+
editor: {
100+
project: { components: [], project_type: "code_editor_scratch" },
101+
loading: "success",
102+
openFiles: [[]],
103+
focussedFileIndices: [0],
104+
},
105+
auth: {},
106+
};
107+
const store = mockStore(initialState);
108+
render(
109+
<Provider store={store}>
110+
<MemoryRouter>
111+
<Project />
112+
</MemoryRouter>
113+
</Provider>,
114+
);
115+
expect(screen.getByTestId("scratch-project-bar")).toBeInTheDocument();
116+
expect(screen.queryByTestId("default-project-bar")).not.toBeInTheDocument();
117+
});
118+
});
119+
70120
test("Renders container for scratch projects", () => {
71121
const middlewares = [];
72122
const mockStore = configureStore(middlewares);

src/components/ProjectBar/ProjectBar.jsx

Lines changed: 3 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -3,43 +3,29 @@ import { useSelector } from "react-redux";
33
import { useTranslation } from "react-i18next";
44
import SaveStatus from "../SaveStatus/SaveStatus";
55
import DownloadIcon from "../../assets/icons/download.svg";
6-
import SaveIcon from "../../assets/icons/save.svg";
76
import ProjectName from "../ProjectName/ProjectName";
87
import DownloadButton from "../DownloadButton/DownloadButton";
98
import SaveButton from "../SaveButton/SaveButton";
10-
import DesignSystemButton from "../DesignSystemButton/DesignSystemButton";
119

1210
import "../../assets/stylesheets/ProjectBar.scss";
1311
import { isOwner } from "../../utils/projectHelpers";
14-
import { useScratchSaveState } from "../../hooks/useScratchSaveState";
1512

1613
const ProjectBar = ({ nameEditable = true }) => {
1714
const { t } = useTranslation();
18-
1915
const project = useSelector((state) => state.editor.project);
2016
const user = useSelector((state) => state.auth.user);
2117
const loading = useSelector((state) => state.editor.loading);
2218
const saving = useSelector((state) => state.editor.saving);
2319
const lastSavedTime = useSelector((state) => state.editor.lastSavedTime);
2420
const projectOwner = isOwner(user, project);
2521
const readOnly = useSelector((state) => state.editor.readOnly);
26-
const isScratchProject = project?.project_type === "code_editor_scratch";
27-
const showScratchSaveButton = Boolean(isScratchProject && user && !readOnly);
28-
const enableScratchSaveState = Boolean(
29-
loading === "success" && showScratchSaveButton,
30-
);
31-
const { isScratchSaving, saveScratchProject, scratchSaveLabelKey } =
32-
useScratchSaveState({
33-
enabled: enableScratchSaveState,
34-
});
35-
const scratchSaveLabel = t(scratchSaveLabelKey);
3622

3723
if (loading !== "success") {
3824
return null;
3925
}
4026

4127
return (
42-
<div className="project-bar">
28+
<div className="project-bar" data-testid="default-project-bar">
4329
<ProjectName editable={!readOnly && nameEditable} isHeading={true} />
4430
<div className="project-bar__right">
4531
<div className="project-bar__btn-wrapper">
@@ -50,25 +36,12 @@ const ProjectBar = ({ nameEditable = true }) => {
5036
type="tertiary"
5137
/>
5238
</div>
53-
{!isScratchProject && !projectOwner && !readOnly && (
39+
{!projectOwner && !readOnly && (
5440
<div className="project-bar__btn-wrapper">
5541
<SaveButton className="project-bar__btn btn--save" />
5642
</div>
5743
)}
58-
{showScratchSaveButton && (
59-
<div className="project-bar__btn-wrapper">
60-
<DesignSystemButton
61-
className="project-bar__btn btn--save btn--primary"
62-
onClick={saveScratchProject}
63-
text={scratchSaveLabel}
64-
textAlways
65-
icon={<SaveIcon />}
66-
type="primary"
67-
disabled={isScratchSaving}
68-
/>
69-
</div>
70-
)}
71-
{lastSavedTime && user && !readOnly && !isScratchProject && (
44+
{lastSavedTime && user && !readOnly && (
7245
<SaveStatus saving={saving} lastSavedTime={lastSavedTime} />
7346
)}
7447
</div>

0 commit comments

Comments
 (0)