Skip to content

Commit

Permalink
[Security Solution] Add callout to promote blog post (elastic#195943)
Browse files Browse the repository at this point in the history
**Resolves: elastic#195423**

## Summary

This PR adds a callout to the Rule Management page. This callout
displays a
[link](https://www.elastic.co/blog/elastic-security-detection-engineering)
to a post in Elastic blog.

Once a user clicks on "x" in the top-right corner the callout will be
dismissed forever. Dismissal state is saved in `localStorage`.

This is only for ESS v8.16.0 and beyond. Not for Serverless.

⚠️ Currently the
[link](https://www.elastic.co/blog/elastic-security-detection-engineering)
leads to a 404 page because the blog post is not yet created. It'll be
published in time for 8.16 release.

⚠️ UI copy is not final. It'll be reviewed by the Docs folks on Monday.
I'll change it to their suggestion once they review it on Monday.

### Screenshot

<img width="1392" alt="Scherm­afbeelding 2024-10-11 om 16 43 59"
src="https://github.com/user-attachments/assets/282430c1-4b02-4188-a052-5027e7433981">

---------

Co-authored-by: Joe Peeples <[email protected]>
  • Loading branch information
nikitaindik and joepeeples authored Oct 14, 2024
1 parent 06a2faa commit ee7bc7e
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { SpyRoute } from '../../../../common/utils/route/spy_routes';
import { MissingPrivilegesCallOut } from '../../../../detections/components/callouts/missing_privileges_callout';
import { MlJobCompatibilityCallout } from '../../../../detections/components/callouts/ml_job_compatibility_callout';
import { NeedAdminForUpdateRulesCallOut } from '../../../../detections/components/callouts/need_admin_for_update_callout';
import { BlogPostDetectionEngineeringCallout } from '../../../../detections/components/callouts/blog_post_detection_engineering_callout';
import { AddElasticRulesButton } from '../../../../detections/components/rules/pre_packaged_rules/add_elastic_rules_button';
import { ValueListsFlyout } from '../../../../detections/components/value_lists_management_flyout';
import { useUserData } from '../../../../detections/components/user_info';
Expand Down Expand Up @@ -173,6 +174,7 @@ const RulesPageComponent: React.FC = () => {
kibanaServices={kibanaServices}
categories={[DEFAULT_APP_CATEGORIES.security.id]}
/>
<BlogPostDetectionEngineeringCallout />
<RuleFeatureTour />
<AllRules data-test-subj="all-rules" />
</SecuritySolutionPageWrapper>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
* 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, { useCallback } from 'react';
import { css } from '@emotion/css';
import avcBannerBackground from '@kbn/avc-banner/src/avc_banner_background.svg';
import { EuiSpacer, EuiButton, EuiCallOut, useEuiTheme } from '@elastic/eui';
import { type CallOutMessage } from '../../../../common/components/callouts';
import { useCallOutStorage } from '../../../../common/components/callouts/use_callout_storage';
import * as i18n from './translations';

const BLOG_POST_URL = 'https://www.elastic.co/blog/elastic-security-detection-engineering';

const calloutMessage: CallOutMessage = {
type: 'success',
id: 'blog-post-elastic-security-detection-engineering',
title: i18n.NEW_FEATURES_BLOG_POST_CALLOUT_TITLE,
description: <Description />,
};

export function BlogPostDetectionEngineeringCallout() {
const { euiTheme } = useEuiTheme();

const { isVisible, dismiss } = useCallOutStorage([calloutMessage], 'detections');

const calloutStyles = css({
paddingLeft: `${euiTheme.size.xl}`,
backgroundImage: `url(${avcBannerBackground})`,
backgroundRepeat: 'no-repeat',
backgroundPositionX: 'right',
backgroundPositionY: 'bottom',
});

const handleDismiss = useCallback(() => {
dismiss(calloutMessage);
}, [dismiss]);

if (!isVisible(calloutMessage)) {
return null;
}

return (
<>
<EuiCallOut
title={calloutMessage.title}
color={calloutMessage.type}
iconType="cheer"
onDismiss={handleDismiss}
className={calloutStyles}
>
{calloutMessage.description}
</EuiCallOut>
<EuiSpacer size="l" />
</>
);
}

function Description() {
return (
<>
{i18n.NEW_FEATURES_BLOG_POST_CALLOUT_DESCRIPTION}
<EuiSpacer size="s" />
<EuiButton size="s" color="success" href={BLOG_POST_URL} target="_blank">
{i18n.NEW_FEATURES_BLOG_POST_CALLOUT_BUTTON_LABEL}
</EuiButton>
</>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* 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 NEW_FEATURES_BLOG_POST_CALLOUT_TITLE = i18n.translate(
'xpack.securitySolution.detectionEngine.newFeaturesBlogPostCallout.calloutTitle',
{
defaultMessage: `Discover the power of Elastic's threat detection!`,
}
);

export const NEW_FEATURES_BLOG_POST_CALLOUT_DESCRIPTION = i18n.translate(
'xpack.securitySolution.detectionEngine.newFeaturesBlogPostCallout.calloutDescription',
{
defaultMessage: 'Learn about new and existing detection capabilities of Elastic Security.',
}
);

export const NEW_FEATURES_BLOG_POST_CALLOUT_BUTTON_LABEL = i18n.translate(
'xpack.securitySolution.detectionEngine.newFeaturesBlogPostCallout.calloutButtonLabel',
{
defaultMessage: 'Read the blog',
}
);

0 comments on commit ee7bc7e

Please sign in to comment.