diff --git a/public/translations/en.json b/public/translations/en.json index eec4fa86a..1c7a8e45f 100644 --- a/public/translations/en.json +++ b/public/translations/en.json @@ -96,21 +96,50 @@ }, "instructionsPanel": { "demoInstructions": { - "title": "How instructions work", - "enablingInstructions": "Enabling instructions", - "visible": "Any text written here will be visible to students in the sidebar.", - "writingInstructions": "Writing instructions", - "markdown": "Write your instructions using [Markdown](https://www.markdownguide.org/).", - "whatYouCanDo": "What you can do", - "lists": "Lists", - "bulletPoints": "Bullet points", - "numberedSteps": "Numbered steps" + "title": "How to use instructions", + "replaceThisText": "Replace this text with your own instructions. Anything you write here appears in the sidebar for your students.", + "writtenInMarkdown": "Instructions are written in Markdown. Switch between the **Edit** and **View** tabs to see how each example below was written, or read our [instructions guide](https://help.editor.raspberrypi.org/hc/en-us/articles/52495086715028-How-to-write-project-instructions).", + "studentCopyWarning": "Students get their own copy of a project when they start it, so changes you make later will not reach anyone who has already started.", + "formattingTextHeading": "Formatting text", + "bold": "**Bold text** draws attention to a word.", + "italic": "_Italic text_ is useful for emphasis.", + "boldItalic": "**_Bold and italic_** works too.", + "strikethrough": "~~Strikethrough~~ shows something that has changed.", + "lineBreaks": "A backslash at the end of a line starts a new line, as above. A blank line starts a new paragraph. Three dashes on their own line draw a divider, like the one above.", + "headingsHeading": "Headings", + "headings": "Start a line with `##`, and add more `#` characters for smaller headings — like the headings in this document.", + "listsHeading": "Lists", + "addSprite": "Add a sprite", + "addBackdrop": "Add a backdrop", + "nestList": "Indent by two spaces to put a list inside a list", + "openFile": "Open the file `main.py`", + "typeCode": "Type your code", + "clickRun": "Click **Run**", + "linksHeading": "Links", + "links": "Link to a website like this: [Raspberry Pi Foundation](https://www.raspberrypi.org).", + "codeHeading": "Code", + "codeIntro": "In Python and Web projects you might want to include some example code in your instructions.", + "inlineCode": "Use single backticks for code in a sentence:", + "inlineCodeExample": "The `print()` function shows a message.", + "codeBlocks": "For longer code, use three backticks and name the language. `python`, `html`, `css` or `javascript`:", + "pythonPrompt": "What is your name?", + "pythonNameVar": "name", + "pythonGreeting": "Hello, ", + "blocksHeading": "Blocks code", + "blocksIntro": "In a Blocks project you might want to reference a particular type of block in your instructions. You can highlight words with the same colour that's used for the block type itself:", + "blocksColourExample": "Get a block from the `Looks`{:block-type=\"looks\"} section.", + "blocksTypes": "The [instructions guide](https://help.editor.raspberrypi.org/hc/en-us/articles/52495086715028-How-to-write-project-instructions#h_01M0034Z2SSTYV4W3CQZ4JCV3A) lists all types you can use.", + "blocksExample": "You might also want to include an example of blocks. To do this use `blocks` as the language name in a code block:", + "blocksGreeting": "Hello!", + "blockSyntaxReference": "For more information on how to write blocks as text, see the [block syntax reference](https://en.scratch-wiki.info/wiki/Block_Plugin/Syntax).", + "turningOffFormattingHeading": "Turning off formatting", + "turningOffFormatting": "\\_A backslash before a symbol stops it formatting\\_, so this is not italic." }, "emptyState": { "addInstructions": "Add instructions", "edits": "Like project code, students will not see any edits you make to the instructions after they have saved their version of the project.", "location": "These instructions will be shown to students in their project sidebar and will be view-only.", - "markdown": "Instructions are written in <0>markdown.", + "markdown": "Instructions are written in markdown. See our <0>guide for more help", "purpose": "Instructions can be added to your project to guide students." }, "addStep": "Add a step", @@ -120,6 +149,7 @@ "previousStep": "Previous step", "stepCounter": "{{currentStep}} of {{totalSteps}}", "projectSteps": "Project instructions", + "guideLink": "View instructions guide", "edit": "Edit", "view": "View", "removeStepModal": { diff --git a/src/assets/markdown/demoInstructions.md b/src/assets/markdown/demoInstructions.md index c27b9ecca..722153be1 100644 --- a/src/assets/markdown/demoInstructions.md +++ b/src/assets/markdown/demoInstructions.md @@ -1,19 +1,72 @@ # {{instructionsPanel.demoInstructions.title}} -## {{instructionsPanel.demoInstructions.enablingInstructions}} +{{instructionsPanel.demoInstructions.replaceThisText}} -{{instructionsPanel.demoInstructions.visible}} +{{instructionsPanel.demoInstructions.writtenInMarkdown}} -## {{instructionsPanel.demoInstructions.writingInstructions}} +**{{instructionsPanel.demoInstructions.studentCopyWarning}}** -{{instructionsPanel.demoInstructions.markdown}} +--- -### {{instructionsPanel.demoInstructions.whatYouCanDo}} +## {{instructionsPanel.demoInstructions.formattingTextHeading}} -{{instructionsPanel.demoInstructions.lists}}: +{{instructionsPanel.demoInstructions.bold}}\ +{{instructionsPanel.demoInstructions.italic}}\ +{{instructionsPanel.demoInstructions.boldItalic}}\ +{{instructionsPanel.demoInstructions.strikethrough}} -- {{instructionsPanel.demoInstructions.bulletPoints}} -- {{instructionsPanel.demoInstructions.bulletPoints}} +{{instructionsPanel.demoInstructions.lineBreaks}} -1. {{instructionsPanel.demoInstructions.numberedSteps}} -2. {{instructionsPanel.demoInstructions.numberedSteps}} +## {{instructionsPanel.demoInstructions.headingsHeading}} + +{{instructionsPanel.demoInstructions.headings}} + +## {{instructionsPanel.demoInstructions.listsHeading}} + +- {{instructionsPanel.demoInstructions.addSprite}} +- {{instructionsPanel.demoInstructions.addBackdrop}} + - {{instructionsPanel.demoInstructions.nestList}} + +1. {{instructionsPanel.demoInstructions.openFile}} +2. {{instructionsPanel.demoInstructions.typeCode}} +3. {{instructionsPanel.demoInstructions.clickRun}} + +## {{instructionsPanel.demoInstructions.linksHeading}} + +{{instructionsPanel.demoInstructions.links}} + +## {{instructionsPanel.demoInstructions.codeHeading}} + +{{instructionsPanel.demoInstructions.codeIntro}} + +{{instructionsPanel.demoInstructions.inlineCode}} + +{{instructionsPanel.demoInstructions.inlineCodeExample}} + +{{instructionsPanel.demoInstructions.codeBlocks}} + +```python +{{instructionsPanel.demoInstructions.pythonNameVar}} = input("{{instructionsPanel.demoInstructions.pythonPrompt}} ") +print("{{instructionsPanel.demoInstructions.pythonGreeting}}" + {{instructionsPanel.demoInstructions.pythonNameVar}}) +``` + +## {{instructionsPanel.demoInstructions.blocksHeading}} + +{{instructionsPanel.demoInstructions.blocksIntro}} + +{{instructionsPanel.demoInstructions.blocksColourExample}} + +{{instructionsPanel.demoInstructions.blocksTypes}} + +{{instructionsPanel.demoInstructions.blocksExample}} + +```blocks +when green flag clicked +say [{{instructionsPanel.demoInstructions.blocksGreeting}}] for (2) seconds +``` + +{{instructionsPanel.demoInstructions.blockSyntaxReference}} + +## {{instructionsPanel.demoInstructions.turningOffFormattingHeading}} + +{{instructionsPanel.demoInstructions.turningOffFormatting}} diff --git a/src/assets/stylesheets/InternalStyles.scss b/src/assets/stylesheets/InternalStyles.scss index 9fdbb5f8a..59aa2557e 100644 --- a/src/assets/stylesheets/InternalStyles.scss +++ b/src/assets/stylesheets/InternalStyles.scss @@ -108,6 +108,14 @@ --editor-color-tab-background: var(--editor-color-layer-3); + // Design System + + // Link (.rpf-link) + + --link-color: var(--editor-color-theme); + --link-color-hover: color-mix(in srgb, var(--editor-color-theme), white 15%); + --link-color-active: color-mix(in srgb, var(--editor-color-theme), white 30%); + // Primary button --rpf-button-text-color: var(--rpf-black); --rpf-button-background-color: var(--editor-color-theme); diff --git a/src/components/Menus/Sidebar/InstructionsPanel/InstructionsPanel.jsx b/src/components/Menus/Sidebar/InstructionsPanel/InstructionsPanel.jsx index 00e85325e..e9f53558d 100644 --- a/src/components/Menus/Sidebar/InstructionsPanel/InstructionsPanel.jsx +++ b/src/components/Menus/Sidebar/InstructionsPanel/InstructionsPanel.jsx @@ -27,6 +27,9 @@ import InstructionsStep from "./InstructionsStep/InstructionsStep"; import ProgressBar from "./ProgressBar/ProgressBar"; import BinIcon from "../../../../assets/icons/bin.svg"; +const INSTRUCTIONS_GUIDE_URL = + "https://help.editor.raspberrypi.org/hc/en-us/articles/52495086715028-How-to-write-project-instructions"; + const InstructionsPanel = () => { const [tabIndex, setTabIndex] = useState(0); const [showRemoveStepModal, setShowRemoveStepModal] = useState(false); @@ -106,6 +109,18 @@ const InstructionsPanel = () => { ] : [] } + headerContent={ + instructionsEditable && hasInstructions ? ( + + {t("instructionsPanel.guideLink")} + + ) : undefined + } Footer={ hasInstructions && (hasMultipleSteps || onEditTab) ? () => @@ -173,7 +188,7 @@ const InstructionsPanel = () => { i18nKey="instructionsPanel.emptyState.markdown" components={[ , diff --git a/src/components/Menus/Sidebar/InstructionsPanel/InstructionsPanel.test.jsx b/src/components/Menus/Sidebar/InstructionsPanel/InstructionsPanel.test.jsx index bf782bcf7..bfa47f5e8 100644 --- a/src/components/Menus/Sidebar/InstructionsPanel/InstructionsPanel.test.jsx +++ b/src/components/Menus/Sidebar/InstructionsPanel/InstructionsPanel.test.jsx @@ -106,6 +106,18 @@ describe("When instructionsEditable is true", () => { screen.queryByText("instructionsPanel.emptyState.addInstructions"), ).not.toBeInTheDocument(); }); + + test("Renders a link to the how to write instructions guide", () => { + const link = screen.getByRole("link", { + name: "instructionsPanel.guideLink", + }); + + expect(link).toHaveAttribute( + "href", + "https://help.editor.raspberrypi.org/hc/en-us/articles/52495086715028-How-to-write-project-instructions", + ); + expect(link).toHaveAttribute("target", "_blank"); + }); }); describe("Adding and removing steps", () => { @@ -268,6 +280,7 @@ describe("When instructionsEditable is true", () => { instructionsEditable: true, }, instructions: { + permitOverride: true, project: { steps: [], }, @@ -301,6 +314,24 @@ describe("When instructionsEditable is true", () => { screen.queryByText("instructionsPanel.emptyState.purpose"), ).toBeInTheDocument(); }); + + test("Does not render the guide link in the panel header", () => { + expect( + screen.queryByRole("link", { name: "instructionsPanel.guideLink" }), + ).not.toBeInTheDocument(); + }); + + test("Adding instructions reveals the guide link", () => { + act(() => { + fireEvent.click( + screen.getByText("instructionsPanel.emptyState.addInstructions"), + ); + }); + + expect( + screen.getByRole("link", { name: "instructionsPanel.guideLink" }), + ).toBeInTheDocument(); + }); }); }); @@ -335,6 +366,12 @@ describe("When instructions are not editable", () => { ).not.toBeInTheDocument(); }); + test("Does not render the how to write instructions guide link", () => { + expect( + screen.queryByRole("link", { name: "instructionsPanel.guideLink" }), + ).not.toBeInTheDocument(); + }); + test("It renders without crashing", () => { expect( screen.queryByText("instructionsPanel.projectSteps"), @@ -390,6 +427,12 @@ describe("When instructions are not editable", () => { }); }); + test("Does not render the how to write instructions guide link", () => { + expect( + screen.queryByRole("link", { name: "instructionsPanel.guideLink" }), + ).not.toBeInTheDocument(); + }); + test("Renders no tab titles", () => { expect(screen.queryAllByRole("tab")).toHaveLength(0); }); diff --git a/src/components/Menus/Sidebar/SidebarPanel.jsx b/src/components/Menus/Sidebar/SidebarPanel.jsx index d147f2de1..713eead77 100644 --- a/src/components/Menus/Sidebar/SidebarPanel.jsx +++ b/src/components/Menus/Sidebar/SidebarPanel.jsx @@ -12,6 +12,7 @@ const SidebarPanel = (props) => { Footer, className, buttons, + headerContent, panelRef, defaultWidth = "320px", } = props; @@ -27,6 +28,7 @@ const SidebarPanel = (props) => { {buttons && !buttonsIsEmptyArray && (
{buttons}
)} + {headerContent}
{children} @@ -62,6 +64,7 @@ SidebarPanel.propTypes = { heading: PropTypes.string.isRequired, className: PropTypes.string, buttons: PropTypes.arrayOf(PropTypes.node), + headerContent: PropTypes.node, }; export default SidebarPanel; diff --git a/src/components/Menus/Sidebar/SidebarPanel.test.jsx b/src/components/Menus/Sidebar/SidebarPanel.test.jsx index f1ec1ee36..7555eab66 100644 --- a/src/components/Menus/Sidebar/SidebarPanel.test.jsx +++ b/src/components/Menus/Sidebar/SidebarPanel.test.jsx @@ -33,6 +33,29 @@ test("Renders a single button", () => { expect(screen.queryByText("button")).toBeInTheDocument(); }); +test("Renders arbitrary header content", () => { + render( + a link}> + some content + , + ); + expect(screen.queryByRole("link", { name: "a link" })).toBeInTheDocument(); +}); + +test("Renders header content alongside buttons", () => { + render( + button]} + headerContent={

header note

} + > + some content +
, + ); + expect(screen.queryByText("button")).toBeInTheDocument(); + expect(screen.queryByText("header note")).toBeInTheDocument(); +}); + test("Renders multiple buttons", () => { render(