in x-pack/platform/plugins/shared/ml/public/application/components/anomalies_table/anomalies_table_columns.js [57:379]
export function getColumns(
mlFieldFormatService,
items,
jobIds,
examplesByJobId,
isAggregatedData,
interval,
bounds,
showViewSeriesLink,
showRuleEditorFlyout,
itemIdToExpandedRowMap,
toggleRow,
filter,
influencerFilter,
sourceIndicesWithGeoFields
) {
const columns = [
{
name: (
<EuiScreenReaderOnly>
<p>
<FormattedMessage
id="xpack.ml.anomaliesTable.showDetailsColumn.screenReaderDescription"
defaultMessage="This column contains clickable controls for showing more details on each anomaly"
/>
</p>
</EuiScreenReaderOnly>
),
render: (item) => (
<EuiButtonIcon
onClick={() => toggleRow(item)}
iconType={itemIdToExpandedRowMap[item.rowId] ? 'arrowDown' : 'arrowRight'}
aria-label={
itemIdToExpandedRowMap[item.rowId]
? i18n.translate('xpack.ml.anomaliesTable.hideDetailsAriaLabel', {
defaultMessage: 'Hide details',
})
: i18n.translate('xpack.ml.anomaliesTable.showDetailsAriaLabel', {
defaultMessage: 'Show details',
})
}
data-row-id={item.rowId}
data-test-subj="mlAnomaliesListRowDetailsToggle"
/>
),
},
{
field: 'time',
'data-test-subj': 'mlAnomaliesListColumnTime',
name: i18n.translate('xpack.ml.anomaliesTable.timeColumnName', {
defaultMessage: 'Time',
}),
dataType: 'date',
scope: 'row',
render: (date) => renderTime(date, interval),
textOnly: true,
sortable: true,
},
{
field: 'severity',
'data-test-subj': 'mlAnomaliesListColumnSeverity',
name: (
<span>
{i18n.translate('xpack.ml.anomaliesTable.severityColumnName', {
defaultMessage: 'Severity',
})}
<EuiIconTip
size="s"
color="subdued"
type="questionInCircle"
className="eui-alignTop"
content={i18n.translate('xpack.ml.overview.anomalyDetection.tableSeverityTooltip', {
defaultMessage:
'A normalized score between 0-100, which indicates the relative significance of the anomaly record results.',
})}
/>
</span>
),
render: (score, item) => (
<SeverityCell score={score} isMultiBucketAnomaly={isMultiBucketAnomaly(item.source)} />
),
sortable: true,
},
{
field: 'detector',
'data-test-subj': 'mlAnomaliesListColumnDetector',
name: i18n.translate('xpack.ml.anomaliesTable.detectorColumnName', {
defaultMessage: 'Detector',
}),
render: (detectorDescription, item) => (
<DetectorCell detectorDescription={detectorDescription} numberOfRules={item.rulesLength} />
),
textOnly: true,
sortable: true,
},
];
if (items.some((item) => item.entityValue !== undefined)) {
columns.push({
field: 'entityValue',
'data-test-subj': 'mlAnomaliesListColumnFoundFor',
name: i18n.translate('xpack.ml.anomaliesTable.entityValueColumnName', {
defaultMessage: 'Found for',
}),
render: (entityValue, item) => (
<EntityCell
entityName={item.entityName}
entityValue={entityValue}
filter={filter}
wrapText={true}
/>
),
textOnly: true,
sortable: true,
});
}
if (items.some((item) => item.influencers !== undefined)) {
columns.push({
field: 'influencers',
'data-test-subj': 'mlAnomaliesListColumnInfluencers',
name: i18n.translate('xpack.ml.anomaliesTable.influencersColumnName', {
defaultMessage: 'Influenced by',
}),
render: (influencers) => (
<InfluencersCell
limit={INFLUENCERS_LIMIT}
influencers={influencers}
influencerFilter={influencerFilter}
/>
),
textOnly: true,
sortable: true,
});
}
// Map the additional 'sort' fields to the actual, typical and description
// fields to ensure sorting is done correctly on the underlying metric value
// and not on e.g. the actual values array as a String.
if (items.some((item) => item.actual !== undefined)) {
columns.push({
field: 'actualSort',
'data-test-subj': 'mlAnomaliesListColumnActual',
name: (
<span>
{i18n.translate('xpack.ml.anomaliesTable.actualSortColumnName', {
defaultMessage: 'Actual',
})}
<EuiIconTip
size="s"
color="subdued"
type="questionInCircle"
className="eui-alignTop"
content={i18n.translate('xpack.ml.overview.anomalyDetection.tableActualTooltip', {
defaultMessage: 'The actual values in the anomaly record results.',
})}
/>
</span>
),
render: (actual, item) => {
const fieldFormat = mlFieldFormatService.getFieldFormat(
item.jobId,
item.source.detector_index
);
return (
<AnomalyValueDisplay
value={item.actual}
function={item.source.function}
fieldFormat={fieldFormat}
record={item.source}
/>
);
},
sortable: true,
className: 'eui-textBreakNormal',
});
}
if (items.some((item) => item.typical !== undefined)) {
columns.push({
field: 'typicalSort',
'data-test-subj': 'mlAnomaliesListColumnTypical',
name: (
<span>
{i18n.translate('xpack.ml.anomaliesTable.typicalSortColumnName', {
defaultMessage: 'Typical',
})}
<EuiIconTip
size="s"
color="subdued"
type="questionInCircle"
className="eui-alignTop"
content={i18n.translate('xpack.ml.overview.anomalyDetection.tableTypicalTooltip', {
defaultMessage: 'The typical values in the anomaly record results.',
})}
/>
</span>
),
render: (typical, item) => {
const fieldFormat = mlFieldFormatService.getFieldFormat(
item.jobId,
item.source.detector_index
);
return (
<AnomalyValueDisplay
value={item.typical}
function={item.source.function}
fieldFormat={fieldFormat}
record={item.source}
/>
);
},
sortable: true,
className: 'eui-textBreakNormal',
});
// Assume that if we are showing typical, there will be an actual too,
// so we can add a column to describe how actual compares to typical.
const nonTimeOfDayOrWeek = items.some((item) => {
const summaryRecFunc = item.source.function;
return summaryRecFunc !== 'time_of_day' && summaryRecFunc !== 'time_of_week';
});
if (nonTimeOfDayOrWeek === true) {
columns.push({
field: 'metricDescriptionSort',
'data-test-subj': 'mlAnomaliesListColumnDescription',
name: i18n.translate('xpack.ml.anomaliesTable.metricDescriptionSortColumnName', {
defaultMessage: 'Description',
}),
render: (metricDescriptionSort, item) => (
<DescriptionCell actual={item.actual} typical={item.typical} />
),
textOnly: true,
sortable: true,
});
}
}
if (jobIds && jobIds.length > 1) {
columns.push({
field: 'jobId',
'data-test-subj': 'mlAnomaliesListColumnJobID',
name: i18n.translate('xpack.ml.anomaliesTable.jobIdColumnName', {
defaultMessage: 'Job ID',
}),
sortable: true,
});
}
const showExamples = items.some((item) => item.entityName === 'mlcategory');
if (showExamples === true) {
columns.push({
'data-test-subj': 'mlAnomaliesListColumnCategoryExamples',
name: i18n.translate('xpack.ml.anomaliesTable.categoryExamplesColumnName', {
defaultMessage: 'Category examples',
}),
truncateText: true,
render: (item) => {
const examples = get(examplesByJobId, [item.jobId, item.entityValue], []);
return (
<EuiLink
css={{
width: '100%',
}}
onClick={() => toggleRow(item, ANOMALIES_TABLE_TABS.CATEGORY_EXAMPLES)}
>
{examples.map((example, i) => {
return (
<span
key={`example${i}`}
css={{
display: 'block',
whiteSpace: 'nowrap',
overflow: 'hidden',
textOverflow: 'ellipsis',
}}
>
{example}
</span>
);
})}
</EuiLink>
);
},
width: '13%',
});
}
const showLinks = items.some((item) =>
showLinksMenuForItem(item, showViewSeriesLink, sourceIndicesWithGeoFields)
);
if (showLinks === true) {
columns.push({
'data-test-subj': 'mlAnomaliesListColumnAction',
name: i18n.translate('xpack.ml.anomaliesTable.actionsColumnName', {
defaultMessage: 'Actions',
}),
render: (item) => {
if (showLinksMenuForItem(item, showViewSeriesLink, sourceIndicesWithGeoFields) === true) {
return (
<LinksMenu
anomaly={item}
bounds={bounds}
showViewSeriesLink={showViewSeriesLink}
isAggregatedData={isAggregatedData}
interval={interval}
showRuleEditorFlyout={showRuleEditorFlyout}
sourceIndicesWithGeoFields={sourceIndicesWithGeoFields}
/>
);
} else {
return null;
}
},
});
}
return columns;
}