[Security Solution][Data Quality Dashboard]: migrate styled-component… (#205559)

…s to Emotion

Addresses #205449

This PR migrates `ecs_data_quality_dashboard` package from
`styled-components` to `emotion`. In the process we also convert the
`kbn/ui-theme` json tokens to `euiTheme` counterparts.

Additionally we decorate root `babel-jest`
[transform](211d4a6889/packages/kbn-test/src/jest/transforms/babel.js)
locally in `security_solution/public/overview` and
`ecs_data_quality_dashboard` package folder to include
`@emotion/babel-preset-css-prop`.

The reason for local `babel-jest` transforms is that root `babel-jest`
transform doesn't include `@emotion/babel-preset-css-prop` which is
necessary for proper compilation of emotion css prop in tests. Without
it there is a warning

![image](https://github.com/user-attachments/assets/c75b9827-a731-469c-a762-ff04f86cd80e)
appearing in every test that tests a component that uses css prop with
theme function passed into it. Other use cases seem to be compiling fine
without this babel preset. But theme callback is a valid way of using
emotion so we shouldn't avoid using it just because it's not added
properly to the test compilation step. Hence I am adding it locally to
`ecs_data_quality_dashboard` package and
`security_solution/public/overview`.

---------

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Karen Grigoryan 2025-01-06 14:48:11 +01:00 committed by GitHub
parent e04b20018a
commit 72980e1676
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
69 changed files with 1050 additions and 697 deletions

View file

@ -179,39 +179,6 @@ module.exports = {
/x-pack[\/\\]solutions[\/\\]security[\/\\]packages[\/\\]data_table[\/\\]components[\/\\]data_table[\/\\]index.tsx/,
/x-pack[\/\\]solutions[\/\\]security[\/\\]packages[\/\\]data_table[\/\\]components[\/\\]toolbar[\/\\]unit[\/\\]styles.tsx/,
/x-pack[\/\\]solutions[\/\\]security[\/\\]packages[\/\\]data_table[\/\\]mock[\/\\]test_providers.tsx/,
/x-pack[\/\\]solutions[\/\\]security[\/\\]packages[\/\\]ecs_data_quality_dashboard[\/\\]impl[\/\\]data_quality_panel[\/\\]actions[\/\\]chat[\/\\]index.tsx/,
/x-pack[\/\\]solutions[\/\\]security[\/\\]packages[\/\\]ecs_data_quality_dashboard[\/\\]impl[\/\\]data_quality_panel[\/\\]actions[\/\\]styles.tsx/,
/x-pack[\/\\]solutions[\/\\]security[\/\\]packages[\/\\]ecs_data_quality_dashboard[\/\\]impl[\/\\]data_quality_panel[\/\\]data_quality_details[\/\\]ilm_phases_empty_prompt[\/\\]index.tsx/,
/x-pack[\/\\]solutions[\/\\]security[\/\\]packages[\/\\]ecs_data_quality_dashboard[\/\\]impl[\/\\]data_quality_panel[\/\\]data_quality_details[\/\\]indices_details[\/\\]index.tsx/,
/x-pack[\/\\]solutions[\/\\]security[\/\\]packages[\/\\]ecs_data_quality_dashboard[\/\\]impl[\/\\]data_quality_panel[\/\\]data_quality_details[\/\\]indices_details[\/\\]pattern[\/\\]historical_results_tour[\/\\]index.tsx/,
/x-pack[\/\\]solutions[\/\\]security[\/\\]packages[\/\\]ecs_data_quality_dashboard[\/\\]impl[\/\\]data_quality_panel[\/\\]data_quality_details[\/\\]indices_details[\/\\]pattern[\/\\]index_check_flyout[\/\\]check_fields_tabs[\/\\]index.tsx/,
/x-pack[\/\\]solutions[\/\\]security[\/\\]packages[\/\\]ecs_data_quality_dashboard[\/\\]impl[\/\\]data_quality_panel[\/\\]data_quality_details[\/\\]indices_details[\/\\]pattern[\/\\]index_check_flyout[\/\\]historical_results[\/\\]historical_results_list[\/\\]historical_result[\/\\]styles.tsx/,
/x-pack[\/\\]solutions[\/\\]security[\/\\]packages[\/\\]ecs_data_quality_dashboard[\/\\]impl[\/\\]data_quality_panel[\/\\]data_quality_details[\/\\]indices_details[\/\\]pattern[\/\\]index_check_flyout[\/\\]historical_results[\/\\]historical_results_list[\/\\]styles.tsx/,
/x-pack[\/\\]solutions[\/\\]security[\/\\]packages[\/\\]ecs_data_quality_dashboard[\/\\]impl[\/\\]data_quality_panel[\/\\]data_quality_details[\/\\]indices_details[\/\\]pattern[\/\\]index_check_flyout[\/\\]historical_results[\/\\]styles.tsx/,
/x-pack[\/\\]solutions[\/\\]security[\/\\]packages[\/\\]ecs_data_quality_dashboard[\/\\]impl[\/\\]data_quality_panel[\/\\]data_quality_details[\/\\]indices_details[\/\\]pattern[\/\\]index_check_flyout[\/\\]index_invalid_values[\/\\]index.tsx/,
/x-pack[\/\\]solutions[\/\\]security[\/\\]packages[\/\\]ecs_data_quality_dashboard[\/\\]impl[\/\\]data_quality_panel[\/\\]data_quality_details[\/\\]indices_details[\/\\]pattern[\/\\]index_check_flyout[\/\\]index_stats_panel[\/\\]index.tsx/,
/x-pack[\/\\]solutions[\/\\]security[\/\\]packages[\/\\]ecs_data_quality_dashboard[\/\\]impl[\/\\]data_quality_panel[\/\\]data_quality_details[\/\\]indices_details[\/\\]pattern[\/\\]index_check_flyout[\/\\]latest_results[\/\\]latest_check_fields[\/\\]ecs_compliant_tab[\/\\]index.tsx/,
/x-pack[\/\\]solutions[\/\\]security[\/\\]packages[\/\\]ecs_data_quality_dashboard[\/\\]impl[\/\\]data_quality_panel[\/\\]data_quality_details[\/\\]indices_details[\/\\]pattern[\/\\]index_check_flyout[\/\\]latest_results[\/\\]latest_check_fields[\/\\]index.tsx/,
/x-pack[\/\\]solutions[\/\\]security[\/\\]packages[\/\\]ecs_data_quality_dashboard[\/\\]impl[\/\\]data_quality_panel[\/\\]data_quality_details[\/\\]indices_details[\/\\]pattern[\/\\]index_check_flyout[\/\\]latest_results[\/\\]latest_check_fields[\/\\]sticky_actions[\/\\]index.tsx/,
/x-pack[\/\\]solutions[\/\\]security[\/\\]packages[\/\\]ecs_data_quality_dashboard[\/\\]impl[\/\\]data_quality_panel[\/\\]data_quality_details[\/\\]indices_details[\/\\]pattern[\/\\]index_check_flyout[\/\\]same_family[\/\\]index.tsx/,
/x-pack[\/\\]solutions[\/\\]security[\/\\]packages[\/\\]ecs_data_quality_dashboard[\/\\]impl[\/\\]data_quality_panel[\/\\]data_quality_details[\/\\]indices_details[\/\\]pattern[\/\\]index_check_flyout[\/\\]styles.tsx/,
/x-pack[\/\\]solutions[\/\\]security[\/\\]packages[\/\\]ecs_data_quality_dashboard[\/\\]impl[\/\\]data_quality_panel[\/\\]data_quality_details[\/\\]indices_details[\/\\]pattern[\/\\]index_result_badge[\/\\]index.tsx/,
/x-pack[\/\\]solutions[\/\\]security[\/\\]packages[\/\\]ecs_data_quality_dashboard[\/\\]impl[\/\\]data_quality_panel[\/\\]data_quality_details[\/\\]indices_details[\/\\]pattern[\/\\]pattern_summary[\/\\]pattern_label[\/\\]ilm_phase_counts[\/\\]index.tsx/,
/x-pack[\/\\]solutions[\/\\]security[\/\\]packages[\/\\]ecs_data_quality_dashboard[\/\\]impl[\/\\]data_quality_panel[\/\\]data_quality_details[\/\\]indices_details[\/\\]pattern[\/\\]styles.tsx/,
/x-pack[\/\\]solutions[\/\\]security[\/\\]packages[\/\\]ecs_data_quality_dashboard[\/\\]impl[\/\\]data_quality_panel[\/\\]data_quality_details[\/\\]indices_details[\/\\]pattern[\/\\]summary_table[\/\\]utils[\/\\]columns.tsx/,
/x-pack[\/\\]solutions[\/\\]security[\/\\]packages[\/\\]ecs_data_quality_dashboard[\/\\]impl[\/\\]data_quality_panel[\/\\]data_quality_details[\/\\]storage_details[\/\\]storage_treemap[\/\\]chart_legend_item[\/\\]index.tsx/,
/x-pack[\/\\]solutions[\/\\]security[\/\\]packages[\/\\]ecs_data_quality_dashboard[\/\\]impl[\/\\]data_quality_panel[\/\\]data_quality_details[\/\\]storage_details[\/\\]storage_treemap[\/\\]index.tsx/,
/x-pack[\/\\]solutions[\/\\]security[\/\\]packages[\/\\]ecs_data_quality_dashboard[\/\\]impl[\/\\]data_quality_panel[\/\\]data_quality_details[\/\\]storage_details[\/\\]storage_treemap[\/\\]no_data[\/\\]index.tsx/,
/x-pack[\/\\]solutions[\/\\]security[\/\\]packages[\/\\]ecs_data_quality_dashboard[\/\\]impl[\/\\]data_quality_panel[\/\\]data_quality_summary[\/\\]ilm_phase_filter[\/\\]styles.tsx/,
/x-pack[\/\\]solutions[\/\\]security[\/\\]packages[\/\\]ecs_data_quality_dashboard[\/\\]impl[\/\\]data_quality_panel[\/\\]data_quality_summary[\/\\]index.tsx/,
/x-pack[\/\\]solutions[\/\\]security[\/\\]packages[\/\\]ecs_data_quality_dashboard[\/\\]impl[\/\\]data_quality_panel[\/\\]data_quality_summary[\/\\]summary_actions[\/\\]check_all[\/\\]index.tsx/,
/x-pack[\/\\]solutions[\/\\]security[\/\\]packages[\/\\]ecs_data_quality_dashboard[\/\\]impl[\/\\]data_quality_panel[\/\\]data_quality_summary[\/\\]summary_actions[\/\\]check_status[\/\\]errors_popover[\/\\]errors_viewer[\/\\]index.tsx/,
/x-pack[\/\\]solutions[\/\\]security[\/\\]packages[\/\\]ecs_data_quality_dashboard[\/\\]impl[\/\\]data_quality_panel[\/\\]data_quality_summary[\/\\]summary_actions[\/\\]check_status[\/\\]errors_popover[\/\\]index.tsx/,
/x-pack[\/\\]solutions[\/\\]security[\/\\]packages[\/\\]ecs_data_quality_dashboard[\/\\]impl[\/\\]data_quality_panel[\/\\]data_quality_summary[\/\\]summary_actions[\/\\]index.tsx/,
/x-pack[\/\\]solutions[\/\\]security[\/\\]packages[\/\\]ecs_data_quality_dashboard[\/\\]impl[\/\\]data_quality_panel[\/\\]mock[\/\\]test_providers[\/\\]test_providers.tsx/,
/x-pack[\/\\]solutions[\/\\]security[\/\\]packages[\/\\]ecs_data_quality_dashboard[\/\\]impl[\/\\]data_quality_panel[\/\\]stat[\/\\]index.tsx/,
/x-pack[\/\\]solutions[\/\\]security[\/\\]packages[\/\\]ecs_data_quality_dashboard[\/\\]impl[\/\\]data_quality_panel[\/\\]stats_rollup[\/\\]index.tsx/,
/x-pack[\/\\]solutions[\/\\]security[\/\\]packages[\/\\]ecs_data_quality_dashboard[\/\\]impl[\/\\]data_quality_panel[\/\\]styles.tsx/,
/x-pack[\/\\]solutions[\/\\]security[\/\\]plugins[\/\\]lists[\/\\]public[\/\\]exceptions[\/\\]components[\/\\]and_or_badge[\/\\]rounded_badge.tsx/,
/x-pack[\/\\]solutions[\/\\]security[\/\\]plugins[\/\\]lists[\/\\]public[\/\\]exceptions[\/\\]components[\/\\]and_or_badge[\/\\]rounded_badge_antenna.tsx/,
/x-pack[\/\\]solutions[\/\\]security[\/\\]plugins[\/\\]lists[\/\\]public[\/\\]exceptions[\/\\]components[\/\\]builder[\/\\]and_badge.tsx/,

View file

@ -98,7 +98,7 @@ module.exports = {
// A map from regular expressions to paths to transformers
transform: {
'^.+\\.(js|tsx?)$': '<rootDir>/packages/kbn-test/src/jest/transforms/babel.js',
'^.+\\.(js|tsx?)$': '<rootDir>/packages/kbn-test/src/jest/transforms/babel/index.js',
'^.+\\.(txt|html)?$': '<rootDir>/packages/kbn-test/src/jest/transforms/raw.js',
'^.+\\.peggy?$': '<rootDir>/packages/kbn-test/src/jest/transforms/peggy.js',
},

View file

@ -0,0 +1,13 @@
/*
* 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", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/
const babelJest = require('babel-jest');
const transformerConfig = require('./transformer_config');
module.exports = babelJest.default.createTransformer(transformerConfig);

View file

@ -7,9 +7,7 @@
* License v3.0 only", or the "Server Side Public License, v 1".
*/
const babelJest = require('babel-jest');
module.exports = babelJest.default.createTransformer({
module.exports = {
presets: [
[
require.resolve('@kbn/babel-preset/node_preset'),
@ -22,4 +20,4 @@ module.exports = babelJest.default.createTransformer({
},
],
],
});
};

View file

@ -10,7 +10,7 @@ import { EuiIcon, EuiLink } from '@elastic/eui';
import { useDataQualityContext } from '../../data_quality_context';
import { useAddToNewCase } from './hooks/use_add_to_new_case';
import { StyledLinkText } from '../styles';
import { linkTextCss } from '../styles';
import { ADD_TO_NEW_CASE } from '../../translations';
interface Props {
@ -42,10 +42,10 @@ const AddToNewCaseActionComponent: React.FC<Props> = ({ markdownComment }) => {
disabled={addToNewCaseDisabled || disableAll}
onClick={addToNewCaseContextMenuOnClick}
>
<StyledLinkText>
<span css={linkTextCss}>
<EuiIcon type="listAdd" />
{ADD_TO_NEW_CASE}
</StyledLinkText>
</span>
</EuiLink>
);
};

View file

@ -7,7 +7,8 @@
import React, { FC, useCallback } from 'react';
import { NewChat } from '@kbn/elastic-assistant';
import styled from 'styled-components';
import { css } from '@emotion/react';
import { useEuiTheme } from '@elastic/eui';
import { AssistantIcon } from '@kbn/ai-assistant-icon';
import {
@ -19,10 +20,16 @@ import {
import { useDataQualityContext } from '../../data_quality_context';
import { ASK_ASSISTANT } from './translations';
const StyledLinkText = styled.span`
display: flex;
gap: ${({ theme }) => theme.eui.euiSizeXS};
`;
const useStyles = () => {
const { euiTheme } = useEuiTheme();
return {
linkText: css({
display: 'flex',
gap: euiTheme.size.xs,
}),
};
};
interface Props {
markdownComment: string;
@ -30,8 +37,10 @@ interface Props {
}
const ChatActionComponent: FC<Props> = ({ indexName, markdownComment }) => {
const styles = useStyles();
const { isAssistantEnabled } = useDataQualityContext();
const getPromptContext = useCallback(async () => markdownComment, [markdownComment]);
return (
<NewChat
asLink={true}
@ -44,10 +53,10 @@ const ChatActionComponent: FC<Props> = ({ indexName, markdownComment }) => {
isAssistantEnabled={isAssistantEnabled}
iconType={null}
>
<StyledLinkText>
<span css={styles.linkText}>
<AssistantIcon />
{ASK_ASSISTANT}
</StyledLinkText>
</span>
</NewChat>
);
};

View file

@ -10,7 +10,7 @@ import { EuiIcon, EuiLink, copyToClipboard } from '@elastic/eui';
import { useDataQualityContext } from '../../data_quality_context';
import { COPIED_RESULTS_TOAST_TITLE, COPY_TO_CLIPBOARD } from '../../translations';
import { StyledLinkText } from '../styles';
import { linkTextCss } from '../styles';
interface Props {
markdownComment: string;
@ -33,10 +33,10 @@ const CopyToClipboardActionComponent: React.FC<Props> = ({ markdownComment }) =>
disabled={ilmPhases.length === 0}
onClick={onCopy}
>
<StyledLinkText>
<span css={linkTextCss}>
<EuiIcon type="copyClipboard" />
{COPY_TO_CLIPBOARD}
</StyledLinkText>
</span>
</EuiLink>
);
};

View file

@ -5,9 +5,10 @@
* 2.0.
*/
import styled from 'styled-components';
import { UseEuiTheme } from '@elastic/eui';
import { CSSObject } from '@emotion/react';
export const StyledLinkText = styled.span`
display: flex;
gap: ${({ theme }) => theme.eui.euiSizeS};
`;
export const linkTextCss = ({ euiTheme }: UseEuiTheme): CSSObject => ({
display: 'flex',
gap: euiTheme.size.s,
});

View file

@ -5,9 +5,9 @@
* 2.0.
*/
import { EuiEmptyPrompt, EuiSpacer, EuiText, EuiTitle } from '@elastic/eui';
import { EuiEmptyPrompt, EuiSpacer, EuiText, EuiTitle, useEuiTheme } from '@elastic/eui';
import React, { useMemo } from 'react';
import styled from 'styled-components';
import { css } from '@emotion/react';
import {
COLD_DESCRIPTION,
@ -23,16 +23,21 @@ import {
} from '../../translations';
import * as i18n from './translations';
const Ul = styled.ul`
text-align: left;
`;
const Li = styled.ul`
margin-bottom: ${({ theme }) => theme.eui.euiSizeS};
text-align: left;
`;
const useStyles = () => {
const { euiTheme } = useEuiTheme();
return {
ul: css({
textAlign: 'left',
}),
li: css({
marginBottom: euiTheme.size.s,
textAlign: 'left',
}),
};
};
const IlmPhasesEmptyPromptComponent: React.FC = () => {
const styles = useStyles();
const title = useMemo(() => <h2>{i18n.TITLE}</h2>, []);
const body = useMemo(() => <p>{i18n.BODY}</p>, []);
const footer = useMemo(
@ -44,23 +49,23 @@ const IlmPhasesEmptyPromptComponent: React.FC = () => {
<EuiSpacer size="s" />
<Ul>
<Li>
<ul css={styles.ul}>
<li css={styles.li}>
<strong>{HOT}</strong>
{': '}
{HOT_DESCRIPTION}
</Li>
<Li>
</li>
<li css={styles.li}>
<strong>{WARM}</strong>
{': '}
{WARM_DESCRIPTION}
</Li>
<Li>
</li>
<li css={styles.li}>
<strong>{UNMANAGED}</strong>
{': '}
{UNMANAGED_DESCRIPTION}
</Li>
</Ul>
</li>
</ul>
<EuiSpacer size="m" />
@ -74,21 +79,21 @@ const IlmPhasesEmptyPromptComponent: React.FC = () => {
<EuiSpacer size="s" />
<Ul>
<Li>
<ul css={styles.ul}>
<li css={styles.li}>
<strong>{COLD}</strong>
{': '}
{COLD_DESCRIPTION}
</Li>
<Li>
</li>
<li css={styles.li}>
<strong>{FROZEN}</strong>
{': '}
{FROZEN_DESCRIPTION}
</Li>
</Ul>
</li>
</ul>
</div>
),
[]
[styles.li, styles.ul]
);
return <EuiEmptyPrompt body={body} footer={footer} title={title} />;

View file

@ -5,9 +5,9 @@
* 2.0.
*/
import { EuiFlexItem } from '@elastic/eui';
import { EuiFlexItem, useEuiTheme } from '@elastic/eui';
import React, { useState, useCallback, useEffect } from 'react';
import styled from 'styled-components';
import { css } from '@emotion/react';
import { useResultsRollupContext } from '../../contexts/results_rollup_context';
import { Pattern } from './pattern';
@ -15,13 +15,18 @@ import { SelectedIndex } from '../../types';
import { useDataQualityContext } from '../../data_quality_context';
import { useIsHistoricalResultsTourActive } from './hooks/use_is_historical_results_tour_active';
const StyledPatternWrapperFlexItem = styled(EuiFlexItem)`
margin-bottom: ${({ theme }) => theme.eui.euiSize};
const useStyles = () => {
const { euiTheme } = useEuiTheme();
&:last-child {
margin-bottom: 0;
}
`;
return {
patternWrapperFlexItem: css({
marginBottom: euiTheme.size.base,
':last-child': {
marginBottom: 0,
},
}),
};
};
export interface Props {
chartSelectedIndex: SelectedIndex | null;
@ -32,6 +37,7 @@ const IndicesDetailsComponent: React.FC<Props> = ({
chartSelectedIndex,
setChartSelectedIndex,
}) => {
const styles = useStyles();
const { patternRollups, patternIndexNames } = useResultsRollupContext();
const { patterns } = useDataQualityContext();
@ -73,7 +79,7 @@ const IndicesDetailsComponent: React.FC<Props> = ({
return (
<div data-test-subj="indicesDetails">
{patterns.map((pattern) => (
<StyledPatternWrapperFlexItem grow={false} key={pattern}>
<EuiFlexItem css={styles.patternWrapperFlexItem} grow={false} key={pattern}>
<Pattern
indexNames={patternIndexNames[pattern]}
pattern={pattern}
@ -91,7 +97,7 @@ const IndicesDetailsComponent: React.FC<Props> = ({
// when surrounding accordions get toggled and affect the layout
{...(pattern === firstOpenNonEmptyPattern && { openPatternsUpdatedAt })}
/>
</StyledPatternWrapperFlexItem>
</EuiFlexItem>
))}
</div>
);

View file

@ -7,7 +7,7 @@
import React, { FC, useEffect, useState } from 'react';
import { EuiButton, EuiButtonEmpty, EuiText, EuiTourStep } from '@elastic/eui';
import styled from 'styled-components';
import { css } from '@emotion/react';
import { HISTORICAL_RESULTS_TOUR_SELECTOR_KEY } from '../constants';
import { CLOSE, INTRODUCING_DATA_QUALITY_HISTORY, TRY_IT, VIEW_PAST_RESULTS } from './translations';
@ -20,9 +20,11 @@ export interface Props {
zIndex?: number;
}
const StyledText = styled(EuiText)`
margin-block-start: -10px;
`;
const styles = {
text: css({
marginBlockStart: '-10px',
}),
};
export const HistoricalResultsTour: FC<Props> = ({
anchorSelectorValue,
@ -52,9 +54,9 @@ export const HistoricalResultsTour: FC<Props> = ({
return (
<EuiTourStep
content={
<StyledText size="s">
<EuiText css={styles.text} size="s">
<p>{VIEW_PAST_RESULTS}</p>
</StyledText>
</EuiText>
}
data-test-subj="historicalResultsTour"
isStepOpen={isOpen}

View file

@ -5,7 +5,7 @@
* 2.0.
*/
import { EuiSpacer, useGeneratedHtmlId } from '@elastic/eui';
import { EuiAccordion, EuiSpacer, useGeneratedHtmlId } from '@elastic/eui';
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { ErrorEmptyPrompt } from './error_empty_prompt';
@ -21,7 +21,7 @@ import type { PatternRollup, SelectedIndex, SortConfig } from '../../../types';
import { useIlmExplain } from './hooks/use_ilm_explain';
import { useStats } from './hooks/use_stats';
import { useDataQualityContext } from '../../../data_quality_context';
import { PatternAccordion, PatternAccordionChildren } from './styles';
import { patternAccordionChildrenCss, patternAccordionCss } from './styles';
import { IndexCheckFlyout } from './index_check_flyout';
import { useResultsRollupContext } from '../../../contexts/results_rollup_context';
import { useIndicesCheckContext } from '../../../contexts/indices_check_context';
@ -318,7 +318,8 @@ const PatternComponent: React.FC<Props> = ({
return (
<div data-test-subj={`${pattern}PatternPanel`}>
<HistoricalResultsContext.Provider value={historicalResultsContextValue}>
<PatternAccordion
<EuiAccordion
css={patternAccordionCss}
id={patternComponentAccordionId}
forceState={isAccordionOpen ? 'open' : 'closed'}
onToggle={handleAccordionToggle}
@ -337,7 +338,7 @@ const PatternComponent: React.FC<Props> = ({
/>
}
>
<PatternAccordionChildren>
<div css={patternAccordionChildrenCss}>
{!loading && pattern.includes(':') && (
<>
<RemoteClustersCallout />
@ -404,8 +405,8 @@ const PatternComponent: React.FC<Props> = ({
/>
</div>
)}
</PatternAccordionChildren>
</PatternAccordion>
</div>
</EuiAccordion>
{isFlyoutVisible ? (
<IndexCheckFlyout
pattern={pattern}

View file

@ -14,24 +14,26 @@ import {
EuiSpacer,
EuiToolTip,
} from '@elastic/eui';
import styled, { StyledComponent } from 'styled-components';
import { css } from '@emotion/react';
import { CheckFieldsTab, CheckFieldsTabId } from './types';
const StyledTabFlexGroup = styled(EuiFlexGroup)`
width: 100%;
`;
const styles = {
tabFlexGroup: css({
width: '100%',
}),
const StyledTabFlexItem = styled.div`
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
`;
tabFlexItem: css({
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
overflow: 'hidden',
}),
const StyledBadge = styled(EuiBadge)`
text-align: right;
cursor: pointer;
`;
badge: css({
textAlign: 'right',
cursor: 'pointer',
}),
};
interface CheckFieldsSingleButtonGroupProps {
onChange: (id: string) => void;
@ -46,10 +48,7 @@ export interface Props {
tabs: CheckFieldsTab[];
renderButtonGroup: (
props: CheckFieldsSingleButtonGroupProps
) => React.ReactElement<
EuiButtonGroupProps,
StyledComponent<typeof EuiButtonGroup, {}, {}, never> | typeof EuiButtonGroup
>;
) => React.ReactElement<EuiButtonGroupProps, typeof EuiButtonGroup>;
}
const CheckFieldsTabsComponent: React.FC<Props> = ({ tabs, renderButtonGroup }) => {
@ -58,7 +57,11 @@ const CheckFieldsTabsComponent: React.FC<Props> = ({ tabs, renderButtonGroup })
tabs.map((tab) => ({
id: tab.id,
name: tab.name,
append: <StyledBadge color={tab.badgeColor ?? 'hollow'}>{tab.badgeCount ?? 0}</StyledBadge>,
append: (
<EuiBadge css={styles.badge} color={tab.badgeColor ?? 'hollow'}>
{tab.badgeCount ?? 0}
</EuiBadge>
),
content: tab.content ?? null,
disabled: Boolean(tab.disabled),
...(tab.disabled && { disabledReason: tab.disabledReason }),
@ -72,16 +75,17 @@ const CheckFieldsTabsComponent: React.FC<Props> = ({ tabs, renderButtonGroup })
() =>
checkFieldsTabs.map((tab) => {
let label = (
<StyledTabFlexGroup
<EuiFlexGroup
css={styles.tabFlexGroup}
responsive={false}
justifyContent="center"
gutterSize="s"
alignItems="center"
title={tab.name}
>
<StyledTabFlexItem>{tab.name}</StyledTabFlexItem>
<div css={styles.tabFlexItem}>{tab.name}</div>
{tab.append}
</StyledTabFlexGroup>
</EuiFlexGroup>
);
if (tab.disabled && tab.disabledReason) {

View file

@ -9,7 +9,7 @@ import { EuiCode, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import React from 'react';
import { EMPTY_PLACEHOLDER } from '../../../../../constants';
import { CodeSuccess } from '../../../../../styles';
import { codeSuccessCss } from '../../../../../styles';
import type { AllowedValue } from '../../../../../types';
interface Props {
@ -23,7 +23,7 @@ const EcsAllowedValuesComponent: React.FC<Props> = ({ allowedValues }) =>
<EuiFlexGroup data-test-subj="ecsAllowedValues" direction="row" wrap={true} gutterSize="xs">
{allowedValues.map((x, i) => (
<EuiFlexItem grow={false} key={`${x.name}_${i}`}>
<CodeSuccess>{x.name}</CodeSuccess>
<EuiCode css={codeSuccessCss}>{x.name}</EuiCode>
</EuiFlexItem>
))}
</EuiFlexGroup>

View file

@ -6,6 +6,7 @@
*/
import React, { useMemo } from 'react';
import { EuiButtonGroup } from '@elastic/eui';
import type { NonLegacyHistoricalResult } from '../../../../../../../../types';
import { getIncompatibleStatBadgeColor } from '../../../../../../../../utils/get_incompatible_stat_badge_color';
@ -15,7 +16,7 @@ import { getIncompatibleAndSameFamilyFieldsFromHistoricalResult } from './utils/
import { IncompatibleTab } from '../../../../incompatible_tab';
import { SameFamilyTab } from '../../../../same_family_tab';
import { CheckFieldsTabs } from '../../../../check_fields_tabs';
import { StyledHistoricalResultsCheckFieldsButtonGroup } from '../styles';
import { historicalResultsCheckFieldsButtonGroupCss } from '../styles';
export interface Props {
indexName: string;
@ -101,7 +102,9 @@ const HistoricalCheckFieldsComponent: React.FC<Props> = ({ indexName, historical
<div data-test-subj="historicalCheckFields">
<CheckFieldsTabs
tabs={tabs}
renderButtonGroup={(props) => <StyledHistoricalResultsCheckFieldsButtonGroup {...props} />}
renderButtonGroup={(props) => (
<EuiButtonGroup css={historicalResultsCheckFieldsButtonGroupCss} {...props} />
)}
/>
</div>
);

View file

@ -113,7 +113,7 @@ describe('LegacyHistoricalCheckFields', () => {
<TestExternalProviders>
<EuiMarkdownFormat>{tablesMarkdown}</EuiMarkdownFormat>
</TestExternalProviders>
).container.innerHTML
).container.children[0].innerHTML
);
expect(screen.getByTestId('incompatibleTablesMarkdown')).toBeInTheDocument();

View file

@ -6,7 +6,7 @@
*/
import React, { FC, memo, useMemo } from 'react';
import { EuiMarkdownFormat, EuiSpacer } from '@elastic/eui';
import { EuiButtonGroup, EuiMarkdownFormat, EuiSpacer } from '@elastic/eui';
import { INCOMPATIBLE_FIELDS, SAME_FAMILY } from '../../../../../../../../translations';
import { Actions } from '../../../../../../../../actions';
@ -16,7 +16,7 @@ import { CheckSuccessEmptyPrompt } from '../../../../check_success_empty_prompt'
import { INCOMPATIBLE_TAB_ID, SAME_FAMILY_TAB_ID } from '../../../../constants';
import { getIncompatibleStatBadgeColor } from '../../../../../../../../utils/get_incompatible_stat_badge_color';
import { CheckFieldsTabs } from '../../../../check_fields_tabs';
import { StyledHistoricalResultsCheckFieldsButtonGroup } from '../styles';
import { historicalResultsCheckFieldsButtonGroupCss } from '../styles';
import { NOT_INCLUDED_IN_HISTORICAL_RESULTS } from './translations';
interface Props {
@ -88,7 +88,9 @@ const LegacyHistoricalCheckFieldsComponent: FC<Props> = ({ indexName, historical
<div data-test-subj="legacyHistoricalCheckFields">
<CheckFieldsTabs
tabs={tabs}
renderButtonGroup={(props) => <StyledHistoricalResultsCheckFieldsButtonGroup {...props} />}
renderButtonGroup={(props) => (
<EuiButtonGroup css={historicalResultsCheckFieldsButtonGroupCss} {...props} />
)}
/>
</div>
);

View file

@ -5,17 +5,16 @@
* 2.0.
*/
import styled from 'styled-components';
import { EuiButtonGroup } from '@elastic/eui';
import { css } from '@emotion/react';
import { INCOMPATIBLE_TAB_ID, SAME_FAMILY_TAB_ID } from '../../../constants';
export const StyledHistoricalResultsCheckFieldsButtonGroup = styled(EuiButtonGroup)`
min-width: 50%;
button[data-test-subj='${INCOMPATIBLE_TAB_ID}'] {
flex-grow: 1;
}
button[data-test-subj='${SAME_FAMILY_TAB_ID}'] {
flex-grow: 1;
}
`;
export const historicalResultsCheckFieldsButtonGroupCss = css({
minWidth: '50%',
[`button[data-test-subj='${INCOMPATIBLE_TAB_ID}']`]: {
flexGrow: 1,
},
[`button[data-test-subj='${SAME_FAMILY_TAB_ID}']`]: {
flexGrow: 1,
},
});

View file

@ -7,6 +7,7 @@
import React, { FC, Fragment, memo, useState } from 'react';
import {
EuiAccordion,
EuiEmptyPrompt,
EuiFlexGroup,
EuiFlexItem,
@ -18,11 +19,10 @@ import {
import { useDataQualityContext } from '../../../../../../data_quality_context';
import { useHistoricalResultsContext } from '../../../contexts/historical_results_context';
import { StyledAccordion } from './styles';
import { accordionCss } from './styles';
import { getFormattedCheckTime } from '../../utils/get_formatted_check_time';
import { IndexResultBadge } from '../../../index_result_badge';
import { HistoricalResult } from './historical_result';
import { StyledText } from '../styles';
import { getCheckTextColor } from '../../../utils/get_check_text_color';
import {
CHANGE_YOUR_SEARCH_CRITERIA_OR_RUN,
@ -30,6 +30,7 @@ import {
NO_RESULTS_MATCH_YOUR_SEARCH_CRITERIA,
TOGGLE_HISTORICAL_RESULT_CHECKED_AT,
} from './translations';
import { textCss } from '../styles';
interface Props {
indexName: string;
@ -51,7 +52,8 @@ export const HistoricalResultsListComponent: FC<Props> = ({ indexName }) => {
{results.map((result) => (
<Fragment key={result.checkedAt}>
<EuiSpacer size="m" />
<StyledAccordion
<EuiAccordion
css={accordionCss}
id={historicalResultsAccordionId}
buttonProps={{
'aria-label': TOGGLE_HISTORICAL_RESULT_CHECKED_AT(
@ -71,7 +73,9 @@ export const HistoricalResultsListComponent: FC<Props> = ({ indexName }) => {
<IndexResultBadge incompatible={result.incompatibleFieldCount} />
</EuiFlexItem>
<EuiFlexItem grow={true}>
<StyledText size="s">{getFormattedCheckTime(result.checkedAt)}</StyledText>
<EuiText css={textCss} size="s">
{getFormattedCheckTime(result.checkedAt)}
</EuiText>
</EuiFlexItem>
{!accordionState[result.checkedAt] && (
<EuiFlexItem grow={false}>
@ -87,7 +91,7 @@ export const HistoricalResultsListComponent: FC<Props> = ({ indexName }) => {
}
>
<HistoricalResult indexName={indexName} historicalResult={result} />
</StyledAccordion>
</EuiAccordion>
</Fragment>
))}
</>

View file

@ -5,19 +5,18 @@
* 2.0.
*/
import { EuiAccordion } from '@elastic/eui';
import styled from 'styled-components';
import { UseEuiTheme } from '@elastic/eui';
import { css } from '@emotion/react';
export const StyledAccordion = styled(EuiAccordion)`
padding: 14px ${({ theme }) => theme.eui.euiSize};
border: 1px solid ${({ theme }) => theme.eui.euiBorderColor};
border-radius: ${({ theme }) => theme.eui.euiBorderRadius};
.euiAccordion__button:is(:hover, :focus) {
text-decoration: none;
}
.euiAccordion__buttonContent {
flex-grow: 1;
}
`;
export const accordionCss = ({ euiTheme }: UseEuiTheme) =>
css({
padding: `14px ${euiTheme.size.base}`,
border: `1px solid ${euiTheme.border.color}`,
borderRadius: euiTheme.border.radius.medium,
'.euiAccordion__button:is(:hover, :focus)': {
textDecoration: 'none',
},
'.euiAccordion__buttonContent': {
flexGrow: 1,
},
});

View file

@ -13,7 +13,9 @@ import {
EuiSpacer,
EuiSuperDatePicker,
EuiTablePagination,
EuiText,
} from '@elastic/eui';
import { css } from '@emotion/react';
import React, { FC, useMemo, useReducer } from 'react';
import { useDataQualityContext } from '../../../../../data_quality_context';
import { useHistoricalResultsContext } from '../../contexts/historical_results_context';
@ -25,7 +27,6 @@ import { fetchHistoricalResultsQueryReducer } from './reducers/fetch_historical_
import { FetchHistoricalResultsQueryState } from '../types';
import { LoadingEmptyPrompt } from '../../loading_empty_prompt';
import { ErrorEmptyPrompt } from '../../error_empty_prompt';
import { StyledFilterGroupFlexItem, StyledText } from './styles';
import {
ALL,
ERROR_LOADING_HISTORICAL_RESULTS,
@ -39,9 +40,16 @@ import { useHistoricalResultsPagination } from './hooks/use_historical_results_p
import { FAIL, PASS } from '../../translations';
import { useHistoricalResultsOutcomeFilter } from './hooks/use_historical_results_outcome_filter';
import { useHistoricalResultsDatePicker } from './hooks/use_historical_results_date_picker';
import { textCss } from './styles';
const historicalResultsListId = 'historicalResultsList';
const styles = {
filterGroupFlexItem: css({
flexBasis: '17%',
}),
};
export const initialFetchHistoricalResultsQueryState: FetchHistoricalResultsQueryState = {
startDate: DEFAULT_HISTORICAL_RESULTS_START_DATE,
endDate: DEFAULT_HISTORICAL_RESULTS_END_DATE,
@ -111,7 +119,7 @@ export const HistoricalResultsComponent: FC<Props> = ({ indexName }) => {
return (
<div data-test-subj="historicalResults">
<EuiFlexGroup justifyContent="spaceBetween">
<StyledFilterGroupFlexItem grow={false}>
<EuiFlexItem css={styles.filterGroupFlexItem} grow={false}>
<EuiFilterGroup
data-test-subj="historicalResultsOutcomeFilterGroup"
role="radiogroup"
@ -145,7 +153,7 @@ export const HistoricalResultsComponent: FC<Props> = ({ indexName }) => {
{FAIL}
</EuiFilterButton>
</EuiFilterGroup>
</StyledFilterGroupFlexItem>
</EuiFlexItem>
<EuiFlexItem>
<EuiSuperDatePicker
width="full"
@ -157,7 +165,8 @@ export const HistoricalResultsComponent: FC<Props> = ({ indexName }) => {
</EuiFlexItem>
</EuiFlexGroup>
<EuiSpacer />
<StyledText
<EuiText
css={textCss}
size="s"
role="status"
aria-live="polite"
@ -167,7 +176,7 @@ export const HistoricalResultsComponent: FC<Props> = ({ indexName }) => {
aria-describedby={historicalResultsListId}
>
{totalChecksText}
</StyledText>
</EuiText>
<div id={historicalResultsListId}>
<HistoricalResultsList indexName={indexName} />
</div>

View file

@ -5,13 +5,10 @@
* 2.0.
*/
import { EuiFlexItem, EuiText } from '@elastic/eui';
import styled from 'styled-components';
import { UseEuiTheme } from '@elastic/eui';
import { css } from '@emotion/react';
export const StyledFilterGroupFlexItem = styled(EuiFlexItem)`
flex-basis: 17%;
`;
export const StyledText = styled(EuiText)`
font-weight: ${({ theme }) => theme.eui.euiFontWeightSemiBold};
`;
export const textCss = ({ euiTheme }: UseEuiTheme) =>
css({
fontWeight: euiTheme.font.weight.semiBold,
});

View file

@ -17,7 +17,7 @@ import {
MAPPINGS_THAT_CONFLICT_WITH_ECS,
PAGES_MAY_NOT_DISPLAY_EVENTS,
} from '../../../../../translations';
import { CalloutItem } from '../styles';
import { calloutItemCss } from '../styles';
export interface Props {
incompatibleFieldCount?: number;
@ -41,15 +41,15 @@ const IncompatibleCalloutComponent: React.FC<Props> = ({
>
<div data-test-subj="fieldsAreIncompatible">{INCOMPATIBLE_CALLOUT(ecsVersion)}</div>
<EuiSpacer size="xs" />
<CalloutItem data-test-subj="rulesMayNotMatch">
<div css={calloutItemCss} data-test-subj="rulesMayNotMatch">
{DETECTION_ENGINE_RULES_MAY_NOT_MATCH}
</CalloutItem>
<CalloutItem data-test-subj="pagesMayNotDisplayEvents">
</div>
<div css={calloutItemCss} data-test-subj="pagesMayNotDisplayEvents">
{PAGES_MAY_NOT_DISPLAY_EVENTS}
</CalloutItem>
<CalloutItem data-test-subj="mappingsThatDontComply">
</div>
<div css={calloutItemCss} data-test-subj="mappingsThatDontComply">
{MAPPINGS_THAT_CONFLICT_WITH_ECS}
</CalloutItem>
</div>
</EuiCallOut>
);
};

View file

@ -6,9 +6,9 @@
*/
import React from 'react';
import { EuiTableFieldDataColumnType } from '@elastic/eui';
import { EuiCode, EuiTableFieldDataColumnType } from '@elastic/eui';
import { CodeDanger, CodeSuccess } from '../../../../../../styles';
import { codeDangerCss, codeSuccessCss } from '../../../../../../styles';
import {
AllowedValue,
IncompatibleFieldMetadata,
@ -77,7 +77,11 @@ export const getIncompatibleMappingsTableColumns = (): Array<
{
field: 'type',
name: ECS_MAPPING_TYPE_EXPECTED,
render: (type: string) => <CodeSuccess data-test-subj="codeSuccess">{type}</CodeSuccess>,
render: (type: string) => (
<EuiCode css={codeSuccessCss} data-test-subj="codeSuccess">
{type}
</EuiCode>
),
sortable: true,
truncateText: false,
width: '25%',
@ -86,7 +90,9 @@ export const getIncompatibleMappingsTableColumns = (): Array<
field: 'indexFieldType',
name: INDEX_MAPPING_TYPE_ACTUAL,
render: (indexFieldType: string, x) => (
<CodeDanger data-test-subj="codeDanger">{indexFieldType}</CodeDanger>
<EuiCode css={codeDangerCss} data-test-subj="codeDanger">
{indexFieldType}
</EuiCode>
),
sortable: true,
truncateText: false,

View file

@ -7,14 +7,14 @@
import { EuiCode, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import React from 'react';
import styled from 'styled-components';
import styled from '@emotion/styled';
import { EMPTY_PLACEHOLDER } from '../../../../../constants';
import { CodeDanger } from '../../../../../styles';
import { codeDangerCss } from '../../../../../styles';
import type { UnallowedValueCount } from '../../../../../types';
const IndexInvalidValueFlexItem = styled(EuiFlexItem)`
margin-bottom: ${({ theme }) => theme.eui.euiSizeXS};
margin-bottom: ${({ theme }) => theme.euiTheme.size.xs};
`;
interface Props {
@ -29,7 +29,7 @@ const IndexInvalidValuesComponent: React.FC<Props> = ({ indexInvalidValues }) =>
{indexInvalidValues.map(({ fieldName, count }, i) => (
<IndexInvalidValueFlexItem grow={false} key={`${fieldName}_${i}`}>
<div>
<CodeDanger>{fieldName}</CodeDanger>{' '}
<EuiCode css={codeDangerCss}>{fieldName}</EuiCode>{' '}
<span>
{'('}
{count}

View file

@ -5,9 +5,16 @@
* 2.0.
*/
import { EuiFlexGroup, EuiFlexItem, EuiPanel, EuiSpacer } from '@elastic/eui';
import {
EuiFlexGroup,
EuiFlexItem,
EuiPanel,
EuiSpacer,
useEuiFontSize,
useEuiTheme,
} from '@elastic/eui';
import React from 'react';
import styled from 'styled-components';
import { css } from '@emotion/react';
import { useDataQualityContext } from '../../../../../data_quality_context';
import {
@ -21,31 +28,40 @@ import {
import { Stat } from '../../../../../stat';
import { getIlmPhaseDescription } from '../../../../../utils/get_ilm_phase_description';
const StyledFlexItem = styled(EuiFlexItem)`
justify-content: space-between;
border-right: 1px solid ${({ theme }) => theme.eui.euiBorderColor};
font-size: ${({ theme }) => theme.eui.euiFontSizeXS};
const useStyles = () => {
const { euiTheme } = useEuiTheme();
const { fontSize } = useEuiFontSize('xs');
margin-bottom: 2px;
const baseFlexItem = css`
justify-content: space-between;
border-right: 1px solid ${euiTheme.border.color};
font-size: ${fontSize};
&:last-child {
border-right: none;
}
&:last-child {
border-right: none;
}
strong {
text-transform: capitalize;
}
`;
strong {
text-transform: capitalize;
}
`;
const UnpaddedStyledFlexItem = styled(StyledFlexItem)`
margin-bottom: 0;
`;
return {
flexItem: css`
${baseFlexItem}
margin-bottom: 2px;
`,
unpaddedFlexItem: css`
${baseFlexItem}
margin-bottom: 0;
`,
};
};
export interface Props {
docsCount: number;
ilmPhase?: string;
sizeInBytes?: number;
sameFamilyFieldsCount?: number;
ecsCompliantFieldsCount?: number;
customFieldsCount?: number;
allFieldsCount?: number;
@ -55,22 +71,22 @@ export const IndexStatsPanelComponent: React.FC<Props> = ({
docsCount,
ilmPhase,
sizeInBytes,
sameFamilyFieldsCount,
customFieldsCount,
ecsCompliantFieldsCount,
allFieldsCount,
}) => {
const styles = useStyles();
const { formatBytes, formatNumber } = useDataQualityContext();
return (
<EuiPanel data-test-subj="indexStatsPanel" paddingSize="s" hasShadow={false} hasBorder={true}>
<EuiFlexGroup gutterSize="m">
<StyledFlexItem>
<EuiFlexItem css={styles.flexItem}>
<strong>{DOCS}</strong>
<EuiSpacer />
{formatNumber(docsCount)}
</StyledFlexItem>
</EuiFlexItem>
{ilmPhase && (
<UnpaddedStyledFlexItem>
<EuiFlexItem css={styles.unpaddedFlexItem}>
<strong>{ILM_PHASE}</strong>
<EuiSpacer />
<Stat
@ -78,33 +94,33 @@ export const IndexStatsPanelComponent: React.FC<Props> = ({
tooltipText={getIlmPhaseDescription(ilmPhase)}
badgeProps={{ 'data-test-subj': 'ilmPhase' }}
/>
</UnpaddedStyledFlexItem>
</EuiFlexItem>
)}
<StyledFlexItem>
<EuiFlexItem css={styles.flexItem}>
<strong>{SIZE}</strong>
<EuiSpacer />
{formatBytes(sizeInBytes ?? 0)}
</StyledFlexItem>
</EuiFlexItem>
{customFieldsCount != null && (
<StyledFlexItem>
<EuiFlexItem css={styles.flexItem}>
<strong>{CUSTOM_FIELDS}</strong>
<EuiSpacer />
{formatNumber(customFieldsCount)}
</StyledFlexItem>
</EuiFlexItem>
)}
{ecsCompliantFieldsCount != null && (
<StyledFlexItem>
<EuiFlexItem css={styles.flexItem}>
<strong>{ECS_COMPLIANT_FIELDS}</strong>
<EuiSpacer />
{formatNumber(ecsCompliantFieldsCount)}
</StyledFlexItem>
</EuiFlexItem>
)}
{allFieldsCount != null && (
<StyledFlexItem>
<EuiFlexItem css={styles.flexItem}>
<strong>{ALL_FIELDS}</strong>
<EuiSpacer />
{formatNumber(allFieldsCount)}
</StyledFlexItem>
</EuiFlexItem>
)}
</EuiFlexGroup>
</EuiPanel>

View file

@ -24,7 +24,7 @@ import {
} from '../../../../../../../../translations';
import { EcsAllowedValues } from '../../../../ecs_allowed_values';
import { IndexInvalidValues } from '../../../../index_invalid_values';
import { CodeDanger, CodeSuccess } from '../../../../../../../../styles';
import { codeDangerCss, codeSuccessCss } from '../../../../../../../../styles';
import type {
AllowedValue,
EnrichedFieldMetadata,
@ -45,9 +45,9 @@ export const getAllTableColumns = (): Array<EuiTableFieldDataColumnType<Enriched
field: 'type',
name: ECS_MAPPING_TYPE_EXPECTED,
render: (type: string | undefined) => (
<CodeSuccess data-test-subj="codeSuccess">
<EuiCode css={codeSuccessCss} data-test-subj="codeSuccess">
{type != null ? type : EMPTY_PLACEHOLDER}
</CodeSuccess>
</EuiCode>
),
sortable: true,
truncateText: false,
@ -59,21 +59,31 @@ export const getAllTableColumns = (): Array<EuiTableFieldDataColumnType<Enriched
render: (_, x) => {
// if custom field or ecs based field with mapping match
if (isCustomFieldMetadata(x) || isEcsCompliantFieldMetadata(x)) {
return <CodeSuccess data-test-subj="codeSuccess">{x.indexFieldType}</CodeSuccess>;
return (
<EuiCode css={codeSuccessCss} data-test-subj="codeSuccess">
{x.indexFieldType}
</EuiCode>
);
}
// mapping mismatch due to same family
if (isSameFamilyFieldMetadata(x)) {
return (
<div>
<CodeSuccess data-test-subj="codeSuccess">{x.indexFieldType}</CodeSuccess>
<EuiCode css={codeSuccessCss} data-test-subj="codeSuccess">
{x.indexFieldType}
</EuiCode>
<SameFamily />
</div>
);
}
// mapping mismatch
return <CodeDanger data-test-subj="codeDanger">{x.indexFieldType}</CodeDanger>;
return (
<EuiCode css={codeDangerCss} data-test-subj="codeDanger">
{x.indexFieldType}
</EuiCode>
);
},
sortable: true,
truncateText: false,

View file

@ -9,7 +9,7 @@ import { EcsVersion } from '@elastic/ecs';
import { EuiCallOut, EuiEmptyPrompt, EuiSpacer } from '@elastic/eui';
import React, { useMemo } from 'react';
import styled from 'styled-components';
import { css } from '@emotion/react';
import { CompareFieldsTable } from '../../../compare_fields_table';
import { EmptyPromptBody } from '../../../empty_prompt_body';
@ -17,7 +17,7 @@ import { EmptyPromptTitle } from '../../../empty_prompt_title';
import type { EcsCompliantFieldMetadata } from '../../../../../../../types';
import { isTimestampFieldMissing } from '../utils/is_timestamp_field_missing';
import { getEcsCompliantTableColumns } from './utils/get_ecs_compliant_table_columns';
import { CalloutItem } from '../../../styles';
import { calloutItemCss } from '../../../styles';
import {
CUSTOM_DETECTION_ENGINE_RULES_WORK,
ECS_COMPLIANT_CALLOUT,
@ -30,9 +30,11 @@ import {
PRE_BUILT_DETECTION_ENGINE_RULES_WORK,
} from '../../../translations';
const EmptyPromptContainer = styled.div`
width: 100%;
`;
const styles = {
emptyPromptContainer: css({
width: '100%',
}),
};
interface Props {
indexName: string;
@ -54,11 +56,11 @@ const EcsCompliantTabComponent: React.FC<Props> = ({ indexName, ecsCompliantFiel
version: EcsVersion,
})}
</p>
<CalloutItem>{PRE_BUILT_DETECTION_ENGINE_RULES_WORK}</CalloutItem>
<CalloutItem>{CUSTOM_DETECTION_ENGINE_RULES_WORK}</CalloutItem>
<CalloutItem>{PAGES_DISPLAY_EVENTS}</CalloutItem>
<CalloutItem>{OTHER_APP_CAPABILITIES_WORK_PROPERLY}</CalloutItem>
<CalloutItem>{ECS_COMPLIANT_MAPPINGS_ARE_FULLY_SUPPORTED}</CalloutItem>
<div css={calloutItemCss}>{PRE_BUILT_DETECTION_ENGINE_RULES_WORK}</div>
<div css={calloutItemCss}>{CUSTOM_DETECTION_ENGINE_RULES_WORK}</div>
<div css={calloutItemCss}>{PAGES_DISPLAY_EVENTS}</div>
<div css={calloutItemCss}>{OTHER_APP_CAPABILITIES_WORK_PROPERLY}</div>
<div css={calloutItemCss}>{ECS_COMPLIANT_MAPPINGS_ARE_FULLY_SUPPORTED}</div>
</EuiCallOut>
<EuiSpacer />
<CompareFieldsTable
@ -68,7 +70,7 @@ const EcsCompliantTabComponent: React.FC<Props> = ({ indexName, ecsCompliantFiel
/>
</>
) : (
<EmptyPromptContainer>
<div css={styles.emptyPromptContainer}>
<EuiEmptyPrompt
body={emptyPromptBody}
iconType="cross"
@ -76,7 +78,7 @@ const EcsCompliantTabComponent: React.FC<Props> = ({ indexName, ecsCompliantFiel
title={title}
titleSize="s"
/>
</EmptyPromptContainer>
</div>
)}
</div>
);

View file

@ -6,9 +6,9 @@
*/
import React from 'react';
import { EuiTableFieldDataColumnType } from '@elastic/eui';
import { EuiCode, EuiTableFieldDataColumnType } from '@elastic/eui';
import { CodeSuccess } from '../../../../../../../../styles';
import { codeSuccessCss } from '../../../../../../../../styles';
import { AllowedValue, EcsCompliantFieldMetadata } from '../../../../../../../../types';
import { FIELD } from '../../../../../../../../translations';
import { EcsAllowedValues } from '../../../../ecs_allowed_values';
@ -28,7 +28,11 @@ export const getEcsCompliantTableColumns = (): Array<
{
field: 'type',
name: ECS_MAPPING_TYPE,
render: (type: string) => <CodeSuccess data-test-subj="type">{type}</CodeSuccess>,
render: (type: string) => (
<EuiCode css={codeSuccessCss} data-test-subj="type">
{type}
</EuiCode>
),
sortable: true,
truncateText: false,
width: '25%',

View file

@ -7,7 +7,6 @@
import React, { useMemo } from 'react';
import { EuiButtonGroup } from '@elastic/eui';
import styled from 'styled-components';
import {
ALL_FIELDS,
@ -37,14 +36,16 @@ import { AllTab } from './all_tab';
import { getEcsCompliantBadgeColor } from './utils/get_ecs_compliant_badge_color';
import { CheckFieldsTabs } from '../../check_fields_tabs';
const StyledButtonGroup = styled(EuiButtonGroup)`
button[data-test-subj='${INCOMPATIBLE_TAB_ID}'] {
flex-grow: 1.2;
}
button[data-test-subj='${ECS_COMPLIANT_TAB_ID}'] {
flex-grow: 1.4;
}
`;
const styles = {
buttonGroup: {
[`button[data-test-subj='${INCOMPATIBLE_TAB_ID}']`]: {
flexGrow: 1.2,
},
[`button[data-test-subj='${ECS_COMPLIANT_TAB_ID}']`]: {
flexGrow: 1.4,
},
},
};
export interface Props {
indexName: string;
@ -185,7 +186,9 @@ const LatestCheckFieldsComponent: React.FC<Props> = ({
<div data-test-subj="latestCheckFields">
<CheckFieldsTabs
tabs={tabs}
renderButtonGroup={(props) => <StyledButtonGroup {...props} isFullWidth />}
renderButtonGroup={(props) => (
<EuiButtonGroup css={styles.buttonGroup} {...props} isFullWidth />
)}
/>
</div>
);

View file

@ -6,15 +6,11 @@
*/
import React, { FC } from 'react';
import { EuiButtonEmpty } from '@elastic/eui';
import styled from 'styled-components';
import { useEuiTheme } from '@elastic/eui';
import { css } from '@emotion/react';
import { Actions } from '../../../../../../../actions';
export const CopyToClipboardButton = styled(EuiButtonEmpty)`
margin-left: ${({ theme }) => theme.eui.euiSizeXS};
`;
interface Props {
markdownComment: string;
showAddToNewCaseAction?: boolean;
@ -23,15 +19,21 @@ interface Props {
indexName?: string;
}
const StyledStickyContainer = styled.div`
padding: ${({ theme }) => theme.eui.euiSizeL} 0;
background: ${({ theme }) => theme.eui.euiColorEmptyShade};
position: sticky;
bottom: 0;
left: 0;
right: 0;
border-top: 1px solid ${({ theme }) => theme.eui.euiBorderColor};
`;
const useStyles = () => {
const { euiTheme } = useEuiTheme();
return {
stickyContainer: css({
padding: `${euiTheme.size.l} 0`,
background: euiTheme.colors.emptyShade,
position: 'sticky',
bottom: 0,
left: 0,
right: 0,
borderTop: `1px solid ${euiTheme.border.color}`,
}),
};
};
const StickyActionsComponent: FC<Props> = ({
indexName,
@ -40,8 +42,10 @@ const StickyActionsComponent: FC<Props> = ({
showAddToNewCaseAction,
showChatAction,
}) => {
const styles = useStyles();
return (
<StyledStickyContainer>
<div css={styles.stickyContainer}>
<Actions
indexName={indexName}
markdownComment={markdownComment}
@ -49,7 +53,7 @@ const StickyActionsComponent: FC<Props> = ({
showCopyToClipboardAction={showCopyToClipboardAction}
showAddToNewCaseAction={showAddToNewCaseAction}
/>
</StyledStickyContainer>
</div>
);
};

View file

@ -5,21 +5,29 @@
* 2.0.
*/
import { EuiBadge } from '@elastic/eui';
import { EuiBadge, useEuiTheme } from '@elastic/eui';
import React from 'react';
import styled from 'styled-components';
import { css } from '@emotion/react';
import { SAME_FAMILY_BADGE_LABEL } from '../../../../../translations';
const SameFamilyBadge = styled(EuiBadge)`
margin: ${({ theme }) => `0 ${theme.eui.euiSizeXS}`};
`;
const useStyles = () => {
const { euiTheme } = useEuiTheme();
return {
sameFamilyBadge: css({
margin: `0 ${euiTheme.size.xs}`,
}),
};
};
const SameFamilyComponent: React.FC = () => (
<SameFamilyBadge data-test-subj="sameFamily" color="warning">
{SAME_FAMILY_BADGE_LABEL}
</SameFamilyBadge>
);
const SameFamilyComponent: React.FC = () => {
const styles = useStyles();
return (
<EuiBadge css={styles.sameFamilyBadge} data-test-subj="sameFamily" color="warning">
{SAME_FAMILY_BADGE_LABEL}
</EuiBadge>
);
};
SameFamilyComponent.displayName = 'SameFamilyComponent';

View file

@ -6,7 +6,7 @@
*/
import React from 'react';
import { EuiTableFieldDataColumnType } from '@elastic/eui';
import { EuiCode, EuiTableFieldDataColumnType } from '@elastic/eui';
import { SameFamilyFieldMetadata } from '../../../../../../types';
import {
@ -14,7 +14,7 @@ import {
FIELD,
INDEX_MAPPING_TYPE_ACTUAL,
} from '../../../../../../translations';
import { CodeSuccess } from '../../../../../../styles';
import { codeSuccessCss } from '../../../../../../styles';
import { SameFamily } from '../../same_family';
import { ECS_DESCRIPTION } from '../../translations';
@ -31,7 +31,11 @@ export const getSameFamilyTableColumns = (): Array<
{
field: 'type',
name: ECS_MAPPING_TYPE_EXPECTED,
render: (type: string) => <CodeSuccess data-test-subj="codeSuccess">{type}</CodeSuccess>,
render: (type: string) => (
<EuiCode css={codeSuccessCss} data-test-subj="codeSuccess">
{type}
</EuiCode>
),
sortable: true,
truncateText: false,
width: '25%',
@ -41,7 +45,9 @@ export const getSameFamilyTableColumns = (): Array<
name: INDEX_MAPPING_TYPE_ACTUAL,
render: (indexFieldType: string) => (
<div>
<CodeSuccess data-test-subj="codeSuccess">{indexFieldType}</CodeSuccess>
<EuiCode css={codeSuccessCss} data-test-subj="codeSuccess">
{indexFieldType}
</EuiCode>
<SameFamily />
</div>
),

View file

@ -5,8 +5,10 @@
* 2.0.
*/
import styled from 'styled-components';
import { UseEuiTheme } from '@elastic/eui';
import { css } from '@emotion/react';
export const CalloutItem = styled.div`
margin-left: ${({ theme }) => theme.eui.euiSizeS};
`;
export const calloutItemCss = ({ euiTheme }: UseEuiTheme) =>
css({
marginLeft: euiTheme.size.s,
});

View file

@ -7,21 +7,22 @@
import { EuiBadge, EuiToolTip } from '@elastic/eui';
import React from 'react';
import styled from 'styled-components';
import { css } from '@emotion/react';
import { getIndexResultToolTip } from '../utils/get_index_result_tooltip';
import { getCheckTextColor } from '../utils/get_check_text_color';
import { FAIL, PASS } from '../translations';
const StyledBadge = styled(EuiBadge)`
width: 44px;
text-align: center;
padding-inline: 0;
.euiBadge__content {
justify-content: center;
}
`;
const styles = {
badge: css({
width: 44,
textAlign: 'center',
paddingInline: 0,
'.euiBadge__content': {
justifyContent: 'center',
},
}),
};
export type Props = React.ComponentProps<typeof EuiBadge> & {
incompatible: number;
@ -35,13 +36,14 @@ export const IndexResultBadgeComponent: React.FC<Props> = ({
}) => {
return (
<EuiToolTip content={tooltipText ?? getIndexResultToolTip(incompatible)}>
<StyledBadge
<EuiBadge
css={styles.badge}
data-test-subj="indexResultBadge"
color={getCheckTextColor(incompatible)}
{...props}
>
{incompatible > 0 ? FAIL : PASS}
</StyledBadge>
</EuiBadge>
</EuiToolTip>
);
};

View file

@ -7,14 +7,16 @@
import { EuiBadge, EuiFlexGroup, EuiFlexItem, EuiToolTip } from '@elastic/eui';
import React from 'react';
import styled from 'styled-components';
import { css } from '@emotion/react';
import type { IlmExplainPhaseCounts, IlmPhase } from '../../../../../../types';
import { getPatternIlmPhaseDescription } from './utils/get_pattern_ilm_phase_description';
const PhaseCountsFlexGroup = styled(EuiFlexGroup)`
display: inline-flex;
`;
const styles = {
phaseCounts: css({
display: 'inline-flex',
}),
};
export const phases: IlmPhase[] = ['hot', 'unmanaged', 'warm', 'cold', 'frozen'];
@ -24,7 +26,7 @@ interface Props {
}
const IlmPhaseCountsComponent: React.FC<Props> = ({ ilmExplainPhaseCounts, pattern }) => (
<PhaseCountsFlexGroup data-test-subj="ilmPhaseCounts" gutterSize="s">
<EuiFlexGroup css={styles.phaseCounts} data-test-subj="ilmPhaseCounts" gutterSize="s">
{phases.map((phase) =>
ilmExplainPhaseCounts[phase] != null && ilmExplainPhaseCounts[phase] > 0 ? (
<EuiFlexItem key={phase} grow={false}>
@ -40,7 +42,7 @@ const IlmPhaseCountsComponent: React.FC<Props> = ({ ilmExplainPhaseCounts, patte
</EuiFlexItem>
) : null
)}
</PhaseCountsFlexGroup>
</EuiFlexGroup>
);
IlmPhaseCountsComponent.displayName = 'IlmPhaseCountsComponent';

View file

@ -5,31 +5,29 @@
* 2.0.
*/
import { EuiAccordion } from '@elastic/eui';
import styled from 'styled-components';
import { UseEuiTheme } from '@elastic/eui';
import { CSSObject } from '@emotion/react';
export const PatternAccordion = styled(EuiAccordion)`
.euiAccordion__triggerWrapper {
padding: 14px ${({ theme }) => theme.eui.euiSize};
border: 1px solid ${({ theme }) => theme.eui.euiBorderColor};
border-radius: ${({ theme }) => theme.eui.euiBorderRadius};
}
export const patternAccordionCss = ({ euiTheme }: UseEuiTheme): CSSObject => ({
'.euiAccordion__triggerWrapper': {
padding: `14px ${euiTheme.size.base}`,
border: `1px solid ${euiTheme.border.color}`,
borderRadius: euiTheme.border.radius.medium,
},
'.euiAccordion__button:is(:hover, :focus)': {
textDecoration: 'none',
},
'.euiAccordion__buttonContent': {
flexGrow: 1,
},
});
.euiAccordion__button:is(:hover, :focus) {
text-decoration: none;
}
.euiAccordion__buttonContent {
flex-grow: 1;
}
`;
export const PatternAccordionChildren = styled.div`
padding: ${({ theme }) => theme.eui.euiSize};
padding-bottom: 0;
border: 1px solid ${({ theme }) => theme.eui.euiBorderColor};
border-radius: 0 0 ${({ theme }) => `${theme.eui.euiBorderRadius} ${theme.eui.euiBorderRadius}`};
border-top: none;
width: calc(100% - ${({ theme }) => theme.eui.euiSizeS} * 2);
margin: auto;
`;
export const patternAccordionChildrenCss = ({ euiTheme }: UseEuiTheme): CSSObject => ({
padding: euiTheme.size.s,
paddingBottom: 0,
border: `1px solid ${euiTheme.border.color}`,
borderRadius: `0 0 ${euiTheme.border.radius.medium} ${euiTheme.border.radius.medium}`,
borderTop: 'none',
width: `calc(100% - ${euiTheme.size.s} * 2)`,
margin: 'auto',
});

View file

@ -6,7 +6,7 @@
*/
import type { CriteriaWithPagination, EuiBasicTableColumn, Pagination } from '@elastic/eui';
import { EuiInMemoryTable } from '@elastic/eui';
import { EuiInMemoryTable, useEuiTheme } from '@elastic/eui';
import React, { useCallback, useMemo } from 'react';
import { defaultSort } from '../../../../constants';
@ -23,6 +23,7 @@ export interface Props {
pattern,
onCheckNowAction,
onViewHistoryAction,
dangerColor,
}: {
formatBytes: (value: number | undefined) => string;
formatNumber: (value: number | undefined) => string;
@ -31,6 +32,7 @@ export interface Props {
onCheckNowAction: (indexName: string) => void;
onViewHistoryAction: (indexName: string) => void;
firstIndexName?: string;
dangerColor: string;
}) => Array<EuiBasicTableColumn<IndexSummaryTableItem>>;
items: IndexSummaryTableItem[];
pageIndex: number;
@ -58,6 +60,9 @@ const SummaryTableComponent: React.FC<Props> = ({
onViewHistoryAction,
}) => {
const { isILMAvailable, formatBytes, formatNumber } = useDataQualityContext();
const { euiTheme } = useEuiTheme();
const dangerColor = euiTheme.colors.danger;
const columns = useMemo(
() =>
getTableColumns({
@ -68,6 +73,7 @@ const SummaryTableComponent: React.FC<Props> = ({
onCheckNowAction,
onViewHistoryAction,
firstIndexName: items[0]?.indexName,
dangerColor,
}),
[
getTableColumns,
@ -78,6 +84,7 @@ const SummaryTableComponent: React.FC<Props> = ({
onCheckNowAction,
onViewHistoryAction,
items,
dangerColor,
]
);
const getItemId = useCallback((item: IndexSummaryTableItem) => item.indexName, []);

View file

@ -36,6 +36,8 @@ const defaultNumberFormat = '0,0.[000]';
const formatNumber = (value: number | undefined) =>
value != null ? numeral(value).format(defaultNumberFormat) : EMPTY_STAT;
const testColor = '#123456';
describe('helpers', () => {
describe('getSummaryTableColumns', () => {
const indexName = '.ds-auditbeat-8.6.1-2023.02.07-000001';
@ -65,6 +67,7 @@ describe('helpers', () => {
pattern: 'auditbeat-*',
onCheckNowAction: jest.fn(),
onViewHistoryAction: jest.fn(),
dangerColor: testColor,
}).map((x) => omit('render', x));
expect(columns).toEqual([
@ -126,6 +129,7 @@ describe('helpers', () => {
pattern: 'auditbeat-*',
onCheckNowAction: jest.fn(),
onViewHistoryAction: jest.fn(),
dangerColor: testColor,
});
const checkNowRender = (
(columns[0] as EuiTableActionsColumnType<IndexSummaryTableItem>)
@ -151,6 +155,7 @@ describe('helpers', () => {
pattern: 'auditbeat-*',
onCheckNowAction,
onViewHistoryAction: jest.fn(),
dangerColor: testColor,
});
const checkNowRender = (
(columns[0] as EuiTableActionsColumnType<IndexSummaryTableItem>)
@ -179,6 +184,7 @@ describe('helpers', () => {
pattern: 'auditbeat-*',
onCheckNowAction: jest.fn(),
onViewHistoryAction,
dangerColor: testColor,
});
const expandActionRender = (
@ -208,6 +214,7 @@ describe('helpers', () => {
onCheckNowAction: jest.fn(),
onViewHistoryAction: jest.fn(),
firstIndexName: indexName,
dangerColor: testColor,
});
const expandActionRender = (
@ -235,6 +242,7 @@ describe('helpers', () => {
onCheckNowAction: jest.fn(),
onViewHistoryAction: jest.fn(),
firstIndexName: 'another-index',
dangerColor: testColor,
});
const expandActionRender = (
@ -267,6 +275,7 @@ describe('helpers', () => {
pattern: 'auditbeat-*',
onCheckNowAction: jest.fn(),
onViewHistoryAction: jest.fn(),
dangerColor: testColor,
});
const incompatibleRender = (
columns[1] as EuiTableFieldDataColumnType<IndexSummaryTableItem>
@ -290,6 +299,7 @@ describe('helpers', () => {
pattern: 'auditbeat-*',
onCheckNowAction: jest.fn(),
onViewHistoryAction: jest.fn(),
dangerColor: testColor,
});
const incompatibleRender = (
columns[1] as EuiTableFieldDataColumnType<IndexSummaryTableItem>
@ -317,6 +327,7 @@ describe('helpers', () => {
pattern: 'auditbeat-*',
onCheckNowAction: jest.fn(),
onViewHistoryAction: jest.fn(),
dangerColor: testColor,
});
const incompatibleRender = (
columns[1] as EuiTableFieldDataColumnType<IndexSummaryTableItem>
@ -341,6 +352,7 @@ describe('helpers', () => {
pattern: 'auditbeat-*',
onCheckNowAction: jest.fn(),
onViewHistoryAction: jest.fn(),
dangerColor: testColor,
});
const indexNameRender = (columns[2] as EuiTableFieldDataColumnType<IndexSummaryTableItem>)
.render;
@ -365,6 +377,7 @@ describe('helpers', () => {
pattern: 'auditbeat-*',
onCheckNowAction: jest.fn(),
onViewHistoryAction: jest.fn(),
dangerColor: testColor,
});
const docsCountRender = (columns[3] as EuiTableFieldDataColumnType<IndexSummaryTableItem>)
.render;
@ -400,6 +413,7 @@ describe('helpers', () => {
pattern: 'auditbeat-*',
onCheckNowAction: jest.fn(),
onViewHistoryAction: jest.fn(),
dangerColor: testColor,
});
const incompatibleRender = (
columns[4] as EuiTableFieldDataColumnType<IndexSummaryTableItem>
@ -422,6 +436,7 @@ describe('helpers', () => {
pattern: 'auditbeat-*',
onCheckNowAction: jest.fn(),
onViewHistoryAction: jest.fn(),
dangerColor: testColor,
});
const incompatibleRender = (
columns[4] as EuiTableFieldDataColumnType<IndexSummaryTableItem>
@ -479,6 +494,7 @@ describe('helpers', () => {
pattern: 'auditbeat-*',
onCheckNowAction: jest.fn(),
onViewHistoryAction: jest.fn(),
dangerColor: testColor,
});
const ilmPhaseRender = (columns[5] as EuiTableFieldDataColumnType<IndexSummaryTableItem>)
.render;
@ -505,6 +521,7 @@ describe('helpers', () => {
pattern: 'auditbeat-*',
onCheckNowAction: jest.fn(),
onViewHistoryAction: jest.fn(),
dangerColor: testColor,
});
const ilmPhaseRender = (columns[5] as EuiTableFieldDataColumnType<IndexSummaryTableItem>)
.render;
@ -530,6 +547,7 @@ describe('helpers', () => {
pattern: 'auditbeat-*',
onCheckNowAction: jest.fn(),
onViewHistoryAction: jest.fn(),
dangerColor: testColor,
});
const ilmPhaseRender = (columns[5] as EuiTableFieldDataColumnType<IndexSummaryTableItem>)
.render;
@ -553,6 +571,7 @@ describe('helpers', () => {
pattern: 'auditbeat-*',
onCheckNowAction: jest.fn(),
onViewHistoryAction: jest.fn(),
dangerColor: testColor,
});
const sizeInBytesRender = (columns[6] as EuiTableFieldDataColumnType<IndexSummaryTableItem>)
@ -577,6 +596,7 @@ describe('helpers', () => {
pattern: 'auditbeat-*',
onCheckNowAction: jest.fn(),
onViewHistoryAction: jest.fn(),
dangerColor: testColor,
});
const sizeInBytesRender = (columns[6] as EuiTableFieldDataColumnType<IndexSummaryTableItem>)
@ -595,22 +615,22 @@ describe('helpers', () => {
});
describe('getIncompatibleStatColor', () => {
test('it returns the expected color when incompatible is greater than zero', () => {
test('it returns the provided incompatible color when incompatible is greater than zero', () => {
const incompatible = 123;
expect(getIncompatibleStatColor(incompatible)).toBe('#bd271e');
expect(getIncompatibleStatColor(incompatible, testColor)).toBe(testColor);
});
test('it returns undefined when incompatible is zero', () => {
const incompatible = 0;
expect(getIncompatibleStatColor(incompatible)).toBeUndefined();
expect(getIncompatibleStatColor(incompatible, testColor)).toBeUndefined();
});
test('it returns undefined when incompatible is undefined', () => {
const incompatible = undefined;
expect(getIncompatibleStatColor(incompatible)).toBeUndefined();
expect(getIncompatibleStatColor(incompatible, testColor)).toBeUndefined();
});
});
});

View file

@ -15,9 +15,8 @@ import {
} from '@elastic/eui';
import React from 'react';
import moment from 'moment';
import styled from 'styled-components';
import { css } from '@emotion/react';
import { euiThemeVars } from '@kbn/ui-theme';
import { getDocsCountPercent } from '../../../../../utils/stats';
import { IndexSummaryTableItem } from '../../../../../types';
import { EMPTY_STAT } from '../../../../../constants';
@ -39,9 +38,11 @@ import { getIndexResultToolTip } from '../../utils/get_index_result_tooltip';
import { CHECK_NOW } from '../../translations';
import { HISTORICAL_RESULTS_TOUR_SELECTOR_KEY } from '../../constants';
const ProgressContainer = styled.div`
width: 150px;
`;
const styles = {
progressContainer: css({
width: '150px',
}),
};
export const getSummaryTableILMPhaseColumn = (
isILMAvailable: boolean
@ -93,8 +94,10 @@ export const getSummaryTableSizeInBytesColumn = ({
]
: [];
export const getIncompatibleStatColor = (incompatible: number | undefined): string | undefined =>
incompatible != null && incompatible > 0 ? euiThemeVars.euiColorDanger : undefined;
export const getIncompatibleStatColor = (
incompatible: number | undefined,
dangerColor: string
): string | undefined => (incompatible != null && incompatible > 0 ? dangerColor : undefined);
export const getSummaryTableColumns = ({
formatBytes,
@ -104,6 +107,7 @@ export const getSummaryTableColumns = ({
onCheckNowAction,
onViewHistoryAction,
firstIndexName,
dangerColor,
}: {
formatBytes: (value: number | undefined) => string;
formatNumber: (value: number | undefined) => string;
@ -112,6 +116,7 @@ export const getSummaryTableColumns = ({
onCheckNowAction: (indexName: string) => void;
onViewHistoryAction: (indexName: string) => void;
firstIndexName?: string;
dangerColor: string;
}): Array<EuiBasicTableColumn<IndexSummaryTableItem>> => [
{
name: i18n.ACTIONS,
@ -186,7 +191,7 @@ export const getSummaryTableColumns = ({
field: 'docsCount',
name: DOCS,
render: (_, { docsCount, patternDocsCount }) => (
<ProgressContainer>
<div css={styles.progressContainer}>
<EuiProgress
data-test-subj="docsCount"
label={formatNumber(docsCount)}
@ -195,7 +200,7 @@ export const getSummaryTableColumns = ({
value={docsCount}
valueText={getDocsCountPercent({ docsCount, patternDocsCount })}
/>
</ProgressContainer>
</div>
),
sortable: true,
truncateText: false,
@ -209,7 +214,7 @@ export const getSummaryTableColumns = ({
<EuiText
size="xs"
data-test-subj="incompatibleStat"
color={getIncompatibleStatColor(incompatible)}
color={getIncompatibleStatColor(incompatible, dangerColor)}
>
{incompatible ?? EMPTY_STAT}
</EuiText>

View file

@ -7,19 +7,21 @@
import { EuiFlexGroup, EuiFlexItem, EuiHealth, EuiLink, EuiText, EuiToolTip } from '@elastic/eui';
import React from 'react';
import styled from 'styled-components';
import { css } from '@emotion/react';
const DEFAULT_DATA_TEST_SUBJ = 'chartLegendItem';
export const ChartLegendLink = styled(EuiLink)`
width: 100%;
`;
export const FixedWidthLegendText = styled(EuiText)<{
$width: number | undefined;
}>`
text-align: left;
${({ $width }) => ($width != null ? `width: ${$width}px;` : '')}
`;
const getStyles = (width?: number) => {
return {
chartLegendLink: css({
width: '100%',
}),
fixedWidthLegendText: css({
textAlign: 'left',
...(width != null ? { width: `${width}px` } : {}),
}),
};
};
interface Props {
color: string | null;
@ -37,36 +39,40 @@ const ChartLegendItemComponent: React.FC<Props> = ({
onClick,
text,
textWidth,
}) => (
<ChartLegendLink
color="text"
data-test-subj={dataTestSubj}
disabled={onClick == null}
onClick={onClick}
>
<EuiFlexGroup alignItems="center" gutterSize="none" justifyContent="spaceBetween">
<EuiFlexItem grow={false}>
<EuiToolTip content={text}>
{color != null ? (
<EuiHealth color={color}>
<FixedWidthLegendText className="eui-textTruncate" size="xs" $width={textWidth}>
}) => {
const styles = getStyles(textWidth);
return (
<EuiLink
css={styles.chartLegendLink}
color="text"
data-test-subj={dataTestSubj}
disabled={onClick == null}
onClick={onClick}
>
<EuiFlexGroup alignItems="center" gutterSize="none" justifyContent="spaceBetween">
<EuiFlexItem grow={false}>
<EuiToolTip content={text}>
{color != null ? (
<EuiHealth color={color}>
<EuiText css={styles.fixedWidthLegendText} className="eui-textTruncate" size="xs">
{text}
</EuiText>
</EuiHealth>
) : (
<EuiText css={styles.fixedWidthLegendText} className="eui-textTruncate" size="xs">
{text}
</FixedWidthLegendText>
</EuiHealth>
) : (
<FixedWidthLegendText className="eui-textTruncate" size="xs" $width={textWidth}>
{text}
</FixedWidthLegendText>
)}
</EuiToolTip>
</EuiFlexItem>
</EuiText>
)}
</EuiToolTip>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiText size="xs">{count}</EuiText>
</EuiFlexItem>
</EuiFlexGroup>
</ChartLegendLink>
);
<EuiFlexItem grow={false}>
<EuiText size="xs">{count}</EuiText>
</EuiFlexItem>
</EuiFlexGroup>
</EuiLink>
);
};
ChartLegendItemComponent.displayName = 'ChartLegendItemComponent';

View file

@ -1,10 +0,0 @@
/*
* 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 { euiThemeVars } from '@kbn/ui-theme';
export const DEFAULT_INDEX_COLOR = euiThemeVars.euiColorPrimary;

View file

@ -66,6 +66,10 @@ jest.mock('@elastic/charts', () => {
};
});
const successColor = 'test-success-color';
const dangerColor = 'test-danger-color';
const primaryColor = 'test-primary-color';
describe('StorageTreemap', () => {
describe('when data is provided', () => {
beforeEach(() => {
@ -85,7 +89,7 @@ describe('StorageTreemap', () => {
});
test('it renders the legend with the expected overflow-y style', () => {
expect(screen.getByTestId('legend')).toHaveClass('eui-yScroll');
expect(screen.getByTestId('legend')).toHaveClass('eui-scrollBar');
});
test('it uses a theme with the expected `minFontSize` to show more labels at various screen resolutions', () => {
@ -93,7 +97,14 @@ describe('StorageTreemap', () => {
});
describe('legend items', () => {
const allLegendItems = getLegendItems({ patterns, flattenedBuckets, patternRollups });
const allLegendItems = getLegendItems({
patterns,
flattenedBuckets,
patternRollups,
successColor,
dangerColor,
primaryColor,
});
describe('pattern legend items', () => {
const justPatterns = allLegendItems.filter((x) => x.ilmPhase == null);

View file

@ -18,10 +18,10 @@ import type {
XYChartElementEvent,
} from '@elastic/charts';
import { Chart, Partition, PartitionLayout, Settings } from '@elastic/charts';
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { EuiFlexGroup, EuiFlexItem, useEuiTheme } from '@elastic/eui';
import React, { useCallback, useMemo } from 'react';
import { i18n } from '@kbn/i18n';
import styled from 'styled-components';
import { css } from '@emotion/react';
import { ChartLegendItem } from './chart_legend_item';
import { NoData } from './no_data';
@ -32,24 +32,32 @@ import { getPathToFlattenedBucketMap } from './utils/get_path_to_flattened_bucke
import { getLayersMultiDimensional } from './utils/get_layers_multi_dimensional';
import { getLegendItems } from './utils/get_legend_items';
export const ChartFlexItem = styled(EuiFlexItem)<{
$maxChartHeight: number | undefined;
$minChartHeight: number;
}>`
${({ $maxChartHeight }) => ($maxChartHeight != null ? `max-height: ${$maxChartHeight}px;` : '')}
min-height: ${({ $minChartHeight }) => `${$minChartHeight}px`};
`;
interface StyleProps {
maxChartHeight?: number;
minChartHeight: number;
height?: number;
width?: number;
}
export const LegendContainer = styled.div<{
$height?: number;
$width?: number;
}>`
margin-left: ${({ theme }) => theme.eui.euiSizeM};
margin-top: ${({ theme }) => theme.eui.euiSizeM};
${({ $height }) => ($height != null ? `height: ${$height}px;` : '')}
scrollbar-width: thin;
${({ $width }) => ($width != null ? `width: ${$width}px;` : '')}
`;
const useStyles = ({ maxChartHeight, minChartHeight, height, width }: StyleProps) => {
const { euiTheme } = useEuiTheme();
return {
chart: css({
...(maxChartHeight != null && { maxHeight: `${maxChartHeight}px` }),
minHeight: `${minChartHeight}px`,
}),
legendContainer: css({
marginLeft: euiTheme.size.m,
marginTop: euiTheme.size.m,
overflowY: 'auto',
...(height != null && { height: `${height}px` }),
scrollbarWidth: 'thin',
...(width != null && { width: `${width}px` }),
}),
};
};
export const DEFAULT_MIN_CHART_HEIGHT = 240; // px
export const LEGEND_WIDTH = 220; // px
@ -107,6 +115,13 @@ const StorageTreemapComponent: React.FC<Props> = ({
patternRollups,
valueFormatter,
}: Props) => {
const styles = useStyles({
maxChartHeight,
minChartHeight,
height: maxChartHeight,
width: LEGEND_WIDTH,
});
const { euiTheme } = useEuiTheme();
const { theme, baseTheme, patterns } = useDataQualityContext();
const fillColor = useMemo(
() => theme?.background?.color ?? baseTheme.background.color,
@ -146,15 +161,40 @@ const StorageTreemapComponent: React.FC<Props> = ({
valueFormatter,
layer0FillColor: fillColor,
pathToFlattenedBucketMap,
successColor: euiTheme.colors.success,
dangerColor: euiTheme.colors.danger,
primaryColor: euiTheme.colors.primary,
}),
[fillColor, valueFormatter, pathToFlattenedBucketMap]
[
valueFormatter,
fillColor,
pathToFlattenedBucketMap,
euiTheme.colors.success,
euiTheme.colors.danger,
euiTheme.colors.primary,
]
);
const valueAccessor = useCallback((d: Datum) => d[accessor], [accessor]);
const legendItems = useMemo(
() => getLegendItems({ patterns, flattenedBuckets, patternRollups }),
[flattenedBuckets, patternRollups, patterns]
() =>
getLegendItems({
patterns,
flattenedBuckets,
patternRollups,
successColor: euiTheme.colors.success,
dangerColor: euiTheme.colors.danger,
primaryColor: euiTheme.colors.primary,
}),
[
euiTheme.colors.danger,
euiTheme.colors.primary,
euiTheme.colors.success,
flattenedBuckets,
patternRollups,
patterns,
]
);
if (flattenedBuckets.length === 0) {
@ -163,7 +203,7 @@ const StorageTreemapComponent: React.FC<Props> = ({
return (
<EuiFlexGroup data-test-subj="storageTreemap" gutterSize="none">
<ChartFlexItem grow={true} $maxChartHeight={maxChartHeight} $minChartHeight={minChartHeight}>
<EuiFlexItem css={styles.chart} grow={true}>
{flattenedBuckets.length === 0 ? (
<NoData />
) : (
@ -185,15 +225,10 @@ const StorageTreemapComponent: React.FC<Props> = ({
/>
</Chart>
)}
</ChartFlexItem>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<LegendContainer
data-test-subj="legend"
$height={maxChartHeight}
className="eui-yScroll"
$width={LEGEND_WIDTH}
>
<div css={styles.legendContainer} data-test-subj="legend" className="eui-scrollBar">
{legendItems.map(({ color, ilmPhase, index, pattern, sizeInBytes, docsCount }) => (
<ChartLegendItem
color={color}
@ -211,7 +246,7 @@ const StorageTreemapComponent: React.FC<Props> = ({
textWidth={LEGEND_TEXT_WITH}
/>
))}
</LegendContainer>
</div>
</EuiFlexItem>
</EuiFlexGroup>
);

View file

@ -5,44 +5,57 @@
* 2.0.
*/
import { EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiText } from '@elastic/eui';
import { EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiText, useEuiTheme } from '@elastic/eui';
import React from 'react';
import styled from 'styled-components';
import { css } from '@emotion/react';
import * as i18n from '../translations';
const NoDataLabel = styled(EuiText)`
text-align: center;
`;
const useStyles = () => {
const { euiTheme } = useEuiTheme();
return {
noDataLabel: css({
textAlign: 'center',
}),
container: css({
padding: `${euiTheme.size.m} 0`,
}),
};
};
interface Props {
reason?: string;
}
const StyledContainer = styled.div`
padding: ${({ theme }) => theme.eui.euiSizeM} 0;
`;
const NoDataComponent: React.FC<Props> = ({ reason }) => {
const styles = useStyles();
return (
<EuiFlexGroup alignItems="center" gutterSize="none">
<EuiFlexItem grow>
<div css={styles.container}>
<EuiText css={styles.noDataLabel} color="subdued" data-test-subj="noDataLabel" size="xs">
{i18n.NO_DATA_LABEL}
</EuiText>
const NoDataComponent: React.FC<Props> = ({ reason }) => (
<EuiFlexGroup alignItems="center" gutterSize="none">
<EuiFlexItem grow>
<StyledContainer>
<NoDataLabel color="subdued" data-test-subj="noDataLabel" size="xs">
{i18n.NO_DATA_LABEL}
</NoDataLabel>
{reason != null && (
<>
<EuiSpacer size="s" />
<NoDataLabel color="subdued" data-test-subj="reasonLabel" size="xs">
{reason}
</NoDataLabel>
</>
)}
</StyledContainer>
</EuiFlexItem>
</EuiFlexGroup>
);
{reason != null && (
<>
<EuiSpacer size="s" />
<EuiText
css={styles.noDataLabel}
color="subdued"
data-test-subj="reasonLabel"
size="xs"
>
{reason}
</EuiText>
</>
)}
</div>
</EuiFlexItem>
</EuiFlexGroup>
);
};
NoDataComponent.displayName = 'NoDataComponent';

View file

@ -5,27 +5,34 @@
* 2.0.
*/
import { euiThemeVars } from '@kbn/ui-theme';
import { getFillColor } from './get_fill_color';
import { DEFAULT_INDEX_COLOR } from '../constants';
const successColor = 'test-success-color';
const dangerColor = 'test-danger-color';
const primaryColor = 'test-primary-color';
describe('getFillColor', () => {
test('it returns success when `incompatible` is zero', () => {
const incompatible = 0;
expect(getFillColor(incompatible)).toEqual(euiThemeVars.euiColorSuccess);
expect(getFillColor(incompatible, successColor, dangerColor, primaryColor)).toEqual(
successColor
);
});
test('it returns danger when `incompatible` is greater than 0', () => {
const incompatible = 1;
expect(getFillColor(incompatible)).toEqual(euiThemeVars.euiColorDanger);
expect(getFillColor(incompatible, successColor, dangerColor, primaryColor)).toEqual(
dangerColor
);
});
test('it returns the default color when `incompatible` is undefined', () => {
const incompatible = undefined;
expect(getFillColor(incompatible)).toEqual(DEFAULT_INDEX_COLOR);
expect(getFillColor(incompatible, successColor, dangerColor, primaryColor)).toEqual(
primaryColor
);
});
});

View file

@ -4,15 +4,18 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { euiThemeVars } from '@kbn/ui-theme';
import { DEFAULT_INDEX_COLOR } from '../constants';
export const getFillColor = (incompatible: number | undefined): string => {
export const getFillColor = (
incompatible: number | undefined,
successColor: string,
dangerColor: string,
primaryColor: string
): string => {
if (incompatible === 0) {
return euiThemeVars.euiColorSuccess;
return successColor;
} else if (incompatible != null && incompatible > 0) {
return euiThemeVars.euiColorDanger;
return dangerColor;
} else {
return DEFAULT_INDEX_COLOR;
return primaryColor;
}
};

View file

@ -28,6 +28,10 @@ const patternRollups: Record<string, PatternRollup> = {
'packetbeat-*': packetbeatNoResults,
};
const successColor = 'test-success-color';
const dangerColor = 'test-danger-color';
const primaryColor = 'test-primary-color';
describe('getGroupFromPath', () => {
it('returns the expected group from the path', () => {
expect(
@ -76,6 +80,9 @@ describe('getLayersMultiDimensional', () => {
valueFormatter: formatBytes,
layer0FillColor,
pathToFlattenedBucketMap,
successColor,
dangerColor,
primaryColor,
}).length
).toEqual(2);
});
@ -85,6 +92,9 @@ describe('getLayersMultiDimensional', () => {
valueFormatter: formatBytes,
layer0FillColor,
pathToFlattenedBucketMap,
successColor,
dangerColor,
primaryColor,
}).forEach((x) => expect(x.fillLabel.valueFormatter(123)).toEqual('123B'));
});
});

View file

@ -25,10 +25,16 @@ export const getLayersMultiDimensional = ({
valueFormatter,
layer0FillColor,
pathToFlattenedBucketMap,
successColor,
dangerColor,
primaryColor,
}: {
valueFormatter: (value: number) => string;
layer0FillColor: string;
pathToFlattenedBucketMap: Record<string, FlattenedBucket | undefined>;
successColor: string;
dangerColor: string;
primaryColor: string;
}) => {
return [
{
@ -52,7 +58,12 @@ export const getLayersMultiDimensional = ({
const pattern = getGroupFromPath(node.path) ?? '';
const flattenedBucket = pathToFlattenedBucketMap[`${pattern}${indexName}`];
return getFillColor(flattenedBucket?.incompatible);
return getFillColor(
flattenedBucket?.incompatible,
successColor,
dangerColor,
primaryColor
);
},
},
},

View file

@ -5,8 +5,6 @@
* 2.0.
*/
import { euiThemeVars } from '@kbn/ui-theme';
import { getFlattenedBuckets } from '../../utils/get_flattened_buckets';
import { alertIndexWithAllResults } from '../../../../mock/pattern_rollup/mock_alerts_pattern_rollup';
import { auditbeatWithAllResults } from '../../../../mock/pattern_rollup/mock_auditbeat_pattern_rollup';
@ -22,6 +20,10 @@ const patternRollups: Record<string, PatternRollup> = {
'packetbeat-*': packetbeatNoResults,
};
const successColor = 'test-success-color';
const dangerColor = 'test-danger-color';
const primaryColor = 'test-primary-color';
describe('getPatternLegendItem', () => {
test('it returns the expected legend item', () => {
const pattern = 'auditbeat-*';
@ -46,9 +48,17 @@ describe('getLegendItemsForPattern', () => {
patternRollups,
});
expect(getLegendItemsForPattern({ pattern, flattenedBuckets })).toEqual([
expect(
getLegendItemsForPattern({
pattern,
flattenedBuckets,
successColor,
dangerColor,
primaryColor,
})
).toEqual([
{
color: euiThemeVars.euiColorSuccess,
color: successColor,
ilmPhase: 'hot',
index: '.ds-auditbeat-8.6.1-2023.02.07-000001',
pattern: 'auditbeat-*',
@ -56,7 +66,7 @@ describe('getLegendItemsForPattern', () => {
docsCount: 19123,
},
{
color: euiThemeVars.euiColorDanger,
color: dangerColor,
ilmPhase: 'unmanaged',
index: 'auditbeat-custom-index-1',
pattern: 'auditbeat-*',
@ -64,7 +74,7 @@ describe('getLegendItemsForPattern', () => {
docsCount: 4,
},
{
color: euiThemeVars.euiColorDanger,
color: dangerColor,
ilmPhase: 'unmanaged',
index: 'auditbeat-custom-empty-index-1',
pattern: 'auditbeat-*',
@ -81,9 +91,17 @@ describe('getLegendItemsForPattern', () => {
isILMAvailable: false,
patternRollups,
});
expect(getLegendItemsForPattern({ pattern, flattenedBuckets })).toEqual([
expect(
getLegendItemsForPattern({
pattern,
flattenedBuckets,
successColor,
dangerColor,
primaryColor,
})
).toEqual([
{
color: euiThemeVars.euiColorSuccess,
color: successColor,
ilmPhase: null,
index: '.ds-auditbeat-8.6.1-2023.02.07-000001',
pattern: 'auditbeat-*',
@ -91,7 +109,7 @@ describe('getLegendItemsForPattern', () => {
docsCount: 19123,
},
{
color: euiThemeVars.euiColorDanger,
color: dangerColor,
ilmPhase: null,
index: 'auditbeat-custom-index-1',
pattern: 'auditbeat-*',
@ -99,7 +117,7 @@ describe('getLegendItemsForPattern', () => {
docsCount: 4,
},
{
color: euiThemeVars.euiColorDanger,
color: dangerColor,
ilmPhase: null,
index: 'auditbeat-custom-empty-index-1',
pattern: 'auditbeat-*',
@ -118,7 +136,16 @@ describe('getLegendItems', () => {
patternRollups,
});
expect(getLegendItems({ flattenedBuckets, patterns, patternRollups })).toEqual([
expect(
getLegendItems({
flattenedBuckets,
patterns,
patternRollups,
successColor,
dangerColor,
primaryColor,
})
).toEqual([
{
color: null,
ilmPhase: null,
@ -128,7 +155,7 @@ describe('getLegendItems', () => {
docsCount: 26093,
},
{
color: euiThemeVars.euiColorSuccess,
color: successColor,
ilmPhase: 'hot',
index: '.internal.alerts-security.alerts-default-000001',
pattern: '.alerts-security.alerts-default',
@ -144,7 +171,7 @@ describe('getLegendItems', () => {
docsCount: 19127,
},
{
color: euiThemeVars.euiColorSuccess,
color: successColor,
ilmPhase: 'hot',
index: '.ds-auditbeat-8.6.1-2023.02.07-000001',
pattern: 'auditbeat-*',
@ -152,7 +179,7 @@ describe('getLegendItems', () => {
docsCount: 19123,
},
{
color: euiThemeVars.euiColorDanger,
color: dangerColor,
ilmPhase: 'unmanaged',
index: 'auditbeat-custom-index-1',
pattern: 'auditbeat-*',
@ -160,7 +187,7 @@ describe('getLegendItems', () => {
docsCount: 4,
},
{
color: euiThemeVars.euiColorDanger,
color: dangerColor,
ilmPhase: 'unmanaged',
index: 'auditbeat-custom-empty-index-1',
pattern: 'auditbeat-*',
@ -176,7 +203,7 @@ describe('getLegendItems', () => {
docsCount: 3258632,
},
{
color: euiThemeVars.euiColorPrimary,
color: primaryColor,
ilmPhase: 'hot',
index: '.ds-packetbeat-8.5.3-2023.02.04-000001',
pattern: 'packetbeat-*',
@ -184,7 +211,7 @@ describe('getLegendItems', () => {
docsCount: 1630289,
},
{
color: euiThemeVars.euiColorPrimary,
color: primaryColor,
ilmPhase: 'hot',
index: '.ds-packetbeat-8.6.1-2023.02.04-000001',
pattern: 'packetbeat-*',

View file

@ -14,9 +14,15 @@ import { getPatternDocsCount, getPatternSizeInBytes } from './stats';
export const getLegendItemsForPattern = ({
pattern,
flattenedBuckets,
successColor,
dangerColor,
primaryColor,
}: {
pattern: string;
flattenedBuckets: FlattenedBucket[];
successColor: string;
dangerColor: string;
primaryColor: string;
}): LegendItem[] =>
orderBy(
['sizeInBytes'],
@ -24,7 +30,7 @@ export const getLegendItemsForPattern = ({
flattenedBuckets
.filter((x) => x.pattern === pattern)
.map((flattenedBucket) => ({
color: getFillColor(flattenedBucket.incompatible),
color: getFillColor(flattenedBucket.incompatible, successColor, dangerColor, primaryColor),
ilmPhase: flattenedBucket.ilmPhase ?? null,
index: flattenedBucket.indexName ?? null,
pattern: flattenedBucket.pattern,
@ -52,16 +58,28 @@ export const getLegendItems = ({
patterns,
flattenedBuckets,
patternRollups,
successColor,
dangerColor,
primaryColor,
}: {
patterns: string[];
flattenedBuckets: FlattenedBucket[];
patternRollups: Record<string, PatternRollup>;
successColor: string;
dangerColor: string;
primaryColor: string;
}): LegendItem[] =>
patterns.reduce<LegendItem[]>(
(acc, pattern) => [
...acc,
getPatternLegendItem({ pattern, patternRollups }),
...getLegendItemsForPattern({ pattern, flattenedBuckets }),
...getLegendItemsForPattern({
pattern,
flattenedBuckets,
successColor,
dangerColor,
primaryColor,
}),
],
[]
);

View file

@ -8,6 +8,7 @@
import {
EuiComboBox,
EuiComboBoxOptionOption,
EuiFormControlLayout,
EuiFormLabel,
EuiToolTip,
useGeneratedHtmlId,
@ -22,17 +23,17 @@ import {
SELECT_ONE_OR_MORE_ILM_PHASES,
} from '../../translations';
import { useDataQualityContext } from '../../data_quality_context';
import { StyledFormControlLayout, StyledOption, StyledOptionLabel } from './styles';
import { formControlLayoutCss, optionCss, optionLabelCss } from './styles';
const renderOption = (
option: EuiComboBoxOptionOption<string | number | string[] | undefined>
): React.ReactNode => (
<EuiToolTip content={`${option.label}: ${getIlmPhaseDescription(option.label)}`}>
<StyledOption>
<StyledOptionLabel>{`${option.label}`}</StyledOptionLabel>
<div css={optionCss}>
<span css={optionLabelCss}>{`${option.label}`}</span>
{': '}
<span>{getIlmPhaseDescription(option.label)}</span>
</StyledOption>
</div>
</EuiToolTip>
);
@ -53,7 +54,7 @@ const IlmPhaseFilterComponent: React.FC = () => {
return (
<EuiToolTip display="block" content={INDEX_LIFECYCLE_MANAGEMENT_PHASES}>
<StyledFormControlLayout fullWidth={true} prepend={ilmFormLabel}>
<EuiFormControlLayout css={formControlLayoutCss} fullWidth={true} prepend={ilmFormLabel}>
<EuiComboBox
id={labelInputId}
data-test-subj="selectIlmPhases"
@ -64,7 +65,7 @@ const IlmPhaseFilterComponent: React.FC = () => {
options={ilmPhaseOptionsStatic}
onChange={handleSetSelectedOptions}
/>
</StyledFormControlLayout>
</EuiFormControlLayout>
</EuiToolTip>
);
};

View file

@ -5,24 +5,23 @@
* 2.0.
*/
import { EuiFormControlLayout } from '@elastic/eui';
import styled from 'styled-components';
import { UseEuiTheme } from '@elastic/eui';
import { CSSObject, css } from '@emotion/react';
export const StyledOption = styled.div`
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
width: 100%;
`;
export const optionCss = css({
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
width: '100%',
});
export const StyledOptionLabel = styled.span`
font-weight: ${({ theme }) => theme.eui.euiFontWeightBold};
`;
export const optionLabelCss = ({ euiTheme }: UseEuiTheme): CSSObject => ({
fontWeight: euiTheme.font.weight.bold,
});
export const StyledFormControlLayout = styled(EuiFormControlLayout)`
height: 42px;
.euiFormControlLayout__childrenWrapper {
overflow: visible;
}
`;
export const formControlLayoutCss = css({
height: 42,
'.euiFormControlLayout__childrenWrapper': {
overflow: 'visible',
},
});

View file

@ -5,9 +5,9 @@
* 2.0.
*/
import { EuiFlexGroup, EuiFlexItem, EuiPanel } from '@elastic/eui';
import { EuiFlexGroup, EuiFlexItem, EuiPanel, useEuiTheme } from '@elastic/eui';
import React from 'react';
import styled from 'styled-components';
import { css } from '@emotion/react';
import { StatsRollup } from '../stats_rollup';
import { SummaryActions } from './summary_actions';
@ -18,53 +18,57 @@ import { useResultsRollupContext } from '../contexts/results_rollup_context';
const MAX_SUMMARY_ACTIONS_CONTAINER_WIDTH = 400;
const MIN_SUMMARY_ACTIONS_CONTAINER_WIDTH = 235;
const StyledFlexGroup = styled(EuiFlexGroup)`
min-height: calc(174px - ${({ theme }) => theme.eui.euiSizeL} * 2);
`;
const useStyles = () => {
const { euiTheme } = useEuiTheme();
const StyledFlexItem = styled(EuiFlexItem)`
gap: ${({ theme }) => theme.eui.euiSizeL};
`;
const SummaryActionsContainerFlexItem = styled(EuiFlexItem)`
max-width: ${MAX_SUMMARY_ACTIONS_CONTAINER_WIDTH}px;
min-width: ${MIN_SUMMARY_ACTIONS_CONTAINER_WIDTH}px;
`;
const StyledIlmPhaseFilterContainer = styled.div`
width: 100%;
max-width: 432px;
align-self: flex-end;
`;
const StyledRollupContainer = styled.div`
margin-top: auto;
`;
return {
flexGroup: css({
minHeight: `calc(174px - ${euiTheme.size.l} * 2)`,
}),
flexItem: css({
gap: euiTheme.size.l,
}),
summaryActionsContainer: css({
maxWidth: MAX_SUMMARY_ACTIONS_CONTAINER_WIDTH,
minWidth: MIN_SUMMARY_ACTIONS_CONTAINER_WIDTH,
}),
ilmPhaseFilterContainer: css({
width: '100%',
maxWidth: 432,
alignSelf: 'flex-end',
}),
rollupContainer: css({
marginTop: 'auto',
}),
};
};
const DataQualitySummaryComponent: React.FC = () => {
const styles = useStyles();
const { isILMAvailable } = useDataQualityContext();
const { totalIndices, totalDocsCount, totalIndicesChecked, totalIncompatible, totalSizeInBytes } =
useResultsRollupContext();
return (
<EuiPanel paddingSize="l" data-test-subj="dataQualitySummary" hasShadow={true}>
<StyledFlexGroup
<EuiFlexGroup
css={styles.flexGroup}
alignItems="stretch"
gutterSize="none"
justifyContent="spaceBetween"
wrap={true}
>
<SummaryActionsContainerFlexItem grow={false}>
<EuiFlexItem css={styles.summaryActionsContainer} grow={false}>
<SummaryActions />
</SummaryActionsContainerFlexItem>
</EuiFlexItem>
<StyledFlexItem grow={false}>
<EuiFlexItem css={styles.flexItem} grow={false}>
{isILMAvailable && (
<StyledIlmPhaseFilterContainer>
<div css={styles.ilmPhaseFilterContainer}>
<IlmPhaseFilter />
</StyledIlmPhaseFilterContainer>
</div>
)}
<StyledRollupContainer>
<div css={styles.rollupContainer}>
<StatsRollup
docsCount={totalDocsCount}
incompatible={totalIncompatible}
@ -72,9 +76,9 @@ const DataQualitySummaryComponent: React.FC = () => {
indicesChecked={totalIndicesChecked}
sizeInBytes={totalSizeInBytes}
/>
</StyledRollupContainer>
</StyledFlexItem>
</StyledFlexGroup>
</div>
</EuiFlexItem>
</EuiFlexGroup>
</EuiPanel>
);
};

View file

@ -7,7 +7,7 @@
import { EuiButton } from '@elastic/eui';
import React, { useCallback, useEffect, useState } from 'react';
import styled from 'styled-components';
import { css } from '@emotion/react';
import { v4 as uuidv4 } from 'uuid';
import { getAllIndicesToCheck } from './utils/get_all_indices_to_check';
@ -18,9 +18,11 @@ import * as i18n from '../../../translations';
import type { IndexToCheck } from '../../../types';
import { useAbortControllerRef } from '../../../hooks/use_abort_controller_ref';
const CheckAllButton = styled(EuiButton)`
width: 112px;
`;
const styles = {
checkAllButton: css({
width: '112px',
}),
};
async function wait(ms: number) {
const delay = () =>
@ -150,7 +152,8 @@ const CheckAllComponent: React.FC<Props> = ({
const disabled = isILMAvailable && ilmPhases.length === 0;
return (
<CheckAllButton
<EuiButton
css={styles.checkAllButton}
aria-label={isRunning ? i18n.CANCEL : i18n.CHECK_ALL}
data-test-subj="checkAll"
disabled={disabled}
@ -158,7 +161,7 @@ const CheckAllComponent: React.FC<Props> = ({
onClick={onClick}
>
{isRunning ? i18n.CANCEL : i18n.CHECK_ALL}
</CheckAllButton>
</EuiButton>
);
};

View file

@ -7,7 +7,7 @@
import { EuiInMemoryTable } from '@elastic/eui';
import React, { useMemo } from 'react';
import styled from 'styled-components';
import { css } from '@emotion/react';
import {
ERRORS_CONTAINER_MAX_WIDTH,
@ -16,10 +16,12 @@ import {
} from './helpers';
import type { ErrorSummary } from '../../../../../types';
const ErrorsViewerContainer = styled.div`
max-width: ${ERRORS_CONTAINER_MAX_WIDTH}px;
min-width: ${ERRORS_CONTAINER_MIN_WIDTH}px;
`;
const styles = {
errorsViewerContainer: css({
maxWidth: ERRORS_CONTAINER_MAX_WIDTH,
minWidth: ERRORS_CONTAINER_MIN_WIDTH,
}),
};
interface Props {
errorSummary: ErrorSummary[];
@ -29,7 +31,7 @@ const ErrorsViewerComponent: React.FC<Props> = ({ errorSummary }) => {
const columns = useMemo(() => getErrorsViewerTableColumns(), []);
return (
<ErrorsViewerContainer data-test-subj="errorsViewer">
<div css={styles.errorsViewerContainer} data-test-subj="errorsViewer">
<EuiInMemoryTable
columns={columns}
compressed={true}
@ -37,7 +39,7 @@ const ErrorsViewerComponent: React.FC<Props> = ({ errorSummary }) => {
sorting={false}
pagination={true}
/>
</ErrorsViewerContainer>
</div>
);
};

View file

@ -14,7 +14,7 @@ import {
EuiSpacer,
} from '@elastic/eui';
import React, { useCallback, useMemo, useState } from 'react';
import styled from 'styled-components';
import { css } from '@emotion/react';
import {
COPY_TO_CLIPBOARD,
@ -35,9 +35,11 @@ import { getErrorsMarkdownTable, getErrorsMarkdownTableRows } from '../../utils/
import { ERROR, ERRORS, PATTERN } from '../../translations';
import { COPIED_ERRORS_TOAST_TITLE, VIEW_ERRORS } from './translations';
const CallOut = styled(EuiCallOut)`
max-width: ${ERRORS_CONTAINER_MAX_WIDTH}px;
`;
const styles = {
callout: css({
maxWidth: `${ERRORS_CONTAINER_MAX_WIDTH}px`,
}),
};
interface Props {
addSuccessToast: (toast: { title: string }) => void;
@ -89,7 +91,13 @@ const ErrorsPopoverComponent: React.FC<Props> = ({ addSuccessToast, errorSummary
isOpen={isPopoverOpen}
panelPaddingSize="s"
>
<CallOut color="danger" data-test-subj="callout" size="s" title={ERRORS}>
<EuiCallOut
css={styles.callout}
color="danger"
data-test-subj="callout"
size="s"
title={ERRORS}
>
<p>{ERRORS_CALLOUT_SUMMARY}</p>
<p>{ERRORS_MAY_OCCUR}</p>
@ -116,7 +124,7 @@ const ErrorsPopoverComponent: React.FC<Props> = ({ addSuccessToast, errorSummary
>
{COPY_TO_CLIPBOARD}
</EuiButtonEmpty>
</CallOut>
</EuiCallOut>
<EuiSpacer />

View file

@ -8,7 +8,7 @@
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { sortBy } from 'lodash/fp';
import React, { useCallback, useMemo, useState } from 'react';
import styled from 'styled-components';
import { css } from '@emotion/react';
import { CheckAll } from './check_all';
import { CheckStatus } from './check_status';
@ -30,10 +30,12 @@ import { getSummaryTableMarkdownHeader, getSummaryTableMarkdownRow } from '../..
import { ERROR, ERRORS, PATTERN } from './translations';
import { INDEX } from '../../translations';
const StyledActionsContainerFlexItem = styled(EuiFlexItem)`
margin-top: auto;
padding-bottom: 3px;
`;
const styles = {
actionsContainer: css({
marginTop: 'auto',
paddingBottom: '3px',
}),
};
export const getResultsSortedByDocsCount = (
results: Record<string, DataQualityCheckResult> | undefined
@ -218,13 +220,13 @@ const SummaryActionsComponent: React.FC = () => {
/>
</EuiFlexItem>
<StyledActionsContainerFlexItem grow={false}>
<EuiFlexItem css={styles.actionsContainer} grow={false}>
<Actions
showAddToNewCaseAction={true}
showCopyToClipboardAction={true}
markdownComment={markdownComment}
/>
</StyledActionsContainerFlexItem>
</EuiFlexItem>
</EuiFlexGroup>
</>
);

View file

@ -9,14 +9,15 @@ import { actionTypeRegistryMock } from '@kbn/triggers-actions-ui-plugin/public/a
import { httpServiceMock } from '@kbn/core-http-browser-mocks';
import { notificationServiceMock } from '@kbn/core-notifications-browser-mocks';
import { AssistantAvailability, AssistantProvider } from '@kbn/elastic-assistant';
import { I18nProvider } from '@kbn/i18n-react';
import { euiDarkVars } from '@kbn/ui-theme';
import React from 'react';
import { ThemeProvider } from 'styled-components';
import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { Theme } from '@elastic/charts';
import { coreMock } from '@kbn/core/public/mocks';
import { UserProfileService } from '@kbn/core/public';
import { I18nProvider } from '@kbn/i18n-react';
import { EuiThemeProvider } from '@elastic/eui';
import { DataQualityProvider, DataQualityProviderProps } from '../../data_quality_context';
import { ResultsRollupContext } from '../../contexts/results_rollup_context';
import { IndicesCheckContext } from '../../contexts/indices_check_context';
@ -66,30 +67,32 @@ const TestExternalProvidersComponent: React.FC<TestExternalProvidersProps> = ({
});
return (
<I18nProvider>
<ThemeProvider theme={() => ({ eui: euiDarkVars, darkMode: true })}>
<QueryClientProvider client={queryClient}>
<AssistantProvider
actionTypeRegistry={actionTypeRegistry}
assistantAvailability={mockAssistantAvailability}
augmentMessageCodeBlocks={jest.fn()}
basePath={'https://localhost:5601/kbn'}
docLinks={{
ELASTIC_WEBSITE_URL: 'https://www.elastic.co/',
DOC_LINK_VERSION: 'current',
}}
getComments={mockGetComments}
http={mockHttp}
baseConversations={{}}
navigateToApp={mockNavigateToApp}
currentAppId={'securitySolutionUI'}
userProfileService={jest.fn() as unknown as UserProfileService}
>
{children}
</AssistantProvider>
</QueryClientProvider>
</ThemeProvider>
</I18nProvider>
<KibanaRenderContextProvider {...coreMock.createStart()}>
<I18nProvider>
<EuiThemeProvider>
<QueryClientProvider client={queryClient}>
<AssistantProvider
actionTypeRegistry={actionTypeRegistry}
assistantAvailability={mockAssistantAvailability}
augmentMessageCodeBlocks={jest.fn()}
basePath={'https://localhost:5601/kbn'}
docLinks={{
ELASTIC_WEBSITE_URL: 'https://www.elastic.co/',
DOC_LINK_VERSION: 'current',
}}
getComments={mockGetComments}
http={mockHttp}
baseConversations={{}}
navigateToApp={mockNavigateToApp}
currentAppId={'securitySolutionUI'}
userProfileService={jest.fn() as unknown as UserProfileService}
>
{children}
</AssistantProvider>
</QueryClientProvider>
</EuiThemeProvider>
</I18nProvider>
</KibanaRenderContextProvider>
);
};

View file

@ -6,17 +6,22 @@
*/
import React from 'react';
import { EuiBadge, EuiText, EuiToolTip } from '@elastic/eui';
import styled from 'styled-components';
import { EuiBadge, EuiText, EuiToolTip, useEuiTheme } from '@elastic/eui';
import { css } from '@emotion/react';
const StyledText = styled(EuiText)`
white-space: nowrap;
`;
const useStyles = () => {
const { euiTheme } = useEuiTheme();
const StyledDescription = styled.span`
margin-right: ${({ theme }) => theme.eui.euiSizeS};
vertical-align: baseline;
`;
return {
text: css({
whiteSpace: 'nowrap',
}),
description: css({
marginRight: euiTheme.size.s,
verticalAlign: 'baseline',
}),
};
};
export interface Props {
badgeText: string;
@ -33,14 +38,15 @@ const StatComponent: React.FC<Props> = ({
children,
badgeProps,
}) => {
const styles = useStyles();
return (
<EuiToolTip content={tooltipText}>
<StyledText data-test-subj="stat" size={'xs'}>
{children && <StyledDescription>{children}</StyledDescription>}
<EuiText css={styles.text} data-test-subj="stat" size={'xs'}>
{children && <span css={styles.description}>{children}</span>}
<EuiBadge color={badgeColor} {...badgeProps}>
{badgeText}
</EuiBadge>
</StyledText>
</EuiText>
</EuiToolTip>
);
};

View file

@ -5,9 +5,9 @@
* 2.0.
*/
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { EuiFlexGroup, EuiFlexItem, useEuiTheme } from '@elastic/eui';
import React from 'react';
import styled from 'styled-components';
import { css } from '@emotion/react';
import { EMPTY_STAT } from '../constants';
import { useDataQualityContext } from '../data_quality_context';
@ -15,19 +15,25 @@ import * as i18n from '../stat_label/translations';
import { Stat } from '../stat';
import { getIncompatibleStatBadgeColor } from '../utils/get_incompatible_stat_badge_color';
const StyledStatWrapperFlexItem = styled(EuiFlexItem)`
padding: 0 ${({ theme }) => theme.eui.euiSize};
border-right: ${({ theme }) => theme.eui.euiBorderThin};
border-color: ${({ theme }) => theme.eui.euiBorderColor};
const useStyles = () => {
const { euiTheme } = useEuiTheme();
&:last-child {
padding-right: 0;
border-right: none;
}
&:first-child {
padding-left: 0;
}
`;
return {
statWrapper: css({
padding: `0 ${euiTheme.size.base}`,
borderRight: euiTheme.border.thin,
borderColor: euiTheme.border.color,
'&:last-child': {
paddingRight: 0,
borderRight: 'none',
},
'&:first-child': {
paddingLeft: 0,
},
}),
};
};
interface Props {
docsCount?: number;
@ -46,6 +52,7 @@ const StatsRollupComponent: React.FC<Props> = ({
pattern,
sizeInBytes,
}) => {
const styles = useStyles();
const { formatNumber, formatBytes } = useDataQualityContext();
return (
@ -55,7 +62,7 @@ const StatsRollupComponent: React.FC<Props> = ({
gutterSize="none"
justifyContent="flexEnd"
>
<StyledStatWrapperFlexItem grow={false}>
<EuiFlexItem css={styles.statWrapper} grow={false}>
<Stat
tooltipText={
pattern != null
@ -67,9 +74,9 @@ const StatsRollupComponent: React.FC<Props> = ({
>
{i18n.INCOMPATIBLE_FIELDS}
</Stat>
</StyledStatWrapperFlexItem>
</EuiFlexItem>
<StyledStatWrapperFlexItem grow={false}>
<EuiFlexItem css={styles.statWrapper} grow={false}>
<Stat
tooltipText={
pattern != null
@ -80,9 +87,9 @@ const StatsRollupComponent: React.FC<Props> = ({
>
{i18n.INDICES_CHECKED}
</Stat>
</StyledStatWrapperFlexItem>
</EuiFlexItem>
<StyledStatWrapperFlexItem grow={false}>
<EuiFlexItem css={styles.statWrapper} grow={false}>
<Stat
tooltipText={
pattern != null ? i18n.TOTAL_INDICES_PATTERN_TOOL_TIP : i18n.TOTAL_INDICES_TOOL_TIP
@ -91,10 +98,10 @@ const StatsRollupComponent: React.FC<Props> = ({
>
{i18n.INDICES}
</Stat>
</StyledStatWrapperFlexItem>
</EuiFlexItem>
{sizeInBytes != null && (
<StyledStatWrapperFlexItem grow={false}>
<EuiFlexItem css={styles.statWrapper} grow={false}>
<Stat
tooltipText={
pattern != null ? i18n.TOTAL_SIZE_PATTERN_TOOL_TIP : i18n.TOTAL_SIZE_TOOL_TIP
@ -103,10 +110,10 @@ const StatsRollupComponent: React.FC<Props> = ({
>
{i18n.SIZE}
</Stat>
</StyledStatWrapperFlexItem>
</EuiFlexItem>
)}
<StyledStatWrapperFlexItem grow={false}>
<EuiFlexItem css={styles.statWrapper} grow={false}>
<Stat
tooltipText={
pattern != null ? i18n.TOTAL_DOCS_PATTERN_TOOL_TIP : i18n.TOTAL_DOCS_TOOL_TIP
@ -115,7 +122,7 @@ const StatsRollupComponent: React.FC<Props> = ({
>
{i18n.DOCS}
</Stat>
</StyledStatWrapperFlexItem>
</EuiFlexItem>
</EuiFlexGroup>
);
};

View file

@ -5,19 +5,14 @@
* 2.0.
*/
import { EuiCode } from '@elastic/eui';
import { euiThemeVars } from '@kbn/ui-theme';
import { UseEuiTheme } from '@elastic/eui';
import styled from 'styled-components';
import { CSSObject } from '@emotion/react';
export const CodeDanger = styled(EuiCode)`
color: ${euiThemeVars.euiColorDanger};
`;
export const codeDangerCss = ({ euiTheme }: UseEuiTheme): CSSObject => ({
color: euiTheme.colors.danger,
});
export const CodeSuccess = styled(EuiCode)`
color: ${euiThemeVars.euiColorSuccess};
`;
export const CodeWarning = styled(EuiCode)`
color: ${euiThemeVars.euiColorWarning};
`;
export const codeSuccessCss = ({ euiTheme }: UseEuiTheme): CSSObject => ({
color: euiTheme.colors.success,
});

View file

@ -5,6 +5,9 @@
* 2.0.
*/
// eslint-disable-next-line import/no-extraneous-dependencies
const rootConfig = require('@kbn/test/jest-preset');
module.exports = {
coverageDirectory:
'<rootDir>/target/kibana-coverage/jest/x-pack/solutions/security/packages/ecs_data_quality_dashboard_impl',
@ -23,4 +26,9 @@ module.exports = {
setupFilesAfterEnv: [
'<rootDir>/x-pack/solutions/security/packages/ecs_data_quality_dashboard/setup_tests.ts',
],
transform: {
...rootConfig.transform,
'^.+\\.(js|tsx?)$':
'<rootDir>/x-pack/solutions/security/packages/ecs_data_quality_dashboard/jest/babel-transformer.js',
},
};

View file

@ -0,0 +1,25 @@
/*
* 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.
*/
// eslint-disable-next-line import/no-extraneous-dependencies
const babelJest = require('babel-jest');
// eslint-disable-next-line import/no-extraneous-dependencies
const rootTransformerConfig = require('@kbn/test/src/jest/transforms/babel/transformer_config');
module.exports = babelJest.default.createTransformer({
...rootTransformerConfig,
presets: [
...rootTransformerConfig.presets,
[
require.resolve('@emotion/babel-preset-css-prop'),
{
labelFormat: '[filename]--[local]',
},
],
],
});

View file

@ -5,20 +5,16 @@
"types": [
"jest",
"node",
"react"
"react",
"@emotion/react/types/css-prop",
"../../../../../typings/emotion.d.ts"
]
},
"include": [
"**/*.ts",
"**/*.tsx",
],
"exclude": [
"target/**/*"
],
"include": ["**/*.ts", "**/*.tsx"],
"exclude": ["target/**/*"],
"kbn_references": [
"@kbn/i18n",
"@kbn/i18n-react",
"@kbn/ui-theme",
"@kbn/core-http-browser",
"@kbn/core-http-browser-mocks",
"@kbn/elastic-assistant",
@ -27,5 +23,6 @@
"@kbn/core-notifications-browser",
"@kbn/core-notifications-browser-mocks",
"@kbn/ai-assistant-icon",
"@kbn/react-kibana-context-render"
]
}

View file

@ -0,0 +1,25 @@
/*
* 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.
*/
// eslint-disable-next-line import/no-extraneous-dependencies
const babelJest = require('babel-jest');
// eslint-disable-next-line import/no-extraneous-dependencies
const rootTransformerConfig = require('@kbn/test/src/jest/transforms/babel/transformer_config');
module.exports = babelJest.default.createTransformer({
...rootTransformerConfig,
presets: [
...rootTransformerConfig.presets,
[
require.resolve('@emotion/babel-preset-css-prop'),
{
labelFormat: '[filename]--[local]',
},
],
],
});

View file

@ -74,8 +74,8 @@ export const TestProvidersComponent = ({
});
return (
<I18nProvider>
<MockKibanaContextProvider startServices={startServices}>
<MockKibanaContextProvider startServices={startServices}>
<I18nProvider>
<UpsellingProviderMock>
<ReduxStoreProvider store={store}>
<ThemeProvider theme={() => ({ eui: euiDarkVars, darkMode: true })}>
@ -97,8 +97,8 @@ export const TestProvidersComponent = ({
</ThemeProvider>
</ReduxStoreProvider>
</UpsellingProviderMock>
</MockKibanaContextProvider>
</I18nProvider>
</I18nProvider>
</MockKibanaContextProvider>
);
};

View file

@ -5,6 +5,9 @@
* 2.0.
*/
// eslint-disable-next-line import/no-extraneous-dependencies
const rootConfig = require('@kbn/test/jest-preset');
module.exports = {
preset: '@kbn/test',
rootDir: '../../../../../../..',
@ -16,4 +19,9 @@ module.exports = {
'<rootDir>/x-pack/solutions/security/plugins/security_solution/public/overview/**/*.{ts,tsx}',
],
moduleNameMapper: require('../../server/__mocks__/module_name_map'),
transform: {
...rootConfig.transform,
'^.+\\.(js|tsx?)$':
'<rootDir>/x-pack/solutions/security/plugins/security_solution/jest/babel-transformer.js',
},
};

View file

@ -14,6 +14,7 @@ import { useKibana as mockUseKibana } from '../../common/lib/kibana/__mocks__';
import { TestProviders } from '../../common/mock';
import { DataQuality } from './data_quality';
import { useKibana } from '../../common/lib/kibana';
import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render';
const mockedUseKibana = mockUseKibana();
@ -104,11 +105,13 @@ describe('DataQuality', () => {
describe('when indices exist, and loading is complete', () => {
beforeEach(async () => {
render(
<TestProviders>
<MemoryRouter>
<DataQuality />
</MemoryRouter>
</TestProviders>
<KibanaRenderContextProvider {...mockedUseKibana.services}>
<TestProviders>
<MemoryRouter>
<DataQuality />
</MemoryRouter>
</TestProviders>
</KibanaRenderContextProvider>
);
await waitFor(() => {});
@ -136,11 +139,13 @@ describe('DataQuality', () => {
mockUseSourcererDataView.mockReturnValue({ ...defaultUseSourcererReturn, loading: true });
render(
<TestProviders>
<MemoryRouter>
<DataQuality />
</MemoryRouter>
</TestProviders>
<KibanaRenderContextProvider {...mockedUseKibana.services}>
<TestProviders>
<MemoryRouter>
<DataQuality />
</MemoryRouter>
</TestProviders>
</KibanaRenderContextProvider>
);
await waitFor(() => {});
@ -168,11 +173,13 @@ describe('DataQuality', () => {
mockUseSignalIndex.mockReturnValue({ ...defaultUseSignalIndexReturn, loading: true });
render(
<TestProviders>
<MemoryRouter>
<DataQuality />
</MemoryRouter>
</TestProviders>
<KibanaRenderContextProvider {...mockedUseKibana.services}>
<TestProviders>
<MemoryRouter>
<DataQuality />
</MemoryRouter>
</TestProviders>
</KibanaRenderContextProvider>
);
await waitFor(() => {});
@ -205,11 +212,13 @@ describe('DataQuality', () => {
mockUseSignalIndex.mockReturnValue({ ...defaultUseSignalIndexReturn, loading: false });
render(
<TestProviders>
<MemoryRouter>
<DataQuality />
</MemoryRouter>
</TestProviders>
<KibanaRenderContextProvider {...mockedUseKibana.services}>
<TestProviders>
<MemoryRouter>
<DataQuality />
</MemoryRouter>
</TestProviders>
</KibanaRenderContextProvider>
);
await waitFor(() => {});
@ -242,11 +251,13 @@ describe('DataQuality', () => {
mockUseSignalIndex.mockReturnValue({ ...defaultUseSignalIndexReturn, loading: false });
render(
<TestProviders>
<MemoryRouter>
<DataQuality />
</MemoryRouter>
</TestProviders>
<KibanaRenderContextProvider {...mockedUseKibana.services}>
<TestProviders>
<MemoryRouter>
<DataQuality />
</MemoryRouter>
</TestProviders>
</KibanaRenderContextProvider>
);
await waitFor(() => {});
@ -279,11 +290,13 @@ describe('DataQuality', () => {
mockUseSignalIndex.mockReturnValue({ ...defaultUseSignalIndexReturn, loading: true });
render(
<TestProviders>
<MemoryRouter>
<DataQuality />
</MemoryRouter>
</TestProviders>
<KibanaRenderContextProvider {...mockedUseKibana.services}>
<TestProviders>
<MemoryRouter>
<DataQuality />
</MemoryRouter>
</TestProviders>
</KibanaRenderContextProvider>
);
await waitFor(() => {});
@ -335,11 +348,13 @@ describe('DataQuality', () => {
});
render(
<TestProviders>
<MemoryRouter>
<DataQuality />
</MemoryRouter>
</TestProviders>
<KibanaRenderContextProvider {...mockedUseKibana.services}>
<TestProviders>
<MemoryRouter>
<DataQuality />
</MemoryRouter>
</TestProviders>
</KibanaRenderContextProvider>
);
await waitFor(() => {});