diff --git a/frontend/src/metabase/query_builder/actions.js b/frontend/src/metabase/query_builder/actions.js index 436fca365e4698f74bb052512f8fd157f1925c98..11470876a0feb4bc4da0fd452a393a0b264dbe54 100644 --- a/frontend/src/metabase/query_builder/actions.js +++ b/frontend/src/metabase/query_builder/actions.js @@ -61,7 +61,7 @@ import { getResultsMetadata, getSnippetCollectionId, getTableForeignKeys, - getTimelines, + getFetchedTimelines, getTransformedSeries, getZoomedObjectId, isBasedOnExistingQuestion, @@ -1678,10 +1678,10 @@ export const showTimelinesForCollection = collectionId => ( dispatch, getState, ) => { - const availableTimelines = getTimelines(getState()); + const fetchedTimelines = getFetchedTimelines(getState()); const collectionTimelines = collectionId - ? availableTimelines.filter(t => t.collection_id === collectionId) - : availableTimelines.filter(t => t.collection_id == null); + ? fetchedTimelines.filter(t => t.collection_id === collectionId) + : fetchedTimelines.filter(t => t.collection_id == null); dispatch(showTimelines(collectionTimelines)); }; diff --git a/frontend/src/metabase/query_builder/components/QueryModals.jsx b/frontend/src/metabase/query_builder/components/QueryModals.jsx index 438459e46d2ca3c2227f6b39fb979e5d0327e4b8..370300ac33ebe85e668478f69bcc196696b4a736 100644 --- a/frontend/src/metabase/query_builder/components/QueryModals.jsx +++ b/frontend/src/metabase/query_builder/components/QueryModals.jsx @@ -23,7 +23,6 @@ import { ImpossibleToCreateModelModal } from "metabase/query_builder/components/ import NewDatasetModal from "metabase/query_builder/components/NewDatasetModal"; import EntityCopyModal from "metabase/entities/containers/EntityCopyModal"; import NewEventModal from "metabase/timelines/questions/containers/NewEventModal"; -import NewEventWithTimelineModal from "metabase/timelines/questions/containers/NewEventWithTimelineModal"; import EditEventModal from "metabase/timelines/questions/containers/EditEventModal"; export default class QueryModals extends React.Component { @@ -230,13 +229,6 @@ export default class QueryModals extends React.Component { onClose={onCloseModal} /> </Modal> - ) : modal === MODAL_TYPES.NEW_EVENT_WITH_TIMELINE ? ( - <Modal onClose={onCloseModal}> - <NewEventWithTimelineModal - collectionId={question.collectionId()} - onClose={onCloseModal} - /> - </Modal> ) : modal === MODAL_TYPES.EDIT_EVENT ? ( <Modal onClose={onCloseModal}> <EditEventModal eventId={modalContext} onClose={onCloseModal} /> diff --git a/frontend/src/metabase/query_builder/components/view/View.jsx b/frontend/src/metabase/query_builder/components/view/View.jsx index 7d1a5baec36c3715c7f674da0d0de44f345e5726..62aba2be0595adbdcdad687a38142cda64d8888a 100644 --- a/frontend/src/metabase/query_builder/components/view/View.jsx +++ b/frontend/src/metabase/query_builder/components/view/View.jsx @@ -158,6 +158,7 @@ export default class View extends React.Component { getRightSidebarForStructuredQuery = () => { const { question, + timelines, isResultDirty, isShowingSummarySidebar, isShowingFilterSidebar, @@ -192,6 +193,7 @@ export default class View extends React.Component { return ( <TimelineSidebar question={question} + timelines={timelines} visibleTimelineIds={visibleTimelineIds} selectedTimelineEventIds={selectedTimelineEventIds} onShowTimelines={showTimelines} diff --git a/frontend/src/metabase/query_builder/components/view/sidebars/TimelineSidebar/TimelineSidebar.tsx b/frontend/src/metabase/query_builder/components/view/sidebars/TimelineSidebar/TimelineSidebar.tsx index 051debf060ed6a1ad274bc9443d42c2c1a48b8cf..2bbf54e1691eafde868cfb508a1af6e300cd14f8 100644 --- a/frontend/src/metabase/query_builder/components/view/sidebars/TimelineSidebar/TimelineSidebar.tsx +++ b/frontend/src/metabase/query_builder/components/view/sidebars/TimelineSidebar/TimelineSidebar.tsx @@ -8,6 +8,7 @@ import { Timeline, TimelineEvent } from "metabase-types/api"; export interface TimelineSidebarProps { question: Question; + timelines: Timeline[]; visibleTimelineIds: number[]; selectedTimelineEventIds: number[]; onShowTimelines?: (timelines: Timeline[]) => void; @@ -18,6 +19,7 @@ export interface TimelineSidebarProps { const TimelineSidebar = ({ question, + timelines, visibleTimelineIds, selectedTimelineEventIds, onOpenModal, @@ -29,10 +31,6 @@ const TimelineSidebar = ({ onOpenModal?.(MODAL_TYPES.NEW_EVENT); }, [onOpenModal]); - const handleNewEventWithTimeline = useCallback(() => { - onOpenModal?.(MODAL_TYPES.NEW_EVENT_WITH_TIMELINE); - }, [onOpenModal]); - const handleEditEvent = useCallback( (event: TimelineEvent) => { onOpenModal?.(MODAL_TYPES.EDIT_EVENT, event.id); @@ -54,11 +52,11 @@ const TimelineSidebar = ({ return ( <SidebarContent title={t`Events`} onClose={onClose}> <TimelinePanel + timelines={timelines} + collectionId={question.collectionId()} visibleTimelineIds={visibleTimelineIds} selectedEventIds={selectedTimelineEventIds} - collectionId={question.collectionId()} onNewEvent={handleNewEvent} - onNewEventWithTimeline={handleNewEventWithTimeline} onEditEvent={handleEditEvent} onToggleTimeline={handleToggleTimeline} /> diff --git a/frontend/src/metabase/query_builder/constants.js b/frontend/src/metabase/query_builder/constants.js index 2f81e41039cd7e063250fa11d8c75d33d54a1c3a..56aa05eee4d71afeb60cd56fedd0704768103238 100644 --- a/frontend/src/metabase/query_builder/constants.js +++ b/frontend/src/metabase/query_builder/constants.js @@ -15,6 +15,5 @@ export const MODAL_TYPES = { TURN_INTO_DATASET: "turn-into-dataset", CAN_NOT_CREATE_MODEL: "can-not-create-model", NEW_EVENT: "new-event", - NEW_EVENT_WITH_TIMELINE: "new-event-with-timeline", EDIT_EVENT: "edit-event", }; diff --git a/frontend/src/metabase/query_builder/containers/QueryBuilder.jsx b/frontend/src/metabase/query_builder/containers/QueryBuilder.jsx index 188ac9132613f096a391f64034863d76477fa82a..9d647931972b100b8129eb9baf95bb2fb3e8d64e 100644 --- a/frontend/src/metabase/query_builder/containers/QueryBuilder.jsx +++ b/frontend/src/metabase/query_builder/containers/QueryBuilder.jsx @@ -65,9 +65,9 @@ import { getNativeEditorSelectedText, getIsBookmarked, getVisibleTimelineIds, - getVisibleTimelines, getVisibleTimelineEvents, getSelectedTimelineEventIds, + getFilteredTimelines, } from "../selectors"; import * as actions from "../actions"; @@ -116,10 +116,10 @@ const mapStateToProps = (state, props) => { query: getQuery(state), metadata: getMetadata(state), - timelines: getVisibleTimelines(state, props), - timelineEvents: getVisibleTimelineEvents(state, props), - visibleTimelineIds: getVisibleTimelineIds(state, props), - selectedTimelineEventIds: getSelectedTimelineEventIds(state, props), + timelines: getFilteredTimelines(state), + timelineEvents: getVisibleTimelineEvents(state), + visibleTimelineIds: getVisibleTimelineIds(state), + selectedTimelineEventIds: getSelectedTimelineEventIds(state), result: getFirstQueryResult(state), results: getQueryResults(state), diff --git a/frontend/src/metabase/query_builder/selectors.js b/frontend/src/metabase/query_builder/selectors.js index 5d4141827881744abdeebbe16ed69981767ce486..0239f9e848cb737ea0533b57d491a63563f7a4a0 100644 --- a/frontend/src/metabase/query_builder/selectors.js +++ b/frontend/src/metabase/query_builder/selectors.js @@ -1,5 +1,6 @@ /*eslint no-use-before-define: "error"*/ +import d3 from "d3"; import { createSelector } from "reselect"; import _ from "underscore"; import { assocIn, getIn, merge, updateIn } from "icepick"; @@ -26,6 +27,10 @@ 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 { + getXValues, + isTimeseries, +} from "metabase/visualizations/lib/renderer_utils"; export const getUiControls = state => state.qb.uiControls; @@ -286,35 +291,6 @@ export const getQuestion = createSelector( }, ); -export const getTimelines = createSelector([getEntities], entities => { - const entityQuery = { include: "events" }; - return Timelines.selectors.getList({ entities }, { entityQuery }) ?? []; -}); - -export const getVisibleTimelines = createSelector( - [getQuestion, getTimelines, getVisibleTimelineIds], - (question, timelines, timelineIds) => { - if (!question) { - return []; - } - - return timelines.filter(t => timelineIds.includes(t.id)); - }, -); - -export const getVisibleTimelineEvents = createSelector( - [getVisibleTimelines], - timelines => { - return _.chain(timelines) - .map(timeline => timeline.events ?? []) - .flatten() - .filter(event => !event.archived) - .map(event => updateIn(event, ["timestamp"], parseTimestamp)) - .sortBy(event => event.timestamp) - .value(); - }, -); - function normalizeClause(clause) { return typeof clause?.raw === "function" ? clause.raw() : clause; } @@ -618,6 +594,75 @@ const getNativeEditorSelectedRange = createSelector( uiControls => uiControls && uiControls.nativeEditorSelectedRange, ); +function isEventWithinDomain(event, xDomain) { + return event.timestamp.isBetween(xDomain[0], xDomain[1], undefined, "[]"); +} + +const getIsTimeseries = createSelector( + [getVisualizationSettings], + settings => settings && isTimeseries(settings), +); + +const getTimeseriesXValues = createSelector( + [getIsTimeseries, getTransformedSeries, getVisualizationSettings], + (isTimeseries, series, settings) => + isTimeseries && series && settings && getXValues({ series, settings }), +); + +const getTimeseriesXDomain = createSelector( + [getIsTimeseries, getTimeseriesXValues], + (isTimeseries, xValues) => xValues && isTimeseries && d3.extent(xValues), +); + +export const getFetchedTimelines = createSelector([getEntities], entities => { + const entityQuery = { include: "events" }; + return Timelines.selectors.getList({ entities }, { entityQuery }) ?? []; +}); + +export const getTransformedTimelines = createSelector( + [getFetchedTimelines], + timelines => { + return timelines.map(timeline => + updateIn(timeline, ["events"], (events = []) => + _.chain(events) + .map(event => updateIn(event, ["timestamp"], parseTimestamp)) + .filter(event => !event.archived) + .sortBy(event => event.timestamp) + .value(), + ), + ); + }, +); + +export const getFilteredTimelines = createSelector( + [getTransformedTimelines, getTimeseriesXDomain], + (timelines, xDomain) => { + if (!xDomain) { + return []; + } + + return timelines + .map(timeline => + updateIn(timeline, ["events"], events => + events.filter(event => isEventWithinDomain(event, xDomain)), + ), + ) + .filter(timeline => timeline.events.length > 0); + }, +); + +export const getVisibleTimelines = createSelector( + [getFilteredTimelines, getVisibleTimelineIds], + (timelines, timelineIds) => { + return timelines.filter(t => timelineIds.includes(t.id)); + }, +); + +export const getVisibleTimelineEvents = createSelector( + [getVisibleTimelines], + timelines => timelines.flatMap(timeline => timeline.events), +); + function getOffsetForQueryAndPosition(queryText, { row, column }) { const queryLines = queryText.split("\n"); return ( diff --git a/frontend/src/metabase/timelines/questions/components/TimelineCard/TimelineCard.tsx b/frontend/src/metabase/timelines/questions/components/TimelineCard/TimelineCard.tsx index 8213e65deada6df1e9d7567500c70036f193c815..cb996ca7f464aa5b866793fd3e5a6ec18d2173fe 100644 --- a/frontend/src/metabase/timelines/questions/components/TimelineCard/TimelineCard.tsx +++ b/frontend/src/metabase/timelines/questions/components/TimelineCard/TimelineCard.tsx @@ -7,7 +7,6 @@ import React, { useEffect, } from "react"; import _ from "underscore"; -import { parseTimestamp } from "metabase/lib/time"; import { Collection, Timeline, TimelineEvent } from "metabase-types/api"; import EventCard from "../EventCard"; import { @@ -96,8 +95,7 @@ const TimelineCard = ({ const getEvents = (events: TimelineEvent[] = []) => { return _.chain(events) - .filter(e => !e.archived) - .sortBy(e => parseTimestamp(e.timestamp)) + .sortBy(e => e.timestamp) .reverse() .value(); }; diff --git a/frontend/src/metabase/timelines/questions/components/TimelineCard/TimelineCard.unit.spec.tsx b/frontend/src/metabase/timelines/questions/components/TimelineCard/TimelineCard.unit.spec.tsx index cd1f1840c366ac185e12f3dc2de4f19f17f77826..d4a378d9281fc0bceaa4f3beb6caa7e604f4c3b2 100644 --- a/frontend/src/metabase/timelines/questions/components/TimelineCard/TimelineCard.unit.spec.tsx +++ b/frontend/src/metabase/timelines/questions/components/TimelineCard/TimelineCard.unit.spec.tsx @@ -13,32 +13,25 @@ describe("TimelineCard", () => { const props = getProps({ timeline: createMockTimeline({ name: "Releases", - events: [ - createMockTimelineEvent({ name: "RC1" }), - createMockTimelineEvent({ name: "RC2" }), - createMockTimelineEvent({ name: "RC3", archived: true }), - ], + events: [createMockTimelineEvent({ name: "RC" })], }), }); render(<TimelineCard {...props} />); - expect(screen.queryByText("RC1")).not.toBeInTheDocument(); - expect(screen.queryByText("RC3")).not.toBeInTheDocument(); + expect(screen.queryByText("RC")).not.toBeInTheDocument(); userEvent.click(screen.getByText("Releases")); - expect(screen.getByText("RC1")).toBeInTheDocument(); - expect(screen.queryByText("RC3")).not.toBeInTheDocument(); + expect(screen.getByText("RC")).toBeInTheDocument(); userEvent.click(screen.getByText("Releases")); - expect(screen.queryByText("RC1")).not.toBeInTheDocument(); - expect(screen.queryByText("RC3")).not.toBeInTheDocument(); + expect(screen.queryByText("RC")).not.toBeInTheDocument(); }); it("should toggle visibility of the card", () => { const props = getProps({ timeline: createMockTimeline({ name: "Releases", - events: [createMockTimelineEvent({ name: "RC1" })], + events: [createMockTimelineEvent({ name: "RC" })], }), }); diff --git a/frontend/src/metabase/timelines/questions/components/TimelineEmptyState/TimelineEmptyState.tsx b/frontend/src/metabase/timelines/questions/components/TimelineEmptyState/TimelineEmptyState.tsx index 9adfb3049ce0d704f3f1eff78614d4b3ad56a9d9..09d8e0e154e200e9cfcedb1949897898fcf1fb36 100644 --- a/frontend/src/metabase/timelines/questions/components/TimelineEmptyState/TimelineEmptyState.tsx +++ b/frontend/src/metabase/timelines/questions/components/TimelineEmptyState/TimelineEmptyState.tsx @@ -10,12 +10,12 @@ import { export interface TimelineEmptyStateProps { collection: Collection; - onNewEventWithTimeline?: () => void; + onNewEvent?: () => void; } const TimelineEmptyState = ({ collection, - onNewEventWithTimeline, + onNewEvent, }: TimelineEmptyStateProps): JSX.Element => { const canWrite = collection.can_write; @@ -28,7 +28,7 @@ const TimelineEmptyState = ({ : t`Events in Metabase let you see helpful context alongside your data.`} </EmptyStateText> {canWrite && ( - <EmptyStateButton primary onClick={onNewEventWithTimeline}> + <EmptyStateButton primary onClick={onNewEvent}> {t`Add an event`} </EmptyStateButton> )} diff --git a/frontend/src/metabase/timelines/questions/components/TimelineEmptyState/TimelineEmptyState.unit.spec.tsx b/frontend/src/metabase/timelines/questions/components/TimelineEmptyState/TimelineEmptyState.unit.spec.tsx index 077d176501604055f65316d488f2d614e978b2ab..83fb9dd777374c030926a63316c8046ff32cb599 100644 --- a/frontend/src/metabase/timelines/questions/components/TimelineEmptyState/TimelineEmptyState.unit.spec.tsx +++ b/frontend/src/metabase/timelines/questions/components/TimelineEmptyState/TimelineEmptyState.unit.spec.tsx @@ -37,6 +37,6 @@ const getProps = ( opts?: Partial<TimelineEmptyStateProps>, ): TimelineEmptyStateProps => ({ collection: createMockCollection(), - onNewEventWithTimeline: jest.fn(), + onNewEvent: jest.fn(), ...opts, }); diff --git a/frontend/src/metabase/timelines/questions/components/TimelinePanel/TimelinePanel.tsx b/frontend/src/metabase/timelines/questions/components/TimelinePanel/TimelinePanel.tsx index e1887d4a15568f26ee7d14ba8d178384ac2b3eea..89480c842bb3ac68ffd29ef579f2815afd2eb6f0 100644 --- a/frontend/src/metabase/timelines/questions/components/TimelinePanel/TimelinePanel.tsx +++ b/frontend/src/metabase/timelines/questions/components/TimelinePanel/TimelinePanel.tsx @@ -12,7 +12,6 @@ export interface TimelinePanelProps { visibleTimelineIds?: number[]; selectedEventIds?: number[]; onNewEvent?: () => void; - onNewEventWithTimeline?: () => void; onEditEvent?: (event: TimelineEvent) => void; onArchiveEvent?: (event: TimelineEvent) => void; onToggleTimeline?: (timeline: Timeline, isVisible: boolean) => void; @@ -24,7 +23,6 @@ const TimelinePanel = ({ visibleTimelineIds, selectedEventIds, onNewEvent, - onNewEventWithTimeline, onEditEvent, onArchiveEvent, onToggleTimeline, @@ -50,10 +48,7 @@ const TimelinePanel = ({ onArchiveEvent={onArchiveEvent} /> ) : ( - <TimelineEmptyState - collection={collection} - onNewEventWithTimeline={onNewEventWithTimeline} - /> + <TimelineEmptyState collection={collection} onNewEvent={onNewEvent} /> )} </PanelRoot> ); diff --git a/frontend/src/metabase/timelines/questions/components/TimelinePanel/TimelinePanel.unit.spec.tsx b/frontend/src/metabase/timelines/questions/components/TimelinePanel/TimelinePanel.unit.spec.tsx index affb9e89b4c3f12c425d5ce1dc97678765857b68..0a9cc628204c9a89c718141316c0f8e121144730 100644 --- a/frontend/src/metabase/timelines/questions/components/TimelinePanel/TimelinePanel.unit.spec.tsx +++ b/frontend/src/metabase/timelines/questions/components/TimelinePanel/TimelinePanel.unit.spec.tsx @@ -17,7 +17,7 @@ describe("TimelinePanel", () => { render(<TimelinePanel {...props} />); userEvent.click(screen.getByText("Add an event")); - expect(props.onNewEventWithTimeline).toHaveBeenCalled(); + expect(props.onNewEvent).toHaveBeenCalled(); }); it("should allow creating an event within existing timelines", () => { @@ -48,7 +48,6 @@ const getProps = (opts?: Partial<TimelinePanelProps>): TimelinePanelProps => ({ timelines: [], collection: createMockCollection(), onNewEvent: jest.fn(), - onNewEventWithTimeline: jest.fn(), onEditEvent: jest.fn(), onArchiveEvent: jest.fn(), onToggleTimeline: jest.fn(), diff --git a/frontend/src/metabase/timelines/questions/containers/EditEventModal/EditEventModal.tsx b/frontend/src/metabase/timelines/questions/containers/EditEventModal/EditEventModal.tsx index 3cc30e040d4b1e27fe78a076dbe0234a6883fe09..67327b688429619727bb300ffe62f6f7ad100b51 100644 --- a/frontend/src/metabase/timelines/questions/containers/EditEventModal/EditEventModal.tsx +++ b/frontend/src/metabase/timelines/questions/containers/EditEventModal/EditEventModal.tsx @@ -1,6 +1,8 @@ import { connect } from "react-redux"; +import { t } from "ttag"; import _ from "underscore"; import TimelineEvents from "metabase/entities/timeline-events"; +import { addUndo } from "metabase/redux/undo"; import { TimelineEvent } from "metabase-types/api"; import { State } from "metabase-types/store"; import EditEventModal from "../../components/EditEventModal"; @@ -17,6 +19,7 @@ const timelineEventProps = { const mapDispatchToProps = (dispatch: any) => ({ onSubmit: async (event: TimelineEvent) => { await dispatch(TimelineEvents.actions.update(event)); + dispatch(addUndo({ message: t`Updated event` })); }, onArchive: async (event: TimelineEvent) => { await dispatch(TimelineEvents.actions.setArchived(event, true)); diff --git a/frontend/src/metabase/timelines/questions/containers/NewEventModal/NewEventModal.tsx b/frontend/src/metabase/timelines/questions/containers/NewEventModal/NewEventModal.tsx index 4b860e732b84eaa6ecf98a42cbc6739fcc42cfeb..90564ffac3b4d4af7fd270dd65dada80052fa47a 100644 --- a/frontend/src/metabase/timelines/questions/containers/NewEventModal/NewEventModal.tsx +++ b/frontend/src/metabase/timelines/questions/containers/NewEventModal/NewEventModal.tsx @@ -1,9 +1,11 @@ import { connect } from "react-redux"; +import { t } from "ttag"; import _ from "underscore"; import Collections, { ROOT_COLLECTION } from "metabase/entities/collections"; import Timelines from "metabase/entities/timelines"; import TimelineEvents from "metabase/entities/timeline-events"; -import { TimelineEvent } from "metabase-types/api"; +import { addUndo } from "metabase/redux/undo"; +import { Collection, TimelineEvent } from "metabase-types/api"; import { State } from "metabase-types/store"; import NewEventModal from "../../components/NewEventModal"; @@ -26,8 +28,14 @@ const collectionProps = { }; const mapDispatchToProps = (dispatch: any) => ({ - onSubmit: async (values: Partial<TimelineEvent>) => { - await dispatch(TimelineEvents.actions.create(values)); + onSubmit: async (values: Partial<TimelineEvent>, collection: Collection) => { + if (values.timeline_id) { + await dispatch(TimelineEvents.actions.create(values)); + } else { + await dispatch(Timelines.actions.createWithEvent(values, collection)); + } + + dispatch(addUndo({ message: t`Created event` })); }, }); diff --git a/frontend/src/metabase/timelines/questions/containers/NewEventWithTimelineModal/NewEventWithTimelineModal.tsx b/frontend/src/metabase/timelines/questions/containers/NewEventWithTimelineModal/NewEventWithTimelineModal.tsx deleted file mode 100644 index 6cab685b8bb86ea0dc14d812a2929f78931884fd..0000000000000000000000000000000000000000 --- a/frontend/src/metabase/timelines/questions/containers/NewEventWithTimelineModal/NewEventWithTimelineModal.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import { connect } from "react-redux"; -import _ from "underscore"; -import Collections, { ROOT_COLLECTION } from "metabase/entities/collections"; -import Timelines from "metabase/entities/timelines"; -import { Collection, TimelineEvent } from "metabase-types/api"; -import { State } from "metabase-types/store"; -import NewEventModal from "../../components/NewEventModal"; - -interface TimelinePanelProps { - collectionId?: number; -} - -const collectionProps = { - id: (state: State, props: TimelinePanelProps) => { - return props.collectionId ?? ROOT_COLLECTION.id; - }, -}; - -const mapDispatchToProps = (dispatch: any) => ({ - onSubmit: async (values: Partial<TimelineEvent>, collection: Collection) => { - await dispatch(Timelines.actions.createWithEvent(values, collection)); - }, -}); - -export default _.compose( - Collections.load(collectionProps), - connect(null, mapDispatchToProps), -)(NewEventModal); diff --git a/frontend/src/metabase/timelines/questions/containers/NewEventWithTimelineModal/index.ts b/frontend/src/metabase/timelines/questions/containers/NewEventWithTimelineModal/index.ts deleted file mode 100644 index dab3201d6950f52affba41b99b4a313472a6725c..0000000000000000000000000000000000000000 --- a/frontend/src/metabase/timelines/questions/containers/NewEventWithTimelineModal/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default } from "./NewEventWithTimelineModal"; diff --git a/frontend/src/metabase/timelines/questions/containers/TimelinePanel/TimelinePanel.tsx b/frontend/src/metabase/timelines/questions/containers/TimelinePanel/TimelinePanel.tsx index 5de09c3b2ce3c708671f690e923fa021b0865b05..2a8797a7c5d9f03c0ee5810c8966d2a859d3a44b 100644 --- a/frontend/src/metabase/timelines/questions/containers/TimelinePanel/TimelinePanel.tsx +++ b/frontend/src/metabase/timelines/questions/containers/TimelinePanel/TimelinePanel.tsx @@ -1,7 +1,6 @@ import { connect } from "react-redux"; import _ from "underscore"; import Collections, { ROOT_COLLECTION } from "metabase/entities/collections"; -import Timelines from "metabase/entities/timelines"; import TimelineEvents from "metabase/entities/timeline-events"; import { TimelineEvent } from "metabase-types/api"; import { State } from "metabase-types/store"; @@ -11,10 +10,6 @@ interface TimelinePanelProps { collectionId?: number; } -const timelineProps = { - query: { include: "events" }, -}; - const collectionProps = { id: (state: State, props: TimelinePanelProps) => { return props.collectionId ?? ROOT_COLLECTION.id; @@ -28,7 +23,6 @@ const mapDispatchToProps = (dispatch: any) => ({ }); export default _.compose( - Timelines.loadList(timelineProps), Collections.load(collectionProps), connect(null, mapDispatchToProps), )(TimelinePanel); diff --git a/frontend/src/metabase/visualizations/lib/timelines.js b/frontend/src/metabase/visualizations/lib/timelines.js index 8e88e78cd2acf9ca5281dca4117a9010f5117125..ecaa238160f310087335c8712f6e160481339130 100644 --- a/frontend/src/metabase/visualizations/lib/timelines.js +++ b/frontend/src/metabase/visualizations/lib/timelines.js @@ -9,18 +9,6 @@ const EVENT_ICON_MARGIN_TOP = 10; const EVENT_GROUP_COUNT_MARGIN_LEFT = 10; const EVENT_GROUP_COUNT_MARGIN_TOP = EVENT_ICON_MARGIN_TOP + 8; -function isAxisEvent(event, [xAxisMin, xAxisMax]) { - return ( - event.timestamp.isSame(xAxisMin) || - event.timestamp.isBetween(xAxisMin, xAxisMax) || - event.timestamp.isSame(xAxisMax) - ); -} - -function getAxisEvents(events, xDomain) { - return events.filter(event => isAxisEvent(event, xDomain)); -} - function getEventGroups(events, xInterval) { return _.groupBy(events, event => event.timestamp @@ -178,16 +166,12 @@ export function renderEvents( }, ) { const xAxis = getXAxis(chart); - if (!xAxis || !isTimeseries) { + if (!xAxis || !isTimeseries || !timelineEvents.length) { return; } - const events = getAxisEvents(timelineEvents, xDomain); - const eventGroups = getEventGroups(events, xInterval); + const eventGroups = getEventGroups(timelineEvents, xInterval); const eventTicks = getEventTicks(eventGroups); - if (!events.length) { - return; - } const eventAxis = getEventAxis(xAxis, xDomain, xInterval, eventTicks); renderEventTicks(chart, { @@ -202,9 +186,5 @@ export function renderEvents( } export function hasEventAxis({ timelineEvents = [], xDomain, isTimeseries }) { - if (!isTimeseries) { - return false; - } - - return timelineEvents.some(event => isAxisEvent(event, xDomain)); + return isTimeseries && timelineEvents.length > 0; }