From 66eda03e6d46a798bf495c5bf70c113861a299b6 Mon Sep 17 00:00:00 2001 From: loiswells97 Date: Fri, 19 Aug 2022 16:37:16 +0000 Subject: [PATCH 01/11] Create draft PR for #168 From 93d59ba90fd1c55b08847dca64bd121d30c781e2 Mon Sep 17 00:00:00 2001 From: Lois Wells Date: Fri, 19 Aug 2022 17:38:46 +0100 Subject: [PATCH 02/11] First attempt at allowing default astro pi component, not working yet --- src/components/Editor/EditorSlice.js | 7 +++++++ .../Editor/Runners/PythonRunner/PythonRunner.js | 11 +++++++---- .../WebComponentLoader/WebComponentLoader.js | 5 +++-- src/web-component.html | 2 +- src/web-component.js | 2 +- 5 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/components/Editor/EditorSlice.js b/src/components/Editor/EditorSlice.js index 12a3b8c4a..e309193f3 100644 --- a/src/components/Editor/EditorSlice.js +++ b/src/components/Editor/EditorSlice.js @@ -13,6 +13,7 @@ export const EditorSlice = createSlice({ codeRunStopped: false, projectList: [], projectListLoaded: false, + senseHatAlwaysEnabled: false, }, reducers: { updateImages: (state, action) => { @@ -38,6 +39,11 @@ export const EditorSlice = createSlice({ setProjectLoaded: (state, action) => { state.projectLoaded = action.payload; }, + setSenseHatAlwaysEnabled: (state, action) => { + console.log('setting sense hat always enabled') + console.log(action.payload) + state.senseHatAlwaysEnabled = action.payload; + }, triggerDraw: (state) => { state.drawTriggered = true; }, @@ -99,6 +105,7 @@ export const { setProjectList, setProjectListLoaded, setProjectLoaded, + setSenseHatAlwaysEnabled, stopCodeRun, stopDraw, triggerCodeRun, diff --git a/src/components/Editor/Runners/PythonRunner/PythonRunner.js b/src/components/Editor/Runners/PythonRunner/PythonRunner.js index a2f433f55..28b128b35 100644 --- a/src/components/Editor/Runners/PythonRunner/PythonRunner.js +++ b/src/components/Editor/Runners/PythonRunner/PythonRunner.js @@ -16,11 +16,12 @@ const PythonRunner = () => { const codeRunTriggered = useSelector((state) => state.editor.codeRunTriggered); const codeRunStopped = useSelector((state) => state.editor.codeRunStopped); const drawTriggered = useSelector((state) => state.editor.drawTriggered); + const senseHatAlwaysEnabled = useSelector((state) => state.editor.senseHatAlwaysEnabled); const outputCanvas = useRef(); const output = useRef(); const pygalOutput = useRef(); const p5Output = useRef(); - const senseHatContainer = useRef(); + // const senseHatContainer = useRef(); const dispatch = useDispatch(); const [senseHatEnabled, setSenseHatEnabled] = useState(false); @@ -91,7 +92,7 @@ const PythonRunner = () => { if (x==="./_internal_sense_hat/__init__.js") { setSenseHatEnabled(true) - senseHatContainer.current.hidden=false + // senseHatContainer.current.hidden=false } let localProjectFiles = projectCode.filter((component) => component.name !== 'main').map((component) => `./${component.name}.py`); @@ -220,7 +221,9 @@ const PythonRunner = () => { output.current.innerHTML = ''; pygalOutput.current.innerHTML = ''; p5Output.current.innerHTML = ''; - senseHatContainer.current.hidden = true + // senseHatContainer.current.hidden = true + + setSenseHatEnabled(false) var prog = projectCode[0].content; @@ -305,7 +308,7 @@ const PythonRunner = () => {
- +
{senseHatEnabled || senseHatAlwaysEnabled ?:null}
diff --git a/src/components/WebComponent/WebComponentLoader/WebComponentLoader.js b/src/components/WebComponent/WebComponentLoader/WebComponentLoader.js index ea0dc3039..3c7329788 100644 --- a/src/components/WebComponent/WebComponentLoader/WebComponentLoader.js +++ b/src/components/WebComponent/WebComponentLoader/WebComponentLoader.js @@ -1,11 +1,11 @@ import React, { useEffect } from 'react'; import { useSelector, useDispatch } from 'react-redux' -import { setProject, setProjectLoaded } from '../../Editor/EditorSlice'; +import { setProject, setProjectLoaded, setSenseHatAlwaysEnabled } from '../../Editor/EditorSlice'; import WebComponentProject from '../Project/WebComponentProject'; const ProjectComponentLoader = (props) => { const projectLoaded = useSelector((state) => state.editor.projectLoaded); - const { code } = props; + const { code, senseHatAlwaysEnabled } = props; const dispatch = useDispatch() useEffect(() => { @@ -13,6 +13,7 @@ const ProjectComponentLoader = (props) => { type: 'python', components: [{ name: 'main', extension: 'py', content: code }] } + dispatch(setSenseHatAlwaysEnabled(senseHatAlwaysEnabled)) dispatch(setProject(proj)) dispatch(setProjectLoaded(true)) }, []); diff --git a/src/web-component.html b/src/web-component.html index 84a41f024..8704e0a69 100644 --- a/src/web-component.html +++ b/src/web-component.html @@ -6,7 +6,7 @@ Editor Web component - +

diff --git a/src/web-component.js b/src/web-component.js index 3fa91a49d..6eebf9a1f 100644 --- a/src/web-component.js +++ b/src/web-component.js @@ -20,7 +20,7 @@ class WebComponent extends HTMLElement { } static get observedAttributes() { - return ['code']; + return ['code', 'sense_hat_always_enabled']; } attributeChangedCallback(name, _oldVal, newVal) { From 5c49dd2027950f46e4792239a3dc4758e23b45da Mon Sep 17 00:00:00 2001 From: Lois Wells Date: Tue, 30 Aug 2022 11:40:29 +0100 Subject: [PATCH 03/11] sense_hat_always_enabled boolean attribute --- src/components/Editor/EditorSlice.js | 2 -- .../WebComponent/WebComponentLoader/WebComponentLoader.js | 4 ++-- src/web-component.html | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/components/Editor/EditorSlice.js b/src/components/Editor/EditorSlice.js index e309193f3..7209b940c 100644 --- a/src/components/Editor/EditorSlice.js +++ b/src/components/Editor/EditorSlice.js @@ -40,8 +40,6 @@ export const EditorSlice = createSlice({ state.projectLoaded = action.payload; }, setSenseHatAlwaysEnabled: (state, action) => { - console.log('setting sense hat always enabled') - console.log(action.payload) state.senseHatAlwaysEnabled = action.payload; }, triggerDraw: (state) => { diff --git a/src/components/WebComponent/WebComponentLoader/WebComponentLoader.js b/src/components/WebComponent/WebComponentLoader/WebComponentLoader.js index 3c7329788..441ebf05f 100644 --- a/src/components/WebComponent/WebComponentLoader/WebComponentLoader.js +++ b/src/components/WebComponent/WebComponentLoader/WebComponentLoader.js @@ -5,7 +5,7 @@ import WebComponentProject from '../Project/WebComponentProject'; const ProjectComponentLoader = (props) => { const projectLoaded = useSelector((state) => state.editor.projectLoaded); - const { code, senseHatAlwaysEnabled } = props; + const { code, sense_hat_always_enabled } = props; const dispatch = useDispatch() useEffect(() => { @@ -13,7 +13,7 @@ const ProjectComponentLoader = (props) => { type: 'python', components: [{ name: 'main', extension: 'py', content: code }] } - dispatch(setSenseHatAlwaysEnabled(senseHatAlwaysEnabled)) + dispatch(setSenseHatAlwaysEnabled(typeof sense_hat_always_enabled !== 'undefined')) dispatch(setProject(proj)) dispatch(setProjectLoaded(true)) }, []); diff --git a/src/web-component.html b/src/web-component.html index 8704e0a69..84a41f024 100644 --- a/src/web-component.html +++ b/src/web-component.html @@ -6,7 +6,7 @@ Editor Web component - +

From 44e5d45f7fce18c83974862fa4ced8907e5bf246 Mon Sep 17 00:00:00 2001 From: Lois Wells Date: Tue, 30 Aug 2022 11:53:01 +0100 Subject: [PATCH 04/11] Defaulting to visual tab if sense hat always enabled --- src/components/Editor/Runners/PythonRunner/PythonRunner.js | 2 +- src/web-component.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Editor/Runners/PythonRunner/PythonRunner.js b/src/components/Editor/Runners/PythonRunner/PythonRunner.js index 28b128b35..d67eef90f 100644 --- a/src/components/Editor/Runners/PythonRunner/PythonRunner.js +++ b/src/components/Editor/Runners/PythonRunner/PythonRunner.js @@ -295,7 +295,7 @@ const PythonRunner = () => { return (
- + Visual Output Text Output diff --git a/src/web-component.html b/src/web-component.html index 84a41f024..600ed65b8 100644 --- a/src/web-component.html +++ b/src/web-component.html @@ -6,7 +6,7 @@ Editor Web component - +

From a8ffbe4319f9921b9ad27a89b00eba21d6464d45 Mon Sep 17 00:00:00 2001 From: Lois Wells Date: Tue, 30 Aug 2022 12:18:34 +0100 Subject: [PATCH 05/11] Tests astro pi component appears automatically when relevant attribute set --- cypress/e2e/missionZero-wc.cy.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/cypress/e2e/missionZero-wc.cy.js b/cypress/e2e/missionZero-wc.cy.js index b8ee7ddc9..ff101d3e4 100644 --- a/cypress/e2e/missionZero-wc.cy.js +++ b/cypress/e2e/missionZero-wc.cy.js @@ -4,6 +4,15 @@ beforeEach(() => { cy.visit(baseUrl) }) +it("renders the astro pi component on page load", () => { + cy.get("editor-wc").shadow().find("#root").should("contain", "yaw") +}) + +it("defaults to the visual output tab", () => { + const runnerContainer = cy.get("editor-wc").shadow().find('.proj-runner-container') + runnerContainer.find('.react-tabs__tab--selected').should("contain", "Visual Output") +}) + it("loads the sense hat library", () => { cy.get("editor-wc").shadow().find("div[class=cm-content]").invoke('text', 'import _internal_sense_hat') cy.get("editor-wc").shadow().find(".btn--run").click() From 5cb18bbc002ebf77ec17751d1cebd54dc543c91c Mon Sep 17 00:00:00 2001 From: Lois Wells Date: Tue, 30 Aug 2022 12:21:36 +0100 Subject: [PATCH 06/11] updating snapshot --- .../EmbeddedViewer/__snapshots__/EmbeddedViewer.test.js.snap | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/EmbeddedViewer/__snapshots__/EmbeddedViewer.test.js.snap b/src/components/EmbeddedViewer/__snapshots__/EmbeddedViewer.test.js.snap index f9ed07ef3..1693e08d3 100644 --- a/src/components/EmbeddedViewer/__snapshots__/EmbeddedViewer.test.js.snap +++ b/src/components/EmbeddedViewer/__snapshots__/EmbeddedViewer.test.js.snap @@ -91,7 +91,6 @@ exports[`Renders without crashing 1`] = ` />
From acf9d7f9b811bbf49fedeb2f5ef75a1d1e9862fa Mon Sep 17 00:00:00 2001 From: Lois Wells Date: Tue, 30 Aug 2022 13:59:00 +0100 Subject: [PATCH 07/11] Trying to debug flaky test --- cypress/e2e/missionZero-wc.cy.js | 1 + 1 file changed, 1 insertion(+) diff --git a/cypress/e2e/missionZero-wc.cy.js b/cypress/e2e/missionZero-wc.cy.js index 235b7addf..e5b766e58 100644 --- a/cypress/e2e/missionZero-wc.cy.js +++ b/cypress/e2e/missionZero-wc.cy.js @@ -66,6 +66,7 @@ it("confirms LEDs used when single led set", () => { it("confirms LEDs used when display set", () => { cy.get("editor-wc").shadow().find("div[class=cm-content]").invoke('text', 'from sense_hat import SenseHat\nsense = SenseHat()\nsense.set_pixels([[100,0,0]] * 64)') cy.get("editor-wc").shadow().find(".btn--run").click() + cy.scrollTo('bottom') cy.get("#results").should("contain", '"usedLEDs":true') }) From a178c9e8f20664e5318679ed233e096c86bff729 Mon Sep 17 00:00:00 2001 From: Lois Wells Date: Thu, 1 Sep 2022 10:15:34 +0100 Subject: [PATCH 08/11] Testing web component with different attributes --- cypress/e2e/missionZero-wc.cy.js | 27 +++++++++++++++++---------- cypress/e2e/spec-wc.cy.js | 26 ++++++++++++++++++++++++++ src/web-component.html | 10 ++++++++-- 3 files changed, 51 insertions(+), 12 deletions(-) diff --git a/cypress/e2e/missionZero-wc.cy.js b/cypress/e2e/missionZero-wc.cy.js index e5b766e58..cfd425c31 100644 --- a/cypress/e2e/missionZero-wc.cy.js +++ b/cypress/e2e/missionZero-wc.cy.js @@ -1,16 +1,22 @@ const baseUrl = "http://localhost:3001" beforeEach(() => { - cy.visit(baseUrl) + cy.visit(`${baseUrl}?sense_hat_always_enabled=true`) +}) + +it("defaults to the visual output tab", () => { + const runnerContainer = cy.get("editor-wc").shadow().find('.proj-runner-container') + runnerContainer.find('.react-tabs__tab--selected').should("contain", "Visual Output") }) it("renders the astro pi component on page load", () => { cy.get("editor-wc").shadow().find("#root").should("contain", "yaw") }) -it("defaults to the visual output tab", () => { - const runnerContainer = cy.get("editor-wc").shadow().find('.proj-runner-container') - runnerContainer.find('.react-tabs__tab--selected').should("contain", "Visual Output") +it("keeps astro pi component if code run without sense hat imported", () => { + cy.get("editor-wc").shadow().find("div[class=cm-content]").invoke('text', '') + cy.get("editor-wc").shadow().find(".btn--run").click() + cy.get("editor-wc").shadow().find("#root").should("contain", "yaw") }) it("loads the sense hat library", () => { @@ -70,12 +76,13 @@ it("confirms LEDs used when display set", () => { cy.get("#results").should("contain", '"usedLEDs":true') }) -// it("picks up calls to input()", () => { -// cy.get("editor-wc").shadow().find("div[class=cm-content]").invoke('text', 'input()') -// cy.get("editor-wc").shadow().find(".btn--run").click() -// cy.get("editor-wc").shadow().find("span[contenteditable=true]").type('{enter}') -// cy.get("#results").should("contain", '"noInputEvents":false') -// }) +it("picks up calls to input()", () => { + cy.get("editor-wc").shadow().find("div[class=cm-content]").invoke('text', 'input()') + cy.get("editor-wc").shadow().find(".btn--run").click() + cy.get("editor-wc").shadow().contains('Text Output').click() + cy.get("editor-wc").shadow().find("span[contenteditable=true]").type('{enter}') + cy.get("#results").should("contain", '"noInputEvents":false') +}) it("picks up calls to wait for motion", () => { cy.get("editor-wc").shadow().find("div[class=cm-content]").invoke('text', 'from sense_hat import SenseHat\nsense = SenseHat()\nsense.motion.wait_for_motion()') diff --git a/cypress/e2e/spec-wc.cy.js b/cypress/e2e/spec-wc.cy.js index 6f2ffcb3f..b0d278174 100644 --- a/cypress/e2e/spec-wc.cy.js +++ b/cypress/e2e/spec-wc.cy.js @@ -8,8 +8,34 @@ it("renders the web component", () => { cy.get("editor-wc").shadow().find("button").should("contain", "Run") }) +it("defaults to the text output tab", () => { + const runnerContainer = cy.get("editor-wc").shadow().find('.proj-runner-container') + runnerContainer.find('.react-tabs__tab--selected').should("contain", "Text Output") +}) + it("runs the python code", () => { cy.get("editor-wc").shadow().find("div[class=cm-content]").invoke('text', 'print("Hello world")') cy.get("editor-wc").shadow().find(".btn--run").click() cy.get("editor-wc").shadow().find(".pythonrunner-console-output-line").should("contain", "Hello world") }) + +it("does not render the astro pi component on page load", () => { + cy.get("editor-wc").shadow().contains('Visual Output').click() + cy.get("editor-wc").shadow().find("#root").should("not.contain", "yaw") +}) + +it("renders astro pi component if sense hat imported", () => { + cy.get("editor-wc").shadow().find("div[class=cm-content]").invoke('text', 'import sense_hat') + cy.get("editor-wc").shadow().find(".btn--run").click() + cy.get("editor-wc").shadow().contains('Visual Output').click() + cy.get("editor-wc").shadow().find("#root").should("contain", "yaw") +}) + +it("does not render astro pi component if sense hat unimported", () => { + cy.get("editor-wc").shadow().find("div[class=cm-content]").invoke('text', 'import sense_hat') + cy.get("editor-wc").shadow().find(".btn--run").click() + cy.get("editor-wc").shadow().find("div[class=cm-content]").invoke('text', '') + cy.get("editor-wc").shadow().find(".btn--run").click() + cy.get("editor-wc").shadow().contains('Visual Output').click() + cy.get("editor-wc").shadow().find("#root").should("not.contain", "yaw") +}) diff --git a/src/web-component.html b/src/web-component.html index 600ed65b8..3eeb74a92 100644 --- a/src/web-component.html +++ b/src/web-component.html @@ -6,13 +6,18 @@ Editor Web component -

From f94d5d37574571154819c7c32b9f97f11b8e11f2 Mon Sep 17 00:00:00 2001 From: Lois Wells Date: Thu, 1 Sep 2022 11:22:15 +0100 Subject: [PATCH 09/11] Removing commented out code --- src/components/Editor/Runners/PythonRunner/PythonRunner.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/components/Editor/Runners/PythonRunner/PythonRunner.js b/src/components/Editor/Runners/PythonRunner/PythonRunner.js index 0cbd4ac63..368911663 100644 --- a/src/components/Editor/Runners/PythonRunner/PythonRunner.js +++ b/src/components/Editor/Runners/PythonRunner/PythonRunner.js @@ -21,7 +21,6 @@ const PythonRunner = () => { const output = useRef(); const pygalOutput = useRef(); const p5Output = useRef(); - // const senseHatContainer = useRef(); const dispatch = useDispatch(); const [senseHatEnabled, setSenseHatEnabled] = useState(false); @@ -98,7 +97,6 @@ const PythonRunner = () => { if (x==="./_internal_sense_hat/__init__.js") { setSenseHatEnabled(true) - // senseHatContainer.current.hidden=false } let localProjectFiles = projectCode.filter((component) => component.name !== 'main').map((component) => `./${component.name}.py`); @@ -227,7 +225,6 @@ const PythonRunner = () => { output.current.innerHTML = ''; pygalOutput.current.innerHTML = ''; p5Output.current.innerHTML = ''; - // senseHatContainer.current.hidden = true setSenseHatEnabled(false) From 3dcddd157aba7304e02e84bf67278dedd30f92d7 Mon Sep 17 00:00:00 2001 From: "Patrick J. Cherry" Date: Thu, 1 Sep 2022 12:23:59 +0100 Subject: [PATCH 10/11] Update test HTML page to iterate through any attribute given in the query string --- src/web-component.html | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/web-component.html b/src/web-component.html index 3eeb74a92..e3c142742 100644 --- a/src/web-component.html +++ b/src/web-component.html @@ -10,14 +10,14 @@ From e15c6be10e449c67fb7ec6f634c9c899c85f6b94 Mon Sep 17 00:00:00 2001 From: "Patrick J. Cherry" Date: Thu, 1 Sep 2022 12:29:55 +0100 Subject: [PATCH 11/11] Add some docs around embedding the webcomponent --- README.md | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index ec7436aa1..cc733a51d 100644 --- a/README.md +++ b/README.md @@ -48,12 +48,22 @@ See the section about [deployment](https://facebook.github.io/create-react-app/d The repo includes the Editor Web Component which shares components with the editor application but has a separate build process. -### `yarn stat:wc` +### Embedding -Runs the web component in development mode. -Open [http://localhost:3001](http://localhost:3001) to view it in the browser. +The web component can be included in a page by using the `` HTML element. It takes the following attributes -There is no production build setup for the web component at present. +* `code`: A preset blob of code to show in the editor pane. +* `sense_hat_always_enabled`: Show the Astro Pi Sense HAT emulator on page load + +### `yarn start:wc` + +Runs the web component in development mode. Open [http://localhost:3001](http://localhost:3001) to view it in the browser. + +**NB** You need to have the main `yarn start` process running too. + +It is possible to add query strings to control how the web component is configured. Any HTML attribute can be set on the query string, including `class`, `style` etc. + +For example, to load the page with the Sense Hat always showing, add [`?sense_hat_always_enabled` to the URL](http://localhost:3001?sense_hat_always_enabled) ## Review apps