Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ The `editor-wc` tag accepts the following attributes, which must be provided as
- `code`: A preset blob of code to show in the editor pane (overrides content of `main.py`/`index.html`)
- `editable_instructions`: Boolean whether to show edit panel for instructions
- `embedded`: Enable embedded mode which hides some functionality (defaults to `false`)
- `feedback_form_url`: URL used by the Feedback link in the info panel (defaults to the Code Editor feedback form)
- `host_styles`: Styles passed into the web component from the host page
- `identifier`: Load the project with this identifier from the database
- `instructions`: Stringified JSON containing steps to be displayed in the instructions panel in the sidebar
Expand Down
2 changes: 2 additions & 0 deletions src/components/Editor/Project/Project.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const Project = (props) => {
withProjectbar = true,
withSidebar = true,
sidebarOptions = [],
feedbackFormUrl,
sidebarPlugins = [],
} = props;
const saving = useSelector((state) => state.editor.saving);
Expand Down Expand Up @@ -73,6 +74,7 @@ const Project = (props) => {
<Sidebar
options={sidebarOptions}
plugins={sidebarPlugins}
feedbackFormUrl={feedbackFormUrl}
allowMobileView={!isCodeEditorScratchProject}
/>
)}
Expand Down
22 changes: 20 additions & 2 deletions src/components/Menus/Sidebar/InfoPanel/InfoPanel.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,25 @@ import SidebarPanel from "../SidebarPanel";

import "../../../../assets/stylesheets/InfoPanel.scss";

