diff --git a/package-lock.json b/package-lock.json index 1ddc5412..1fb7ea5f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9116,6 +9116,11 @@ "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", "integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=" }, + "deep-diff": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/deep-diff/-/deep-diff-1.0.2.tgz", + "integrity": "sha512-aWS3UIVH+NPGCD1kki+DCU9Dua032iSsO43LqQpcs4R3+dVv7tX0qBGjiVHJHjplsoUM2XRO/KB92glqc68awg==" + }, "deep-equal": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", @@ -10613,6 +10618,11 @@ } } }, + "exenv": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/exenv/-/exenv-1.2.2.tgz", + "integrity": "sha1-KueOhdmJQVhnCwPUe+wfA72Ru50=" + }, "exit": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", @@ -12694,6 +12704,11 @@ } } }, + "is-lite": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/is-lite/-/is-lite-0.8.1.tgz", + "integrity": "sha512-ekSwuewzOmwFnzzAOWuA5fRFPqOeTrLIL3GWT7hdVVi+oLuD+Rau8gCmkb94vH5hjXc1Q/CfIW/y/td1RrNQIg==" + }, "is-module": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", @@ -16192,6 +16207,11 @@ "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" }, + "nested-property": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/nested-property/-/nested-property-4.0.0.tgz", + "integrity": "sha512-yFehXNWRs4cM0+dz7QxCd06hTbWbSkV0ISsqBfkntU6TOY4Qm3Q88fRRLOddkGh2Qq6dZvnKVAahfhjcUvLnyA==" + }, "next-tick": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", @@ -18553,6 +18573,40 @@ "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.9.tgz", "integrity": "sha512-nQTTcUu+ATDbrSD1BZHr5kgSD4oF8OFjxun8uAaL8RwPBacGBNPf/yAuVVdx17N8XNzRDMrZ9XcKZHCjPW+9ew==" }, + "react-floater": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/react-floater/-/react-floater-0.7.3.tgz", + "integrity": "sha512-d1wAEph+xRxQ0RJ3woMmYLlZHTaCIsja7Bv6JNo2ezsVUgdMan4CxOR4Do4/xgpmRFfsQMdlygexLAZZypWirw==", + "requires": { + "deepmerge": "^4.2.2", + "exenv": "^1.2.2", + "is-lite": "^0.8.1", + "popper.js": "^1.16.0", + "react-proptype-conditional-require": "^1.0.4", + "tree-changes": "^0.5.1" + }, + "dependencies": { + "nested-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/nested-property/-/nested-property-1.0.1.tgz", + "integrity": "sha512-BnBBoo/8bBNRdAnJc7+m79oWk7dXwW1+vCesaEQhfDGVwXGLMvmI4NwYgLTW94R/x+R2s/yr2g/hB/4w/YSAvA==" + }, + "popper.js": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz", + "integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==" + }, + "tree-changes": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/tree-changes/-/tree-changes-0.5.1.tgz", + "integrity": "sha512-O873xzV2xRZ6N059Mn06QzmGKEE21LlvIPbsk2G+GS9ZX5OCur6PIwuuh0rWpAPvLWQZPj0XObyG27zZyLHUzw==", + "requires": { + "deep-diff": "^1.0.2", + "nested-property": "1.0.1" + } + } + } + }, "react-form-validator-core": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/react-form-validator-core/-/react-form-validator-core-1.0.0.tgz", @@ -18577,6 +18631,23 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, + "react-joyride": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/react-joyride/-/react-joyride-2.3.1.tgz", + "integrity": "sha512-MmyhECU3V+4kZAJrcDPPXcXxaoTpwc7g+E7Cq6QZ5IqJZrWYSVvpVCfudQcdcf6BsNbgawRhvCvbQyeWoPtNig==", + "requires": { + "deep-diff": "^1.0.2", + "deepmerge": "^4.2.2", + "exenv": "^1.2.2", + "is-lite": "^0.8.1", + "nested-property": "^4.0.0", + "react-floater": "^0.7.3", + "react-is": "^16.13.1", + "scroll": "^3.0.1", + "scrollparent": "^2.0.1", + "tree-changes": "^0.7.1" + } + }, "react-konva": { "version": "16.13.0-3", "resolved": "https://registry.npmjs.org/react-konva/-/react-konva-16.13.0-3.tgz", @@ -18600,6 +18671,11 @@ "react-form-validator-core": "1.0.0" } }, + "react-proptype-conditional-require": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/react-proptype-conditional-require/-/react-proptype-conditional-require-1.0.4.tgz", + "integrity": "sha1-acLVdB5t9eCPIw82u8KUTuEiJVU=" + }, "react-reconciler": { "version": "0.25.1", "resolved": "https://registry.npmjs.org/react-reconciler/-/react-reconciler-0.25.1.tgz", @@ -19819,6 +19895,16 @@ "ajv-keywords": "^3.5.2" } }, + "scroll": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/scroll/-/scroll-3.0.1.tgz", + "integrity": "sha512-pz7y517OVls1maEzlirKO5nPYle9AXsFzTMNJrRGmT951mzpIBy7sNHOg5o/0MQd/NqliCiWnAi0kZneMPFLcg==" + }, + "scrollparent": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/scrollparent/-/scrollparent-2.0.1.tgz", + "integrity": "sha1-cV1bnMV3YPsivczDvvtb/gaxoxc=" + }, "select-hose": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", @@ -20922,9 +21008,9 @@ "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==" }, "tar": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.0.tgz", - "integrity": "sha512-DUCttfhsnLCjwoDoFcI+B2iJgYa93vBnDUATYEeRx6sntCTdN01VnqsIuTlALXla/LWooNg0yEGeB+Y8WdFxGA==", + "version": "6.1.6", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.6.tgz", + "integrity": "sha512-oaWyu5dQbHaYcyZCTfyPpC+VmI62/OM2RTUYavTk1MDr1cwW5Boi3baeYQKiZbY2uSQJGr+iMOzb/JFxLrft+g==", "requires": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", @@ -21300,6 +21386,15 @@ "punycode": "^2.1.1" } }, + "tree-changes": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/tree-changes/-/tree-changes-0.7.1.tgz", + "integrity": "sha512-sPIt8PKDi0OQTglr7lsetcB9DU19Ls/ZgFSjFvK6DWJGisAn4sOxtjpmQfuqjexQE4UU9U53LNmataL1kRJ3Uw==", + "requires": { + "fast-deep-equal": "^3.1.3", + "is-lite": "^0.8.1" + } + }, "tryer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/tryer/-/tryer-1.0.1.tgz", @@ -22057,8 +22152,7 @@ }, "ssri": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", - "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", + "resolved": "", "requires": { "figgy-pudding": "^3.5.1" } diff --git a/package.json b/package.json index 85c2b84a..e44c7e86 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "react-debounce-input": "^3.2.2", "react-dom": "^16.13.1", "react-hotkeys-hook": "^2.4.0", + "react-joyride": "^2.3.1", "react-konva": "^16.13.0-3", "react-material-ui-form-validator": "^2.1.1", "react-router-dom": "^5.2.0", diff --git a/src/App.jsx b/src/App.jsx index 94bf8867..c0ebb34c 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -8,6 +8,7 @@ import { BuildProvider, TestRunProvider, SocketProvider, + HelpProvider, } from "./contexts"; import Router from "./Router"; @@ -19,12 +20,14 @@ function App() { - -
- - - - + + +
+ + + + + diff --git a/src/_test/test.moun.helper.tsx b/src/_test/test.moun.helper.tsx index a08bcc22..002258c0 100644 --- a/src/_test/test.moun.helper.tsx +++ b/src/_test/test.moun.helper.tsx @@ -5,6 +5,7 @@ import { UserProvider, BuildProvider, TestRunProvider, + HelpProvider, } from "../contexts"; import { MemoryRouter, Route } from "react-router-dom"; import { MemoryRouterProps } from "react-router"; @@ -26,7 +27,9 @@ export const mountVrtComponent = ({ - {component} + + {component} + diff --git a/src/components/BuildDetails.tsx b/src/components/BuildDetails.tsx index 6a001e31..47901b52 100644 --- a/src/components/BuildDetails.tsx +++ b/src/components/BuildDetails.tsx @@ -9,6 +9,7 @@ import { import { BuildStatusChip } from "./BuildStatusChip"; import { formatDateTime } from "../_helpers/format.helper"; import { useBuildState } from "../contexts"; +import { LOCATOR_BUILD_DETAILS } from '../constants/help'; const BuildDetails: React.FunctionComponent = () => { const { selectedBuild } = useBuildState(); @@ -20,7 +21,7 @@ const BuildDetails: React.FunctionComponent = () => { const loadingAnimation = selectedBuild.isRunning && ; return ( - + diff --git a/src/components/Filters.tsx b/src/components/Filters.tsx index 81bc61e2..5647aea5 100644 --- a/src/components/Filters.tsx +++ b/src/components/Filters.tsx @@ -10,6 +10,7 @@ import { } from "@material-ui/core"; import { TestRun, TestVariation } from "../types"; import { DebounceInput } from "react-debounce-input"; +import { LOCATOR_RESET_FILTER } from "../constants/help"; interface IProps { items: (TestRun | TestVariation)[]; @@ -239,7 +240,7 @@ const Filters: React.FunctionComponent = ({ )} {branchNameList && branchNameList.length > 0 && ( - + Branch diff --git a/src/components/GuidedTour.tsx b/src/components/GuidedTour.tsx new file mode 100644 index 00000000..febc74aa --- /dev/null +++ b/src/components/GuidedTour.tsx @@ -0,0 +1,69 @@ +import React, { FunctionComponent } from "react"; +import Joyride, { CallBackProps, STATUS } from "react-joyride"; +import { IconButton, Avatar } from "@material-ui/core"; +import { HelpOutline } from "@material-ui/icons"; +import { useHelpState } from "../contexts"; + +const GuidedTour: FunctionComponent = () => { + const [run, setRun] = React.useState(false); + const { helpSteps } = useHelpState(); + + const getHelpSteps = React.useCallback(() => { + const firstStep = helpSteps[0]; + //Below line is to prevent application breaking if element is not present for any reason (e.g. if the user deletes build or if there is no data.) + if ( + firstStep && + document.getElementById(firstStep.target.toString().slice(1)) + ) { + helpSteps.forEach((e) => { + e.disableBeacon = true; + e.hideCloseButton = true; + }); + return helpSteps; + } + return []; + }, [helpSteps]); + + const handleJoyrideCallback = (data: CallBackProps) => { + const { status } = data; + const finishedStatuses: string[] = [STATUS.FINISHED, STATUS.SKIPPED]; + + if (finishedStatuses.includes(status)) { + setRun(false); + } + }; + + const handleClickStart = (e: React.MouseEvent) => { + e.preventDefault(); + setRun(true); + }; + + return ( + + + + + + + + + ); +}; + +export default GuidedTour; diff --git a/src/components/Header.spec.tsx b/src/components/Header.spec.tsx index efceff16..7dbda17a 100644 --- a/src/components/Header.spec.tsx +++ b/src/components/Header.spec.tsx @@ -1,36 +1,25 @@ /* global cy */ import React from "react"; -import { mount } from "@cypress/react"; import Header from "./Header"; -import { UserProvider } from "../contexts"; -import { BrowserRouter } from "react-router-dom"; import { haveUserLogged } from "../_test/precondition.helper"; -import { TEST_USER } from "../_test/test.data.helper"; +import { TEST_PROJECT, TEST_USER } from "../_test/test.data.helper"; +import { mountVrtComponent } from "../_test/test.moun.helper"; +import { projectStub } from "../_test/stub.helper"; describe("Header", () => { describe("image", () => { it("Guest", () => { localStorage.clear(); - mount( - - -
- - - ); + mountVrtComponent({ component:
}); cy.get("#__cy_root").vrtTrack("Header. Guest"); }); it("Logged", () => { haveUserLogged(TEST_USER); - mount( - - -
- - - ); + projectStub.getAll([TEST_PROJECT]); + + mountVrtComponent({ component:
}); cy.get("#__cy_root").vrtTrack("Header. Logged"); }); diff --git a/src/components/Header.tsx b/src/components/Header.tsx index 5f86d24c..290688e2 100644 --- a/src/components/Header.tsx +++ b/src/components/Header.tsx @@ -12,6 +12,7 @@ import { Link } from "react-router-dom"; import { useUserDispatch, useUserState, logout } from "../contexts"; import { routes } from "../constants"; import logo from "../static/logo.png"; +import GuidedTour from "./GuidedTour"; const Header: FunctionComponent = () => { const [menuRef, setMenuRef] = React.useState(null); @@ -78,15 +79,18 @@ const Header: FunctionComponent = () => { - {loggedIn && ( - ) => - setMenuRef(event.currentTarget) - } - > - {`${user?.firstName[0]}${user?.lastName[0]}`} - - )} + + + {loggedIn && ( + ) => + setMenuRef(event.currentTarget) + } + > + {`${user?.firstName[0]}${user?.lastName[0]}`} + + )} + diff --git a/src/components/LoginForm.tsx b/src/components/LoginForm.tsx index 21cd591e..3b7c7429 100644 --- a/src/components/LoginForm.tsx +++ b/src/components/LoginForm.tsx @@ -9,7 +9,7 @@ import { Typography, } from "@material-ui/core"; import { useUserDispatch, login } from "../contexts"; -import { routes } from "../constants"; +import { LOCATOR_LOGIN_FORM, routes } from "../constants"; import { useSnackbar } from "notistack"; import { TextValidator, ValidatorForm } from "react-material-ui-form-validator"; @@ -33,7 +33,7 @@ const LoginForm = () => { return ( - + = ({ /> - diff --git a/src/constants/help.ts b/src/constants/help.ts new file mode 100644 index 00000000..4b1306ff --- /dev/null +++ b/src/constants/help.ts @@ -0,0 +1,79 @@ +import { Step } from "react-joyride"; + +export const LOCATOR_LOGIN_FORM = "loginform-1"; +export const LOCATOR_BUILD_DETAILS = "build-details"; +export const LOCATOR_RESET_FILTER = "reset-filter"; +export const LOCATOR_TEST_VARIATION_SELECT_BRANCH = "select-branch"; +export const LOCATOR_PROJECT_LIST_PAGE_PROJECT_LIST = "projectlist-1"; +export const LOCATOR_PROJECT_PAGE_SELECT_PROJECT = "select-project"; +export const LOCATOR_PROJECT_PAGE_BUILD_LIST = "build-list"; +export const LOCATOR_PROJECT_PAGE_BUILD_DETAILS = "build-details"; +export const LOCATOR_PROJECT_PAGE_TEST_RUN_LIST = "test-run-list"; +export const LOCATOR_TEST_VARIATION_LIST_PAGE_SELECT_PROJECT = "select-project"; +export const LOCATOR_TEST_VARIATION_LIST_PAGE_SELECT_BRANCH = "select-branch"; +export const LOCATOR_TEST_VARIATION_LIST_PAGE_SELECT_RESET_FILTER = + "reset-filter"; + +export const LOGIN_PAGE_STEPS: Step[] = [ + { + target: `#${LOCATOR_LOGIN_FORM}`, + content: + "Default admin account: visual-regression-tracker@example.com / 123456. Make sure to change default password.", + }, + { + target: `#${LOCATOR_LOGIN_FORM}`, + content: "Create new account without restrictions.", + }, +]; + +export const PROJECT_LIST_PAGE_STEPS: Step[] = [ + { + target: `#${LOCATOR_PROJECT_LIST_PAGE_PROJECT_LIST}`, + content: + "Default project is created after first start, feel free to edit/add/delete projects.", + title: "Project List", + }, +]; + +export const PROJECT_PAGE_STEPS: Step[] = [ + { + target: "#" + LOCATOR_PROJECT_PAGE_SELECT_PROJECT, + content: "Select the project for which you want to view details.", + }, + { + target: "#" + LOCATOR_PROJECT_PAGE_BUILD_LIST, + content: "List of Builds", + }, + { + target: "#" + LOCATOR_PROJECT_PAGE_BUILD_LIST, + content: + "If you see 'No Builds', please run your image comparison from any client.", + }, + { + target: "#" + LOCATOR_PROJECT_PAGE_BUILD_DETAILS, + content: "Breif details for selected build.", + }, + { + target: "#" + LOCATOR_PROJECT_PAGE_TEST_RUN_LIST, + content: "TestRuns for selected build.", + }, +]; + +export const TEST_VARIATION_LIST_PAGE = [ + { + target: "#" + LOCATOR_TEST_VARIATION_LIST_PAGE_SELECT_PROJECT, + title: + "Shows all the historical record of baselines by Name + Branch + OS + Browser + Viewport + Device", + content: "Select the project you want to act on.", + }, + { + target: "#" + LOCATOR_TEST_VARIATION_SELECT_BRANCH, + title: "Merge from one branch to another", + content: + "Select the branch from/to which you want to merge the variations.", + }, + { + target: "#" + LOCATOR_TEST_VARIATION_LIST_PAGE_SELECT_RESET_FILTER, + content: "Only filtered items are displayed/merged to the target branch.", + }, +]; diff --git a/src/constants/index.ts b/src/constants/index.ts index 3e2e87b6..1d59c57e 100644 --- a/src/constants/index.ts +++ b/src/constants/index.ts @@ -1,2 +1,3 @@ export * from "./routes"; export * from "./project"; +export * from "./help"; diff --git a/src/contexts/help.context.tsx b/src/contexts/help.context.tsx new file mode 100644 index 00000000..741d1b54 --- /dev/null +++ b/src/contexts/help.context.tsx @@ -0,0 +1,69 @@ +import React from "react"; +import { Step } from "react-joyride"; + +interface ISetStepAction { + type: "setSteps"; + payload: Array; +} + +type IAction = ISetStepAction; + +type Dispatch = (action: IAction) => void; +type State = { + helpSteps: Array; +}; + +type HelpProviderProps = { children: React.ReactNode }; + +const StateContext = React.createContext(undefined); +const DispatchContext = React.createContext(undefined); + +const initialState: State = { + helpSteps: [], +}; + +function reducer(state: State, action: IAction): State { + switch (action.type) { + case "setSteps": + return { + ...state, + helpSteps: action.payload, + }; + default: + return state; + } +} + +function HelpProvider({ children }: HelpProviderProps) { + const [state, dispatch] = React.useReducer(reducer, initialState); + + return ( + + + {children} + + + ); +} + +function useHelpState() { + const context = React.useContext(StateContext); + if (context === undefined) { + throw new Error("must be used within a HelpContext"); + } + return context; +} + +function useHelpDispatch() { + const context = React.useContext(DispatchContext); + if (context === undefined) { + throw new Error("must be used within a HelpContext"); + } + return context; +} + +function setHelpSteps(dispatch: Dispatch, data: Array) { + dispatch({ type: "setSteps", payload: data }); +} + +export { HelpProvider, useHelpDispatch, useHelpState, setHelpSteps }; diff --git a/src/contexts/index.ts b/src/contexts/index.ts index c2e448ad..690917a4 100644 --- a/src/contexts/index.ts +++ b/src/contexts/index.ts @@ -3,3 +3,4 @@ export * from "./build.context"; export * from "./project.context"; export * from "./testRun.context"; export * from "./socket.context"; +export * from "./help.context"; diff --git a/src/pages/LoginPage.tsx b/src/pages/LoginPage.tsx index c1295cae..0322aa30 100644 --- a/src/pages/LoginPage.tsx +++ b/src/pages/LoginPage.tsx @@ -2,13 +2,15 @@ import React, { useEffect } from "react"; import { Grid } from "@material-ui/core"; import LoginForm from "../components/LoginForm"; import { useHistory, useLocation } from "react-router-dom"; -import { useUserState } from "../contexts"; -import { routes } from "../constants"; +import { setHelpSteps, useHelpDispatch, useUserState } from "../contexts"; +import { LOGIN_PAGE_STEPS, routes } from "../constants"; const LoginPage = () => { const history = useHistory(); const location = useLocation<{ from: string }>(); const { loggedIn } = useUserState(); + const helpDispatch = useHelpDispatch(); + const { from } = location.state || { from: { pathname: routes.HOME }, }; @@ -17,6 +19,10 @@ const LoginPage = () => { if (loggedIn) history.replace(from); }); + useEffect(() => { + setHelpSteps(helpDispatch, LOGIN_PAGE_STEPS); + }); + return ( { const { enqueueSnackbar } = useSnackbar(); const projectState = useProjectState(); const projectDispatch = useProjectDispatch(); + const helpDispatch = useHelpDispatch(); const [createDialogOpen, setCreateDialogOpen] = React.useState(false); const [updateDialogOpen, setUpdateDialogOpen] = React.useState(false); const [deleteDialogOpen, setDeleteDialogOpen] = React.useState(false); + useEffect(() => { + setHelpSteps(helpDispatch, PROJECT_LIST_PAGE_STEPS); + }); + const toggleCreateDialogOpen = () => { setCreateDialogOpen(!createDialogOpen); }; @@ -139,7 +150,7 @@ const ProjectsListPage = () => { {projectState.projectList.map((project) => ( - + Id: {project.id} Name: {project.name} diff --git a/src/pages/ProjectPage.tsx b/src/pages/ProjectPage.tsx index 3553675c..311530ee 100644 --- a/src/pages/ProjectPage.tsx +++ b/src/pages/ProjectPage.tsx @@ -1,4 +1,4 @@ -import React from "react"; +import React, { useEffect } from "react"; import { Grid, Box, makeStyles } from "@material-ui/core"; import { useParams, useHistory } from "react-router-dom"; import BuildList from "../components/BuildList"; @@ -6,6 +6,13 @@ import ProjectSelect from "../components/ProjectSelect"; import TestRunList from "../components/TestRunList"; import BuildDetails from "../components/BuildDetails"; import { TestDetailsDialog } from "../components/TestDetailsDialog"; +import { useHelpDispatch, setHelpSteps } from "../contexts"; +import { + PROJECT_PAGE_STEPS, + LOCATOR_PROJECT_PAGE_BUILD_LIST, + LOCATOR_PROJECT_PAGE_SELECT_PROJECT, + LOCATOR_PROJECT_PAGE_TEST_RUN_LIST, +} from "../constants"; const useStyles = makeStyles((theme) => ({ root: { @@ -17,18 +24,23 @@ const ProjectPage = () => { const classes = useStyles(); const { projectId } = useParams<{ projectId: string }>(); const history = useHistory(); + const helpDispatch = useHelpDispatch(); + + useEffect(() => { + setHelpSteps(helpDispatch, PROJECT_PAGE_STEPS); + }); return ( - + history.push(id)} /> - + @@ -36,7 +48,7 @@ const ProjectPage = () => { - + diff --git a/src/pages/TestVariationListPage.tsx b/src/pages/TestVariationListPage.tsx index 4a8c08c6..66c03044 100644 --- a/src/pages/TestVariationListPage.tsx +++ b/src/pages/TestVariationListPage.tsx @@ -8,10 +8,16 @@ import ProjectSelect from "../components/ProjectSelect"; import Filters from "../components/Filters"; import { TestVariationMergeForm } from "../components/TestVariationMergeForm"; import { useSnackbar } from "notistack"; +import { setHelpSteps, useHelpDispatch } from "../contexts/help.context"; +import { + LOCATOR_TEST_VARIATION_LIST_PAGE_SELECT_PROJECT, + TEST_VARIATION_LIST_PAGE, +} from "../constants"; const TestVariationListPage: React.FunctionComponent = () => { const history = useHistory(); const { enqueueSnackbar } = useSnackbar(); + const helpDispatch = useHelpDispatch(); const { projectId = "" } = useParams<{ projectId: string }>(); const [testVariations, setTestVariations] = React.useState( [] @@ -27,6 +33,10 @@ const TestVariationListPage: React.FunctionComponent = () => { const [branchName, setBranchName] = React.useState(""); const [filteredItems, setFilteredItems] = React.useState([]); + React.useEffect(() => { + setHelpSteps(helpDispatch, TEST_VARIATION_LIST_PAGE); + }); + React.useEffect(() => { if (projectId) { testVariationService @@ -55,7 +65,16 @@ const TestVariationListPage: React.FunctionComponent = () => { (browser ? t.browser === browser : true) // by browser ) ); - }, [query, branchName, os, device, browser, viewport, customTags, testVariations]); + }, [ + query, + branchName, + os, + device, + browser, + viewport, + customTags, + testVariations, + ]); const handleDelete = (id: string) => { testVariationService @@ -77,7 +96,7 @@ const TestVariationListPage: React.FunctionComponent = () => { - + history.push(id)}