diff --git a/src/App.js b/src/App.js index e56d07d..af1e50f 100644 --- a/src/App.js +++ b/src/App.js @@ -16,7 +16,6 @@ ReactGA.initialize("UA-170794768-1", { class App extends React.Component { state = { - currentPage: SECTION_NAMES.LandingPage, showPoseMessage: false, showInfo: false, info: {}, @@ -40,25 +39,29 @@ class App extends React.Component { onPageLoad = pageName => { ReactGA.pageview(window.location.pathname + window.location.search) - this.setState({ currentPage: pageName }) if (pageName === SECTION_NAMES.landingPage) { - this.setState({ - currentPage: pageName, + return this.setState({ showInfo: false, showPoseMessage: false, }) - return } - this.setState({ - currentPage: pageName, - showInfo: false, - showPoseMessage: false, - hexapodParams: { ...this.state.hexapodParams, pose: defaults.DEFAULT_POSE }, - }) - - this.updatePlot(this.state.hexapodParams.dimensions, defaults.DEFAULT_POSE) + return this.setState( + { + showInfo: false, + showPoseMessage: false, + hexapodParams: { + ...this.state.hexapodParams, + pose: defaults.DEFAULT_POSE, + }, + }, + () => + this.updatePlot( + this.state.hexapodParams.dimensions, + defaults.DEFAULT_POSE + ) + ) } /* * * * * * * * * * * * * * @@ -67,7 +70,7 @@ class App extends React.Component { updatePlot = (dimensions, pose) => { const newHexapodModel = new VirtualHexapod(dimensions, pose) - this.updatePlotWithHexapod(newHexapodModel) + return this.updatePlotWithHexapod(newHexapodModel) } updatePlotWithHexapod = hexapod => { @@ -76,7 +79,7 @@ class App extends React.Component { } const [data, layout] = getNewPlotParams(hexapod, this.state.plot.latestCameraView) - this.setState({ + return this.setState({ plot: { ...this.state.plot, data, @@ -93,7 +96,7 @@ class App extends React.Component { logCameraView = relayoutData => { const newCameraView = relayoutData["scene.camera"] const plot = { ...this.state.plot, latestCameraView: newCameraView } - this.setState({ ...this.state, plot: plot }) + return this.setState({ ...this.state, plot: plot }) } /* * * * * * * * * * * * * * @@ -102,7 +105,7 @@ class App extends React.Component { updateIk = (hexapod, updatedStateParams) => { this.updatePlotWithHexapod(hexapod) - this.setState({ ...updatedStateParams }) + return this.setState({ ...updatedStateParams }) } updateDimensions = dimensions => diff --git a/src/components/Nav.js b/src/components/Nav.js index 76584c0..3ce8230 100644 --- a/src/components/Nav.js +++ b/src/components/Nav.js @@ -72,6 +72,6 @@ const NavDetailed = React.memo(() => ( )) -const Nav = React.memo(() => ) +const Nav = NavBullets export { Nav, NavDetailed } diff --git a/src/components/pages/ForwardKinematicsPage.js b/src/components/pages/ForwardKinematicsPage.js index 299f089..8db813b 100644 --- a/src/components/pages/ForwardKinematicsPage.js +++ b/src/components/pages/ForwardKinematicsPage.js @@ -3,6 +3,7 @@ import LegPoseWidget from "./LegPoseWidgets" import { Card, ToggleSwitch, BasicButton, NumberInputField, Slider } from "../generic" import { DEFAULT_POSE } from "../../templates" import { SECTION_NAMES, LEG_NAMES, RESET_LABEL } from "../vars" +import { usePageLoad, useUpdatePose } from "../providers/Handlers" const renderTwoColumns = cells => ( <> @@ -97,4 +98,12 @@ class ForwardKinematicsPage extends Component { } } -export default ForwardKinematicsPage +const WithHandlers = props => { + const onMount = usePageLoad() + const onUpdate = useUpdatePose() + return +} + +WithHandlers.displayName = "WithHandlers(ForwardKinematicsPage)" + +export default WithHandlers diff --git a/src/components/pages/InverseKinematicsPage.js b/src/components/pages/InverseKinematicsPage.js index dba8ac7..93aa4de 100644 --- a/src/components/pages/InverseKinematicsPage.js +++ b/src/components/pages/InverseKinematicsPage.js @@ -3,6 +3,7 @@ import { sliderList, Card, BasicButton } from "../generic" import { solveInverseKinematics } from "../../hexapod" import { SECTION_NAMES, IK_SLIDERS_LABELS, RESET_LABEL } from "../vars" import { DEFAULT_IK_PARAMS } from "../../templates" +import { usePageLoad, useUpdateIk } from "../providers/Handlers" const _updatedStateParamsUnsolved = message => ({ showPoseMessage: false, @@ -72,4 +73,12 @@ class InverseKinematicsPage extends Component { } } -export default InverseKinematicsPage +const WithHandlers = props => { + const onMount = usePageLoad() + const onUpdate = useUpdateIk() + return +} + +WithHandlers.displayName = "WithHandlers(InverseKinematicsPage)" + +export default WithHandlers diff --git a/src/components/pages/LandingPage.js b/src/components/pages/LandingPage.js index cfe80a8..72c5f2f 100644 --- a/src/components/pages/LandingPage.js +++ b/src/components/pages/LandingPage.js @@ -1,11 +1,14 @@ import React, { useLayoutEffect } from "react" import { NavDetailed } from ".." import { LANDING_PAGE_TITLE, LANDING_PAGE_SUBTITLE, SECTION_NAMES } from "../vars" +import { usePageLoad } from "../providers/Handlers" + +function LandingPage() { + const onPageLoad = usePageLoad() -function LandingPage({ onMount }) { useLayoutEffect(() => { - onMount(SECTION_NAMES.landingPage) - }, [onMount]) + onPageLoad(SECTION_NAMES.landingPage) + }, [onPageLoad]) return ( <> diff --git a/src/components/pages/LegPatternPage.js b/src/components/pages/LegPatternPage.js index 64b8a54..534d06e 100644 --- a/src/components/pages/LegPatternPage.js +++ b/src/components/pages/LegPatternPage.js @@ -2,6 +2,7 @@ import React, { Component } from "react" import { sliderList, Card, BasicButton } from "../generic" import { DEFAULT_POSE, DEFAULT_PATTERN_PARAMS } from "../../templates" import { SECTION_NAMES, ANGLE_NAMES, RESET_LABEL } from "../vars" +import { usePageLoad, useUpdatePose } from "../providers/Handlers" class LegPatternPage extends Component { pageName = SECTION_NAMES.legPatterns @@ -45,4 +46,12 @@ class LegPatternPage extends Component { ) } -export default LegPatternPage +const WithHandlers = props => { + const onMount = usePageLoad() + const onUpdate = useUpdatePose() + return +} + +WithHandlers.displayName = "WithHandlers(LegPatternPage)" + +export default WithHandlers diff --git a/src/components/pages/WalkingGaitsPage.js b/src/components/pages/WalkingGaitsPage.js index 3db7e7d..2efcbd0 100644 --- a/src/components/pages/WalkingGaitsPage.js +++ b/src/components/pages/WalkingGaitsPage.js @@ -3,6 +3,8 @@ import { sliderList, Card, BasicButton, ToggleSwitch } from "../generic" import { SECTION_NAMES, RESET_LABEL } from "../vars" import getWalkSequence from "../../hexapod/solvers/walkSequenceSolver" +import { usePageLoad, useUpdatePose } from "../providers/Handlers" + const SLIDER_LABELS = [ "tz", "tx", @@ -179,4 +181,12 @@ class WalkingGaitsPage extends Component { } } -export default WalkingGaitsPage +const WithHandlers = props => { + const onMount = usePageLoad() + const onUpdate = useUpdatePose() + return +} + +WithHandlers.displayName = "WithHandlers(WalkingGaitsPage)" + +export default WithHandlers diff --git a/src/components/providers/Handlers.js b/src/components/providers/Handlers.js new file mode 100644 index 0000000..a8b8af5 --- /dev/null +++ b/src/components/providers/Handlers.js @@ -0,0 +1,37 @@ +import React, { createContext, useContext, useMemo } from "react" + +const HandlersCtx = createContext() + +export function HandlersProvider({ + onPageLoad, + updateIk, + updatePose, + updateDimensions, + children, +}) { + const value = useMemo( + () => ({ + onPageLoad, + updateIk, + updatePose, + updateDimensions, + }), + [onPageLoad, updateIk, updatePose, updateDimensions] + ) + return {children} +} + +export const usePageLoad = () => { + const { onPageLoad } = useContext(HandlersCtx) + return useMemo(() => onPageLoad, [onPageLoad]) +} + +export const useUpdatePose = () => { + const { updatePose } = useContext(HandlersCtx) + return useMemo(() => updatePose, [updatePose]) +} + +export const useUpdateIk = () => { + const { updateIk } = useContext(HandlersCtx) + return useMemo(() => updateIk, [updateIk]) +} diff --git a/src/components/vars.js b/src/components/vars.js index ac23806..eec6146 100644 --- a/src/components/vars.js +++ b/src/components/vars.js @@ -130,6 +130,7 @@ const HEXAPOD_LINK_PATHS = [ PATHS.inverseKinematics.path, PATHS.forwardKinematics.path, PATHS.legPatterns.path, + PATHS.walkingGaits.path, ] const URL_LINKS = [KOFI_LINK_PROPERTIES, REPO_LINK_PROPERTIES] diff --git a/src/routes.js b/src/routes.js index ba3de78..6029f13 100644 --- a/src/routes.js +++ b/src/routes.js @@ -13,8 +13,9 @@ import { SuspenseHexapodPlot, } from "./loadables" +import { HandlersProvider } from "./components/providers/Handlers" + export const Routes = ({ - onPageLoad, showPoseMessage, showInfo, info, @@ -24,6 +25,7 @@ export const Routes = ({ hexapodParams, patternParams, ikParams, + onPageLoad, updateIk, updatePose, updateDimensions, @@ -31,7 +33,12 @@ export const Routes = ({ }) => { const { path } = useRouteMatch() return ( - <> +
@@ -44,13 +51,11 @@ export const Routes = ({ - + @@ -59,15 +64,11 @@ export const Routes = ({ dimensions: hexapodParams.dimensions, ikParams: ikParams, }} - onUpdate={updateIk} - onMount={onPageLoad} /> @@ -75,8 +76,6 @@ export const Routes = ({ params={{ dimensions: hexapodParams.dimensions, }} - onUpdate={updatePose} - onMount={onPageLoad} /> @@ -98,7 +97,7 @@ export const Routes = ({ - + ) } diff --git a/src/tests/components/App.test.js b/src/tests/components/App.test.js index 905ad24..c1acac8 100644 --- a/src/tests/components/App.test.js +++ b/src/tests/components/App.test.js @@ -1,5 +1,5 @@ import React from "react" -import { render, screen, fireEvent, act } from "@testing-library/react" +import { render, screen, fireEvent, act, waitFor } from "@testing-library/react" import App from "../../App" import { PATH_LINKS, URL_LINKS } from "../../components/vars" @@ -145,7 +145,12 @@ const expectEachPage = ( expectToHaveNav() } -const click = name => fireEvent.click(screen.getByRole("link", { name })) +const click = async name => { + const element = await waitFor(() => screen.getByRole("link", { name })) + await act(async () => { + await fireEvent.click(element) + }) +} /* * * * Application @@ -159,33 +164,27 @@ describe("App", () => { }) test("Navigates to Leg Patterns page", async () => { - await act(async () => { - click("Leg Patterns") - }) + await click("Leg Patterns") expectEachPage() expectToHaveDefaultLegPatternsPage() }) test("Navigates to Inverse Kinematics page", async () => { - await act(async () => { - click("Inverse Kinematics") - }) + await click("Inverse Kinematics") + expectEachPage() expectToHaveDefaultInverseKinematics() }) test("Navigates to Forward Kinematics page", async () => { - await act(async () => { - click("Forward Kinematics") - }) + await click("Forward Kinematics") + expectEachPage({ numberOfResetButtons: 2, numberOfToggleSwitches: 2 }) expectToHaveDefaultForwardKinematics() }) test("Navigates to Landing Page", async () => { - await act(async () => { - click("Root") - }) + await click("Root") const heading = screen.getByRole("heading", { name: "Mithi's Bare Minimum Hexapod Robot Simulator",