const InfoPanel = () => {
const CODE_EDITOR_FEEDBACK_URL =
"https://form.raspberrypi.org/f/code-editor-feedback";

const feedbackUrl = (url) => {
if (typeof url !== "string" || url.trim() === "") {
return CODE_EDITOR_FEEDBACK_URL;
}

try {
const parsedUrl = new URL(url.trim());
return parsedUrl.protocol === "https:"
? parsedUrl.href
: CODE_EDITOR_FEEDBACK_URL;
Comment thread
abcampo-iry marked this conversation as resolved.
} catch {
return CODE_EDITOR_FEEDBACK_URL;
}
};

const InfoPanel = ({ feedbackFormUrl = CODE_EDITOR_FEEDBACK_URL }) => {
const { t } = useTranslation();
const links = [
{
Expand All @@ -15,7 +33,7 @@ const InfoPanel = () => {
{
id: "feedback",
text: t("sidebar.feedback"),
href: "https://form.raspberrypi.org/f/code-editor-feedback",
href: feedbackUrl(feedbackFormUrl),
},
Comment thread
abcampo-iry marked this conversation as resolved.
{
id: "privacy",
Expand Down
85 changes: 59 additions & 26 deletions src/components/Menus/Sidebar/InfoPanel/InfoPanel.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,40 +6,44 @@ import { MemoryRouter } from "react-router-dom";

import InfoPanel from "./InfoPanel";

describe("Info panel", () => {
beforeEach(() => {
const middlewares = [];
const mockStore = configureStore(middlewares);
const initialState = {
editor: {
project: {
components: [
{
name: "main",
extension: "py",
},
],
},
const renderInfoPanel = (props = {}) => {
const middlewares = [];
const mockStore = configureStore(middlewares);
const initialState = {
editor: {
project: {
components: [
{
name: "main",
extension: "py",
},
],
},
};
const store = mockStore(initialState);

render(
<Provider store={store}>
<MemoryRouter>
<InfoPanel t={() => {}} />
</MemoryRouter>
</Provider>,
);
});
},
};
const store = mockStore(initialState);

render(
<Provider store={store}>
<MemoryRouter>
<InfoPanel {...props} />
</MemoryRouter>
</Provider>,
);
};

describe("Info panel", () => {
test("Links are rendered", () => {
renderInfoPanel();

expect(screen.queryByText("sidebar.help")).toBeInTheDocument();
expect(screen.queryByText("sidebar.feedback")).toBeInTheDocument();
expect(screen.queryByText("sidebar.privacy")).toBeInTheDocument();
});

test("Links have the expected source", () => {
test("Links have the expected default source", () => {
renderInfoPanel();

expect(screen.queryByText("sidebar.feedback")).toHaveAttribute(
"href",
"https://form.raspberrypi.org/f/code-editor-feedback",
Expand All @@ -49,4 +53,33 @@ describe("Info panel", () => {
"https://help.editor.raspberrypi.org/hc/en-us",
);
});

test("Uses the supplied feedback form source", () => {
renderInfoPanel({
feedbackFormUrl: "https://form.raspberrypi.org/4873801",
});

expect(screen.queryByText("sidebar.feedback")).toHaveAttribute(
"href",
"https://form.raspberrypi.org/4873801",
);
});

test.each([
`java${"script"}:alert(1)`,
"http://example.com",
"ftp://example.com",
"not-a-url",
"",
])(
"Falls back to the default feedback form for invalid feedback form source %p",
(feedbackFormUrl) => {
renderInfoPanel({ feedbackFormUrl });

expect(screen.queryByText("sidebar.feedback")).toHaveAttribute(
"href",
"https://form.raspberrypi.org/f/code-editor-feedback",
);
},
);
});
Comment thread
abcampo-iry marked this conversation as resolved.
15 changes: 13 additions & 2 deletions src/components/Menus/Sidebar/Sidebar.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,12 @@ import InstructionsPanel from "./InstructionsPanel/InstructionsPanel";
import SidebarPanel from "./SidebarPanel";
import { setSidebarOption } from "../../../redux/EditorSlice";

const Sidebar = ({ options = [], plugins = [], allowMobileView = true }) => {
const Sidebar = ({
options = [],
plugins = [],
feedbackFormUrl,
allowMobileView = true,
}) => {
const { t } = useTranslation();
const dispatch = useDispatch();
const projectType = useSelector((state) => state.editor.project.project_type);
Expand Down Expand Up @@ -92,6 +97,7 @@ const Sidebar = ({ options = [], plugins = [], allowMobileView = true }) => {
title: t("sidebar.information"),
position: "bottom",
panel: InfoPanel,
panelProps: { feedbackFormUrl },
},
].filter((option) => {
if (!options.includes(option.name)) return false;
Expand Down Expand Up @@ -203,7 +209,12 @@ const Sidebar = ({ options = [], plugins = [], allowMobileView = true }) => {
instructions={instructionsSteps}
allowMobileView={allowMobileView}
/>
{activeOption && <CustomSidebarPanel isMobile={isMobile} />}
{activeOption && (
<CustomSidebarPanel
isMobile={isMobile}
{...(optionDict.panelProps || {})}
/>
)}
</div>
);
};
Expand Down
24 changes: 24 additions & 0 deletions src/components/Menus/Sidebar/Sidebar.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -511,6 +511,30 @@ describe("When plugins are provided", () => {
});
});

describe("When a feedback form URL is provided", () => {
test("Passes the feedback form URL to the info panel", () => {
const initialState = {
editor: {
project: {
components: [],
image_list: [],
},
},
instructions: {},
};
renderSidebarWithState(initialState, {
feedbackFormUrl: "https://form.raspberrypi.org/4873801",
});

fireEvent.click(screen.getByTitle("sidebar.information"));

expect(screen.queryByText("sidebar.feedback")).toHaveAttribute(
"href",
"https://form.raspberrypi.org/4873801",
);
});
});

describe("When the project type is code_editor_scratch", () => {
beforeEach(() => {
const mockStore = configureStore([]);
Expand Down
7 changes: 6 additions & 1 deletion src/components/Mobile/MobileProject/MobileProject.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { showSidebar } from "../../../redux/EditorSlice";
const MobileProject = ({
withSidebar,
sidebarOptions = [],
feedbackFormUrl,
sidebarPlugins = [],
}) => {
const projectType = useSelector((state) => state.editor.project.project_type);
Expand Down Expand Up @@ -52,7 +53,11 @@ const MobileProject = ({
>
{withSidebar && (
<TabPanel>
<Sidebar options={sidebarOptions} plugins={sidebarPlugins} />
<Sidebar
options={sidebarOptions}
plugins={sidebarPlugins}
feedbackFormUrl={feedbackFormUrl}
/>
</TabPanel>
)}
<TabPanel>
Expand Down
3 changes: 3 additions & 0 deletions src/components/WebComponentProject/WebComponentProject.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ const WebComponentProject = ({
outputOnly = false,
outputPanels = ["text", "visual"],
outputSplitView = false,
feedbackFormUrl,
sidebarPlugins = [],
}) => {
const loading = useSelector((state) => state.editor.loading);
Expand Down Expand Up @@ -161,6 +162,7 @@ const WebComponentProject = ({
<MobileProject
withSidebar={withSidebar}
sidebarOptions={sidebarOptions}
feedbackFormUrl={feedbackFormUrl}
sidebarPlugins={sidebarPlugins}
/>
) : (
Expand All @@ -169,6 +171,7 @@ const WebComponentProject = ({
withProjectbar={withProjectbar}
withSidebar={withSidebar}
sidebarOptions={sidebarOptions}
feedbackFormUrl={feedbackFormUrl}
sidebarPlugins={sidebarPlugins}
/>
))}
Expand Down
5 changes: 5 additions & 0 deletions src/containers/WebComponentLoader.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ import {
projectOwnerLoadedEvent,
} from "../events/WebComponentCustomEvents";

const CODE_EDITOR_FEEDBACK_URL =
"https://form.raspberrypi.org/f/code-editor-feedback";

const TOAST_CONTAINER_DEFAULTS = {
...(ToastContainer.defaultProps || {}),
};
Expand All @@ -54,6 +57,7 @@ const WebComponentLoader = (props) => {
code,
embedded = false,
editableInstructions,
feedbackFormUrl = CODE_EDITOR_FEEDBACK_URL,
hostStyles, // Pass in styles from the host
identifier,
instructions,
Expand Down Expand Up @@ -259,6 +263,7 @@ const WebComponentLoader = (props) => {
outputPanels={outputPanels}
outputSplitView={outputSplitView}
editableInstructions={editableInstructions}
feedbackFormUrl={feedbackFormUrl}
sidebarPlugins={sidebarPlugins}
/>
{errorModalShowing && <ErrorModal />}
Expand Down
1 change: 1 addition & 0 deletions src/web-component.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ class WebComponent extends HTMLElement {
"code",
"editable_instructions",
"embedded",
"feedback_form_url",
"host_styles",
"identifier",
"initial_project",
Expand Down
Loading