diff --git a/src/plugins/data/common/constants.ts b/src/plugins/data/common/constants.ts index 3e94ae6333d86..da5124549dc30 100644 --- a/src/plugins/data/common/constants.ts +++ b/src/plugins/data/common/constants.ts @@ -35,4 +35,5 @@ export const UI_SETTINGS = { FILTERS_EDITOR_SUGGEST_VALUES: 'filterEditor:suggestValues', AUTOCOMPLETE_USE_TIMERANGE: 'autocomplete:useTimeRange', AUTOCOMPLETE_VALUE_SUGGESTION_METHOD: 'autocomplete:valueSuggestionMethod', + DATE_FORMAT: 'dateFormat', } as const; diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/exploratory_view.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/exploratory_view.tsx index f3f332b5094b6..ceb87429f9de4 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/exploratory_view.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/exploratory_view.tsx @@ -7,8 +7,8 @@ import { i18n } from '@kbn/i18n'; import React, { useEffect, useRef, useState } from 'react'; -import { EuiButtonEmpty, EuiPanel, EuiResizableContainer, EuiTitle } from '@elastic/eui'; import styled from 'styled-components'; +import { EuiButtonEmpty, EuiResizableContainer, EuiTitle, EuiPanel } from '@elastic/eui'; import { PanelDirection } from '@elastic/eui/src/components/resizable_container/types'; import { useKibana } from '../../../../../../../src/plugins/kibana_react/public'; import { ObservabilityPublicPluginsStart } from '../../../plugin'; @@ -20,6 +20,7 @@ import { useAppIndexPatternContext } from './hooks/use_app_index_pattern'; import { SeriesViews } from './views/series_views'; import { LensEmbeddable } from './lens_embeddable'; import { EmptyView } from './components/empty_view'; +import type { ChartTimeRange } from './header/last_updated'; export type PanelId = 'seriesPanel' | 'chartPanel'; @@ -37,7 +38,7 @@ export function ExploratoryView({ const [height, setHeight] = useState('100vh'); - const [lastUpdated, setLastUpdated] = useState(); + const [chartTimeRangeContext, setChartTimeRangeContext] = useState(); const [lensAttributes, setLensAttributes] = useState( null @@ -96,7 +97,10 @@ export function ExploratoryView({ {lens ? ( <> - + {lensAttributes ? ( ) : ( diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/header/chart_creation_info.test.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/header/chart_creation_info.test.tsx new file mode 100644 index 0000000000000..570362a63c33f --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/header/chart_creation_info.test.tsx @@ -0,0 +1,35 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { screen } from '@testing-library/dom'; +import { render } from '../rtl_helpers'; +import { ChartCreationInfo } from './chart_creation_info'; + +const info = { + to: 1634071132571, + from: 1633406400000, + lastUpdated: 1634071140788, +}; + +describe('ChartCreationInfo', () => { + it('renders chart creation info', async () => { + render(); + + expect(screen.getByText('Chart created')).toBeInTheDocument(); + expect(screen.getByText('Oct 12, 2021 4:39 PM')).toBeInTheDocument(); + expect(screen.getByText('Displaying from')).toBeInTheDocument(); + expect(screen.getByText('Oct 5, 2021 12:00 AM → Oct 12, 2021 4:38 PM')).toBeInTheDocument(); + }); + + it('does not display info when props are falsey', async () => { + render(); + + expect(screen.queryByText('Chart created')).not.toBeInTheDocument(); + expect(screen.queryByText('Displaying from')).not.toBeInTheDocument(); + }); +}); diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/header/chart_creation_info.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/header/chart_creation_info.tsx new file mode 100644 index 0000000000000..4814bc8d8630a --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/header/chart_creation_info.tsx @@ -0,0 +1,61 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import moment from 'moment'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { EuiFlexGroup, EuiFlexItem, EuiText, EuiSpacer } from '@elastic/eui'; +import type { ChartTimeRange } from './last_updated'; + +export function ChartCreationInfo(props: Partial) { + const dateFormat = 'lll'; + const from = moment(props.from).format(dateFormat); + const to = moment(props.to).format(dateFormat); + const created = moment(props.lastUpdated).format(dateFormat); + + return ( + <> + {props.lastUpdated && ( + <> + + + + + + + + {created} + + + + + )} + {props.to && props.from && ( + <> + + + + + + + + + {from} → {to} + + + + + )} + + ); +} diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/header/header.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/header/header.tsx index 181c8342b87af..22245f111293c 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/header/header.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/header/header.tsx @@ -10,16 +10,17 @@ import { i18n } from '@kbn/i18n'; import { EuiBetaBadge, EuiButton, EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui'; import { TypedLensByValueInput } from '../../../../../../lens/public'; import { useSeriesStorage } from '../hooks/use_series_storage'; -import { LastUpdated } from './last_updated'; import { ExpViewActionMenu } from '../components/action_menu'; import { useExpViewTimeRange } from '../hooks/use_time_range'; +import { LastUpdated } from './last_updated'; +import type { ChartTimeRange } from './last_updated'; interface Props { - lastUpdated?: number; + chartTimeRange?: ChartTimeRange; lensAttributes: TypedLensByValueInput['attributes'] | null; } -export function ExploratoryViewHeader({ lensAttributes, lastUpdated }: Props) { +export function ExploratoryViewHeader({ lensAttributes, chartTimeRange }: Props) { const { setLastRefresh } = useSeriesStorage(); const timeRange = useExpViewTimeRange(); @@ -46,7 +47,7 @@ export function ExploratoryViewHeader({ lensAttributes, lastUpdated }: Props) { - + setLastRefresh(Date.now())}> diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/header/last_updated.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/header/last_updated.tsx index c352ec0423dd8..bc82c48214a01 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/header/last_updated.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/header/last_updated.tsx @@ -6,14 +6,24 @@ */ import React, { useEffect, useState } from 'react'; -import { EuiIcon, EuiText } from '@elastic/eui'; import moment from 'moment'; +import styled from 'styled-components'; +import { EuiIcon, EuiText, EuiToolTip } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; +import { ChartCreationInfo } from './chart_creation_info'; + +export interface ChartTimeRange { + lastUpdated: number; + to: number; + from: number; +} interface Props { - lastUpdated?: number; + chartTimeRange?: ChartTimeRange; } -export function LastUpdated({ lastUpdated }: Props) { + +export function LastUpdated({ chartTimeRange }: Props) { + const { lastUpdated } = chartTimeRange || {}; const [refresh, setRefresh] = useState(() => Date.now()); useEffect(() => { @@ -39,7 +49,13 @@ export function LastUpdated({ lastUpdated }: Props) { return ( - + } + > + + {' '} ); } + +export const StyledToolTipWrapper = styled.div` + min-width: 30vw; +`; diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/lens_embeddable.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/lens_embeddable.tsx index 235790e72862c..b3ec7ee184f00 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/lens_embeddable.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/lens_embeddable.tsx @@ -13,14 +13,16 @@ import { useSeriesStorage } from './hooks/use_series_storage'; import { ObservabilityPublicPluginsStart } from '../../../plugin'; import { useKibana } from '../../../../../../../src/plugins/kibana_react/public'; import { useExpViewTimeRange } from './hooks/use_time_range'; +import { parseRelativeDate } from './components/date_range_picker'; +import type { ChartTimeRange } from './header/last_updated'; interface Props { lensAttributes: TypedLensByValueInput['attributes']; - setLastUpdated: Dispatch>; + setChartTimeRangeContext: Dispatch>; } export function LensEmbeddable(props: Props) { - const { lensAttributes, setLastUpdated } = props; + const { lensAttributes, setChartTimeRangeContext } = props; const { services: { lens, notifications }, @@ -35,8 +37,12 @@ export function LensEmbeddable(props: Props) { const timeRange = useExpViewTimeRange(); const onLensLoad = useCallback(() => { - setLastUpdated(Date.now()); - }, [setLastUpdated]); + setChartTimeRangeContext({ + lastUpdated: Date.now(), + to: parseRelativeDate(timeRange?.to || '').valueOf(), + from: parseRelativeDate(timeRange?.from || '').valueOf(), + }); + }, [setChartTimeRangeContext, timeRange]); const onBrushEnd = useCallback( ({ range }: { range: number[] }) => { diff --git a/x-pack/plugins/uptime/public/components/common/header/action_menu_content.tsx b/x-pack/plugins/uptime/public/components/common/header/action_menu_content.tsx index 789953258750b..26f9e28101ea4 100644 --- a/x-pack/plugins/uptime/public/components/common/header/action_menu_content.tsx +++ b/x-pack/plugins/uptime/public/components/common/header/action_menu_content.tsx @@ -51,7 +51,7 @@ export function ActionMenuContent(): React.ReactElement { allSeries: [ { dataType: 'synthetics', - seriesType: 'area_stacked', + seriesType: 'area', selectedMetricField: 'monitor.duration.us', time: { from: dateRangeStart, to: dateRangeEnd }, breakdown: monitorId ? 'observer.geo.name' : 'monitor.type',