Skip to content

Commit d01c6dd

Browse files
Refactor sidepanel for multiple buttons (#1270)
closes RaspberryPiFoundation/digital-editor-issues#991
1 parent 3a5bc69 commit d01c6dd

File tree

11 files changed

+110
-133
lines changed

11 files changed

+110
-133
lines changed

CHANGELOG.md

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
99
### Added
1010

1111
- Font-family variables that can be used to customise the sans-serif and monospace fonts used in the editor (#1264)
12+
- Material symbols font to web component preview page since the Design System depends on this (#1261)
1213

1314
### Changed
1415

1516
- Changed the horizontal scrollbar to show without needing to scroll to the bottom of the editor window (#1257)
17+
- Changed SidebarPanel to accept an array of buttons (#1270)
1618
- Updated Design System react to v2.6.2 (#1261)
1719

18-
### Added
19-
20-
- Material symbols font to web component preview page since the Design System depends on this (#1261)
21-
2220
### Fixed
2321

2422
- Styling design system components used in the web component (#1263)

src/assets/stylesheets/Sidebar.scss

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,12 @@
131131
scrollbar-width: thin;
132132
}
133133

134+
.sidebar__panel-buttons {
135+
display: flex;
136+
flex-direction: column;
137+
gap: $space-1;
138+
}
139+
134140
.sidebar__panel-footer {
135141
border-block-start: 1px solid $rpf-grey-150;
136142
inset-block-end: 0px;

src/components/Editor/NewComponentButton/NewComponentButton.jsx

Lines changed: 0 additions & 29 deletions
This file was deleted.

src/components/Editor/NewComponentButton/NewComponentButton.test.js

Lines changed: 0 additions & 47 deletions
This file was deleted.

src/components/Menus/Sidebar/FilePanel/FilePanel.jsx

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,25 @@ import { useDispatch, useSelector } from "react-redux";
33
import { useTranslation } from "react-i18next";
44

55
import FileMenu from "../../FileMenu/FileMenu";
6-
import NewComponentButton from "../../../Editor/NewComponentButton/NewComponentButton";
76
import DesignSystemButton from "../../../DesignSystemButton/DesignSystemButton";
87
import {
98
openFile,
109
setFocussedFileIndex,
1110
hideSidebar,
11+
showNewFileModal,
1212
} from "../../../../redux/EditorSlice";
1313

1414
import "../../../../assets/stylesheets/FilePanel.scss";
1515
import "../../../../assets/stylesheets/Sidebar.scss";
1616
import SidebarPanel from "../SidebarPanel";
1717
import FileIcon from "../../../../utils/FileIcon";
18+
import PlusIcon from "../../../../assets/icons/plus.svg";
1819

1920
const FilePanel = ({ isMobile }) => {
2021
const project = useSelector((state) => state.editor.project);
2122
const openFiles = useSelector((state) => state.editor.openFiles);
2223
const readOnly = useSelector((state) => state.editor.readOnly);
23-
24+
const { t } = useTranslation();
2425
const dispatch = useDispatch();
2526

2627
const switchToFileTab = (panelIndex, fileIndex) => {
@@ -42,16 +43,30 @@ const FilePanel = ({ isMobile }) => {
4243
dispatch(hideSidebar());
4344
}
4445
};
45-
const { t } = useTranslation();
4646

47-
const Button = readOnly ? null : NewComponentButton;
47+
const openNewFileModal = () => {
48+
dispatch(showNewFileModal());
49+
};
50+
51+
const buttons = readOnly
52+
? []
53+
: [
54+
{
55+
text: t("filePanel.newFileButton"),
56+
textAlways: true,
57+
icon: <PlusIcon />,
58+
onClick: openNewFileModal,
59+
className: "btn--primary",
60+
fill: true,
61+
},
62+
];
4863

4964
if (!project || !project.components) {
5065
return null;
5166
}
5267

5368
return (
54-
<SidebarPanel heading={t("filePanel.files")} Button={Button}>
69+
<SidebarPanel heading={t("filePanel.files")} buttons={buttons}>
5570
{project.components.map((file, i) => (
5671
<div className="files-list-item-wrapper" key={i}>
5772
<DesignSystemButton

src/components/Menus/Sidebar/FilePanel/FilePanel.test.js

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,11 @@ import { Provider } from "react-redux";
44
import configureStore from "redux-mock-store";
55

66
import FilePanel from "./FilePanel";
7-
import { openFile, setFocussedFileIndex } from "../../../../redux/EditorSlice";
7+
import {
8+
openFile,
9+
setFocussedFileIndex,
10+
showNewFileModal,
11+
} from "../../../../redux/EditorSlice";
812

913
const createMockStore = function ({ components, openFiles = [[]], readOnly }) {
1014
const mockStore = configureStore([]);
@@ -166,8 +170,10 @@ describe("it renders the expected icon for individual files", () => {
166170
});
167171

168172
describe("When not read only", () => {
173+
let store;
174+
169175
beforeEach(() => {
170-
const store = createMockStore({
176+
store = createMockStore({
171177
components: [{ name: "a", extension: "py" }],
172178
readOnly: false,
173179
});
@@ -187,6 +193,13 @@ describe("When not read only", () => {
187193
test("it renders the file menu button", () => {
188194
expect(screen.queryByTitle("filePanel.fileMenu.label")).toBeInTheDocument();
189195
});
196+
197+
test("Clicking button opens new file modal", () => {
198+
const button = screen.queryByText("filePanel.newFileButton");
199+
fireEvent.click(button);
200+
const expectedActions = [showNewFileModal()];
201+
expect(store.getActions()).toEqual(expectedActions);
202+
});
190203
});
191204

192205
describe("When read only", () => {

src/components/Menus/Sidebar/InstructionsPanel/InstructionsPanel.jsx

Lines changed: 22 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -135,34 +135,6 @@ const InstructionsPanel = () => {
135135
setShowModal(false);
136136
};
137137

138-
const AddInstructionsButton = () => {
139-
return (
140-
<DesignSystemButton
141-
className="btn--primary"
142-
icon="add"
143-
text={t("instructionsPanel.emptyState.addInstructions")}
144-
onClick={addInstructions}
145-
fill
146-
textAlways
147-
small
148-
/>
149-
);
150-
};
151-
152-
const RemoveInstructionsButton = () => {
153-
return (
154-
<DesignSystemButton
155-
className="btn--secondary"
156-
text={t("instructionsPanel.removeInstructions")}
157-
onClick={() => {
158-
setShowModal(true);
159-
}}
160-
fill
161-
textAlways
162-
small
163-
/>
164-
);
165-
};
166138
const onChange = (e) => {
167139
dispatch(setProjectInstructions(e.target.value));
168140
};
@@ -171,11 +143,30 @@ const InstructionsPanel = () => {
171143
<SidebarPanel
172144
defaultWidth="30vw"
173145
heading={t("instructionsPanel.projectSteps")}
174-
Button={
146+
buttons={
175147
instructionsEditable
176148
? hasInstructions
177-
? RemoveInstructionsButton
178-
: AddInstructionsButton
149+
? [
150+
{
151+
className: "btn--secondary",
152+
text: t("instructionsPanel.removeInstructions"),
153+
onClick: () => setShowModal(true),
154+
fill: true,
155+
textAlways: true,
156+
small: true,
157+
},
158+
]
159+
: [
160+
{
161+
className: "btn--primary",
162+
icon: "add",
163+
text: t("instructionsPanel.emptyState.addInstructions"),
164+
onClick: addInstructions,
165+
fill: true,
166+
textAlways: true,
167+
small: true,
168+
},
169+
]
179170
: null
180171
}
181172
{...{ Footer: hasMultipleSteps && ProgressBar }}

src/components/Menus/Sidebar/ProjectsPanel/ProjectsPanel.jsx

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import { useSelector } from "react-redux";
1212
import { MOBILE_MEDIA_QUERY } from "../../../../utils/mediaQueryBreakpoints";
1313
import { useMediaQuery } from "react-responsive";
1414
import SaveStatus from "../../../SaveStatus/SaveStatus";
15-
import DesignSystemButton from "../../../DesignSystemButton/DesignSystemButton";
1615
import { navigateToProjectsPageEvent } from "../../../../events/WebComponentCustomEvents";
1716

1817
const ProjectsPanel = () => {
@@ -37,18 +36,21 @@ const ProjectsPanel = () => {
3736
document.dispatchEvent(navigateToProjectsPageEvent);
3837
};
3938

39+
const buttons = isLoggedIn
40+
? [
41+
{
42+
className: "btn--primary projects-panel__your-projects-button",
43+
onClick: navigateToProjectsPage,
44+
text: t("projectsPanel.yourProjectsButton"),
45+
textAlways: true,
46+
},
47+
]
48+
: [];
49+
4050
return (
4151
<SidebarPanel
4252
heading={t("projectsPanel.projects")}
43-
Button={() =>
44-
isLoggedIn && (
45-
<DesignSystemButton
46-
className="btn--primary projects-panel__your-projects-button"
47-
onClick={navigateToProjectsPage}
48-
text={t("projectsPanel.yourProjectsButton")}
49-
/>
50-
)
51-
}
53+
buttons={buttons}
5254
className="projects-panel-wrapper"
5355
>
5456
<ProjectName

src/components/Menus/Sidebar/Sidebar.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ const Sidebar = ({ options = [], plugins = [] }) => {
8787
title: plugin.title,
8888
position: plugin.position || "top",
8989
panel: () => (
90-
<SidebarPanel heading={plugin.heading} Button={plugin.button}>
90+
<SidebarPanel heading={plugin.heading} buttons={plugin.buttons || []}>
9191
{plugin.panel()}
9292
</SidebarPanel>
9393
),

src/components/Menus/Sidebar/SidebarPanel.jsx

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,15 @@ import classNames from "classnames";
44
import PropTypes from "prop-types";
55
import { MOBILE_MEDIA_QUERY } from "../../../utils/mediaQueryBreakpoints";
66
import { useMediaQuery } from "react-responsive";
7+
import DesignSystemButton from "../../DesignSystemButton/DesignSystemButton";
78

89
const SidebarPanel = (props) => {
910
const {
1011
children,
1112
heading,
1213
Footer,
1314
className,
14-
Button,
15+
buttons = [],
1516
defaultWidth = "225px",
1617
} = props;
1718
const isMobile = useMediaQuery({ query: MOBILE_MEDIA_QUERY });
@@ -20,9 +21,14 @@ const SidebarPanel = (props) => {
2021
<>
2122
<div className="sidebar__panel-header">
2223
<h2 className="sidebar__panel-heading">{heading}</h2>
23-
{Button ? <Button /> : null}
24+
{buttons?.length > 0 && (
25+
<div className="sidebar__panel-buttons">
26+
{buttons.map((btn, i) => (
27+
<DesignSystemButton key={i} {...btn} />
28+
))}
29+
</div>
30+
)}
2431
</div>
25-
2632
<div className="sidebar__panel-content">{children}</div>
2733
{Footer && <div className="sidebar__panel-footer">{<Footer />}</div>}
2834
</>
@@ -62,7 +68,7 @@ SidebarPanel.propTypes = {
6268
children: PropTypes.any.isRequired,
6369
heading: PropTypes.string.isRequired,
6470
className: PropTypes.string,
65-
Button: PropTypes.func,
71+
buttons: PropTypes.arrayOf(PropTypes.object),
6672
};
6773

6874
export default SidebarPanel;

0 commit comments

Comments
 (0)