From 07095e13184107a60d6395f2c363b846a2dae767 Mon Sep 17 00:00:00 2001 From: Alexander Polyankin <alexander.polyankin@metabase.com> Date: Fri, 8 Apr 2022 20:03:57 +0300 Subject: [PATCH] Handle names for default timelines (#21514) --- .../src/metabase-types/api/mocks/timeline.ts | 1 + frontend/src/metabase-types/api/timeline.ts | 1 + .../entities/timeline-events/forms.js | 4 +- frontend/src/metabase/entities/timelines.js | 2 +- .../src/metabase/entities/timelines/forms.js | 6 +- frontend/src/metabase/lib/timelines.ts | 27 +++++-- .../src/metabase/query_builder/selectors.js | 3 +- frontend/src/metabase/schema.js | 1 + .../EditTimelineModal/EditTimelineModal.tsx | 8 +- .../NewTimelineModal/NewTimelineModal.tsx | 1 + .../components/TimelineCard/TimelineCard.tsx | 3 +- .../TimelineDetailsModal.tsx | 3 +- .../TimelineDetailsModal.unit.spec.tsx | 32 ++++++++ .../TimelineListModal/TimelineListModal.tsx | 8 +- .../TimelineDetailsModal.tsx | 4 +- .../components/TimelineCard/TimelineCard.tsx | 5 +- .../__support__/e2e/commands/api/timeline.js | 2 + .../collections/timelines.cy.spec.js | 77 +++++++++++++++++-- .../scenarios/question/timelines.cy.spec.js | 4 +- src/metabase/api/timeline.clj | 2 +- 20 files changed, 161 insertions(+), 33 deletions(-) diff --git a/frontend/src/metabase-types/api/mocks/timeline.ts b/frontend/src/metabase-types/api/mocks/timeline.ts index e7898fbd43f..b0ed0639bba 100644 --- a/frontend/src/metabase-types/api/mocks/timeline.ts +++ b/frontend/src/metabase-types/api/mocks/timeline.ts @@ -7,6 +7,7 @@ export const createMockTimeline = (opts?: Partial<Timeline>): Timeline => ({ name: "Events", description: "A timeline of events", icon: "star", + default: false, archived: false, ...opts, }); diff --git a/frontend/src/metabase-types/api/timeline.ts b/frontend/src/metabase-types/api/timeline.ts index 3a6645ab7c5..2aa4437f669 100644 --- a/frontend/src/metabase-types/api/timeline.ts +++ b/frontend/src/metabase-types/api/timeline.ts @@ -7,6 +7,7 @@ export interface Timeline { name: string; description: string | null; icon: string; + default: boolean; archived: boolean; collection?: Collection; events?: TimelineEvent[]; diff --git a/frontend/src/metabase/entities/timeline-events/forms.js b/frontend/src/metabase/entities/timeline-events/forms.js index d9111977b70..7a09270a51c 100644 --- a/frontend/src/metabase/entities/timeline-events/forms.js +++ b/frontend/src/metabase/entities/timeline-events/forms.js @@ -1,5 +1,5 @@ import { t } from "ttag"; -import { getTimelineIcons } from "metabase/lib/timelines"; +import { getTimelineIcons, getTimelineName } from "metabase/lib/timelines"; import validate from "metabase/lib/validate"; const createForm = ({ timelines }) => { @@ -37,7 +37,7 @@ const createForm = ({ timelines }) => { name: "timeline_id", title: t`Timeline`, type: timelines.length > 1 ? "select" : "hidden", - options: timelines.map(t => ({ name: t.name, value: t.id })), + options: timelines.map(t => ({ name: getTimelineName(t), value: t.id })), }, { name: "source", diff --git a/frontend/src/metabase/entities/timelines.js b/frontend/src/metabase/entities/timelines.js index 355365d0472..87aa80221be 100644 --- a/frontend/src/metabase/entities/timelines.js +++ b/frontend/src/metabase/entities/timelines.js @@ -37,7 +37,7 @@ const Timelines = createEntity({ setArchived: ({ id }, archived, opts) => Timelines.actions.update( { id }, - { archived }, + { archived, default: false }, undo(opts, t`timeline`, archived ? t`archived` : t`unarchived`), ), }, diff --git a/frontend/src/metabase/entities/timelines/forms.js b/frontend/src/metabase/entities/timelines/forms.js index 2f41dbca78b..d0fee6e135a 100644 --- a/frontend/src/metabase/entities/timelines/forms.js +++ b/frontend/src/metabase/entities/timelines/forms.js @@ -6,7 +6,7 @@ const createForm = () => { return [ { name: "name", - title: t`Timeline name`, + title: t`Name`, placeholder: t`Product releases`, autoFocus: true, validate: validate.required().maxLength(255), @@ -28,6 +28,10 @@ const createForm = () => { name: "collection_id", type: "hidden", }, + { + name: "default", + type: "hidden", + }, ]; }; diff --git a/frontend/src/metabase/lib/timelines.ts b/frontend/src/metabase/lib/timelines.ts index f02262894f0..81207c91431 100644 --- a/frontend/src/metabase/lib/timelines.ts +++ b/frontend/src/metabase/lib/timelines.ts @@ -2,14 +2,10 @@ import { t } from "ttag"; import { Collection, Timeline } from "metabase-types/api"; import { canonicalCollectionId } from "metabase/collections/utils"; -export const getDefaultTimeline = ( - collection: Collection, -): Partial<Timeline> => { - return { - name: t`${collection.name} events`, - collection_id: canonicalCollectionId(collection.id), - icon: getDefaultTimelineIcon(), - }; +export const getTimelineName = (timeline: Timeline) => { + return timeline.default && timeline.collection + ? getDefaultTimelineName(timeline.collection) + : timeline.name; }; export const getTimelineIcons = () => { @@ -23,6 +19,21 @@ export const getTimelineIcons = () => { ]; }; +export const getDefaultTimeline = ( + collection: Collection, +): Partial<Timeline> => { + return { + name: getDefaultTimelineName(collection), + collection_id: canonicalCollectionId(collection.id), + icon: getDefaultTimelineIcon(), + default: true, + }; +}; + +export const getDefaultTimelineName = (collection: Collection) => { + return t`${collection.name} events`; +}; + export const getDefaultTimelineIcon = () => { return "star"; }; diff --git a/frontend/src/metabase/query_builder/selectors.js b/frontend/src/metabase/query_builder/selectors.js index b0f02a614fb..2d27480f233 100644 --- a/frontend/src/metabase/query_builder/selectors.js +++ b/frontend/src/metabase/query_builder/selectors.js @@ -27,6 +27,7 @@ import Timelines from "metabase/entities/timelines"; import { getMetadata } from "metabase/selectors/metadata"; import { getAlerts } from "metabase/alert/selectors"; import { parseTimestamp } from "metabase/lib/time"; +import { getTimelineName } from "metabase/lib/timelines"; import { getXValues, isTimeseries, @@ -674,7 +675,7 @@ export const getTransformedTimelines = createSelector( .value(), ), ) - .sortBy(timeline => timeline.name) + .sortBy(getTimelineName) .sortBy(timeline => timeline.collection?.personal_owner_id != null) // personal collections last .value(); }, diff --git a/frontend/src/metabase/schema.js b/frontend/src/metabase/schema.js index 82e49b2bfa7..d6935db9ba0 100644 --- a/frontend/src/metabase/schema.js +++ b/frontend/src/metabase/schema.js @@ -86,6 +86,7 @@ MetricSchema.define({ }); TimelineSchema.define({ + collection: CollectionSchema, events: [TimelineEventSchema], }); diff --git a/frontend/src/metabase/timelines/collections/components/EditTimelineModal/EditTimelineModal.tsx b/frontend/src/metabase/timelines/collections/components/EditTimelineModal/EditTimelineModal.tsx index f6cab3faab0..3be1a1a0d6e 100644 --- a/frontend/src/metabase/timelines/collections/components/EditTimelineModal/EditTimelineModal.tsx +++ b/frontend/src/metabase/timelines/collections/components/EditTimelineModal/EditTimelineModal.tsx @@ -1,4 +1,4 @@ -import React, { useCallback } from "react"; +import React, { useCallback, useMemo } from "react"; import { t } from "ttag"; import Form from "metabase/containers/Form"; import forms from "metabase/entities/timelines/forms"; @@ -23,6 +23,10 @@ const EditTimelineModal = ({ onCancel, onClose, }: EditTimelineModalProps): JSX.Element => { + const initialValues = useMemo(() => { + return { ...timeline, default: false }; + }, [timeline]); + const handleSubmit = useCallback( async (values: Partial<Timeline>) => { await onSubmit(values, collection); @@ -40,7 +44,7 @@ const EditTimelineModal = ({ <ModalBody> <Form form={forms.details} - initialValues={timeline} + initialValues={initialValues} isModal={true} onSubmit={handleSubmit} onClose={onCancel} diff --git a/frontend/src/metabase/timelines/collections/components/NewTimelineModal/NewTimelineModal.tsx b/frontend/src/metabase/timelines/collections/components/NewTimelineModal/NewTimelineModal.tsx index 335d083e58d..2319f8dd973 100644 --- a/frontend/src/metabase/timelines/collections/components/NewTimelineModal/NewTimelineModal.tsx +++ b/frontend/src/metabase/timelines/collections/components/NewTimelineModal/NewTimelineModal.tsx @@ -25,6 +25,7 @@ const NewTimelineModal = ({ return { collection_id: canonicalCollectionId(collection.id), icon: getDefaultTimelineIcon(), + default: false, }; }, [collection]); diff --git a/frontend/src/metabase/timelines/collections/components/TimelineCard/TimelineCard.tsx b/frontend/src/metabase/timelines/collections/components/TimelineCard/TimelineCard.tsx index 3c5b333ab0a..837bce6d77c 100644 --- a/frontend/src/metabase/timelines/collections/components/TimelineCard/TimelineCard.tsx +++ b/frontend/src/metabase/timelines/collections/components/TimelineCard/TimelineCard.tsx @@ -1,6 +1,7 @@ import React, { memo } from "react"; import { t, msgid, ngettext } from "ttag"; import * as Urls from "metabase/lib/urls"; +import { getTimelineName } from "metabase/lib/timelines"; import EntityMenu from "metabase/components/EntityMenu"; import { Collection, Timeline } from "metabase-types/api"; import { @@ -35,7 +36,7 @@ const TimelineCard = ({ <CardRoot to={!timeline.archived ? timelineUrl : ""}> <CardIcon name={timeline.icon} /> <CardBody> - <CardTitle>{timeline.name}</CardTitle> + <CardTitle>{getTimelineName(timeline)}</CardTitle> {timeline.description && ( <CardDescription>{timeline.description}</CardDescription> )} diff --git a/frontend/src/metabase/timelines/collections/components/TimelineDetailsModal/TimelineDetailsModal.tsx b/frontend/src/metabase/timelines/collections/components/TimelineDetailsModal/TimelineDetailsModal.tsx index 3c02d56c7ec..18f5314d0ae 100644 --- a/frontend/src/metabase/timelines/collections/components/TimelineDetailsModal/TimelineDetailsModal.tsx +++ b/frontend/src/metabase/timelines/collections/components/TimelineDetailsModal/TimelineDetailsModal.tsx @@ -2,6 +2,7 @@ import React, { useCallback, useMemo, useState } from "react"; import { t } from "ttag"; import _ from "underscore"; import { parseTimestamp } from "metabase/lib/time"; +import { getTimelineName } from "metabase/lib/timelines"; import { SEARCH_DEBOUNCE_DURATION } from "metabase/lib/constants"; import * as Urls from "metabase/lib/urls"; import { useDebouncedValue } from "metabase/hooks/use-debounced-value"; @@ -42,7 +43,7 @@ const TimelineDetailsModal = ({ onClose, onGoBack, }: TimelineDetailsModalProps): JSX.Element => { - const title = isArchive ? t`Archived events` : timeline.name; + const title = isArchive ? t`Archived events` : getTimelineName(timeline); const [inputText, setInputText] = useState(""); const searchText = useDebouncedValue( diff --git a/frontend/src/metabase/timelines/collections/components/TimelineDetailsModal/TimelineDetailsModal.unit.spec.tsx b/frontend/src/metabase/timelines/collections/components/TimelineDetailsModal/TimelineDetailsModal.unit.spec.tsx index f9976b1dce9..5615364ecc7 100644 --- a/frontend/src/metabase/timelines/collections/components/TimelineDetailsModal/TimelineDetailsModal.unit.spec.tsx +++ b/frontend/src/metabase/timelines/collections/components/TimelineDetailsModal/TimelineDetailsModal.unit.spec.tsx @@ -11,6 +11,38 @@ import TimelineDetailsModal, { } from "./TimelineDetailsModal"; describe("TimelineDetailsModal", () => { + it("should use the collection's name for default timelines", () => { + const props = getProps({ + timeline: createMockTimeline({ + name: "Metrics events", + default: true, + collection: createMockCollection({ + name: "Analytics", + }), + }), + }); + + render(<TimelineDetailsModal {...props} />); + + expect(screen.getByText("Analytics events")).toBeInTheDocument(); + }); + + it("should use the timeline's name for non-default timelines", () => { + const props = getProps({ + timeline: createMockTimeline({ + name: "Metrics events", + default: false, + collection: createMockCollection({ + name: "Analytics", + }), + }), + }); + + render(<TimelineDetailsModal {...props} />); + + expect(screen.getByText("Metrics events")).toBeInTheDocument(); + }); + it("should search a list of events", async () => { const props = getProps({ timeline: createMockTimeline({ diff --git a/frontend/src/metabase/timelines/collections/components/TimelineListModal/TimelineListModal.tsx b/frontend/src/metabase/timelines/collections/components/TimelineListModal/TimelineListModal.tsx index b5d8479a257..60bf887a031 100644 --- a/frontend/src/metabase/timelines/collections/components/TimelineListModal/TimelineListModal.tsx +++ b/frontend/src/metabase/timelines/collections/components/TimelineListModal/TimelineListModal.tsx @@ -2,6 +2,10 @@ import React, { useCallback } from "react"; import { t } from "ttag"; import _ from "underscore"; import * as Urls from "metabase/lib/urls"; +import { + getDefaultTimelineName, + getTimelineName, +} from "metabase/lib/timelines"; import EntityMenu from "metabase/components/EntityMenu"; import { Collection, Timeline } from "metabase-types/api"; import ModalHeader from "metabase/timelines/common/components/ModalHeader"; @@ -75,7 +79,7 @@ const getTitle = ( } else if (timelines.length) { return t`Events`; } else { - return t`${collection.name} events`; + return getDefaultTimelineName(collection); } }; @@ -102,7 +106,7 @@ const getMenuItems = ( const getSortedTimelines = (timelines: Timeline[]) => { return _.chain(timelines) - .sortBy(timeline => timeline.name) + .sortBy(getTimelineName) .sortBy(timeline => timeline.collection?.personal_owner_id != null) // personal collections last .value(); }; diff --git a/frontend/src/metabase/timelines/collections/containers/TimelineDetailsModal/TimelineDetailsModal.tsx b/frontend/src/metabase/timelines/collections/containers/TimelineDetailsModal/TimelineDetailsModal.tsx index 7981b2b6715..e810aa08e6a 100644 --- a/frontend/src/metabase/timelines/collections/containers/TimelineDetailsModal/TimelineDetailsModal.tsx +++ b/frontend/src/metabase/timelines/collections/containers/TimelineDetailsModal/TimelineDetailsModal.tsx @@ -24,7 +24,9 @@ const timelineProps = { }; const timelinesProps = { - query: { include: "events" }, + query: (state: State, props: ModalProps) => ({ + collectionId: Urls.extractCollectionId(props.params.slug), + }), LoadingAndErrorWrapper, }; diff --git a/frontend/src/metabase/timelines/questions/components/TimelineCard/TimelineCard.tsx b/frontend/src/metabase/timelines/questions/components/TimelineCard/TimelineCard.tsx index 004c22c0096..40e5c29fb1d 100644 --- a/frontend/src/metabase/timelines/questions/components/TimelineCard/TimelineCard.tsx +++ b/frontend/src/metabase/timelines/questions/components/TimelineCard/TimelineCard.tsx @@ -7,6 +7,7 @@ import React, { useEffect, } from "react"; import _ from "underscore"; +import { getTimelineName } from "metabase/lib/timelines"; import Ellipsified from "metabase/components/Ellipsified"; import { Timeline, TimelineEvent } from "metabase-types/api"; import EventCard from "../EventCard"; @@ -74,7 +75,9 @@ const TimelineCard = ({ onChange={handleCheckboxChange} /> <CardLabel> - <Ellipsified tooltipMaxWidth="100%">{timeline.name}</Ellipsified> + <Ellipsified tooltipMaxWidth="100%"> + {getTimelineName(timeline)} + </Ellipsified> </CardLabel> <CardIcon name={isExpanded ? "chevronup" : "chevrondown"} /> </CardHeader> diff --git a/frontend/test/__support__/e2e/commands/api/timeline.js b/frontend/test/__support__/e2e/commands/api/timeline.js index ab614e70c8f..915401d501a 100644 --- a/frontend/test/__support__/e2e/commands/api/timeline.js +++ b/frontend/test/__support__/e2e/commands/api/timeline.js @@ -5,6 +5,7 @@ Cypress.Commands.add( description, icon = "star", collection_id, + default: is_default = false, archived = false, } = {}) => { return cy.request("POST", "/api/timeline", { @@ -12,6 +13,7 @@ Cypress.Commands.add( description, icon, collection_id, + default: is_default, archived, }); }, diff --git a/frontend/test/metabase/scenarios/collections/timelines.cy.spec.js b/frontend/test/metabase/scenarios/collections/timelines.cy.spec.js index dc1073c74dc..61d6cb07813 100644 --- a/frontend/test/metabase/scenarios/collections/timelines.cy.spec.js +++ b/frontend/test/metabase/scenarios/collections/timelines.cy.spec.js @@ -10,6 +10,13 @@ import { describe("scenarios > collections > timelines", () => { beforeEach(() => { restore(); + cy.intercept("PUT", "/api/collection/*").as("updateCollection"); + cy.intercept("POST", "/api/timeline").as("createTimeline"); + cy.intercept("PUT", "/api/timeline/*").as("updateTimeline"); + cy.intercept("DELETE", "/api/timeline/*").as("deleteTimeline"); + cy.intercept("POST", "/api/timeline-event").as("createEvent"); + cy.intercept("PUT", "/api/timeline-event/*").as("updateEvent"); + cy.intercept("DELETE", "/api/timeline-event/*").as("deleteEvent"); }); describe("as admin", () => { @@ -25,6 +32,7 @@ describe("scenarios > collections > timelines", () => { cy.findByLabelText("Event name").type("RC1"); cy.findByLabelText("Date").type("10/20/2020"); cy.button("Create").click(); + cy.wait("@createEvent"); cy.findByText("RC1").should("be.visible"); cy.findByText("October 20, 2020").should("be.visible"); @@ -36,6 +44,7 @@ describe("scenarios > collections > timelines", () => { cy.findByText("Star").click(); cy.findByText("Balloons").click(); cy.button("Create").click(); + cy.wait("@createEvent"); cy.findByText("RC2").should("be.visible"); cy.findByText("May 12, 2021").should("be.visible"); @@ -51,6 +60,7 @@ describe("scenarios > collections > timelines", () => { cy.findByLabelText("Event name").type("RC1"); cy.findByLabelText("Date").type("10/20/2020"); cy.button("Create").click(); + cy.wait("@createEvent"); cy.findByText("RC1").should("be.visible"); }); @@ -85,6 +95,7 @@ describe("scenarios > collections > timelines", () => { cy.findByText("15").click(); cy.findByText("Done").click(); cy.findByText("Create").click(); + cy.wait("@createEvent"); cy.findByText("Our analytics events").should("be.visible"); cy.findByText("RC1").should("be.visible"); @@ -102,6 +113,7 @@ describe("scenarios > collections > timelines", () => { cy.findByText("Markdown supported").should("be.visible"); cy.findByLabelText("Description").type("*1.0-rc1* release"); cy.findByText("Create").click(); + cy.wait("@createEvent"); cy.findByText("Our analytics events").should("be.visible"); cy.findByText("RC1").should("be.visible"); @@ -126,6 +138,7 @@ describe("scenarios > collections > timelines", () => { .type("20"); cy.findByText("Done").click(); cy.findByText("Create").click(); + cy.wait("@createEvent"); cy.findByText("Our analytics events").should("be.visible"); cy.findByText("RC1").should("be.visible"); @@ -144,6 +157,7 @@ describe("scenarios > collections > timelines", () => { cy.findByText("Add time").click(); cy.findByText("Done").click(); cy.findByText("Create").click(); + cy.wait("@createEvent"); cy.findByText("Our analytics events").should("be.visible"); cy.findByText("RC1").should("be.visible"); @@ -160,6 +174,7 @@ describe("scenarios > collections > timelines", () => { .clear() .type("RC2"); cy.button("Update").click(); + cy.wait("@updateEvent"); cy.findByText("RC2").should("be.visible"); }); @@ -175,6 +190,7 @@ describe("scenarios > collections > timelines", () => { openMenu("RC1"); cy.findByText("Edit event").click(); cy.findByText("Archive event").click(); + cy.wait("@updateEvent"); cy.findByText("Releases").should("be.visible"); cy.findByText("RC1").should("not.exist"); @@ -190,9 +206,11 @@ describe("scenarios > collections > timelines", () => { openMenu("RC1"); cy.findByText("Archive event").click(); + cy.wait("@updateEvent"); cy.findByText("RC1").should("not.exist"); cy.findByText("Undo").click(); + cy.wait("@updateEvent"); cy.findByText("RC1").should("be.visible"); }); @@ -210,9 +228,11 @@ describe("scenarios > collections > timelines", () => { openMenu("RC1"); cy.findByText("Unarchive event").click(); + cy.wait("@updateEvent"); cy.findByText("No events found").should("be.visible"); cy.findByText("Undo").click(); + cy.wait("@updateEvent"); cy.findByText("RC1").should("be.visible"); }); @@ -230,6 +250,7 @@ describe("scenarios > collections > timelines", () => { openMenu("RC1"); cy.findByText("Delete event").click(); cy.findByText("Delete").click(); + cy.wait("@deleteEvent"); cy.findByText("No events found").should("be.visible"); }); @@ -262,8 +283,9 @@ describe("scenarios > collections > timelines", () => { cy.visit("/collection/root/timelines"); openMenu("Releases"); cy.findByText("New timeline").click(); - cy.findByLabelText("Timeline name").type("Launches"); + cy.findByLabelText("Name").type("Launches"); cy.findByText("Create").click(); + cy.wait("@createTimeline"); cy.findByText("Launches").should("be.visible"); cy.findByText("Add an event").should("be.visible"); @@ -278,10 +300,11 @@ describe("scenarios > collections > timelines", () => { cy.visit("/collection/root/timelines"); openMenu("Releases"); cy.findByText("Edit timeline details").click(); - cy.findByLabelText("Timeline name") + cy.findByLabelText("Name") .clear() .type("Launches"); cy.findByText("Update").click(); + cy.wait("@updateTimeline"); cy.findByText("Launches").should("be.visible"); }); @@ -296,10 +319,12 @@ describe("scenarios > collections > timelines", () => { openMenu("Releases"); cy.findByText("Edit timeline details").click(); cy.findByText("Archive timeline and all events").click(); + cy.wait("@updateTimeline"); cy.findByText("Our analytics events").should("be.visible"); cy.findByText("Add an event").should("be.visible"); cy.findByText("Undo").click(); + cy.wait("@updateTimeline"); cy.findByText("Releases").should("be.visible"); cy.findByText("RC1").should("be.visible"); cy.findByText("RC2").should("be.visible"); @@ -348,16 +373,16 @@ describe("scenarios > collections > timelines", () => { openMenu("Releases"); cy.findByText("Edit timeline details").click(); cy.findByText("Archive timeline and all events").click(); + cy.wait("@updateTimeline"); openMenu("Our analytics events"); cy.findByText("View archived timelines").click(); openMenu("Releases"); cy.findByText("Unarchive timeline").click(); + cy.wait("@updateTimeline"); cy.findByText("No timelines found"); - cy.get(".Modal").within(() => { - cy.icon("chevronleft").click(); - }); + getModal().within(() => cy.icon("chevronleft").click()); cy.findByText("Releases"); }); @@ -371,6 +396,7 @@ describe("scenarios > collections > timelines", () => { openMenu("Releases"); cy.findByText("Edit timeline details").click(); cy.findByText("Archive timeline and all events").click(); + cy.wait("@updateTimeline"); openMenu("Our analytics events"); cy.findByText("View archived timelines").click(); @@ -378,12 +404,43 @@ describe("scenarios > collections > timelines", () => { openMenu("Releases"); cy.findByText("Delete timeline").click(); cy.findByText("Delete").click(); + cy.wait("@deleteTimeline"); cy.findByText("No timelines found"); - cy.get(".Modal").within(() => { - cy.icon("chevronleft").click(); - }); + getModal().within(() => cy.icon("chevronleft").click()); cy.findByText("Our analytics events"); }); + + it("should preserve collection names for default timelines", () => { + cy.visit("/"); + cy.findByText("First collection").click(); + + cy.icon("calendar").click(); + cy.findByText("Add an event").click(); + cy.findByLabelText("Event name").type("RC1"); + cy.findByLabelText("Date").type("10/20/2020"); + cy.button("Create").click(); + cy.findByText("First collection events"); + cy.wait("@createTimeline"); + cy.icon("close").click(); + + cy.icon("pencil").click(); + cy.findByText("Edit this collection").click(); + cy.findByLabelText("Name") + .clear() + .type("1st collection"); + cy.button("Update").click(); + cy.wait("@updateCollection"); + + cy.icon("calendar").click(); + openMenu("1st collection events"); + cy.findByText("Edit timeline details").click(); + cy.findByLabelText("Name") + .clear() + .type("Releases"); + cy.button("Update").click(); + cy.wait("@updateTimeline"); + cy.findByText("Releases"); + }); }); describe("as readonly user", () => { @@ -445,6 +502,10 @@ describeWithSnowplow("scenarios > collections > timelines", () => { }); }); +const getModal = () => { + return cy.get(".Modal"); +}; + const openMenu = name => { return cy .findByText(name) diff --git a/frontend/test/metabase/scenarios/question/timelines.cy.spec.js b/frontend/test/metabase/scenarios/question/timelines.cy.spec.js index 23a045a8150..96082e5873a 100644 --- a/frontend/test/metabase/scenarios/question/timelines.cy.spec.js +++ b/frontend/test/metabase/scenarios/question/timelines.cy.spec.js @@ -65,9 +65,7 @@ describe("scenarios > collections > timelines", () => { cy.icon("calendar").click(); cy.findByText("Releases"); - sidebar().within(() => { - cy.icon("ellipsis").click(); - }); + sidebar().within(() => cy.icon("ellipsis").click()); cy.findByTextEnsureVisible("Edit event").click(); cy.findByLabelText("Event name") diff --git a/src/metabase/api/timeline.clj b/src/metabase/api/timeline.clj index 00ae9b3c5eb..3bb68956c4e 100644 --- a/src/metabase/api/timeline.clj +++ b/src/metabase/api/timeline.clj @@ -86,7 +86,7 @@ (collection/check-allowed-to-change-collection existing timeline-updates) (db/update! Timeline id (u/select-keys-when timeline-updates - :present #{:description :icon :collection_id :archived} + :present #{:description :icon :collection_id :default :archived} :non-nil #{:name})) (when (and (some? archived) (not= current-archived archived)) (db/update-where! TimelineEvent {:timeline_id id} :archived archived)) -- GitLab