Skip to content

Commit

Permalink
[ML] Match Data Visualizer/Field stats table content with the popover (
Browse files Browse the repository at this point in the history
  • Loading branch information
qn895 authored Sep 19, 2022
1 parent 5b368ca commit 7b36f54
Show file tree
Hide file tree
Showing 9 changed files with 204 additions and 97 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import { css } from '@emotion/react';
import { useDiscoverServices } from '../../../../hooks/use_discover_services';
import { FIELD_STATISTICS_LOADED } from './constants';
import type { GetStateReturn } from '../../services/discover_state';
import { AvailableFields$, DataRefetch$ } from '../../hooks/use_saved_search';
import { AvailableFields$, DataRefetch$, DataTotalHits$ } from '../../hooks/use_saved_search';

export interface DataVisualizerGridEmbeddableInput extends EmbeddableInput {
dataView: DataView;
Expand All @@ -38,6 +38,7 @@ export interface DataVisualizerGridEmbeddableInput extends EmbeddableInput {
onAddFilter?: (field: DataViewField | string, value: string, type: '+' | '-') => void;
sessionId?: string;
fieldsToFetch?: string[];
totalDocuments?: number;
}
export interface DataVisualizerGridEmbeddableOutput extends EmbeddableOutput {
showDistributions?: boolean;
Expand Down Expand Up @@ -89,6 +90,7 @@ export interface FieldStatisticsTableProps {
savedSearchRefetch$?: DataRefetch$;
availableFields$?: AvailableFields$;
searchSessionId?: string;
savedSearchDataTotalHits$?: DataTotalHits$;
}

export const FieldStatisticsTable = (props: FieldStatisticsTableProps) => {
Expand All @@ -104,6 +106,7 @@ export const FieldStatisticsTable = (props: FieldStatisticsTableProps) => {
trackUiMetric,
savedSearchRefetch$,
searchSessionId,
savedSearchDataTotalHits$,
} = props;
const services = useDiscoverServices();
const [embeddable, setEmbeddable] = useState<
Expand Down Expand Up @@ -157,6 +160,9 @@ export const FieldStatisticsTable = (props: FieldStatisticsTableProps) => {
onAddFilter,
sessionId: searchSessionId,
fieldsToFetch: availableFields$?.getValue().fields,
totalDocuments: savedSearchDataTotalHits$
? savedSearchDataTotalHits$.getValue()?.result
: undefined,
});
embeddable.reload();
}
Expand All @@ -170,6 +176,7 @@ export const FieldStatisticsTable = (props: FieldStatisticsTableProps) => {
onAddFilter,
searchSessionId,
availableFields$,
savedSearchDataTotalHits$,
]);

useEffect(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ export const DiscoverMainContent = ({
onAddFilter={!isPlainRecord ? onAddFilter : undefined}
trackUiMetric={trackUiMetric}
savedSearchRefetch$={savedSearchRefetch$}
savedSearchDataTotalHits$={savedSearchData$.totalHits$}
/>
)}
</EuiFlexGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export interface GeoPointExample {
}

export interface FieldVisStats {
totalDocuments?: number;
error?: Error;
cardinality?: number;
count?: number;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,18 @@ export const IndexBasedDataVisualizerExpandedRow = ({
dataView,
combinedQuery,
onAddFilter,
totalDocuments,
}: {
item: FieldVisConfig;
dataView: DataView | undefined;
combinedQuery: CombinedQuery;
totalDocuments?: number;
/**
* Callback to add a filter to filter bar
*/
onAddFilter?: (field: DataViewField | string, value: string, type: '+' | '-') => void;
}) => {
const config = item;
const config = { ...item, stats: { ...item.stats, totalDocuments } };
const { loading, type, existsInDocs, fieldName } = config;

function getCardContent() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,19 @@ import {
EuiFlexGroup,
EuiFlexItem,
EuiProgress,
EuiSpacer,
EuiText,
EuiButtonIcon,
EuiSpacer,
} from '@elastic/eui';

import { FormattedMessage } from '@kbn/i18n-react';

import classNames from 'classnames';
import { i18n } from '@kbn/i18n';
import { DataViewField } from '@kbn/data-views-plugin/public';
import { ES_FIELD_TYPES, KBN_FIELD_TYPES } from '@kbn/data-plugin/common';
import { css } from '@emotion/react';
import { useDataVisualizerKibana } from '../../../kibana_context';
import { roundToDecimalPlace, kibanaFieldFormat } from '../utils';
import { ExpandedRowFieldHeader } from '../stats_table/components/expanded_row_field_header';
import { FieldVisStats } from '../../../../../common/types';
Expand All @@ -43,17 +46,78 @@ function getPercentLabel(docCount: number, topValuesSampleSize: number): string
}

export const TopValues: FC<Props> = ({ stats, fieldFormat, barColor, compressed, onAddFilter }) => {
const {
services: { data },
} = useDataVisualizerKibana();

const { fieldFormats } = data;

if (stats === undefined || !stats.topValues) return null;
const {
topValues,
topValuesSampleSize,
topValuesSamplerShardSize,
count,
isTopValuesSampled,
fieldName,
sampleCount,
topValuesSamplerShardSize,
} = stats;

const totalDocuments = stats.totalDocuments;

const progressBarMax = isTopValuesSampled === true ? topValuesSampleSize : count;

const topValuesOtherCount =
(progressBarMax ?? 0) -
(topValues ? topValues.map((value) => value.doc_count).reduce((v, acc) => acc + v) : 0);

const countsElement =
totalDocuments !== undefined ? (
<EuiText color="subdued" size="xs">
{isTopValuesSampled ? (
<FormattedMessage
id="xpack.dataVisualizer.dataGrid.field.topValues.calculatedFromSampleRecordsLabel"
defaultMessage="Calculated from {sampledDocumentsFormatted} sample {sampledDocuments, plural, one {record} other {records}}."
values={{
sampledDocuments: sampleCount,
sampledDocumentsFormatted: (
<strong>
{fieldFormats
.getDefaultInstance(KBN_FIELD_TYPES.NUMBER, [ES_FIELD_TYPES.INTEGER])
.convert(sampleCount)}
</strong>
),
}}
/>
) : (
<FormattedMessage
id="xpack.dataVisualizer.dataGrid.field.topValues.calculatedFromTotalRecordsLabel"
defaultMessage="Calculated from {totalDocumentsFormatted} {totalDocuments, plural, one {record} other {records}}."
values={{
totalDocuments,
totalDocumentsFormatted: (
<strong>
{fieldFormats
.getDefaultInstance(KBN_FIELD_TYPES.NUMBER, [ES_FIELD_TYPES.INTEGER])
.convert(totalDocuments ?? 0)}
</strong>
),
}}
/>
)}
</EuiText>
) : (
<EuiText size="xs" textAlign={'center'}>
<FormattedMessage
id="xpack.dataVisualizer.dataGrid.field.topValues.calculatedFromSampleDescription"
defaultMessage="Calculated from sample of {topValuesSamplerShardSize} documents per shard"
values={{
topValuesSamplerShardSize,
}}
/>
</EuiText>
);

return (
<ExpandedRowPanel
dataTestSubj={'dataVisualizerFieldDataTopValues'}
Expand All @@ -70,96 +134,125 @@ export const TopValues: FC<Props> = ({ stats, fieldFormat, barColor, compressed,
data-test-subj="dataVisualizerFieldDataTopValuesContent"
className={classNames('fieldDataTopValuesContainer', 'dvTopValues__wrapper')}
>
{Array.isArray(topValues) &&
topValues.map((value) => (
<EuiFlexGroup gutterSize="xs" alignItems="center" key={value.key}>
<EuiFlexItem data-test-subj="dataVisualizerFieldDataTopValueBar">
<EuiProgress
value={value.doc_count}
max={progressBarMax}
color={barColor}
size="xs"
label={kibanaFieldFormat(value.key, fieldFormat)}
className={classNames('eui-textTruncate', 'topValuesValueLabelContainer')}
valueText={`${value.doc_count}${
progressBarMax !== undefined
? ` (${getPercentLabel(value.doc_count, progressBarMax)})`
: ''
}`}
/>
</EuiFlexItem>
{fieldName !== undefined && value.key !== undefined && onAddFilter !== undefined ? (
<>
<EuiButtonIcon
iconSize="s"
iconType="plusInCircle"
onClick={() =>
onAddFilter(
fieldName,
typeof value.key === 'number' ? value.key.toString() : value.key,
'+'
)
}
aria-label={i18n.translate(
'xpack.dataVisualizer.dataGrid.field.addFilterAriaLabel',
{
defaultMessage: 'Filter for {fieldName}: "{value}"',
values: { fieldName, value: value.key },
}
)}
data-test-subj={`dvFieldDataTopValuesAddFilterButton-${value.key}-${value.key}`}
style={{
minHeight: 'auto',
minWidth: 'auto',
paddingRight: 2,
paddingLeft: 2,
paddingTop: 0,
paddingBottom: 0,
}}
{Array.isArray(topValues)
? topValues.map((value) => (
<EuiFlexGroup gutterSize="xs" alignItems="center" key={value.key}>
<EuiFlexItem data-test-subj="dataVisualizerFieldDataTopValueBar">
<EuiProgress
value={value.doc_count}
max={progressBarMax}
color={barColor}
size="xs"
label={kibanaFieldFormat(value.key, fieldFormat)}
className={classNames('eui-textTruncate', 'topValuesValueLabelContainer')}
valueText={`${value.doc_count}${
progressBarMax !== undefined
? ` (${getPercentLabel(value.doc_count, progressBarMax)})`
: ''
}`}
/>
<EuiButtonIcon
iconSize="s"
iconType="minusInCircle"
onClick={() =>
onAddFilter(
fieldName,
typeof value.key === 'number' ? value.key.toString() : value.key,
'-'
)
}
aria-label={i18n.translate(
'xpack.dataVisualizer.dataGrid.field.removeFilterAriaLabel',
{
defaultMessage: 'Filter out {fieldName}: "{value}"',
values: { fieldName, value: value.key },
</EuiFlexItem>
{fieldName !== undefined && value.key !== undefined && onAddFilter !== undefined ? (
<div
css={css`
width: 48px;
`}
>
<EuiButtonIcon
iconSize="s"
iconType="plusInCircle"
onClick={() =>
onAddFilter(
fieldName,
typeof value.key === 'number' ? value.key.toString() : value.key,
'+'
)
}
)}
data-test-subj={`dvFieldDataTopValuesExcludeFilterButton-${value.key}-${value.key}`}
style={{
minHeight: 'auto',
minWidth: 'auto',
paddingTop: 0,
paddingBottom: 0,
paddingRight: 2,
paddingLeft: 2,
}}
aria-label={i18n.translate(
'xpack.dataVisualizer.dataGrid.field.addFilterAriaLabel',
{
defaultMessage: 'Filter for {fieldName}: "{value}"',
values: { fieldName, value: value.key },
}
)}
data-test-subj={`dvFieldDataTopValuesAddFilterButton-${value.key}-${value.key}`}
style={{
minHeight: 'auto',
minWidth: 'auto',
paddingRight: 2,
paddingLeft: 2,
paddingTop: 0,
paddingBottom: 0,
}}
/>
<EuiButtonIcon
iconSize="s"
iconType="minusInCircle"
onClick={() =>
onAddFilter(
fieldName,
typeof value.key === 'number' ? value.key.toString() : value.key,
'-'
)
}
aria-label={i18n.translate(
'xpack.dataVisualizer.dataGrid.field.removeFilterAriaLabel',
{
defaultMessage: 'Filter out {fieldName}: "{value}"',
values: { fieldName, value: value.key },
}
)}
data-test-subj={`dvFieldDataTopValuesExcludeFilterButton-${value.key}-${value.key}`}
style={{
minHeight: 'auto',
minWidth: 'auto',
paddingTop: 0,
paddingBottom: 0,
paddingRight: 2,
paddingLeft: 2,
}}
/>
</div>
) : null}
</EuiFlexGroup>
))
: null}
{topValuesOtherCount > 0 ? (
<EuiFlexGroup gutterSize="xs" alignItems="center" key="other">
<EuiFlexItem data-test-subj="dataVisualizerFieldDataTopValueBar">
<EuiProgress
value={topValuesOtherCount}
max={progressBarMax}
color={barColor}
size="xs"
label={
<FormattedMessage
id="xpack.dataVisualizer.dataGrid.field.topValuesOtherLabel"
defaultMessage="Other"
/>
</>
) : null}
</EuiFlexGroup>
))}
}
className={classNames('eui-textTruncate', 'topValuesValueLabelContainer')}
valueText={`${topValuesOtherCount}${
progressBarMax !== undefined
? ` (${getPercentLabel(topValuesOtherCount, progressBarMax)})`
: ''
}`}
/>
</EuiFlexItem>
{onAddFilter ? (
<div
css={css`
width: 48px;
`}
/>
) : null}
</EuiFlexGroup>
) : null}

{isTopValuesSampled === true && (
<Fragment>
<EuiSpacer size="xs" />
<EuiText size="xs" textAlign={'center'}>
<FormattedMessage
id="xpack.dataVisualizer.dataGrid.field.topValues.calculatedFromSampleDescription"
defaultMessage="Calculated from sample of {topValuesSamplerShardSize} documents per shard"
values={{
topValuesSamplerShardSize,
}}
/>
</EuiText>
{countsElement}
</Fragment>
)}
</div>
Expand Down
Loading

0 comments on commit 7b36f54

Please sign in to comment.