Skip to content

Commit

Permalink
[Security Solution] Display readonly placeholders when field value is…
Browse files Browse the repository at this point in the history
… empty (#203826)

**Partially addresses: #171520

## Summary
This PR updates readonly components of the Rule Upgrade flyout to
display placeholders in cases when a field value is empty.

## Changes
 - Added placeholders to readonly components in the Rule Upgrade flyout
- Simplified Storybook stories to make them more readable and less
dependent on flyout context
- Added stories that showcase empty states. You can run Storybook with
`yarn storybook security_solution`.

## Screenshots
**Before / After**
<img width="2560" alt="Scherm­afbeelding 2024-12-12 om 00 05 56"
src="https://github.com/user-attachments/assets/e85a4514-4861-4be7-b0d2-534f7ad2d8cf"
/>

Work started on 11-Dec-2024
  • Loading branch information
nikitaindik authored Dec 13, 2024
1 parent f9e1089 commit 27fe7e4
Show file tree
Hide file tree
Showing 65 changed files with 568 additions and 1,236 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -39040,7 +39040,6 @@
"xpack.securitySolution.detectionEngine.rules.updatePrebuiltRulesCalloutTitle": "Mises à jour disponibles pour les règles installées. Examinez et mettez à jour dans {link}.",
"xpack.securitySolution.detectionEngine.rules.updatePrePackagedRulesAndTimelinesButton": "Mettez à jour {updateRules} {updateRules, plural, =1 {règle prédéfinie} other {règles prédéfinies}} d'Elastic et {updateTimelines} {updateTimelines, plural, =1 {chronologie prédéfinie} other {chronologies prédéfinies}} d'Elastic",
"xpack.securitySolution.detectionEngine.rules.updatePrePackagedTimelinesButton": "Mettez à jour {updateTimelines} {updateTimelines, plural, =1 {chronologie prédéfinie} other {chronologies prédéfinies}} d'Elastic",
"xpack.securitySolution.detectionEngine.rules.upgradeRuleFields.dataSourceLabel": "Source de données",
"xpack.securitySolution.detectionEngine.rules.upgradeRuleFields.eqlQueryLabel": "Requête EQL",
"xpack.securitySolution.detectionEngine.rules.upgradeRuleFields.esqlQueryLabel": "Requête ESQL",
"xpack.securitySolution.detectionEngine.rules.upgradeRuleFields.kqlQueryLabel": "Requête KQL",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38897,7 +38897,6 @@
"xpack.securitySolution.detectionEngine.rules.updatePrebuiltRulesCalloutTitle": "インストールされたルールの更新が利用可能です。{link}で確認して更新してください。",
"xpack.securitySolution.detectionEngine.rules.updatePrePackagedRulesAndTimelinesButton": "{updateRules} Elasticの事前構築済みの{updateRules, plural, other {個のルール}}と{updateTimelines} Elasticの事前構築済みの{updateTimelines, plural, other {個のタイムライン}}を更新",
"xpack.securitySolution.detectionEngine.rules.updatePrePackagedTimelinesButton": "{updateTimelines} Elastic事前構築済み{updateTimelines, plural, other {タイムライン}}を更新",
"xpack.securitySolution.detectionEngine.rules.upgradeRuleFields.dataSourceLabel": "データソース",
"xpack.securitySolution.detectionEngine.rules.upgradeRuleFields.eqlQueryLabel": "EQL クエリ",
"xpack.securitySolution.detectionEngine.rules.upgradeRuleFields.esqlQueryLabel": "EQLクエリ",
"xpack.securitySolution.detectionEngine.rules.upgradeRuleFields.kqlQueryLabel": "KQLクエリ",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38321,7 +38321,6 @@
"xpack.securitySolution.detectionEngine.rules.updatePrebuiltRulesCalloutTitle": "已安装规则有可用更新。在 {link} 中复查并更新。",
"xpack.securitySolution.detectionEngine.rules.updatePrePackagedRulesAndTimelinesButton": "更新 {updateRules} 个 Elastic 预构建{updateRules, plural, other {规则}}及 {updateTimelines} 个 Elastic 预构建{updateTimelines, plural, other {时间线}}",
"xpack.securitySolution.detectionEngine.rules.updatePrePackagedTimelinesButton": "更新 {updateTimelines} 个 Elastic 预构建{updateTimelines, plural, other {时间线}}",
"xpack.securitySolution.detectionEngine.rules.upgradeRuleFields.dataSourceLabel": "数据源",
"xpack.securitySolution.detectionEngine.rules.upgradeRuleFields.eqlQueryLabel": "EQL 查询",
"xpack.securitySolution.detectionEngine.rules.upgradeRuleFields.esqlQueryLabel": "ESQL 查询",
"xpack.securitySolution.detectionEngine.rules.upgradeRuleFields.kqlQueryLabel": "KQL 查询",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,17 @@ import {
THREAT_INDEX_FIELD_LABEL,
THREAT_MAPPING_FIELD_LABEL,
HISTORY_WINDOW_SIZE_FIELD_LABEL,
DATA_SOURCE_FIELD_LABEL,
ALERT_SUPPRESSION_FIELD_LABEL,
INVESTIGATION_FIELDS_FIELD_LABEL,
} from '../translations';

/**
* Used when fields have different display names or formats than their corresponding rule object fields
*/
export const fieldToDisplayNameMap: Record<string, string> = {
data_source: i18n.translate(
'xpack.securitySolution.detectionEngine.rules.upgradeRuleFields.dataSourceLabel',
{
defaultMessage: 'Data source',
}
),
alert_suppression: ALERT_SUPPRESSION_FIELD_LABEL,
data_source: DATA_SOURCE_FIELD_LABEL,
note: i18n.translate('xpack.securitySolution.detectionEngine.rules.upgradeRuleFields.noteLabel', {
defaultMessage: 'Investigation guide',
}),
Expand All @@ -39,6 +38,7 @@ export const fieldToDisplayNameMap: Record<string, string> = {
references: REFERENCES_FIELD_LABEL,
threat_indicator_path: THREAT_INDICATOR_PATH_LABEL,
index_patterns: INDEX_FIELD_LABEL,
investigation_fields: INVESTIGATION_FIELDS_FIELD_LABEL,
data_view_id: DATA_VIEW_ID_FIELD_LABEL,
threat: THREAT_FIELD_LABEL,
eql_query: i18n.translate(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,10 @@ import type {
} from '@kbn/securitysolution-io-ts-alerting-types';
import { ALERT_RISK_SCORE } from '@kbn/rule-data-utils';
import { requiredOptional } from '@kbn/zod-helpers';
import type { RuleResponse } from '../../../../../common/api/detection_engine/model/rule_schema';
import type {
BuildingBlockType,
RuleResponse,
} from '../../../../../common/api/detection_engine/model/rule_schema';
import { SeverityBadge } from '../../../../common/components/severity_badge';
import { defaultToEmptyTag } from '../../../../common/components/empty_value';
import { filterEmptyThreats } from '../../../rule_creation_ui/pages/rule_creation/helpers';
Expand Down Expand Up @@ -76,9 +79,15 @@ export const Author = ({ author }: AuthorProps) => (
<BadgeList badges={author} data-test-subj="authorPropertyValue" />
);

export const BuildingBlock = () => (
interface BuildingBlockProps {
type: BuildingBlockType | undefined;
}

export const BuildingBlock = ({ type }: BuildingBlockProps) => (
<EuiText size="s" data-test-subj="buildingBlockPropertyValue">
{i18n.BUILDING_BLOCK_FIELD_DESCRIPTION}
{type
? i18n.BUILDING_BLOCK_ENABLED_FIELD_DESCRIPTION
: i18n.BUILDING_BLOCK_DISABLED_FIELD_DESCRIPTION}
</EuiText>
);

Expand Down Expand Up @@ -294,7 +303,7 @@ const prepareAboutSectionListItems = (
title: (
<span data-test-subj="buildingBlockPropertyTitle">{i18n.BUILDING_BLOCK_FIELD_LABEL}</span>
),
description: <BuildingBlock />,
description: <BuildingBlock type="default" />,
});
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*
* 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 React from 'react';
import { EuiText } from '@elastic/eui';
import * as i18n from './translations';

export function EmptyFieldValuePlaceholder() {
return (
<EuiText size="s" color="subdued">
{`<${i18n.EMPTY_FIELD_VALUE_PLACEHOLDER}>`}
</EuiText>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/*
* 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.
*/

export { EmptyFieldValuePlaceholder } from './empty_field_value_placeholder';
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/*
* 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 { i18n } from '@kbn/i18n';

export const EMPTY_FIELD_VALUE_PLACEHOLDER = i18n.translate(
'xpack.securitySolution.detectionEngine.rules.upgradeRules.emptyFieldValuePlaceholder',
{
defaultMessage: 'Field value is empty',
}
);
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,7 @@
*/

import React from 'react';
import type { Story } from '@storybook/react';
import { AlertSuppressionReadOnly } from './alert_suppression';
import { FieldFinalReadOnly } from '../../field_final_readonly';
import type { DiffableRule } from '../../../../../../../../../common/api/detection_engine';
import { mockCustomQueryRule } from '../../storybook/mocks';
import { ThreeWayDiffStorybookProviders } from '../../storybook/three_way_diff_storybook_providers';

export default {
Expand All @@ -19,29 +15,28 @@ export default {
'Rule Management/Prebuilt Rules/Upgrade Flyout/ThreeWayDiff/FieldReadOnly/alert_suppression',
};

interface TemplateProps {
finalDiffableRule: DiffableRule;
}
export const OtherRuleTypes = () => (
<ThreeWayDiffStorybookProviders>
<AlertSuppressionReadOnly
ruleType="query"
alertSuppression={{
group_by: ['host.name'],
duration: { value: 5, unit: 'm' },
missing_fields_strategy: 'suppress',
}}
/>
</ThreeWayDiffStorybookProviders>
);

const Template: Story<TemplateProps> = (args) => {
return (
<ThreeWayDiffStorybookProviders
finalDiffableRule={args.finalDiffableRule}
fieldName="alert_suppression"
>
<FieldFinalReadOnly />
</ThreeWayDiffStorybookProviders>
);
};

export const Default = Template.bind({});
export const Threshold = () => (
<ThreeWayDiffStorybookProviders>
<AlertSuppressionReadOnly
ruleType="threshold"
alertSuppression={{
duration: { value: 5, unit: 'm' },
}}
/>
</ThreeWayDiffStorybookProviders>
);

Default.args = {
finalDiffableRule: mockCustomQueryRule({
alert_suppression: {
group_by: ['host.name'],
duration: { value: 5, unit: 'm' },
missing_fields_strategy: 'suppress',
},
}),
};
export const EmptyValue = () => <AlertSuppressionReadOnly ruleType="query" />;
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
SuppressAlertsByField,
SuppressAlertsDuration,
} from '../../../../rule_definition_section';
import { EmptyFieldValuePlaceholder } from '../../empty_field_value_placeholder';

interface AlertSuppressionReadOnlyProps {
alertSuppression?: AlertSuppression | ThresholdAlertSuppression;
Expand All @@ -30,7 +31,16 @@ export function AlertSuppressionReadOnly({
ruleType,
}: AlertSuppressionReadOnlyProps) {
if (!alertSuppression) {
return null;
return (
<EuiDescriptionList
listItems={[
{
title: ruleDetailsI18n.ALERT_SUPPRESSION_FIELD_LABEL,
description: <EmptyFieldValuePlaceholder />,
},
]}
/>
);
}

const listItems = [];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,38 +6,12 @@
*/

import React from 'react';
import type { Story } from '@storybook/react';
import { AnomalyThresholdReadOnly } from './anomaly_threshold';
import { FieldFinalReadOnly } from '../../field_final_readonly';
import type { DiffableRule } from '../../../../../../../../../common/api/detection_engine';
import { mockMachineLearningRule } from '../../storybook/mocks';
import { ThreeWayDiffStorybookProviders } from '../../storybook/three_way_diff_storybook_providers';

export default {
component: AnomalyThresholdReadOnly,
title:
'Rule Management/Prebuilt Rules/Upgrade Flyout/ThreeWayDiff/FieldReadOnly/anomaly_threshold',
};

interface TemplateProps {
finalDiffableRule: DiffableRule;
}

const Template: Story<TemplateProps> = (args) => {
return (
<ThreeWayDiffStorybookProviders
finalDiffableRule={args.finalDiffableRule}
fieldName="anomaly_threshold"
>
<FieldFinalReadOnly />
</ThreeWayDiffStorybookProviders>
);
};

export const Default = Template.bind({});

Default.args = {
finalDiffableRule: mockMachineLearningRule({
anomaly_threshold: 50,
}),
};
export const Default = () => <AnomalyThresholdReadOnly anomalyThreshold={50} />;
Original file line number Diff line number Diff line change
Expand Up @@ -6,39 +6,13 @@
*/

import React from 'react';
import type { Story } from '@storybook/react';
import { BuildingBlockReadOnly } from './building_block';
import { FieldFinalReadOnly } from '../../field_final_readonly';
import type { DiffableRule } from '../../../../../../../../../common/api/detection_engine';
import { mockCustomQueryRule } from '../../storybook/mocks';
import { ThreeWayDiffStorybookProviders } from '../../storybook/three_way_diff_storybook_providers';

export default {
component: BuildingBlockReadOnly,
title: 'Rule Management/Prebuilt Rules/Upgrade Flyout/ThreeWayDiff/FieldReadOnly/building_block',
};

interface TemplateProps {
finalDiffableRule: DiffableRule;
}
export const Default = () => <BuildingBlockReadOnly buildingBlock={{ type: 'default' }} />;

const Template: Story<TemplateProps> = (args) => {
return (
<ThreeWayDiffStorybookProviders
finalDiffableRule={args.finalDiffableRule}
fieldName="building_block"
>
<FieldFinalReadOnly />
</ThreeWayDiffStorybookProviders>
);
};

export const Default = Template.bind({});

Default.args = {
finalDiffableRule: mockCustomQueryRule({
building_block: {
type: 'default',
},
}),
};
export const NoValue = () => <BuildingBlockReadOnly />;
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,12 @@ interface BuildingBlockReadOnlyProps {
}

export function BuildingBlockReadOnly({ buildingBlock }: BuildingBlockReadOnlyProps) {
if (!buildingBlock || !buildingBlock.type) {
return null;
}

return (
<EuiDescriptionList
listItems={[
{
title: ruleDetailsI18n.BUILDING_BLOCK_FIELD_LABEL,
description: <BuildingBlock />,
description: <BuildingBlock type={buildingBlock?.type} />,
},
]}
/>
Expand Down
Loading

0 comments on commit 27fe7e4

Please sign in to comment.