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/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/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 7bae4da401..ec07294b49 100644 --- a/package.json +++ b/package.json @@ -69,8 +69,8 @@ "@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/icon": "11.12.1", + "@leafygreen-ui/guide-cue": "5.0.5", + "@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", @@ -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", @@ -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/src/analytics/task/useTaskAnalytics.ts b/src/analytics/task/useTaskAnalytics.ts index dbb7057dd2..1d78d77f9b 100644 --- a/src/analytics/task/useTaskAnalytics.ts +++ b/src/analytics/task/useTaskAnalytics.ts @@ -70,6 +70,10 @@ type Action = } | { name: "Click Trace Link" } | { name: "Click Trace Metrics Link" } + | { name: "Click Last Passing Stepback Task Link" } + | { name: "Click Last Failing Stepback Task Link" } + | { name: "Click Previous Stepback Task Link" } + | { name: "Click Next Stepback Task Link" } | { name: "Submit Previous Commit Selector"; type: CommitType }; export const useTaskAnalytics = () => { 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/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/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..ae8b0f91b9 100644 --- a/src/gql/generated/types.ts +++ b/src/gql/generated/types.ts @@ -2418,6 +2418,14 @@ export type StatusCount = { status: Scalars["String"]["output"]; }; +export type StepbackInfo = { + __typename?: "StepbackInfo"; + lastFailingStepbackTaskId?: Maybe; + lastPassingStepbackTaskId?: Maybe; + nextStepbackTaskId?: Maybe; + previousStepbackTaskId?: Maybe; +}; + export type Subscriber = { __typename?: "Subscriber"; emailSubscriber?: Maybe; @@ -2526,6 +2534,7 @@ export type Task = { spawnHostLink?: Maybe; startTime?: Maybe; status: Scalars["String"]["output"]; + stepbackInfo?: Maybe; /** @deprecated Use files instead */ taskFiles: TaskFiles; taskGroup?: Maybe; @@ -3459,6 +3468,7 @@ export type ProjectGeneralSettingsFragment = { repo: string; repotrackerDisabled?: boolean | null; spawnHostScriptPath: string; + stepbackBisect?: boolean | null; stepbackDisabled?: boolean | null; versionControlEnabled?: boolean | null; taskSync: { @@ -3481,6 +3491,7 @@ export type RepoGeneralSettingsFragment = { repo: string; repotrackerDisabled: boolean; spawnHostScriptPath: string; + stepbackBisect?: boolean | null; stepbackDisabled: boolean; versionControlEnabled: boolean; taskSync: { @@ -3628,6 +3639,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 +3846,7 @@ export type RepoSettingsFieldsFragment = { repo: string; repotrackerDisabled: boolean; spawnHostScriptPath: string; + stepbackBisect?: boolean | null; stepbackDisabled: boolean; versionControlEnabled: boolean; notifyOnBuildFailure: boolean; @@ -4235,6 +4248,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 +6712,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 +6925,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 +7213,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 +7472,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 +7685,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 +7902,7 @@ export type RepoSettingsQuery = { repo: string; repotrackerDisabled: boolean; spawnHostScriptPath: string; + stepbackBisect?: boolean | null; stepbackDisabled: boolean; versionControlEnabled: boolean; notifyOnBuildFailure: boolean; @@ -8551,6 +8571,13 @@ export type TaskQuery = { }; pod?: { __typename?: "Pod"; id: string } | null; project?: { __typename?: "Project"; id: string; identifier: string } | null; + stepbackInfo?: { + __typename?: "StepbackInfo"; + lastFailingStepbackTaskId?: string | null; + lastPassingStepbackTaskId?: string | null; + nextStepbackTaskId?: string | null; + previousStepbackTaskId?: string | null; + } | null; versionMetadata: { __typename?: "Version"; author: string; diff --git a/src/gql/queries/task.graphql b/src/gql/queries/task.graphql index 742884fa45..b2338dc62d 100644 --- a/src/gql/queries/task.graphql +++ b/src/gql/queries/task.graphql @@ -107,6 +107,12 @@ query Task($taskId: String!, $execution: Int) { resetWhenFinished spawnHostLink startTime + stepbackInfo { + lastFailingStepbackTaskId + lastPassingStepbackTaskId + nextStepbackTaskId + previousStepbackTaskId + } timeTaken totalTestCount versionMetadata { 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, 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/src/pages/task/metadata/index.tsx b/src/pages/task/metadata/index.tsx index 0843f9dcb0..11e151533a 100644 --- a/src/pages/task/metadata/index.tsx +++ b/src/pages/task/metadata/index.tsx @@ -79,6 +79,7 @@ export const Metadata: React.FC = ({ error, loading, task, taskId }) => { spawnHostLink, startTime, status, + stepbackInfo, timeTaken, versionMetadata, } = task || {}; @@ -403,6 +404,66 @@ export const Metadata: React.FC = ({ error, loading, task, taskId }) => { )} + {stepbackInfo?.lastPassingStepbackTaskId && ( + <> + + Last Passing Stepback Task:{" "} + + taskAnalytics.sendEvent({ + name: "Click Last Passing Stepback Task Link", + }) + } + > + {stepbackInfo.lastPassingStepbackTaskId} + + + + Last Failing Stepback Task:{" "} + + taskAnalytics.sendEvent({ + name: "Click Last Failing Stepback Task Link", + }) + } + > + {stepbackInfo.lastFailingStepbackTaskId} + + + + )} + {stepbackInfo?.previousStepbackTaskId && ( + + Previous Stepback Task:{" "} + + taskAnalytics.sendEvent({ + name: "Click Previous Stepback Task Link", + }) + } + > + {stepbackInfo.previousStepbackTaskId} + + + )} + {stepbackInfo?.nextStepbackTaskId && ( + + Next Stepback Task:{" "} + + taskAnalytics.sendEvent({ + name: "Click Next Stepback Task Link", + }) + } + > + {stepbackInfo.nextStepbackTaskId} + + + )} ); }; 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 11a56ea441..ef49362481 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== @@ -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" @@ -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" @@ -3863,24 +3856,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 +3895,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" @@ -6144,10 +6118,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"