mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[Synthetics] Fix date format for Waterfall and TLS Certificates (#162099)
Fixes #161499 ## Summary The PR adjusts date format in waterfall flyout and TLS Certificates page to the common date format used in Synthetics (via `useDateFormat` hook). |Before|After| |:---:|:---:| |<img width="438" alt="Screenshot 2023-07-18 at 00 25 48" src="f671c5f0
-042d-4fa1-910e-bc9deb85d1dd">|<img width="438" alt="Screenshot 2023-07-17 at 23 40 59" src="bf19344f
-e82e-4188-97a2-65d045544245">| |Before|After| |:---:|:---:| |<img width="1545" alt="Screenshot 2023-07-18 at 00 25 05" src="ddeb4186
-c1e1-4eff-b8ed-4ea6755b3e09">|<img width="1544" alt="Screenshot 2023-07-17 at 23 53 55" src="b35a34ea
-3d16-4479-82db-0a2f808d0a6c">|
This commit is contained in:
parent
82eaddd86c
commit
37521304af
5 changed files with 48 additions and 22 deletions
|
@ -9,6 +9,7 @@ import React from 'react';
|
|||
import moment from 'moment';
|
||||
import { Direction, EuiBasicTable } from '@elastic/eui';
|
||||
import { Cert, CertMonitor, CertResult } from '../../../../../common/runtime_types';
|
||||
import { useDateFormat } from '../../../../hooks/use_date_format';
|
||||
import { CertStatus } from './cert_status';
|
||||
import { CertMonitors } from './cert_monitors';
|
||||
import * as labels from './translations';
|
||||
|
@ -46,6 +47,7 @@ interface Props {
|
|||
}
|
||||
|
||||
export const CertificateList: React.FC<Props> = ({ page, certificates, sort, onChange }) => {
|
||||
const dateFormatter = useDateFormat();
|
||||
const pagination = {
|
||||
pageIndex: page.index,
|
||||
pageSize: page.size,
|
||||
|
@ -80,7 +82,7 @@ export const CertificateList: React.FC<Props> = ({ page, certificates, sort, onC
|
|||
name: labels.VALID_UNTIL_COL,
|
||||
field: 'not_after',
|
||||
sortable: true,
|
||||
render: (value: string) => moment(value).format('L LT'),
|
||||
render: dateFormatter,
|
||||
},
|
||||
{
|
||||
name: labels.AGE_COL,
|
||||
|
|
|
@ -14,6 +14,7 @@ import {
|
|||
} from './data_formatting';
|
||||
import { MimeType, FriendlyFlyoutLabels, FriendlyTimingLabels, Timings, Metadata } from './types';
|
||||
import { WaterfallDataEntry } from './types';
|
||||
import type { DateFormatter } from '../../../../../../hooks/use_date_format';
|
||||
import { mockMoment } from '../../../../utils/formatting/test_helpers';
|
||||
import { NetworkEvent } from '../../../../../../../common/runtime_types';
|
||||
|
||||
|
@ -231,12 +232,14 @@ describe('Palettes', () => {
|
|||
});
|
||||
|
||||
describe('getSeriesAndDomain', () => {
|
||||
let mockDateFormatter: DateFormatter;
|
||||
beforeEach(() => {
|
||||
mockMoment();
|
||||
mockDateFormatter = (dateStr?: string) => (dateStr ? moment(dateStr).format() : '');
|
||||
});
|
||||
|
||||
it('formats series timings', () => {
|
||||
const actual = getSeriesAndDomain(networkItems);
|
||||
const actual = getSeriesAndDomain(networkItems, false, mockDateFormatter);
|
||||
expect(actual.series).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Object {
|
||||
|
@ -409,7 +412,7 @@ describe('getSeriesAndDomain', () => {
|
|||
});
|
||||
|
||||
it('handles series formatting when only total timing values are available', () => {
|
||||
const { series } = getSeriesAndDomain(networkItemsWithoutFullTimings);
|
||||
const { series } = getSeriesAndDomain(networkItemsWithoutFullTimings, false, mockDateFormatter);
|
||||
expect(series).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Object {
|
||||
|
@ -536,7 +539,7 @@ describe('getSeriesAndDomain', () => {
|
|||
});
|
||||
|
||||
it('handles series formatting when there is no timing information available', () => {
|
||||
const { series } = getSeriesAndDomain(networkItemsWithoutAnyTimings);
|
||||
const { series } = getSeriesAndDomain(networkItemsWithoutAnyTimings, false, mockDateFormatter);
|
||||
expect(series).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Object {
|
||||
|
@ -555,7 +558,7 @@ describe('getSeriesAndDomain', () => {
|
|||
});
|
||||
|
||||
it('handles formatting when there is no timing information available', () => {
|
||||
const actual = getSeriesAndDomain(networkItemsWithoutAnyTimings);
|
||||
const actual = getSeriesAndDomain(networkItemsWithoutAnyTimings, false, mockDateFormatter);
|
||||
expect(actual).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"domain": Object {
|
||||
|
@ -636,7 +639,11 @@ describe('getSeriesAndDomain', () => {
|
|||
});
|
||||
|
||||
it('handles formatting when the timings object is undefined', () => {
|
||||
const { series } = getSeriesAndDomain(networkItemsWithoutTimingsObject);
|
||||
const { series } = getSeriesAndDomain(
|
||||
networkItemsWithoutTimingsObject,
|
||||
false,
|
||||
mockDateFormatter
|
||||
);
|
||||
expect(series).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Object {
|
||||
|
@ -653,7 +660,11 @@ describe('getSeriesAndDomain', () => {
|
|||
});
|
||||
|
||||
it('handles formatting when mime type is not mapped to a specific mime type bucket', () => {
|
||||
const { series } = getSeriesAndDomain(networkItemsWithUnknownMimeType);
|
||||
const { series } = getSeriesAndDomain(
|
||||
networkItemsWithUnknownMimeType,
|
||||
false,
|
||||
mockDateFormatter
|
||||
);
|
||||
/* verify that raw mime type appears in the tooltip config and that
|
||||
* the colour is mapped to mime type other */
|
||||
const contentDownloadingConfigItem = series.find((item: WaterfallDataEntry) => {
|
||||
|
@ -682,7 +693,7 @@ describe('getSeriesAndDomain', () => {
|
|||
[FriendlyFlyoutLabels[Metadata.ResourceSize], '1.000 KB'],
|
||||
[FriendlyFlyoutLabels[Metadata.IP], '104.18.8.22'],
|
||||
])('handles metadata details formatting', (name, value) => {
|
||||
const { metadata } = getSeriesAndDomain(networkItems);
|
||||
const { metadata } = getSeriesAndDomain(networkItems, false, mockDateFormatter);
|
||||
const metadataEntry = metadata[0];
|
||||
expect(
|
||||
metadataEntry.details.find((item) => item.value === value && item.name === name)
|
||||
|
@ -690,7 +701,7 @@ describe('getSeriesAndDomain', () => {
|
|||
});
|
||||
|
||||
it('handles metadata headers formatting', () => {
|
||||
const { metadata } = getSeriesAndDomain(networkItems);
|
||||
const { metadata } = getSeriesAndDomain(networkItems, false, mockDateFormatter);
|
||||
const metadataEntry = metadata[0];
|
||||
metadataEntry.requestHeaders?.forEach((header) => {
|
||||
expect(header).toEqual({ name: header.name, value: header.value });
|
||||
|
@ -701,7 +712,7 @@ describe('getSeriesAndDomain', () => {
|
|||
});
|
||||
|
||||
it('handles certificate formatting', () => {
|
||||
const { metadata } = getSeriesAndDomain([networkItems[0]]);
|
||||
const { metadata } = getSeriesAndDomain([networkItems[0]], false, mockDateFormatter);
|
||||
const metadataEntry = metadata[0];
|
||||
expect(metadataEntry.certificates).toEqual([
|
||||
{ name: 'Issuer', value: networkItems[0].certificates?.issuer },
|
||||
|
@ -715,13 +726,15 @@ describe('getSeriesAndDomain', () => {
|
|||
});
|
||||
it('counts the total number of highlighted items', () => {
|
||||
// only one CSS file in this array of network Items
|
||||
const actual = getSeriesAndDomain(networkItems, false, '', ['stylesheet']);
|
||||
const actual = getSeriesAndDomain(networkItems, false, mockDateFormatter, '', ['stylesheet']);
|
||||
expect(actual.totalHighlightedRequests).toBe(1);
|
||||
});
|
||||
|
||||
it('adds isHighlighted to waterfall entry when filter matches', () => {
|
||||
// only one CSS file in this array of network Items
|
||||
const { series } = getSeriesAndDomain(networkItems, false, '', ['stylesheet']);
|
||||
const { series } = getSeriesAndDomain(networkItems, false, mockDateFormatter, '', [
|
||||
'stylesheet',
|
||||
]);
|
||||
series.forEach((item) => {
|
||||
if (item.x === 0) {
|
||||
expect(item.config.isHighlighted).toBe(true);
|
||||
|
@ -733,7 +746,7 @@ describe('getSeriesAndDomain', () => {
|
|||
|
||||
it('adds isHighlighted to waterfall entry when query matches', () => {
|
||||
// only the second item matches this query
|
||||
const { series } = getSeriesAndDomain(networkItems, false, 'director', []);
|
||||
const { series } = getSeriesAndDomain(networkItems, false, mockDateFormatter, 'director', []);
|
||||
series.forEach((item) => {
|
||||
if (item.x === 1) {
|
||||
expect(item.config.isHighlighted).toBe(true);
|
||||
|
|
|
@ -6,9 +6,9 @@
|
|||
*/
|
||||
|
||||
import { euiPaletteColorBlind } from '@elastic/eui';
|
||||
import moment from 'moment';
|
||||
|
||||
import { MarkerItems } from '../../step_waterfall_chart/waterfall/context/waterfall_context';
|
||||
import type { DateFormatter } from '../../../../../../hooks/use_date_format';
|
||||
import { NetworkEvent } from '../../../../../../../common/runtime_types';
|
||||
import { WaterfallData, WaterfallMetadata } from './types';
|
||||
import {
|
||||
|
@ -128,6 +128,7 @@ export const getFilterMatcher = (filters: string[] | undefined): ItemMatcher =>
|
|||
export const getSeriesAndDomain = (
|
||||
items: NetworkEvent[],
|
||||
onlyHighlighted = false,
|
||||
dateFormatter: DateFormatter,
|
||||
query?: string,
|
||||
activeFilters?: string[],
|
||||
markerItems?: MarkerItems
|
||||
|
@ -159,7 +160,7 @@ export const getSeriesAndDomain = (
|
|||
const mimeTypeColour = getColourForMimeType(item.mimeType);
|
||||
const offsetValue = getValueForOffset(item);
|
||||
let currentOffset = offsetValue - zeroOffset;
|
||||
metadata.push(formatMetadata({ item, index, requestStart: currentOffset }));
|
||||
metadata.push(formatMetadata({ item, index, requestStart: currentOffset, dateFormatter }));
|
||||
const isHighlighted = isHighlightedItem(item, queryMatcher, filterMatcher);
|
||||
if (isHighlighted) {
|
||||
totalHighlightedRequests++;
|
||||
|
@ -270,10 +271,12 @@ const formatMetadata = ({
|
|||
item,
|
||||
index,
|
||||
requestStart,
|
||||
dateFormatter,
|
||||
}: {
|
||||
item: NetworkEvent;
|
||||
index: number;
|
||||
requestStart: number;
|
||||
dateFormatter: DateFormatter;
|
||||
}) => {
|
||||
const {
|
||||
certificates,
|
||||
|
@ -301,13 +304,11 @@ const formatMetadata = ({
|
|||
},
|
||||
{
|
||||
name: FriendlyFlyoutLabels[Metadata.CertificateIssueDate],
|
||||
value: certificates.validFrom
|
||||
? moment(certificates.validFrom).format('L LT')
|
||||
: undefined,
|
||||
value: certificates.validFrom ? dateFormatter(certificates.validFrom) : undefined,
|
||||
},
|
||||
{
|
||||
name: FriendlyFlyoutLabels[Metadata.CertificateExpiryDate],
|
||||
value: certificates.validTo ? moment(certificates.validTo).format('L LT') : undefined,
|
||||
value: certificates.validTo ? dateFormatter(certificates.validTo) : undefined,
|
||||
},
|
||||
{
|
||||
name: FriendlyFlyoutLabels[Metadata.CertificateSubject],
|
||||
|
|
|
@ -9,6 +9,7 @@ import React, { useCallback, useMemo, useState } from 'react';
|
|||
import useLocalStorage from 'react-use/lib/useLocalStorage';
|
||||
import { EuiHealth } from '@elastic/eui';
|
||||
import { JourneyStep, NetworkEvent } from '../../../../../../../common/runtime_types';
|
||||
import { useDateFormat } from '../../../../../../hooks/use_date_format';
|
||||
import { getSeriesAndDomain, getSidebarItems } from '../../common/network_data/data_formatting';
|
||||
import { SidebarItem, LegendItem } from '../../common/network_data/types';
|
||||
import { RenderItem, WaterfallDataEntry } from '../../common/network_data/types';
|
||||
|
@ -54,9 +55,17 @@ export const WaterfallChartWrapper: React.FC<Props> = ({
|
|||
|
||||
const hasFilters = activeFilters.length > 0;
|
||||
|
||||
const dateFormatter = useDateFormat();
|
||||
const { series, domain, metadata, totalHighlightedRequests } = useMemo(() => {
|
||||
return getSeriesAndDomain(networkData, onlyHighlighted, query, activeFilters, markerItems);
|
||||
}, [networkData, query, activeFilters, onlyHighlighted, markerItems]);
|
||||
return getSeriesAndDomain(
|
||||
networkData,
|
||||
onlyHighlighted,
|
||||
dateFormatter,
|
||||
query,
|
||||
activeFilters,
|
||||
markerItems
|
||||
);
|
||||
}, [networkData, dateFormatter, query, activeFilters, onlyHighlighted, markerItems]);
|
||||
|
||||
const sidebarItems = useMemo(() => {
|
||||
return getSidebarItems(networkData, onlyHighlighted ?? false, query, activeFilters);
|
||||
|
|
|
@ -9,7 +9,8 @@ import moment from 'moment';
|
|||
import { useEffect } from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
export function useDateFormat(): (timestamp?: string) => string {
|
||||
export type DateFormatter = (timestamp?: string) => string;
|
||||
export function useDateFormat(): DateFormatter {
|
||||
const kibanaLocale = i18n.getLocale();
|
||||
const clientLocale = navigator.language;
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue