From 36b2db918535fc3c50be449e2adb3b34f29e70dd Mon Sep 17 00:00:00 2001 From: minnakt <47064971+minnakt@users.noreply.github.com> Date: Mon, 18 Dec 2023 14:11:17 -0500 Subject: [PATCH 1/8] DEVPROD-754: Add /distros redirect route (#2196) --- .../integration/distroSettings/navigation.ts | 8 ++++++++ src/components/Content/index.tsx | 5 +++++ src/components/Header/AuxiliaryDropdown.tsx | 2 +- .../Redirects/DistroSettingsRedirect.tsx | 19 +++++++++++++++++++ src/components/Redirects/index.ts | 2 ++ src/constants/routes.ts | 2 ++ src/hooks/useFirstDistro.ts | 17 ++++++++++------- 7 files changed, 47 insertions(+), 8 deletions(-) create mode 100644 src/components/Redirects/DistroSettingsRedirect.tsx diff --git a/cypress/integration/distroSettings/navigation.ts b/cypress/integration/distroSettings/navigation.ts index 8b0e39fd26..9d92d620e6 100644 --- a/cypress/integration/distroSettings/navigation.ts +++ b/cypress/integration/distroSettings/navigation.ts @@ -62,3 +62,11 @@ describe("using the distro dropdown", () => { }); }); }); + +describe("/distros redirect route", () => { + it("should redirect to the first distro available", () => { + cy.visit("/distros"); + cy.location("pathname").should("not.contain", "/distros"); + cy.location("pathname").should("eq", "/distro/localhost/settings/general"); + }); +}); diff --git a/src/components/Content/index.tsx b/src/components/Content/index.tsx index fb016acab5..b6df1ed174 100644 --- a/src/components/Content/index.tsx +++ b/src/components/Content/index.tsx @@ -1,5 +1,6 @@ import { Route, Routes, Navigate } from "react-router-dom"; import { + DistroSettingsRedirect, ProjectSettingsRedirect, UserPatchesRedirect, WaterfallCommitsRedirect, @@ -45,6 +46,10 @@ export const Content: React.FC = () => ( }> + } + /> } /> } /> }> diff --git a/src/components/Header/AuxiliaryDropdown.tsx b/src/components/Header/AuxiliaryDropdown.tsx index dc71cf8b53..84c7c1e8f3 100644 --- a/src/components/Header/AuxiliaryDropdown.tsx +++ b/src/components/Header/AuxiliaryDropdown.tsx @@ -18,7 +18,7 @@ export const AuxiliaryDropdown: React.FC = ({ projectIdentifier, }) => { const { sendEvent } = useNavbarAnalytics(); - const distro = useFirstDistro(); + const { distro } = useFirstDistro(); const menuItems = [ { diff --git a/src/components/Redirects/DistroSettingsRedirect.tsx b/src/components/Redirects/DistroSettingsRedirect.tsx new file mode 100644 index 0000000000..345f180668 --- /dev/null +++ b/src/components/Redirects/DistroSettingsRedirect.tsx @@ -0,0 +1,19 @@ +import { Navigate } from "react-router-dom"; +import { + getDistroSettingsRoute, + DistroSettingsTabRoutes, +} from "constants/routes"; +import { useFirstDistro } from "hooks"; + +export const DistroSettingsRedirect: React.FC = () => { + const { distro, loading } = useFirstDistro(); + + if (loading) { + return null; + } + return ( + + ); +}; diff --git a/src/components/Redirects/index.ts b/src/components/Redirects/index.ts index bfea8f8f1c..d8b9def686 100644 --- a/src/components/Redirects/index.ts +++ b/src/components/Redirects/index.ts @@ -1,8 +1,10 @@ +import { DistroSettingsRedirect } from "./DistroSettingsRedirect"; import { ProjectSettingsRedirect } from "./ProjectSettingsRedirect"; import { UserPatchesRedirect } from "./UserPatchesRedirect"; import { WaterfallCommitsRedirect } from "./WaterfallCommitsRedirect"; export { + DistroSettingsRedirect, ProjectSettingsRedirect, UserPatchesRedirect, WaterfallCommitsRedirect, diff --git a/src/constants/routes.ts b/src/constants/routes.ts index 777442bc65..34bf04c19c 100644 --- a/src/constants/routes.ts +++ b/src/constants/routes.ts @@ -56,6 +56,7 @@ const paths = { commits: "/commits", container: "/container", distro: "/distro", + distros: "/distros", host: "/host", hosts: "/hosts", jobLogs: "/job-logs", @@ -74,6 +75,7 @@ const paths = { waterfall: "/waterfall", }; export const redirectRoutes = { + distroSettings: paths.distros, projectSettings: paths.projects, userPatches: `${paths.user}/:id`, waterfall: `${paths.waterfall}/:projectIdentifier`, diff --git a/src/hooks/useFirstDistro.ts b/src/hooks/useFirstDistro.ts index 7c09714de3..fb0972b665 100644 --- a/src/hooks/useFirstDistro.ts +++ b/src/hooks/useFirstDistro.ts @@ -5,14 +5,17 @@ import { DISTROS } from "gql/queries"; /** * `useFirstDistro` returns the alphabetically first distro from Evergreen's list of distros. * This can be used to generate a general link to distro settings. - * @returns the distro ID + * @returns an object containing the distro ID (string) and loading state (boolean) */ export const useFirstDistro = () => { - const { data } = useQuery(DISTROS, { - variables: { - onlySpawnable: false, - }, - }); + const { data, loading } = useQuery( + DISTROS, + { + variables: { + onlySpawnable: false, + }, + } + ); - return data?.distros?.[0]?.name ?? "ubuntu2204-large"; + return { distro: data?.distros?.[0]?.name ?? "ubuntu2204-large", loading }; }; From 1c1397b15c7f83d43fa65a2086384d2f43bb32d7 Mon Sep 17 00:00:00 2001 From: Mohamed Khelif Date: Tue, 19 Dec 2023 12:37:28 -0500 Subject: [PATCH 2/8] DEVPROD-794 Split out ETATimer and RuntimeTimer into their own components (#2189) --- src/hooks/index.ts | 1 + src/hooks/useRunningTime/index.ts | 31 ++++++++++ .../useRunningTime/useRunningTime.test.ts | 49 +++++++++++++++ src/pages/task/metadata/ETATimer.tsx | 62 ------------------- .../task/metadata/ETATimer/ETATimer.test.tsx | 50 +++++++++++++++ src/pages/task/metadata/ETATimer/index.tsx | 30 +++++++++ src/pages/task/metadata/Metadata.test.tsx | 6 +- .../RuntimeTimer/RuntimeTimer.test.tsx | 32 ++++++++++ .../task/metadata/RuntimeTimer/index.tsx | 22 +++++++ src/pages/task/metadata/index.tsx | 6 +- src/utils/string/index.ts | 3 + 11 files changed, 226 insertions(+), 66 deletions(-) create mode 100644 src/hooks/useRunningTime/index.ts create mode 100644 src/hooks/useRunningTime/useRunningTime.test.ts delete mode 100644 src/pages/task/metadata/ETATimer.tsx create mode 100644 src/pages/task/metadata/ETATimer/ETATimer.test.tsx create mode 100644 src/pages/task/metadata/ETATimer/index.tsx create mode 100644 src/pages/task/metadata/RuntimeTimer/RuntimeTimer.test.tsx create mode 100644 src/pages/task/metadata/RuntimeTimer/index.tsx diff --git a/src/hooks/index.ts b/src/hooks/index.ts index 540a59753b..9afda3ba11 100644 --- a/src/hooks/index.ts +++ b/src/hooks/index.ts @@ -25,3 +25,4 @@ export { useDateFormat } from "./useDateFormat"; export { useFirstDistro } from "./useFirstDistro"; export { useBreadcrumbRoot } from "./useBreadcrumbRoot"; export { useResize } from "./useResize"; +export { useRunningTime } from "./useRunningTime"; diff --git a/src/hooks/useRunningTime/index.ts b/src/hooks/useRunningTime/index.ts new file mode 100644 index 0000000000..9eb23f5c61 --- /dev/null +++ b/src/hooks/useRunningTime/index.ts @@ -0,0 +1,31 @@ +import { useState, useEffect, useRef } from "react"; +import differenceInMilliseconds from "date-fns/differenceInMilliseconds"; + +/** + * `useRunningTime` is a hook that returns the time elapsed since the start time. It refreshes every second. + * @param startTime - The start time of the timer + * @returns + * - `runningTime` - The running time in milliseconds + * - `endTimer` - A function that clears the timer + */ +export const useRunningTime = (startTime: Date) => { + const [runningTime, setRunningTime] = useState( + differenceInMilliseconds(Date.now(), startTime) + ); + + const timerRef = useRef(null); + + useEffect(() => { + timerRef.current = setInterval(() => { + const newRunningTime = differenceInMilliseconds(Date.now(), startTime); + setRunningTime(newRunningTime > 0 ? newRunningTime : 0); + }, 1000); + + return () => clearInterval(timerRef.current); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [startTime]); + + const endTimer = () => clearInterval(timerRef.current); + + return { runningTime, endTimer }; +}; diff --git a/src/hooks/useRunningTime/useRunningTime.test.ts b/src/hooks/useRunningTime/useRunningTime.test.ts new file mode 100644 index 0000000000..83207bf98d --- /dev/null +++ b/src/hooks/useRunningTime/useRunningTime.test.ts @@ -0,0 +1,49 @@ +import { renderHook, act } from "test_utils"; +import { useRunningTime } from "."; // Make sure to adjust the import path + +describe("useRunningTime", () => { + beforeAll(() => { + jest.useFakeTimers(); + }); + afterAll(() => { + jest.useRealTimers(); + }); + + it("should initialize runningTime based on the provided startTime", () => { + const startTime = new Date(); + const { result } = renderHook(() => useRunningTime(startTime)); + + expect(result.current.runningTime).toBe(0); + }); + it("should update runningTime at intervals", () => { + const startTime = new Date(); + const { result } = renderHook(() => useRunningTime(startTime)); + + act(() => { + jest.advanceTimersByTime(1000); + }); + + expect(result.current.runningTime).toBe(1000); + }); + + it("should stop the timer when endTimer is called", () => { + const startTime = new Date(); + const { result } = renderHook(() => useRunningTime(startTime)); + + act(() => { + jest.advanceTimersByTime(1000); + }); + + expect(result.current.runningTime).toBe(1000); + + act(() => { + result.current.endTimer(); + }); + + act(() => { + jest.advanceTimersByTime(1000); + }); + + expect(result.current.runningTime).toBe(1000); // Should not have changed after stopping the timer + }); +}); diff --git a/src/pages/task/metadata/ETATimer.tsx b/src/pages/task/metadata/ETATimer.tsx deleted file mode 100644 index fbe531ed97..0000000000 --- a/src/pages/task/metadata/ETATimer.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import { useState, useEffect } from "react"; -import { addMilliseconds, differenceInMilliseconds } from "date-fns"; -import { MetadataItem } from "components/MetadataCard"; -import { string } from "utils"; - -const { msToDuration } = string; -interface ETATimerProps { - startTime: Date; - expectedDuration: number; -} -export const ETATimer: React.FC = ({ - expectedDuration, - startTime, -}) => { - const parsedStartTime = new Date(startTime); - const estimatedCompletionTime = addMilliseconds( - parsedStartTime, - expectedDuration - ); - - const currentTime = Date.now(); - const [eta, setEta] = useState( - differenceInMilliseconds(currentTime, estimatedCompletionTime) - ); - - const [runningTime, setRunningTime] = useState( - differenceInMilliseconds(parsedStartTime, currentTime) - ); - - useEffect(() => { - const timer = setInterval(() => { - const newEta = differenceInMilliseconds( - estimatedCompletionTime, - currentTime - ); - const newRunningTime = differenceInMilliseconds( - currentTime, - parsedStartTime - ); - setEta(newEta > 0 ? newEta : 0); - setRunningTime(newRunningTime > 0 ? newRunningTime : 0); - }, 1000); - if (eta === 0 || runningTime === 0) { - clearInterval(timer); - } - return () => clearInterval(timer); - }); - - return ( - <> - - Running Time:{" "} - {msToDuration(runningTime)} - - {eta >= 0 && ( - - ETA: {msToDuration(eta)} - - )} - - ); -}; diff --git a/src/pages/task/metadata/ETATimer/ETATimer.test.tsx b/src/pages/task/metadata/ETATimer/ETATimer.test.tsx new file mode 100644 index 0000000000..57f174e155 --- /dev/null +++ b/src/pages/task/metadata/ETATimer/ETATimer.test.tsx @@ -0,0 +1,50 @@ +import { render, screen, waitFor, act } from "test_utils"; +import ETATimer from "."; + +describe("etaTimer", () => { + beforeEach(() => { + jest.useFakeTimers(); + jest.spyOn(global, "setInterval"); + jest.spyOn(global, "clearInterval"); + }); + afterEach(() => { + jest.useRealTimers(); + jest.restoreAllMocks(); + }); + it("counts down", async () => { + const startTime = new Date(); + const expectedDuration = 10000; + render( + + ); + expect(screen.getByText("ETA: 10s")).toBeInTheDocument(); + act(() => { + jest.runOnlyPendingTimers(); + }); + await waitFor(() => { + expect(screen.getByText("ETA: 9s")).toBeInTheDocument(); + }); + act(() => { + jest.runOnlyPendingTimers(); + }); + await waitFor(() => { + expect(screen.getByText("ETA: 8s")).toBeInTheDocument(); + }); + }); + it("stops counting down when it reaches 0", async () => { + const startTime = new Date(); + const expectedDuration = 1000; + render( + + ); + expect(screen.getByText("ETA: 1s")).toBeInTheDocument(); + act(() => { + jest.runOnlyPendingTimers(); + }); + await waitFor(() => { + expect(screen.getByText("ETA: 0s")).toBeInTheDocument(); + }); + expect(global.clearInterval).toHaveBeenCalledWith(expect.any(Number)); + expect(jest.getTimerCount()).toBe(0); + }); +}); diff --git a/src/pages/task/metadata/ETATimer/index.tsx b/src/pages/task/metadata/ETATimer/index.tsx new file mode 100644 index 0000000000..438a4e72d0 --- /dev/null +++ b/src/pages/task/metadata/ETATimer/index.tsx @@ -0,0 +1,30 @@ +import { useEffect } from "react"; +import { MetadataItem } from "components/MetadataCard"; +import { useRunningTime } from "hooks"; +import { string } from "utils"; + +const { msToDuration } = string; + +interface ETATimerProps { + startTime: Date; + expectedDuration: number; +} +const ETATimer: React.FC = ({ expectedDuration, startTime }) => { + const parsedStartTime = new Date(startTime); + const { endTimer, runningTime } = useRunningTime(parsedStartTime); + + useEffect(() => { + if (runningTime >= expectedDuration) { + endTimer(); + } + }, [runningTime, expectedDuration, endTimer]); + + const eta = expectedDuration - runningTime; + return ( + + ETA: {msToDuration(eta)} + + ); +}; + +export default ETATimer; diff --git a/src/pages/task/metadata/Metadata.test.tsx b/src/pages/task/metadata/Metadata.test.tsx index 2fb2685a9a..eea9e28287 100644 --- a/src/pages/task/metadata/Metadata.test.tsx +++ b/src/pages/task/metadata/Metadata.test.tsx @@ -26,7 +26,7 @@ describe("metadata", () => { expect( screen.queryByDataCy("task-metadata-estimated_start") ).toHaveTextContent("1s"); - expect(screen.queryByDataCy("metadata-eta-timer")).toBeNull(); + expect(screen.queryByDataCy("task-metadata-eta")).toBeNull(); expect(screen.queryByDataCy("task-metadata-started")).toBeNull(); expect(screen.queryByDataCy("task-metadata-finished")).toBeNull(); }); @@ -45,7 +45,7 @@ describe("metadata", () => { } ); expect(screen.queryByDataCy("task-metadata-estimated_start")).toBeNull(); - expect(screen.getByDataCy("metadata-eta-timer")).toBeInTheDocument(); + expect(screen.getByDataCy("task-metadata-eta")).toBeInTheDocument(); expect(screen.getByDataCy("task-metadata-started")).toBeInTheDocument(); expect(screen.queryByDataCy("task-metadata-finished")).toBeNull(); expect(screen.queryByDataCy("task-trace-link")).toBeNull(); @@ -69,7 +69,7 @@ describe("metadata", () => { ); expect(screen.queryByDataCy("task-metadata-estimated_start")).toBeNull(); - expect(screen.queryByDataCy("metadata-eta-timer")).toBeNull(); + expect(screen.queryByDataCy("task-metadata-eta")).toBeNull(); expect(screen.getByDataCy("task-metadata-started")).toBeInTheDocument(); expect(screen.getByDataCy("task-metadata-finished")).toBeInTheDocument(); expect(screen.getByDataCy("task-trace-link")).toBeInTheDocument(); diff --git a/src/pages/task/metadata/RuntimeTimer/RuntimeTimer.test.tsx b/src/pages/task/metadata/RuntimeTimer/RuntimeTimer.test.tsx new file mode 100644 index 0000000000..fa330fee81 --- /dev/null +++ b/src/pages/task/metadata/RuntimeTimer/RuntimeTimer.test.tsx @@ -0,0 +1,32 @@ +import { render, screen, waitFor, act } from "test_utils"; +import RuntimeTimer from "."; + +describe("runtimeTimer", () => { + beforeEach(() => { + jest.useFakeTimers(); + jest.spyOn(global, "setInterval"); + jest.spyOn(global, "clearInterval"); + }); + afterEach(() => { + jest.useRealTimers(); + jest.restoreAllMocks(); + }); + it("counts up as the run time progresses", async () => { + // 10 seconds ago + const startTime = new Date(Date.now() - 10000); + render(); + expect(screen.getByText("Running Time: 10s")).toBeInTheDocument(); + act(() => { + jest.runOnlyPendingTimers(); + }); + await waitFor(() => { + expect(screen.getByText("Running Time: 11s")).toBeInTheDocument(); + }); + act(() => { + jest.runOnlyPendingTimers(); + }); + await waitFor(() => { + expect(screen.getByText("Running Time: 12s")).toBeInTheDocument(); + }); + }); +}); diff --git a/src/pages/task/metadata/RuntimeTimer/index.tsx b/src/pages/task/metadata/RuntimeTimer/index.tsx new file mode 100644 index 0000000000..a241da9ecc --- /dev/null +++ b/src/pages/task/metadata/RuntimeTimer/index.tsx @@ -0,0 +1,22 @@ +import { MetadataItem } from "components/MetadataCard"; +import { useRunningTime } from "hooks"; +import { string } from "utils"; + +const { msToDuration } = string; + +interface RuntimeTimerProps { + startTime: Date; +} +const RuntimeTimer: React.FC = ({ startTime }) => { + const parsedStartTime = new Date(startTime); + + const { runningTime } = useRunningTime(parsedStartTime); + + return ( + + Running Time: {msToDuration(runningTime)} + + ); +}; + +export default RuntimeTimer; diff --git a/src/pages/task/metadata/index.tsx b/src/pages/task/metadata/index.tsx index ddb1bdebcb..0843f9dcb0 100644 --- a/src/pages/task/metadata/index.tsx +++ b/src/pages/task/metadata/index.tsx @@ -36,7 +36,8 @@ import { TaskStatus } from "types/task"; import { string } from "utils"; import { AbortMessage } from "./AbortMessage"; import { DependsOn } from "./DependsOn"; -import { ETATimer } from "./ETATimer"; +import ETATimer from "./ETATimer"; +import RuntimeTimer from "./RuntimeTimer"; const { applyStrictRegex, msToDuration, shortenGithash } = string; const { red } = palette; @@ -165,6 +166,9 @@ export const Metadata: React.FC = ({ error, loading, task, taskId }) => { {status === TaskStatus.Started && expectedDuration > 0 && ( )} + {status === TaskStatus.Started && startTime && ( + + )} {startTime && ( Started:{" "} diff --git a/src/utils/string/index.ts b/src/utils/string/index.ts index 423024e5ee..cd9d4b2e59 100644 --- a/src/utils/string/index.ts +++ b/src/utils/string/index.ts @@ -9,6 +9,9 @@ export { githubPRLinkify } from "./githubPRLinkify"; * @returns - a string representing the duration in the format of "1d 2h 3m 4s" */ export const msToDuration = (ms: number): string => { + if (ms === 0) { + return "0s"; + } const days = Math.floor(ms / (24 * 60 * 60 * 1000)); const daysMilli = ms % (24 * 60 * 60 * 1000); const hours = Math.floor(daysMilli / (60 * 60 * 1000)); From 494651f4204f9c72b90cb60078cf01b3c3b2f902 Mon Sep 17 00:00:00 2001 From: Arjun Patel Date: Tue, 19 Dec 2023 13:14:17 -0500 Subject: [PATCH 3/8] v3.0.185 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 35ebe76697..563beb4778 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "spruce", - "version": "3.0.184", + "version": "3.0.185", "private": true, "scripts": { "bootstrap-logkeeper": "./scripts/bootstrap-logkeeper.sh", From d72fd8b008f20abede20036b048e1b9916b1ead0 Mon Sep 17 00:00:00 2001 From: SupaJoon Date: Tue, 19 Dec 2023 14:17:15 -0500 Subject: [PATCH 4/8] DEVPROD-1905: Upgrade @leafygreen-ui/select to v11.1.1 (#2192) --- cypress/integration/spawn/host.ts | 2 +- cypress/integration/spawn/volume.ts | 2 +- package.json | 2 +- src/components/Hosts/UpdateStatusModal.tsx | 1 - src/components/PageSizeSelector/index.tsx | 1 - src/components/TupleSelect/index.tsx | 1 - .../spawnVolume/SpawnVolumeModal.test.tsx | 5 +- yarn.lock | 53 ++++++------------- 8 files changed, 24 insertions(+), 43 deletions(-) diff --git a/cypress/integration/spawn/host.ts b/cypress/integration/spawn/host.ts index 432b063564..94aaeaac21 100644 --- a/cypress/integration/spawn/host.ts +++ b/cypress/integration/spawn/host.ts @@ -174,7 +174,7 @@ describe("Navigating to Spawn Host page", () => { cy.dataCy("distro-option-ubuntu1804-workstation") .should("be.visible") .click(); - cy.dataCy("volume-select").should("be.disabled"); + cy.dataCy("volume-select").should("have.attr", "aria-disabled", "true"); }); it("Clicking 'Add new key' hides the key name dropdown and shows the key value text area", () => { diff --git a/cypress/integration/spawn/volume.ts b/cypress/integration/spawn/volume.ts index 6578a58f88..f117857230 100644 --- a/cypress/integration/spawn/volume.ts +++ b/cypress/integration/spawn/volume.ts @@ -270,7 +270,7 @@ describe("Spawn volume page", () => { ).click(); cy.dataCy("distro-input").click(); cy.dataCy("distro-option-ubuntu1804-workstation").click(); - cy.dataCy("region-select").should("be.disabled"); + cy.dataCy("region-select").should("have.attr", "aria-disabled", "true"); cy.dataCy("migrate-modal").contains("Next").click({ force: true }); cy.dataCy("migrate-modal") .contains("Migrate Volume") diff --git a/package.json b/package.json index 563beb4778..59cb26db7e 100644 --- a/package.json +++ b/package.json @@ -86,7 +86,7 @@ "@leafygreen-ui/radio-group": "10.1.1", "@leafygreen-ui/search-input": "2.0.8", "@leafygreen-ui/segmented-control": "8.2.6", - "@leafygreen-ui/select": "10.2.0", + "@leafygreen-ui/select": "11.1.1", "@leafygreen-ui/side-nav": "14.0.3", "@leafygreen-ui/skeleton-loader": "1.1.0", "@leafygreen-ui/table": "12.1.4", diff --git a/src/components/Hosts/UpdateStatusModal.tsx b/src/components/Hosts/UpdateStatusModal.tsx index cdbe810d82..8cd2332d97 100644 --- a/src/components/Hosts/UpdateStatusModal.tsx +++ b/src/components/Hosts/UpdateStatusModal.tsx @@ -113,7 +113,6 @@ export const UpdateStatusModal: React.FC = ({ ); }; -// @ts-expect-error const StyledSelect = styled(Select)` margin-bottom: ${size.xs}; `; diff --git a/src/components/PageSizeSelector/index.tsx b/src/components/PageSizeSelector/index.tsx index c4b762fdbe..ce91e5175e 100644 --- a/src/components/PageSizeSelector/index.tsx +++ b/src/components/PageSizeSelector/index.tsx @@ -33,7 +33,6 @@ const PageSizeSelector: React.FC = ({ onChange, value, ...rest }) => ( ); -// @ts-expect-error const StyledSelect = styled(Select)` width: 120px; `; diff --git a/src/components/TupleSelect/index.tsx b/src/components/TupleSelect/index.tsx index 3a9140b092..5713090199 100644 --- a/src/components/TupleSelect/index.tsx +++ b/src/components/TupleSelect/index.tsx @@ -89,7 +89,6 @@ const LabelContainer = styled.div` flex-direction: row; `; -// @ts-expect-error const GroupedSelect = styled(Select)` width: 30%; /* overwrite lg borders https://jira.mongodb.org/browse/PD-1995 */ diff --git a/src/pages/spawn/spawnVolume/SpawnVolumeModal.test.tsx b/src/pages/spawn/spawnVolume/SpawnVolumeModal.test.tsx index dd4b686c30..565c3a1edc 100644 --- a/src/pages/spawn/spawnVolume/SpawnVolumeModal.test.tsx +++ b/src/pages/spawn/spawnVolume/SpawnVolumeModal.test.tsx @@ -59,7 +59,10 @@ describe("spawnVolumeModal", () => { ); expect(screen.queryByDataCy("type-select")).toHaveTextContent("gp2"); expect(screen.queryByLabelText("Never expire")).not.toBeChecked(); - expect(screen.queryByDataCy("host-select")).toBeDisabled(); + expect(screen.queryByDataCy("host-select")).toHaveAttribute( + "aria-disabled", + "true" + ); expect(screen.queryByText("No hosts available.")).toBeVisible(); }); diff --git a/yarn.lock b/yarn.lock index b8a67bf571..e8dec19340 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3229,7 +3229,7 @@ "@leafygreen-ui/tokens" "^2.1.4" polished "^4.2.2" -"@leafygreen-ui/button@^19.0.1", "@leafygreen-ui/button@^19.0.4": +"@leafygreen-ui/button@^19.0.1": version "19.0.4" resolved "https://registry.yarnpkg.com/@leafygreen-ui/button/-/button-19.0.4.tgz#f94ea61608e56020358ac3f8cd66669f125dff64" integrity sha512-T72lmAHS63cvhyAKaO53tNysL3xDsjnmIoaP0I55eOFOYlEy522U7vf0RMi/BsOePyAJak+x9yZ4uj8AWMCsbg== @@ -3863,24 +3863,25 @@ lodash "^4.17.21" polished "^4.2.2" -"@leafygreen-ui/select@10.2.0": - version "10.2.0" - resolved "https://registry.yarnpkg.com/@leafygreen-ui/select/-/select-10.2.0.tgz#d5c9dd7ca6ec4f043cda45c07c7c5fad89505543" - integrity sha512-w0/XSmN0O85jIahBxgpqGncR0b7R9As4+TNenHe0ULmEwsV1cKpNGt5qXlRyzh6dO7vYKcuSGGhvZW9fCuU/xQ== +"@leafygreen-ui/select@11.1.1", "@leafygreen-ui/select@^11.1.0": + version "11.1.1" + resolved "https://registry.yarnpkg.com/@leafygreen-ui/select/-/select-11.1.1.tgz#b4d03071837feca1d5c206b36a1018b7d936f43f" + integrity sha512-aH0RNrIF3EU+5ChHJxVVjzlmFGkYRTq9DTRqm9eWgWcZIT3+ggIJCOwBNMr/8rD72NhyXm+RD8Gp2wwk8XJJrg== dependencies: - "@leafygreen-ui/button" "^19.0.4" - "@leafygreen-ui/emotion" "^4.0.3" - "@leafygreen-ui/hooks" "^7.3.3" - "@leafygreen-ui/icon" "^11.12.4" - "@leafygreen-ui/lib" "^10.0.0" - "@leafygreen-ui/palette" "^3.4.7" - "@leafygreen-ui/popover" "^11.0.4" - "@leafygreen-ui/tokens" "^2.0.0" - "@leafygreen-ui/typography" "^16.0.0" - "@types/react-is" "^17.0.0" + "@leafygreen-ui/button" "^21.0.10" + "@leafygreen-ui/emotion" "^4.0.7" + "@leafygreen-ui/hooks" "^8.0.1" + "@leafygreen-ui/icon" "^11.25.0" + "@leafygreen-ui/input-option" "^1.0.13" + "@leafygreen-ui/lib" "^13.0.0" + "@leafygreen-ui/palette" "^4.0.7" + "@leafygreen-ui/popover" "^11.1.1" + "@leafygreen-ui/tokens" "^2.2.0" + "@leafygreen-ui/typography" "^18.0.1" + "@types/react-is" "^18.0.0" lodash "^4.17.21" polished "^4.1.3" - react-is "^17.0.1" + react-is "^18.0.1" "@leafygreen-ui/select@^10.1.0", "@leafygreen-ui/select@^10.3.12": version "10.3.12" @@ -3901,26 +3902,6 @@ polished "^4.1.3" react-is "^17.0.1" -"@leafygreen-ui/select@^11.1.0": - version "11.1.1" - resolved "https://registry.yarnpkg.com/@leafygreen-ui/select/-/select-11.1.1.tgz#b4d03071837feca1d5c206b36a1018b7d936f43f" - integrity sha512-aH0RNrIF3EU+5ChHJxVVjzlmFGkYRTq9DTRqm9eWgWcZIT3+ggIJCOwBNMr/8rD72NhyXm+RD8Gp2wwk8XJJrg== - dependencies: - "@leafygreen-ui/button" "^21.0.10" - "@leafygreen-ui/emotion" "^4.0.7" - "@leafygreen-ui/hooks" "^8.0.1" - "@leafygreen-ui/icon" "^11.25.0" - "@leafygreen-ui/input-option" "^1.0.13" - "@leafygreen-ui/lib" "^13.0.0" - "@leafygreen-ui/palette" "^4.0.7" - "@leafygreen-ui/popover" "^11.1.1" - "@leafygreen-ui/tokens" "^2.2.0" - "@leafygreen-ui/typography" "^18.0.1" - "@types/react-is" "^18.0.0" - lodash "^4.17.21" - polished "^4.1.3" - react-is "^18.0.1" - "@leafygreen-ui/side-nav@14.0.3": version "14.0.3" resolved "https://registry.yarnpkg.com/@leafygreen-ui/side-nav/-/side-nav-14.0.3.tgz#cf356dc7733e80bef88c4e33a5623c1ea028f748" From cc5b5510ab81c2de5122cf036f50035c06a93196 Mon Sep 17 00:00:00 2001 From: Mohamed Khelif Date: Tue, 19 Dec 2023 17:27:24 -0500 Subject: [PATCH 5/8] DEVPROD-2637 Add Guide Cue for Parsley viewable log files (#2193) --- cypress/integration/task/files_tables.ts | 2 +- package.json | 2 +- src/constants/cookies.ts | 2 + .../GroupedFileTable.stories.tsx | 2 + .../FileTable/GroupedFileTable/index.tsx | 70 +++++++++++++++++-- yarn.lock | 20 +++--- 6 files changed, 81 insertions(+), 17 deletions(-) diff --git a/cypress/integration/task/files_tables.ts b/cypress/integration/task/files_tables.ts index fea7593da7..f4eacf1e2e 100644 --- a/cypress/integration/task/files_tables.ts +++ b/cypress/integration/task/files_tables.ts @@ -1,7 +1,7 @@ describe("files table", () => { const FILES_ROUTE = "/task/evergreen_ubuntu1604_89/files"; const FILES_ROUTE_WITHOUT_FILES = - "/task/evergreen_ubuntu1604_test_model_commitqueue_patch_5e823e1f28baeaa22ae00823d83e03082cd148ab_5e4ff3abe3c3317e352062e4_20_02_21_15_13_48/files "; + "/task/evergreen_ubuntu1604_test_model_commitqueue_patch_5e823e1f28baeaa22ae00823d83e03082cd148ab_5e4ff3abe3c3317e352062e4_20_02_21_15_13_48/files"; it("File names under name column should link to a new tab", () => { cy.visit(FILES_ROUTE); diff --git a/package.json b/package.json index 59cb26db7e..cda3a8b6e9 100644 --- a/package.json +++ b/package.json @@ -69,7 +69,7 @@ "@leafygreen-ui/confirmation-modal": "5.0.6", "@leafygreen-ui/emotion": "4.0.7", "@leafygreen-ui/expandable-card": "3.0.5", - "@leafygreen-ui/guide-cue": "5.0.4", + "@leafygreen-ui/guide-cue": "5.0.5", "@leafygreen-ui/icon": "11.12.1", "@leafygreen-ui/icon-button": "15.0.5", "@leafygreen-ui/inline-definition": "6.0.14", diff --git a/src/constants/cookies.ts b/src/constants/cookies.ts index 1a7379954b..7bfabd058c 100644 --- a/src/constants/cookies.ts +++ b/src/constants/cookies.ts @@ -16,5 +16,7 @@ export const INCLUDE_COMMIT_QUEUE_USER_PATCHES = "include-commit-queue-user-patches"; export const INCLUDE_HIDDEN_PATCHES = "include-hidden-patches"; export const SEEN_HONEYCOMB_GUIDE_CUE = "seen-honeycomb-guide-cue"; +export const SEEN_PARSLEY_FILES_GUIDE_CUE = "seen-parsley-files-guide-cue"; +export const SEEN_MIGRATE_GUIDE_CUE = "seen-migrate-guide-cue"; export const SLACK_NOTIFICATION_BANNER = "has-closed-slack-banner"; export const SUBSCRIPTION_METHOD = "subscription-method"; diff --git a/src/pages/task/taskTabs/FileTable/GroupedFileTable/GroupedFileTable.stories.tsx b/src/pages/task/taskTabs/FileTable/GroupedFileTable/GroupedFileTable.stories.tsx index b8a6627cca..9a58e08c7b 100644 --- a/src/pages/task/taskTabs/FileTable/GroupedFileTable/GroupedFileTable.stories.tsx +++ b/src/pages/task/taskTabs/FileTable/GroupedFileTable/GroupedFileTable.stories.tsx @@ -5,10 +5,12 @@ const files = [ { name: "some_file", link: "some_link", + urlParsley: null, }, { name: "another_file", link: "another_link", + urlParsley: "parsley_link", }, ]; diff --git a/src/pages/task/taskTabs/FileTable/GroupedFileTable/index.tsx b/src/pages/task/taskTabs/FileTable/GroupedFileTable/index.tsx index 35a26c46ea..0da68b9e01 100644 --- a/src/pages/task/taskTabs/FileTable/GroupedFileTable/index.tsx +++ b/src/pages/task/taskTabs/FileTable/GroupedFileTable/index.tsx @@ -1,12 +1,15 @@ -import { useMemo, useRef } from "react"; +import { useEffect, useMemo, useRef, useState } from "react"; import styled from "@emotion/styled"; import Button from "@leafygreen-ui/button"; +import { GuideCue } from "@leafygreen-ui/guide-cue"; import { useLeafyGreenTable, LGColumnDef } from "@leafygreen-ui/table"; import Tooltip from "@leafygreen-ui/tooltip"; import { Subtitle } from "@leafygreen-ui/typography"; +import Cookies from "js-cookie"; import { useTaskAnalytics } from "analytics"; import { StyledLink } from "components/styles"; import { BaseTable } from "components/Table/BaseTable"; +import { SEEN_PARSLEY_FILES_GUIDE_CUE } from "constants/cookies"; import { size } from "constants/tokens"; import { Unpacked } from "types/utils"; import { GroupedFiles } from "../types"; @@ -14,7 +17,11 @@ import { GroupedFiles } from "../types"; type GroupedFilesFile = Unpacked; const columns = ( - taskAnalytics: ReturnType + taskAnalytics: ReturnType, + options: { + gudeCueTriggerRef: React.RefObject; + firstParsleyFileIndex: number; + } ): LGColumnDef[] => [ { accessorKey: "name", @@ -47,6 +54,11 @@ const columns = ( target="_blank" disabled={value.row.original.urlParsley === null} size="small" + ref={ + options && options.firstParsleyFileIndex === value.row.index + ? options.gudeCueTriggerRef + : null + } onClick={() => { taskAnalytics.sendEvent({ name: "Click Task File Parsley Link", @@ -79,11 +91,39 @@ const GroupedFileTable: React.FC = ({ }) => { const tableContainerRef = useRef(null); const taskAnalytics = useTaskAnalytics(); - + const [openGuideCue, setOpenGuideCue] = useState( + Cookies.get(SEEN_PARSLEY_FILES_GUIDE_CUE) !== "true" + ); + const firstParsleyFileIndex = useMemo( + () => files.findIndex((file) => file.urlParsley !== null), + [files] + ); + const parsleyLinkRef = useRef(null); const memoizedColumns = useMemo( - () => columns(taskAnalytics), - [taskAnalytics] + () => + columns( + taskAnalytics, + firstParsleyFileIndex !== -1 + ? { + gudeCueTriggerRef: parsleyLinkRef, + firstParsleyFileIndex, + } + : { + gudeCueTriggerRef: null, + firstParsleyFileIndex: -1, + } + ), + [taskAnalytics, firstParsleyFileIndex] ); + useEffect(() => { + // Scroll to the first file that can be opened in parsley on initial render. + // Since the button may not always be in view, we need to scroll to it. + if (firstParsleyFileIndex !== -1 && openGuideCue) { + parsleyLinkRef.current?.scrollIntoView(); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + const table = useLeafyGreenTable({ containerRef: tableContainerRef, data: files, @@ -93,6 +133,26 @@ const GroupedFileTable: React.FC = ({ return ( {taskName && {taskName}} + {parsleyLinkRef.current !== null && ( + { + Cookies.set(SEEN_PARSLEY_FILES_GUIDE_CUE, "true", { + expires: 365, + }); + setOpenGuideCue(false); + }} + > + Open your file in Parsley to view it with Parsley's rich text + formatting capabilities. + + )} ); diff --git a/yarn.lock b/yarn.lock index e8dec19340..ecb0a8cc3b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3242,7 +3242,7 @@ "@leafygreen-ui/tokens" "^2.0.0" polished "^4.2.2" -"@leafygreen-ui/button@^21.0.10", "@leafygreen-ui/button@^21.0.3", "@leafygreen-ui/button@^21.0.5", "@leafygreen-ui/button@^21.0.9": +"@leafygreen-ui/button@^21.0.10", "@leafygreen-ui/button@^21.0.3", "@leafygreen-ui/button@^21.0.5": version "21.0.11" resolved "https://registry.yarnpkg.com/@leafygreen-ui/button/-/button-21.0.11.tgz#a1cfa56226d9ccdf43945441340013ff02c55fbe" integrity sha512-NJhllsXO7jAJoAou9kPIk/B8ODYUrGxr4l4TceoAwAM3cW0kZ5kys9KA+0TOmG2AxNKLcElLu+wCg3TbssFk+Q== @@ -3426,22 +3426,22 @@ "@leafygreen-ui/typography" "^16.0.0" react-transition-group "^4.4.1" -"@leafygreen-ui/guide-cue@5.0.4": - version "5.0.4" - resolved "https://registry.yarnpkg.com/@leafygreen-ui/guide-cue/-/guide-cue-5.0.4.tgz#272b47f62f0957b153284a359aa423d62a8bcfe6" - integrity sha512-L+k1EZNfvpixoMSkzds6Qr4rNkFL6f+yiKRq+2XF/lPDRHnohf06VULp0qmHs1UcUNICmMj8SP6qOYy3LyagKw== +"@leafygreen-ui/guide-cue@5.0.5": + version "5.0.5" + resolved "https://registry.yarnpkg.com/@leafygreen-ui/guide-cue/-/guide-cue-5.0.5.tgz#bff9651b81d8c47d380280a9a5a01cf00bb9cf2e" + integrity sha512-E8SqC/UTypkTuxP0HHSh+AG9FpPHwgDvBb+qBYZ8xHNLODVDI81nuRSoEDZx4AUajziRQNOrIbGYnq2hjW02xw== dependencies: "@leafygreen-ui/a11y" "^1.4.11" - "@leafygreen-ui/button" "^21.0.9" + "@leafygreen-ui/button" "^21.0.10" "@leafygreen-ui/emotion" "^4.0.7" - "@leafygreen-ui/hooks" "^8.0.0" - "@leafygreen-ui/icon" "^11.23.0" + "@leafygreen-ui/hooks" "^8.0.1" + "@leafygreen-ui/icon" "^11.25.0" "@leafygreen-ui/icon-button" "^15.0.19" "@leafygreen-ui/lib" "^13.0.0" "@leafygreen-ui/palette" "^4.0.7" "@leafygreen-ui/popover" "^11.1.1" - "@leafygreen-ui/tooltip" "^10.1.0" - "@leafygreen-ui/typography" "^18.0.0" + "@leafygreen-ui/tooltip" "^11.0.0" + "@leafygreen-ui/typography" "^18.0.1" focus-trap "6.9.4" focus-trap-react "9.0.2" polished "^4.2.2" From 6d24d2f11e0c99b01d42797fdada8d0264ede65c Mon Sep 17 00:00:00 2001 From: Zackary Santana <64446617+ZackarySantana@users.noreply.github.com> Date: Thu, 21 Dec 2023 13:46:39 -0500 Subject: [PATCH 6/8] DEVPROD-977 Add stepback bisect to project settings page (#2177) --- .../projectSettings/stepback_bisect.ts | 61 +++++++++++++++++++ .../fragments/projectSettings/general.graphql | 2 + src/gql/generated/types.ts | 11 ++++ .../projectSettings/CopyProjectModal.test.tsx | 1 + .../tabs/EventLogTab/EventLogTab.test.tsx | 2 + .../tabs/GeneralTab/getFormSchema.tsx | 14 +++++ .../tabs/GeneralTab/transformers.test.ts | 4 ++ .../tabs/GeneralTab/transformers.ts | 2 + .../projectSettings/tabs/GeneralTab/types.ts | 1 + src/pages/projectSettings/tabs/testData.ts | 2 + 10 files changed, 100 insertions(+) create mode 100644 cypress/integration/projectSettings/stepback_bisect.ts diff --git a/cypress/integration/projectSettings/stepback_bisect.ts b/cypress/integration/projectSettings/stepback_bisect.ts new file mode 100644 index 0000000000..d75b0dc18e --- /dev/null +++ b/cypress/integration/projectSettings/stepback_bisect.ts @@ -0,0 +1,61 @@ +import { clickSave } from "../../utils"; +import { getGeneralRoute, project, projectUseRepoEnabled } from "./constants"; + +describe("Stepback bisect setting", () => { + describe("Repo project present", () => { + const destination = getGeneralRoute(projectUseRepoEnabled); + + beforeEach(() => { + cy.visit(destination); + }); + + it("Starts as default to repo", () => { + cy.dataCy("stepback-bisect-group") + .contains("Default to repo") + .find("input") + .should("have.attr", "aria-checked", "true"); + }); + + it("Clicking on enabled and then save shows a success toast", () => { + cy.dataCy("stepback-bisect-group").contains("Enable").click(); + clickSave(); + cy.validateToast("success", "Successfully updated project"); + + cy.reload(); + + cy.dataCy("stepback-bisect-group") + .contains("Enable") + .find("input") + .should("have.attr", "aria-checked", "true"); + }); + }); + + describe("Repo project not present", () => { + const destination = getGeneralRoute(project); + + beforeEach(() => { + cy.visit(destination); + }); + + it("Starts as disabled", () => { + cy.dataCy("stepback-bisect-group") + .contains("Disable") + .find("input") + .should("have.attr", "aria-checked", "true"); + }); + + it("Clicking on enabled and then save shows a success toast", () => { + cy.dataCy("stepback-bisect-group").contains("Enable").click(); + + clickSave(); + cy.validateToast("success", "Successfully updated project"); + + cy.reload(); + + cy.dataCy("stepback-bisect-group") + .contains("Enable") + .find("input") + .should("have.attr", "aria-checked", "true"); + }); + }); +}); diff --git a/src/gql/fragments/projectSettings/general.graphql b/src/gql/fragments/projectSettings/general.graphql index 632f9a6df0..17051d8573 100644 --- a/src/gql/fragments/projectSettings/general.graphql +++ b/src/gql/fragments/projectSettings/general.graphql @@ -12,6 +12,7 @@ fragment ProjectGeneralSettings on Project { repo repotrackerDisabled spawnHostScriptPath + stepbackBisect stepbackDisabled taskSync { configEnabled @@ -32,6 +33,7 @@ fragment RepoGeneralSettings on RepoRef { repo repotrackerDisabled spawnHostScriptPath + stepbackBisect stepbackDisabled taskSync { configEnabled diff --git a/src/gql/generated/types.ts b/src/gql/generated/types.ts index 83873f335a..3af7bd17b3 100644 --- a/src/gql/generated/types.ts +++ b/src/gql/generated/types.ts @@ -3459,6 +3459,7 @@ export type ProjectGeneralSettingsFragment = { repo: string; repotrackerDisabled?: boolean | null; spawnHostScriptPath: string; + stepbackBisect?: boolean | null; stepbackDisabled?: boolean | null; versionControlEnabled?: boolean | null; taskSync: { @@ -3481,6 +3482,7 @@ export type RepoGeneralSettingsFragment = { repo: string; repotrackerDisabled: boolean; spawnHostScriptPath: string; + stepbackBisect?: boolean | null; stepbackDisabled: boolean; versionControlEnabled: boolean; taskSync: { @@ -3628,6 +3630,7 @@ export type ProjectSettingsFieldsFragment = { repo: string; repotrackerDisabled?: boolean | null; spawnHostScriptPath: string; + stepbackBisect?: boolean | null; stepbackDisabled?: boolean | null; versionControlEnabled?: boolean | null; notifyOnBuildFailure?: boolean | null; @@ -3834,6 +3837,7 @@ export type RepoSettingsFieldsFragment = { repo: string; repotrackerDisabled: boolean; spawnHostScriptPath: string; + stepbackBisect?: boolean | null; stepbackDisabled: boolean; versionControlEnabled: boolean; notifyOnBuildFailure: boolean; @@ -4235,6 +4239,7 @@ export type ProjectEventSettingsFragment = { repo: string; repotrackerDisabled?: boolean | null; spawnHostScriptPath: string; + stepbackBisect?: boolean | null; stepbackDisabled?: boolean | null; notifyOnBuildFailure?: boolean | null; githubTriggerAliases?: Array | null; @@ -6698,6 +6703,7 @@ export type ProjectEventLogsQuery = { repo: string; repotrackerDisabled?: boolean | null; spawnHostScriptPath: string; + stepbackBisect?: boolean | null; stepbackDisabled?: boolean | null; notifyOnBuildFailure?: boolean | null; githubTriggerAliases?: Array | null; @@ -6910,6 +6916,7 @@ export type ProjectEventLogsQuery = { repo: string; repotrackerDisabled?: boolean | null; spawnHostScriptPath: string; + stepbackBisect?: boolean | null; stepbackDisabled?: boolean | null; notifyOnBuildFailure?: boolean | null; githubTriggerAliases?: Array | null; @@ -7197,6 +7204,7 @@ export type ProjectSettingsQuery = { repo: string; repotrackerDisabled?: boolean | null; spawnHostScriptPath: string; + stepbackBisect?: boolean | null; stepbackDisabled?: boolean | null; versionControlEnabled?: boolean | null; notifyOnBuildFailure?: boolean | null; @@ -7455,6 +7463,7 @@ export type RepoEventLogsQuery = { repo: string; repotrackerDisabled?: boolean | null; spawnHostScriptPath: string; + stepbackBisect?: boolean | null; stepbackDisabled?: boolean | null; notifyOnBuildFailure?: boolean | null; githubTriggerAliases?: Array | null; @@ -7667,6 +7676,7 @@ export type RepoEventLogsQuery = { repo: string; repotrackerDisabled?: boolean | null; spawnHostScriptPath: string; + stepbackBisect?: boolean | null; stepbackDisabled?: boolean | null; notifyOnBuildFailure?: boolean | null; githubTriggerAliases?: Array | null; @@ -7883,6 +7893,7 @@ export type RepoSettingsQuery = { repo: string; repotrackerDisabled: boolean; spawnHostScriptPath: string; + stepbackBisect?: boolean | null; stepbackDisabled: boolean; versionControlEnabled: boolean; notifyOnBuildFailure: boolean; diff --git a/src/pages/projectSettings/CopyProjectModal.test.tsx b/src/pages/projectSettings/CopyProjectModal.test.tsx index cd959f13ed..65aadbc0fd 100644 --- a/src/pages/projectSettings/CopyProjectModal.test.tsx +++ b/src/pages/projectSettings/CopyProjectModal.test.tsx @@ -290,6 +290,7 @@ const projectSettingsMock: ApolloMock< deactivatePrevious: true, repotrackerDisabled: false, stepbackDisabled: null, + stepbackBisect: null, patchingDisabled: false, taskSync: { configEnabled: false, diff --git a/src/pages/projectSettings/tabs/EventLogTab/EventLogTab.test.tsx b/src/pages/projectSettings/tabs/EventLogTab/EventLogTab.test.tsx index b461a90b11..c5ff25f416 100644 --- a/src/pages/projectSettings/tabs/EventLogTab/EventLogTab.test.tsx +++ b/src/pages/projectSettings/tabs/EventLogTab/EventLogTab.test.tsx @@ -100,6 +100,7 @@ const projectEventsQuery: ProjectEventLogsQuery = { deactivatePrevious: true, repotrackerDisabled: false, stepbackDisabled: null, + stepbackBisect: null, patchingDisabled: false, taskSync: { configEnabled: false, @@ -186,6 +187,7 @@ const projectEventsQuery: ProjectEventLogsQuery = { deactivatePrevious: true, repotrackerDisabled: false, stepbackDisabled: null, + stepbackBisect: null, patchingDisabled: false, taskSync: { configEnabled: false, diff --git a/src/pages/projectSettings/tabs/GeneralTab/getFormSchema.tsx b/src/pages/projectSettings/tabs/GeneralTab/getFormSchema.tsx index 3e5d7d5c92..40cb116420 100644 --- a/src/pages/projectSettings/tabs/GeneralTab/getFormSchema.tsx +++ b/src/pages/projectSettings/tabs/GeneralTab/getFormSchema.tsx @@ -165,6 +165,14 @@ export const getFormSchema = ( true ), }, + stepbackBisection: { + type: ["boolean", "null"], + title: "Stepback Bisection", + oneOf: radioBoxOptions( + ["Enabled", "Disabled"], + repoData?.projectFlags?.scheduling?.stepbackBisection + ), + }, deactivateStepback: { type: "null" as "null", }, @@ -342,6 +350,12 @@ export const getFormSchema = ( "ui:description": "Disabling this setting will override all enabled stepback settings for the project. Disabling stepback won't cancel any active stepback tasks, but it will prevent any future ones.", }, + stepbackBisection: { + "ui:widget": widgets.RadioBoxWidget, + "ui:description": + "Bisection will cause your stepback to activate the midway task between the last failing task and last passing task.", + "ui:data-cy": "stepback-bisect-group", + }, deactivateStepback: { "ui:field": "deactivateStepbackTask", "ui:showLabel": false, diff --git a/src/pages/projectSettings/tabs/GeneralTab/transformers.test.ts b/src/pages/projectSettings/tabs/GeneralTab/transformers.test.ts index 9372a65bfa..8c2f41bd0f 100644 --- a/src/pages/projectSettings/tabs/GeneralTab/transformers.test.ts +++ b/src/pages/projectSettings/tabs/GeneralTab/transformers.test.ts @@ -47,6 +47,7 @@ const repoForm: GeneralFormState = { scheduling: { deactivatePrevious: true, stepbackDisabled: true, + stepbackBisection: true, deactivateStepback: null, }, repotracker: { @@ -81,6 +82,7 @@ const repoResult: Pick = { repotrackerDisabled: false, patchingDisabled: false, stepbackDisabled: true, + stepbackBisect: true, taskSync: { configEnabled: true, patchEnabled: true, @@ -111,6 +113,7 @@ const projectForm: GeneralFormState = { scheduling: { deactivatePrevious: null, stepbackDisabled: null, + stepbackBisection: null, deactivateStepback: null, }, repotracker: { @@ -148,6 +151,7 @@ const projectResult: Pick = { repotrackerDisabled: null, patchingDisabled: null, stepbackDisabled: null, + stepbackBisect: null, taskSync: { configEnabled: null, patchEnabled: null, diff --git a/src/pages/projectSettings/tabs/GeneralTab/transformers.ts b/src/pages/projectSettings/tabs/GeneralTab/transformers.ts index 9f0307256a..f859e0fc98 100644 --- a/src/pages/projectSettings/tabs/GeneralTab/transformers.ts +++ b/src/pages/projectSettings/tabs/GeneralTab/transformers.ts @@ -41,6 +41,7 @@ export const gqlToForm = ((data, options = {}) => { scheduling: { deactivatePrevious: projectRef.deactivatePrevious, stepbackDisabled: projectRef.stepbackDisabled, + stepbackBisection: projectRef.stepbackBisect, deactivateStepback: null, }, repotracker: { @@ -91,6 +92,7 @@ export const formToGql = (( deactivatePrevious: projectFlags.scheduling.deactivatePrevious, repotrackerDisabled: projectFlags.repotracker.repotrackerDisabled, stepbackDisabled: projectFlags.scheduling.stepbackDisabled, + stepbackBisect: projectFlags.scheduling.stepbackBisection, patchingDisabled: projectFlags.patch.patchingDisabled, taskSync: { configEnabled: projectFlags.taskSync.configEnabled, diff --git a/src/pages/projectSettings/tabs/GeneralTab/types.ts b/src/pages/projectSettings/tabs/GeneralTab/types.ts index 296c36da6b..04a4d92143 100644 --- a/src/pages/projectSettings/tabs/GeneralTab/types.ts +++ b/src/pages/projectSettings/tabs/GeneralTab/types.ts @@ -22,6 +22,7 @@ export interface GeneralFormState { scheduling: { deactivatePrevious: boolean | null; stepbackDisabled: boolean | null; + stepbackBisection: boolean | null; deactivateStepback: null; }; repotracker: { diff --git a/src/pages/projectSettings/tabs/testData.ts b/src/pages/projectSettings/tabs/testData.ts index 217141a0d6..9621b17e2b 100644 --- a/src/pages/projectSettings/tabs/testData.ts +++ b/src/pages/projectSettings/tabs/testData.ts @@ -46,6 +46,7 @@ const projectBase: ProjectSettingsQuery["projectSettings"] = { repotrackerDisabled: null, patchingDisabled: null, stepbackDisabled: null, + stepbackBisect: null, taskSync: { configEnabled: null, patchEnabled: null, @@ -174,6 +175,7 @@ const repoBase: RepoSettingsQuery["repoSettings"] = { notifyOnBuildFailure: false, patchingDisabled: false, stepbackDisabled: true, + stepbackBisect: true, taskSync: { configEnabled: true, patchEnabled: true, From 2b33d3044006d54ddfb97616ecc6fbd0164cb956 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 26 Dec 2023 11:57:51 -0500 Subject: [PATCH 7/8] CHORE(NPM) - bump @types/prompts from 2.4.6 to 2.4.9 (#2200) --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index cda3a8b6e9..8bcca3e1bf 100644 --- a/package.json +++ b/package.json @@ -158,7 +158,7 @@ "@types/new-relic-browser": "0.1212.2", "@types/node": "^16.11.47", "@types/pluralize": "0.0.29", - "@types/prompts": "2.4.6", + "@types/prompts": "2.4.9", "@types/react": "18.2.0", "@types/react-dom": "18.2.0", "@typescript-eslint/eslint-plugin": "5.57.1", diff --git a/yarn.lock b/yarn.lock index ecb0a8cc3b..13adcd12c1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6123,10 +6123,10 @@ resolved "https://registry.yarnpkg.com/@types/pretty-hrtime/-/pretty-hrtime-1.0.1.tgz#72a26101dc567b0d68fd956cf42314556e42d601" integrity sha512-VjID5MJb1eGKthz2qUerWT8+R4b9N+CHvGCzg9fn4kWZgaF9AhdYikQio3R7wV8YY1NsQKPaCwKz1Yff+aHNUQ== -"@types/prompts@2.4.6": - version "2.4.6" - resolved "https://registry.yarnpkg.com/@types/prompts/-/prompts-2.4.6.tgz#5c14794e7c8850b1d633a17a052b724bd2b019a3" - integrity sha512-hIwnDhvsTV6XwAPo1zNy2MTelR0JmCxklIRsVxwROHLGaf6LfB+sGDkB9n+aqV4gXDz8z5MnlAz4CEC9wQDRsw== +"@types/prompts@2.4.9": + version "2.4.9" + resolved "https://registry.yarnpkg.com/@types/prompts/-/prompts-2.4.9.tgz#8775a31e40ad227af511aa0d7f19a044ccbd371e" + integrity sha512-qTxFi6Buiu8+50/+3DGIWLHM6QuWsEKugJnnP6iv2Mc4ncxE4A/OJkjuVOA+5X0X1S/nq5VJRa8Lu+nwcvbrKA== dependencies: "@types/node" "*" kleur "^3.0.3" From 054203da08284f39ffefdcc6ef64b7124f1fbd5f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 26 Dec 2023 11:59:34 -0500 Subject: [PATCH 8/8] CHORE(NPM) - bump @leafygreen-ui/icon from 11.12.1 to 11.26.0 (#2202) --- package.json | 2 +- yarn.lock | 15 ++++----------- 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/package.json b/package.json index 8bcca3e1bf..01b440c1b5 100644 --- a/package.json +++ b/package.json @@ -70,7 +70,7 @@ "@leafygreen-ui/emotion": "4.0.7", "@leafygreen-ui/expandable-card": "3.0.5", "@leafygreen-ui/guide-cue": "5.0.5", - "@leafygreen-ui/icon": "11.12.1", + "@leafygreen-ui/icon": "11.26.0", "@leafygreen-ui/icon-button": "15.0.5", "@leafygreen-ui/inline-definition": "6.0.14", "@leafygreen-ui/interaction-ring": "7.0.2", diff --git a/yarn.lock b/yarn.lock index 13adcd12c1..61a59c198c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3512,17 +3512,10 @@ "@leafygreen-ui/palette" "^4.0.7" "@leafygreen-ui/tokens" "^2.1.4" -"@leafygreen-ui/icon@11.12.1": - version "11.12.1" - resolved "https://registry.yarnpkg.com/@leafygreen-ui/icon/-/icon-11.12.1.tgz#8ec2a8cd209c07e5e9a3b5a8f42b74afa4364573" - integrity sha512-TOnECGHs3honiuy7JdbGXLYi7mgOzX7mJtO8Ius63pG4D0gTZZrJPE4JhXPmdOgYtZ22/pGxSCHcOHvIBYYjvw== - dependencies: - "@leafygreen-ui/emotion" "^4.0.3" - -"@leafygreen-ui/icon@^11.12.1", "@leafygreen-ui/icon@^11.12.3", "@leafygreen-ui/icon@^11.12.4", "@leafygreen-ui/icon@^11.12.5", "@leafygreen-ui/icon@^11.15.0", "@leafygreen-ui/icon@^11.17.0", "@leafygreen-ui/icon@^11.22.1", "@leafygreen-ui/icon@^11.22.2", "@leafygreen-ui/icon@^11.23.0", "@leafygreen-ui/icon@^11.25.0": - version "11.25.0" - resolved "https://registry.yarnpkg.com/@leafygreen-ui/icon/-/icon-11.25.0.tgz#a6cc0d54e7988cb39d3a2b1e947715b6af8f1ca2" - integrity sha512-54nEMSj/g8cfKsh2iQ9qVr7oYSSbdpZbzrgQ/tExDuaG90UR0FJeWHzPiNbUI7hrRuLYuokn7WafsxqX6Sfr2A== +"@leafygreen-ui/icon@11.26.0", "@leafygreen-ui/icon@^11.12.1", "@leafygreen-ui/icon@^11.12.3", "@leafygreen-ui/icon@^11.12.4", "@leafygreen-ui/icon@^11.12.5", "@leafygreen-ui/icon@^11.15.0", "@leafygreen-ui/icon@^11.17.0", "@leafygreen-ui/icon@^11.22.1", "@leafygreen-ui/icon@^11.22.2", "@leafygreen-ui/icon@^11.23.0", "@leafygreen-ui/icon@^11.25.0": + version "11.26.0" + resolved "https://registry.yarnpkg.com/@leafygreen-ui/icon/-/icon-11.26.0.tgz#2093df50445bd15758925aae9c0f40813eb817a8" + integrity sha512-2+sexm+uLqPC3XxQ8xDky3UhcxwGIuiWnkcG2JImNNMJSnrk87BEG0z7DdCXIpJ46+RtGDYa4B3ADxKSl9M9oQ== dependencies: "@leafygreen-ui/emotion" "^4.0.7" lodash "^4.17.21"