From 467842dfeb37a90e4a45e7aa39060a25519c48ea Mon Sep 17 00:00:00 2001 From: Kristina Date: Mon, 4 Nov 2024 09:48:30 +0000 Subject: [PATCH 1/8] make the help desk available to regular ce users (#1099) ## Closes: https://github.com/RaspberryPiFoundation/digital-editor-issues/issues/300 --- CHANGELOG.md | 8 ++++++++ src/components/Menus/Sidebar/InfoPanel/InfoPanel.jsx | 5 +++++ src/components/Menus/Sidebar/InfoPanel/InfoPanel.test.js | 5 +++++ src/utils/i18n.js | 1 + 4 files changed, 19 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e1e9c2719..e147d65c1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ## Unreleased + +## [0.28.6] - 2024-10-29 + +### Changed + +- added "Help" link to the information panel in the sidebar as the first link + ## [0.28.5] - 2024-10-25 ### Fixed @@ -63,6 +70,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), - Build to include public files (#1112) - Persisting choice of tabbed/split view when running `python` code (#1114) + ## [0.27.1] - 2024-10-01 ### Fixed diff --git a/src/components/Menus/Sidebar/InfoPanel/InfoPanel.jsx b/src/components/Menus/Sidebar/InfoPanel/InfoPanel.jsx index c417da6a4..81e22cf10 100644 --- a/src/components/Menus/Sidebar/InfoPanel/InfoPanel.jsx +++ b/src/components/Menus/Sidebar/InfoPanel/InfoPanel.jsx @@ -7,6 +7,11 @@ import "../../../../assets/stylesheets/InfoPanel.scss"; const InfoPanel = () => { const { t } = useTranslation(); const links = [ + { + id: "help", + text: t("sidebar.help"), + href: "https://help.editor.raspberrypi.org/hc/en-us", + }, { id: "feedback", text: t("sidebar.feedback"), diff --git a/src/components/Menus/Sidebar/InfoPanel/InfoPanel.test.js b/src/components/Menus/Sidebar/InfoPanel/InfoPanel.test.js index eda89847c..14d131ebc 100644 --- a/src/components/Menus/Sidebar/InfoPanel/InfoPanel.test.js +++ b/src/components/Menus/Sidebar/InfoPanel/InfoPanel.test.js @@ -34,6 +34,7 @@ describe("Info panel", () => { }); test("Links are rendered", () => { + expect(screen.queryByText("sidebar.help")).toBeInTheDocument(); expect(screen.queryByText("sidebar.feedback")).toBeInTheDocument(); expect(screen.queryByText("sidebar.privacy")).toBeInTheDocument(); }); @@ -43,5 +44,9 @@ describe("Info panel", () => { "href", "https://form.raspberrypi.org/f/code-editor-feedback", ); + expect(screen.queryByText("sidebar.help")).toHaveAttribute( + "href", + "https://help.editor.raspberrypi.org/hc/en-us", + ); }); }); diff --git a/src/utils/i18n.js b/src/utils/i18n.js index 0c0c71740..d020738ce 100644 --- a/src/utils/i18n.js +++ b/src/utils/i18n.js @@ -397,6 +397,7 @@ i18n "Our Code Editor is a tool to help young people learn to code. We have only included functions that are simple and safe to use. That's why, for example, links to other websites are not allowed.", instructions: "Project steps", feedback: "Feedback", + help: "Help", privacy: "Privacy", cookies: "Cookies", accessibility: "Accessibility", From 932880d764759df5c040fa81f28cb942afa7f698 Mon Sep 17 00:00:00 2001 From: "create-issue-branch[bot]" <53036503+create-issue-branch[bot]@users.noreply.github.com> Date: Mon, 4 Nov 2024 14:48:29 +0000 Subject: [PATCH 2/8] Editor sidebar doesn't fill whole height (#1117) closes #1116 --------- Kristina --- CHANGELOG.md | 8 ++++++++ src/assets/stylesheets/Tabs.scss | 1 - 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e147d65c1..8e5aa1e0a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,12 +7,20 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ## Unreleased +## [0.28.5] - 2024-11-01 + +### Fixed + +- Hight of the sidebar for mobile (#1117) + + ## [0.28.6] - 2024-10-29 ### Changed - added "Help" link to the information panel in the sidebar as the first link + ## [0.28.5] - 2024-10-25 ### Fixed diff --git a/src/assets/stylesheets/Tabs.scss b/src/assets/stylesheets/Tabs.scss index 90492849c..2a6c42bd8 100644 --- a/src/assets/stylesheets/Tabs.scss +++ b/src/assets/stylesheets/Tabs.scss @@ -112,7 +112,6 @@ &__tab-panel--selected { flex: 1; display: flex; - flex-direction: column; overflow-y: auto; } } From 817e28c16c8e774c36e775eb880f1295d3623bf0 Mon Sep 17 00:00:00 2001 From: Conor <905676+conorriches@users.noreply.github.com> Date: Tue, 5 Nov 2024 16:57:24 +0000 Subject: [PATCH 3/8] set REACT_APP _API_ENDPOINT and _AUTHENTICATION_URL as props (#1124) ## Issue Closes https://github.com/RaspberryPiFoundation/learner-experience-integration-tests/issues/5 ## Description * Changed how both utils function: * `src/utils/apiCallHandler.js` * `src/utils/userManager.js` * I've considered how best to adapt these helpers - I could have gone with classes or functions, and while I considered changing these to be classes, this would require more verbose invocation throughout the codebase which ended up making code harder to read, whereas the function-based approach means most calls can be left as-is. * `public/index.html` added which provides a handy link to the web-component.html rather than just a directory listing * Removed a load of code that isn't used any more --------- Co-authored-by: Scott Adams <74183390+sra405@users.noreply.github.com> Co-authored-by: create-issue-branch[bot] <53036503+create-issue-branch[bot]@users.noreply.github.com> Co-authored-by: Lois Wells Co-authored-by: Lois Wells <88904316+loiswells97@users.noreply.github.com> --- .env.example | 3 +- .github/workflows/ci-cd.yml | 2 +- CHANGELOG.md | 8 + public/index.html | 4 + src/app/store.js | 5 +- .../DownloadButton/DownloadButton.jsx | 10 +- .../DownloadButton/DownloadButton.test.js | 25 -- .../ImageUploadButton/ImageUploadButton.jsx | 8 +- .../PyodideRunner/PyodideRunner.jsx | 6 +- .../SkulptRunner/SkulptRunner.jsx | 5 +- src/components/Login/LoginButton.jsx | 42 ---- src/components/Login/LoginButton.test.js | 229 ----------------- src/components/Login/LoginMenu.jsx | 45 ---- src/components/Login/LoginMenu.test.js | 94 ------- src/components/Login/LogoutButton.jsx | 33 --- src/components/Login/LogoutButton.test.js | 45 ---- .../ProjectActionsMenu/ProjectActionsMenu.jsx | 51 ---- .../ProjectActionsMenu.test.js | 55 ----- .../Modals/AccessDeniedNoAuthModal.jsx | 76 ------ .../Modals/AccessDeniedNoAuthModal.test.js | 54 ---- .../AccessDeniedNoAuthModalEmbedded.jsx | 31 --- src/components/Modals/DeleteProjectModal.jsx | 72 ------ .../Modals/DeleteProjectModal.test.js | 94 ------- src/components/Modals/LoginToSaveModal.jsx | 62 ----- .../Modals/LoginToSaveModal.test.js | 45 ---- src/components/Modals/NewProjectModal.jsx | 94 ------- src/components/Modals/NewProjectModal.test.js | 117 --------- src/components/ProjectIndex/ProjectIndex.jsx | 110 --------- .../ProjectIndex/ProjectIndex.test.js | 130 ---------- .../ProjectIndex/ProjectIndexPagination.jsx | 53 ---- .../ProjectIndexPagination.test.js | 166 ------------- .../ProjectIndexHeader/ProjectIndexHeader.jsx | 23 -- .../ProjectListItem/ProjectListItem.jsx | 94 ------- .../ProjectListItem/ProjectListItem.test.js | 49 ---- .../ProjectListTable/ProjectListTable.jsx | 45 ---- .../ProjectListTable/ProjectListTable.test.js | 64 ----- src/containers/WebComponentLoader.jsx | 29 ++- src/containers/WebComponentLoader.test.js | 69 ++++++ src/hooks/useProject.js | 5 + src/hooks/useProject.test.js | 33 ++- src/hooks/useProjectPersistence.js | 3 - src/hooks/useProjectPersistence.test.js | 19 -- src/redux/EditorSlice.js | 100 ++------ src/redux/EditorSlice.test.js | 143 +++-------- src/redux/reducers/loadProjectReducers.js | 2 - .../reducers/loadProjectReducers.test.js | 9 +- src/utils/apiCallHandler.js | 230 ++++++++++-------- src/utils/apiCallHandler.test.js | 22 +- src/utils/i18n.js | 41 ---- src/utils/login.js | 32 --- src/utils/sentry.js | 2 +- src/utils/userManager.js | 26 +- src/web-component.js | 37 +-- 53 files changed, 400 insertions(+), 2451 deletions(-) create mode 100644 public/index.html delete mode 100644 src/components/Login/LoginButton.jsx delete mode 100644 src/components/Login/LoginButton.test.js delete mode 100644 src/components/Login/LoginMenu.jsx delete mode 100644 src/components/Login/LoginMenu.test.js delete mode 100644 src/components/Login/LogoutButton.jsx delete mode 100644 src/components/Login/LogoutButton.test.js delete mode 100644 src/components/Menus/ProjectActionsMenu/ProjectActionsMenu.jsx delete mode 100644 src/components/Menus/ProjectActionsMenu/ProjectActionsMenu.test.js delete mode 100644 src/components/Modals/AccessDeniedNoAuthModal.jsx delete mode 100644 src/components/Modals/AccessDeniedNoAuthModal.test.js delete mode 100644 src/components/Modals/AccessDeniedNoAuthModalEmbedded.jsx delete mode 100644 src/components/Modals/DeleteProjectModal.jsx delete mode 100644 src/components/Modals/DeleteProjectModal.test.js delete mode 100644 src/components/Modals/LoginToSaveModal.jsx delete mode 100644 src/components/Modals/LoginToSaveModal.test.js delete mode 100644 src/components/Modals/NewProjectModal.jsx delete mode 100644 src/components/Modals/NewProjectModal.test.js delete mode 100644 src/components/ProjectIndex/ProjectIndex.jsx delete mode 100644 src/components/ProjectIndex/ProjectIndex.test.js delete mode 100644 src/components/ProjectIndex/ProjectIndexPagination.jsx delete mode 100644 src/components/ProjectIndex/ProjectIndexPagination.test.js delete mode 100644 src/components/ProjectIndexHeader/ProjectIndexHeader.jsx delete mode 100644 src/components/ProjectListItem/ProjectListItem.jsx delete mode 100644 src/components/ProjectListItem/ProjectListItem.test.js delete mode 100644 src/components/ProjectListTable/ProjectListTable.jsx delete mode 100644 src/components/ProjectListTable/ProjectListTable.test.js delete mode 100644 src/utils/login.js diff --git a/.env.example b/.env.example index 85d2e38ad..4d4e2433a 100644 --- a/.env.example +++ b/.env.example @@ -1,10 +1,9 @@ REACT_APP_AUTHENTICATION_CLIENT_ID='editor-dev' -REACT_APP_AUTHENTICATION_URL='http://localhost:9001' REACT_APP_SENTRY_DSN='' REACT_APP_SENTRY_ENV='local' PUBLIC_URL='http://localhost:3011' ASSETS_URL='http://localhost:3011' -REACT_APP_API_ENDPOINT='http://localhost:3009' REACT_APP_GOOGLE_TAG_MANAGER_ID='' +REACT_APP_API_ENDPOINT='http://localhost:3009' REACT_APP_PLAUSIBLE_DATA_DOMAIN='' REACT_APP_PLAUSIBLE_SOURCE='' diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index 8f0a5f4b1..8b7134c1b 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -61,7 +61,7 @@ jobs: run: yarn run test --coverage --maxWorkers=4 --workerThreads=true --reporters=default --reporters=jest-junit --reporters=jest-github-actions-reporter env: JEST_JUNIT_OUTPUT_DIR: ./coverage/ - + REACT_APP_API_ENDPOINT: http://localhost:3009 - name: Record coverage run: ./.github/workflows/record_coverage env: diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e5aa1e0a..a6e9617b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ## Unreleased +## [0.28.8] - 2024-11-04 + +### Changed +- REACT_APP_API_ENDPOINT env var is now only a default for the editor-wc prop, which can be overridden (#1124) + +### Removed +- REACT_APP_AUTHENTICATION_URL env var no longer used and is instead a editor-wc prop (#1124) + ## [0.28.5] - 2024-11-01 diff --git a/public/index.html b/public/index.html new file mode 100644 index 000000000..1dbfdf1f4 --- /dev/null +++ b/public/index.html @@ -0,0 +1,4 @@ +

You may be looking for...

+ diff --git a/src/app/store.js b/src/app/store.js index aa1691a78..ec4e800b2 100644 --- a/src/app/store.js +++ b/src/app/store.js @@ -2,8 +2,11 @@ import { configureStore } from "@reduxjs/toolkit"; import EditorReducer from "../redux/EditorSlice"; import InstructionsReducer from "../redux/InstructionsSlice"; import { reducer, loadUser } from "redux-oidc"; -import userManager from "../utils/userManager"; +import UserManager from "../utils/userManager"; +// TODO - not used but keeping this in preparation for using +// src/components/Editor/ImageUploadButton/ImageUploadButton.jsx +const userManager = UserManager({ reactAppAuthenticationUrl: "TODO" }); const store = configureStore({ reducer: { editor: EditorReducer, diff --git a/src/components/DownloadButton/DownloadButton.jsx b/src/components/DownloadButton/DownloadButton.jsx index 4016ae54c..eb9c15b8d 100644 --- a/src/components/DownloadButton/DownloadButton.jsx +++ b/src/components/DownloadButton/DownloadButton.jsx @@ -4,11 +4,10 @@ import { toSnakeCase } from "js-convert-case"; import JSZip from "jszip"; import JSZipUtils from "jszip-utils"; import { useTranslation } from "react-i18next"; -import { useDispatch, useSelector } from "react-redux"; +import { useSelector } from "react-redux"; import PropTypes from "prop-types"; import DesignSystemButton from "../DesignSystemButton/DesignSystemButton"; -import { closeLoginToSaveModal } from "../../redux/EditorSlice"; const DownloadButton = (props) => { const { @@ -20,10 +19,6 @@ const DownloadButton = (props) => { } = props; const { t } = useTranslation(); const project = useSelector((state) => state.editor.project); - const loginToSaveModalShowing = useSelector( - (state) => state.editor.loginToSaveModalShowing, - ); - const dispatch = useDispatch(); const urlToPromise = (url) => { return new Promise(function (resolve, reject) { @@ -42,9 +37,6 @@ const DownloadButton = (props) => { window.plausible("Download"); } - if (loginToSaveModalShowing) { - dispatch(closeLoginToSaveModal()); - } const zip = new JSZip(); project.components.forEach((file) => { diff --git a/src/components/DownloadButton/DownloadButton.test.js b/src/components/DownloadButton/DownloadButton.test.js index 7c2c50d35..ef9e38253 100644 --- a/src/components/DownloadButton/DownloadButton.test.js +++ b/src/components/DownloadButton/DownloadButton.test.js @@ -6,7 +6,6 @@ import DownloadButton from "./DownloadButton"; import FileSaver from "file-saver"; import JSZip from "jszip"; import JSZipUtils from "jszip-utils"; -import { closeLoginToSaveModal } from "../../redux/EditorSlice"; jest.mock("file-saver"); jest.mock("jszip"); @@ -124,27 +123,3 @@ describe("Downloading project with no name set", () => { ); }); }); - -test("If login to save modal open, closes it when download clicked", () => { - JSZip.mockClear(); - const middlewares = []; - const mockStore = configureStore(middlewares); - const initialState = { - editor: { - project: { - components: [], - image_list: [], - }, - loginToSaveModalShowing: true, - }, - }; - const store = mockStore(initialState); - render( - - {}} /> - , - ); - const downloadButton = screen.queryByText("Download").parentElement; - fireEvent.click(downloadButton); - expect(store.getActions()).toEqual([closeLoginToSaveModal()]); -}); diff --git a/src/components/Editor/ImageUploadButton/ImageUploadButton.jsx b/src/components/Editor/ImageUploadButton/ImageUploadButton.jsx index 11ac2fdf4..ecb3b144d 100644 --- a/src/components/Editor/ImageUploadButton/ImageUploadButton.jsx +++ b/src/components/Editor/ImageUploadButton/ImageUploadButton.jsx @@ -9,7 +9,7 @@ import { updateImages, setNameError } from "../../../redux/EditorSlice"; import Button from "../../Button/Button"; import NameErrorMessage from "../ErrorMessage/NameErrorMessage"; import store from "../../../app/store"; -import { uploadImages } from "../../../utils/apiCallHandler"; +import ApiCallHandler from "../../../utils/apiCallHandler"; const allowedExtensions = { python: ["jpg", "jpeg", "png", "gif"], @@ -30,7 +30,7 @@ const allowedExtensionsString = (projectType) => { } }; -const ImageUploadButton = () => { +const ImageUploadButton = ({ reactAppApiEndpoint }) => { const [modalIsOpen, setIsOpen] = useState(false); const [files, setFiles] = useState([]); const dispatch = useDispatch(); @@ -51,6 +51,10 @@ const ImageUploadButton = () => { setIsOpen(true); }; const saveImages = async () => { + const { uploadImages } = ApiCallHandler({ + reactAppApiEndpoint, + }); + files.every((file) => { const fileName = file.name; const extension = fileName.split(".").slice(1).join(".").toLowerCase(); diff --git a/src/components/Editor/Runners/PythonRunner/PyodideRunner/PyodideRunner.jsx b/src/components/Editor/Runners/PythonRunner/PyodideRunner/PyodideRunner.jsx index 8bb232feb..19a6e71a5 100644 --- a/src/components/Editor/Runners/PythonRunner/PyodideRunner/PyodideRunner.jsx +++ b/src/components/Editor/Runners/PythonRunner/PyodideRunner/PyodideRunner.jsx @@ -12,7 +12,7 @@ import { Tab, Tabs, TabList, TabPanel } from "react-tabs"; import { useMediaQuery } from "react-responsive"; import { MOBILE_MEDIA_QUERY } from "../../../../../utils/mediaQueryBreakpoints"; import ErrorMessage from "../../../ErrorMessage/ErrorMessage"; -import { createError } from "../../../../../utils/apiCallHandler"; +import ApiCallHandler from "../../../../../utils/apiCallHandler"; import VisualOutputPane from "./VisualOutputPane"; import OutputViewToggle from "../OutputViewToggle"; import { SettingsContext } from "../../../../../utils/settings"; @@ -47,6 +47,7 @@ const PyodideRunner = (props) => { const userId = user?.profile?.user; const isSplitView = useSelector((s) => s.editor.isSplitView); const isEmbedded = useSelector((s) => s.editor.isEmbedded); + const reactAppApiEndpoint = useSelector((s) => s.editor.reactAppApiEndpoint); const codeRunTriggered = useSelector((s) => s.editor.codeRunTriggered); const codeRunStopped = useSelector((s) => s.editor.codeRunStopped); const output = useRef(); @@ -180,6 +181,9 @@ const PyodideRunner = (props) => { errorMessage += `:\n${mistake}`; } + const { createError } = ApiCallHandler({ + reactAppApiEndpoint, + }); createError(projectIdentifier, userId, { errorType: type, errorMessage }); } diff --git a/src/components/Editor/Runners/PythonRunner/SkulptRunner/SkulptRunner.jsx b/src/components/Editor/Runners/PythonRunner/SkulptRunner/SkulptRunner.jsx index dc852989c..4e835f4bf 100644 --- a/src/components/Editor/Runners/PythonRunner/SkulptRunner/SkulptRunner.jsx +++ b/src/components/Editor/Runners/PythonRunner/SkulptRunner/SkulptRunner.jsx @@ -15,7 +15,7 @@ import { triggerDraw, } from "../../../../../redux/EditorSlice"; import ErrorMessage from "../../../ErrorMessage/ErrorMessage"; -import { createError } from "../../../../../utils/apiCallHandler"; +import ApiCallHandler from "../../../../../utils/apiCallHandler"; import store from "../../../../../redux/stores/WebComponentStore"; import VisualOutputPane from "../VisualOutputPane"; import OutputViewToggle from "../OutputViewToggle"; @@ -71,6 +71,7 @@ const SkulptRunner = ({ active, outputPanels = ["text", "visual"] }) => { ); const codeRunStopped = useSelector((state) => state.editor.codeRunStopped); const drawTriggered = useSelector((state) => state.editor.drawTriggered); + const reactAppApiEndpoint = useSelector((s) => s.editor.reactAppApiEndpoint); const output = useRef(); const dispatch = useDispatch(); const { t } = useTranslation(); @@ -314,6 +315,8 @@ const SkulptRunner = ({ active, outputPanels = ["text", "visual"] }) => { userId = user.profile?.user; } + const { createError } = ApiCallHandler({ reactAppApiEndpoint }); + errorMessage = `${errorType}: ${errorDescription} on line ${lineNumber} of ${fileName}${ explanation ? `. ${explanation}` : "" }`; diff --git a/src/components/Login/LoginButton.jsx b/src/components/Login/LoginButton.jsx deleted file mode 100644 index c4a703e8b..000000000 --- a/src/components/Login/LoginButton.jsx +++ /dev/null @@ -1,42 +0,0 @@ -import React from "react"; -import { useLocation } from "react-router-dom"; -import { useSelector } from "react-redux"; -import Button from "../Button/Button"; -import { login } from "../../utils/login"; -import PropTypes from "prop-types"; - -const LoginButton = ({ buttonText, className, triggerSave, loginRedirect }) => { - const location = useLocation(); - const project = useSelector((state) => state.editor.project); - const accessDeniedData = useSelector( - (state) => state.editor.modals?.accessDenied || null, - ); - - const onLoginButtonClick = (event) => { - event.preventDefault(); - login({ - project, - location, - triggerSave, - accessDeniedData, - loginRedirect, - }); - }; - - return ( -