From 19d365288902b6c9a007c320386abe489884818b Mon Sep 17 00:00:00 2001 From: Sarah Chung Date: Wed, 18 Dec 2024 16:47:58 -0500 Subject: [PATCH] Add experiment brief links to experiment and rollout tables --- __tests__/lib/nimbusRecipe.test.ts | 55 +++++++++++ app/columns.tsx | 142 +++++++++++++++++++---------- lib/nimbusRecipe.ts | 30 ++++++ 3 files changed, 177 insertions(+), 50 deletions(-) diff --git a/__tests__/lib/nimbusRecipe.test.ts b/__tests__/lib/nimbusRecipe.test.ts index 8d527396..779bb960 100644 --- a/__tests__/lib/nimbusRecipe.test.ts +++ b/__tests__/lib/nimbusRecipe.test.ts @@ -363,4 +363,59 @@ describe("NimbusRecipe", () => { ); }); }); + + describe("getExperimentBriefLink", () => { + it("returns the correct experiment brief link", () => { + const documentationLinks = [ + { + title: "DESIGN_DOC", + link: "https://docs.google.com/document/d/1mKXnU-qbStb1OUNHmDOQY5Awb-Wz5e8wit28jkUKP-4/edit#heading=h.uoblsnu302hk", + }, + { + title: "ENG_TICKET", + link: "https://mozilla-hub.atlassian.net/browse/OMC-811", + }, + { + title: "DESIGN_DOC", + link: "https://www.figma.com/design/V2alIUZh1C4UXoWacJjZCA/Bookmarks-improvements?node-id=2073-16689&node-type=canvas&t=yXRpQavvJl25GGbF-0", + }, + ]; + const rawRecipe = ExperimentFakes.recipe("test-recipe"); + const nimbusRecipe = new NimbusRecipe(rawRecipe); + + const result = nimbusRecipe.getExperimentBriefLink(documentationLinks); + + expect(result).toBe( + "https://docs.google.com/document/d/1mKXnU-qbStb1OUNHmDOQY5Awb-Wz5e8wit28jkUKP-4/edit#heading=h.uoblsnu302hk", + ); + }); + + it("returns undefined if no experiment brief document exists", () => { + const documentationLinks = [ + { + title: "DS_JIRA", + link: "https://mozilla-hub.atlassian.net/browse/DS-3819", + }, + { + title: "ENG_TICKET", + link: "https://mozilla-hub.atlassian.net/browse/FXE-952", + }, + ]; + const rawRecipe = ExperimentFakes.recipe("test-recipe"); + const nimbusRecipe = new NimbusRecipe(rawRecipe); + + const result = nimbusRecipe.getExperimentBriefLink(documentationLinks); + + expect(result).toBeUndefined(); + }); + }); + + it("returns undefined if no documentation link exists", () => { + const rawRecipe = ExperimentFakes.recipe("test-recipe"); + const nimbusRecipe = new NimbusRecipe(rawRecipe); + + const result = nimbusRecipe.getExperimentBriefLink(undefined); + + expect(result).toBeUndefined(); + }); }); diff --git a/app/columns.tsx b/app/columns.tsx index e83e2113..1d2d8656 100644 --- a/app/columns.tsx +++ b/app/columns.tsx @@ -3,11 +3,22 @@ import { types } from "@mozilla/nimbus-shared"; import { ColumnDef, Row } from "@tanstack/react-table"; import { NimbusRecipe } from "@/lib/nimbusRecipe"; import { PreviewLinkButton } from "@/components/ui/previewlinkbutton"; -import { ChevronsUpDown, ChevronDown, ChevronRight } from "lucide-react"; +import { + ChevronsUpDown, + ChevronDown, + ChevronRight, + FileText, +} from "lucide-react"; import { PrettyDateRange } from "./dates"; import { InfoPopover } from "@/components/ui/infopopover"; import { getSurfaceDataForTemplate } from "@/lib/messageUtils"; import { HIDE_DASHBOARD_EXPERIMENTS } from "@/lib/experimentUtils"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@/components/ui/tooltip"; function SurfaceTag(template: string, surface: string) { const { tagColor, docs } = getSurfaceDataForTemplate(template); @@ -98,6 +109,7 @@ export type RecipeInfo = { isBranch?: boolean; branches: BranchInfo[]; // XXX rename this to branchInfos to avoid confusion with the branches property inside NimbusExperiment hasMicrosurvey?: boolean; + experimentBriefLink?: string; }; export type BranchInfo = { @@ -169,11 +181,33 @@ const previewURLInfoButton = ( ); const microsurveyBadge = ( -
+
Microsurvey
); +function experimentBriefTooltip(link: string) { + return ( + + + + + + + + +

See experiment brief

+
+
+
+ ); +} + function filterBySurface( row: Row, filterValue: string, @@ -367,32 +401,36 @@ export const experimentColumns: ColumnDef[] = [ cell: (props: any) => { if (props.row.original.userFacingName) { return ( - <> - - {props.row.original.userFacingName || props.row.original.id} -
+ {props.row.original.experimentBriefLink && + experimentBriefTooltip(props.row.original.experimentBriefLink)} + {props.row.original.hasMicrosurvey && <> {microsurveyBadge} } + - - - - {props.row.original.hasMicrosurvey && <> {microsurveyBadge} } + {props.row.original.userFacingName || props.row.original.id} + + +
{props.row.original.id}
- +
); } @@ -568,32 +606,36 @@ export const completedExperimentColumns: ColumnDef[] = [ cell: (props: any) => { if (props.row.original.userFacingName) { return ( - <> - - {props.row.original.userFacingName || props.row.original.id} -
+ {props.row.original.experimentBriefLink && + experimentBriefTooltip(props.row.original.experimentBriefLink)} + {props.row.original.hasMicrosurvey && <> {microsurveyBadge} } + - - - - {props.row.original.hasMicrosurvey && <> {microsurveyBadge} } + {props.row.original.userFacingName || props.row.original.id} + + +
{props.row.original.id}
- + ); } diff --git a/lib/nimbusRecipe.ts b/lib/nimbusRecipe.ts index 60cf1d52..92ffb489 100644 --- a/lib/nimbusRecipe.ts +++ b/lib/nimbusRecipe.ts @@ -16,6 +16,11 @@ import { getExperimentLookerDashboardDate } from "./lookerUtils.ts"; type NimbusExperiment = types.experiments.NimbusExperiment; +type DocumentationLink = { + title: string; + link: string; +}; + function isMessagingFeature(featureId: string): boolean { return MESSAGING_EXPERIMENTS_DEFAULT_FEATURES.includes(featureId); } @@ -320,6 +325,9 @@ export class NimbusRecipe implements NimbusRecipeType { nimbusExperiment: this._rawRecipe, branches: branchInfos, hasMicrosurvey: hasMicrosurvey, + experimentBriefLink: this.getExperimentBriefLink( + this._rawRecipe.documentationLinks, + ), }; } @@ -382,4 +390,26 @@ export class NimbusRecipe implements NimbusRecipeType { this._rawRecipe.slug, )}/summary#${branchSlug}`; } + + /** + * @param documentationLinks a list of documentation links provided for this Nimbus recipe + * @returns the first documentation link of the experiment brief Google Doc if it exists + */ + getExperimentBriefLink( + documentationLinks: DocumentationLink[] | undefined, + ): string | undefined { + if (documentationLinks) { + const brief = documentationLinks.find( + (documentationLink: DocumentationLink) => { + return ( + documentationLink.title === "DESIGN_DOC" && + documentationLink.link.startsWith( + "https://docs.google.com/document", + ) + ); + }, + ); + return brief && brief.link; + } + } }