Skip to content

Commit

Permalink
Add tests, put number in a Formatted entry
Browse files Browse the repository at this point in the history
  • Loading branch information
Brent Kimmel committed Oct 8, 2020
1 parent 773d592 commit e2e420d
Show file tree
Hide file tree
Showing 2 changed files with 114 additions and 22 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import { compactNotationParts } from './submenu';

describe('The Resolver node pills number presentation', () => {
describe('When given a small number under 1000', () => {
it('does not change the presentation of small numbers', () => {
expect(compactNotationParts(1)).toEqual([1, '', '']);
expect(compactNotationParts(100)).toEqual([100, '', '']);
expect(compactNotationParts(999)).toEqual([999, '', '']);
});
});
describe('When given a number greater or equal to 1000 but less than 1000000', () => {
it('presents the number as untis of k', () => {
expect(compactNotationParts(1000)).toEqual([1, 'k', '']);
expect(compactNotationParts(1001)).toEqual([1, 'k', '+']);
expect(compactNotationParts(10000)).toEqual([10, 'k', '']);
expect(compactNotationParts(10001)).toEqual([10, 'k', '+']);
expect(compactNotationParts(999999)).toEqual([999, 'k', '+']);
});
});
describe('When given a number greater or equal to 1000000 but less than 1000000000', () => {
it('presents the number as untis of M', () => {
expect(compactNotationParts(1000000)).toEqual([1, 'M', '']);
expect(compactNotationParts(1000001)).toEqual([1, 'M', '+']);
expect(compactNotationParts(10000000)).toEqual([10, 'M', '']);
expect(compactNotationParts(10000001)).toEqual([10, 'M', '+']);
expect(compactNotationParts(999999999)).toEqual([999, 'M', '+']);
});
});
describe('When given a number greater or equal to 1000000000 but less than 1000000000000', () => {
it('presents the number as untis of B', () => {
expect(compactNotationParts(1000000000)).toEqual([1, 'B', '']);
expect(compactNotationParts(1000000001)).toEqual([1, 'B', '+']);
expect(compactNotationParts(10000000000)).toEqual([10, 'B', '']);
expect(compactNotationParts(10000000001)).toEqual([10, 'B', '+']);
expect(compactNotationParts(999999999999)).toEqual([999, 'B', '+']);
});
});
describe('When given a number greater or equal to 1000000000000', () => {
it('presents the number as untis of T', () => {
expect(compactNotationParts(1000000000000)).toEqual([1, 'T', '']);
expect(compactNotationParts(1000000000001)).toEqual([1, 'T', '+']);
expect(compactNotationParts(10000000000000)).toEqual([10, 'T', '']);
expect(compactNotationParts(10000000000001)).toEqual([10, 'T', '+']);
expect(compactNotationParts(999999999999999)).toEqual([999, 'T', '+']);
expect(compactNotationParts(9999999999999990)).toEqual([9999, 'T', '+']);
});
});
});
82 changes: 60 additions & 22 deletions x-pack/plugins/security_solution/public/resolver/view/submenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

import { i18n } from '@kbn/i18n';
import React, { useMemo } from 'react';
import { EuiI18nNumber } from '@elastic/eui';
import { FormattedMessage } from 'react-intl';
import { ResolverNodeStats } from '../../../common/endpoint/types';
import { useRelatedEventByCategoryNavigation } from './use_related_event_by_category_navigation';
import { useColors } from './use_colors';
Expand Down Expand Up @@ -38,27 +38,56 @@ interface ResolverSubmenuOption {
action: () => unknown;
prefix?: number | JSX.Element;
}

/**
This can be achieved in an easier way by using `notation="compact"` on
FormattedNumber in the future, but it does not work at present (10/2020)
*/
function compactNotation(num) {
if(!Number.isFinite(num)){
return num;
}
let scale = 1;
while (scale < 1e12 && num / (scale * 1000) >= 1) {
scale *= 1000;
}
const prefixes = {
'1': '',
'1000': 'k',
'1000000': 'M',
'1000000000': 'B',
'1000000000000': 'T',

/**
* Until browser support accomodates the `notation="compact"` feature of Intl.NumberFormat...
* exported for testing
* @param num The number to format
* @returns [Scalar of compact notation (k,M,B,T), mantissa, overflow indicator (+)]
*/
export function compactNotationParts(num: number): [number, string, string] {
if (!Number.isFinite(num)) {
return [num, '', ''];
}
let scale = 1;
while (scale < 1e12 && num / (scale * 1000) >= 1) {
scale *= 1000;
}
const compactPrefixTranslations = {
compactThousands: i18n.translate('xpack.securitySolution.endpoint.resolver.compactThousands', {
defaultMessage: 'k',
}),
compactMillions: i18n.translate('xpack.securitySolution.endpoint.resolver.compactMillions', {
defaultMessage: 'M',
}),

compactBillions: i18n.translate('xpack.securitySolution.endpoint.resolver.compactBillions', {
defaultMessage: 'B',
}),

compactTrillions: i18n.translate('xpack.securitySolution.endpoint.resolver.compactTrillions', {
defaultMessage: 'T',
}),
};
const prefixMap: Map<number, string> = new Map([
[1, ''],
[1000, compactPrefixTranslations.compactThousands],
[1000000, compactPrefixTranslations.compactMillions],
[1000000000, compactPrefixTranslations.compactBillions],
[1000000000000, compactPrefixTranslations.compactTrillions],
]);
const overflowIndicator = i18n.translate(
'xpack.securitySolution.endpoint.resolver.compactOverflow',
{
defaultMessage: '+',
}
return [prefixes[`${scale}`], Math.floor(num / scale), ((num / scale) % 1) > Number.EPSILON]
);
const prefix = prefixMap.get(scale) ?? '';
return [
Math.floor(num / scale),
prefix,
(num / scale) % 1 > Number.EPSILON ? overflowIndicator : '',
];
}

export type ResolverSubmenuOptionList = ResolverSubmenuOption[] | string;
Expand Down Expand Up @@ -92,8 +121,17 @@ export const NodeSubMenuComponents = React.memo(
return [];
} else {
return Object.entries(relatedEventStats.events.byCategory).map(([category, total]) => {
const [mantissa, scale, hasRemainder] = compactNotationParts(total || 0);
const prefix = (
<FormattedMessage
id="xpack.securitySolution.endpoint.resolver.node.pillNumber"
description=""
defaultMessage="{mantissa}{scale}{hasRemainder}"
values={{ mantissa, scale, hasRemainder }}
/>
);
return {
prefix: <EuiI18nNumber value={total || 0} />,
prefix,
optionTitle: category,
action: () => relatedEventCallbacks(category),
};
Expand Down

0 comments on commit e2e420d

Please sign in to comment.