Skip to content

Commit

Permalink
[Security Solution] Connector selector onboarding (elastic#203742)
Browse files Browse the repository at this point in the history
## Summary

Summarize your PR. If it involves visual changes include a screenshot or
gif.

https://github.com/user-attachments/assets/6d7527d1-dc8d-4f3a-9b03-cfd0022701d2

### Checklist

Check the PR satisfies following conditions.

Reviewers should verify this PR satisfies this list as well.

- [ ] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios

---------

Co-authored-by: Elastic Machine <[email protected]>
Co-authored-by: kibanamachine <[email protected]>
(cherry picked from commit 86666bf)

# Conflicts:
#	.github/CODEOWNERS
#	x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/common/connectors/connector_setup.tsx
  • Loading branch information
agusruidiazgd committed Jan 24, 2025
1 parent 9819e87 commit bf9927c
Show file tree
Hide file tree
Showing 26 changed files with 921 additions and 310 deletions.
188 changes: 182 additions & 6 deletions .github/CODEOWNERS

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -821,6 +821,7 @@
"@kbn/security-plugin-types-public": "link:x-pack/platform/packages/shared/security/plugin_types_public",
"@kbn/security-plugin-types-server": "link:x-pack/platform/packages/shared/security/plugin_types_server",
"@kbn/security-role-management-model": "link:x-pack/platform/packages/private/security/role_management_model",
"@kbn/security-solution-connectors": "link:x-pack/solutions/security/packages/connectors",
"@kbn/security-solution-distribution-bar": "link:x-pack/solutions/security/packages/distribution_bar",
"@kbn/security-solution-ess": "link:x-pack/solutions/security/plugins/security_solution_ess",
"@kbn/security-solution-features": "link:x-pack/solutions/security/packages/features",
Expand Down
2 changes: 2 additions & 0 deletions tsconfig.base.json
Original file line number Diff line number Diff line change
Expand Up @@ -1632,6 +1632,8 @@
"@kbn/security-plugin-types-server/*": ["x-pack/platform/packages/shared/security/plugin_types_server/*"],
"@kbn/security-role-management-model": ["x-pack/platform/packages/private/security/role_management_model"],
"@kbn/security-role-management-model/*": ["x-pack/platform/packages/private/security/role_management_model/*"],
"@kbn/security-solution-connectors": ["x-pack/solutions/security/packages/connectors"],
"@kbn/security-solution-connectors/*": ["x-pack/solutions/security/packages/connectors/*"],
"@kbn/security-solution-distribution-bar": ["x-pack/solutions/security/packages/distribution_bar"],
"@kbn/security-solution-distribution-bar/*": ["x-pack/solutions/security/packages/distribution_bar/*"],
"@kbn/security-solution-ess": ["x-pack/solutions/security/plugins/security_solution_ess"],
Expand Down
9 changes: 9 additions & 0 deletions x-pack/solutions/security/packages/connectors/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/*
* 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 { ConnectorSelector } from './src/connector_selector';
export type { ConnectorSelectorProps } from './src/connector_selector';
12 changes: 12 additions & 0 deletions x-pack/solutions/security/packages/connectors/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/*
* 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.
*/

module.exports = {
preset: '@kbn/test',
roots: ['<rootDir>/x-pack/solutions/security/packages/connectors'],
rootDir: '../../../../..',
};
9 changes: 9 additions & 0 deletions x-pack/solutions/security/packages/connectors/kibana.jsonc
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"type": "shared-browser",
"id": "@kbn/security-solution-connectors",
"owner": [
"@elastic/security-threat-hunting-explore"
],
"group": "security",
"visibility": "private"
}
7 changes: 7 additions & 0 deletions x-pack/solutions/security/packages/connectors/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"name": "@kbn/security-solution-connectors",
"private": true,
"version": "1.0.0",
"license": "Elastic License 2.0",
"sideEffects": false
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* 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 { useEuiTheme } from '@elastic/eui';
import { css } from '@emotion/react';

export const useConnectorSelectorStyles = () => {
const { euiTheme } = useEuiTheme();

return {
placeholder: css`
color: ${euiTheme.colors.primary};
margin-right: ${euiTheme.size.xs};
`,
optionDisplay: css`
margin-right: 8px;
overflow: hidden;
text-overflow: ellipsis;
`,
offset: css`
width: 24px;
`,
inputContainer: css`
.euiSuperSelectControl {
border: none;
box-shadow: none;
background: none;
padding-left: 0;
}
.euiFormControlLayoutIcons {
right: 14px;
top: 2px;
}
`,
};
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
/*
* 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 {
EuiButtonEmpty,
EuiFlexGroup,
EuiFlexItem,
EuiSuperSelect,
EuiText,
useEuiTheme,
} from '@elastic/eui';
import React, { useCallback, useMemo } from 'react';
import { some } from 'lodash';
import * as i18n from './translations';
import { useConnectorSelectorStyles } from './connector_selector.styles';
import { ADD_NEW_CONNECTOR } from './constants';

export interface ConnectorDetails {
id: string;
name: string;
description: string;
}

export interface ConnectorSelectorProps {
connectors: ConnectorDetails[];
onChange: (connectorId: string) => void;
selectedId?: string;
onNewConnectorClicked?: () => void;
isDisabled?: boolean;
}

export const ConnectorSelector = React.memo<ConnectorSelectorProps>(
({ connectors, onChange, selectedId, onNewConnectorClicked, isDisabled }) => {
const styles = useConnectorSelectorStyles();
const { euiTheme } = useEuiTheme();

const addNewConnectorOption = useMemo(() => {
return {
value: ADD_NEW_CONNECTOR,
inputDisplay: i18n.ADD_NEW_CONNECTOR,
dropdownDisplay: (
<EuiFlexGroup gutterSize="none" key={ADD_NEW_CONNECTOR}>
<EuiFlexItem grow={true}>
<EuiButtonEmpty
data-test-subj="addNewConnectorButton"
href="#"
isDisabled={isDisabled}
iconType="plus"
size="xs"
>
{i18n.ADD_NEW_CONNECTOR}
</EuiButtonEmpty>
</EuiFlexItem>
<EuiFlexItem grow={false}>
{/* Right offset to compensate for 'selected' icon of EuiSuperSelect since native footers aren't supported*/}
<div css={styles.offset} />
</EuiFlexItem>
</EuiFlexGroup>
),
};
}, [isDisabled, styles.offset]);

const connectorExists = useMemo(
() => some(connectors, ['id', selectedId]),
[connectors, selectedId]
);

const mappedConnectorOptions = connectors.map((connector) => ({
value: connector.id,
'data-test-subj': connector.id,
inputDisplay: (
<EuiText css={styles.optionDisplay} size="s" color={euiTheme.colors.primary}>
{connector.name}
</EuiText>
),
dropdownDisplay: (
<React.Fragment key={connector.id}>
<EuiFlexGroup justifyContent="spaceBetween" gutterSize="none" alignItems="center">
<EuiFlexItem grow={false} data-test-subj={`connector-${connector.name}`}>
<strong>{connector.name}</strong>
<EuiText size="xs" color="subdued">
<p>{connector.description}</p>
</EuiText>
</EuiFlexItem>
</EuiFlexGroup>
</React.Fragment>
),
}));

const allConnectorOptions = useMemo(
() =>
onNewConnectorClicked
? [...mappedConnectorOptions, addNewConnectorOption]
: [...mappedConnectorOptions],
[onNewConnectorClicked, mappedConnectorOptions, addNewConnectorOption]
);

const onChangeConnector = useCallback(
(connectorId: string) => {
if (connectorId === ADD_NEW_CONNECTOR) {
onNewConnectorClicked?.();
return;
}
onChange(connectorId);
},
[onChange, onNewConnectorClicked]
);

return (
<div css={styles.inputContainer}>
{!connectorExists && !connectors.length ? (
<EuiButtonEmpty
data-test-subj="addNewConnectorButton"
iconType="plusInCircle"
isDisabled={isDisabled}
size="xs"
onClick={() => onNewConnectorClicked?.()}
>
{i18n.ADD_CONNECTOR}
</EuiButtonEmpty>
) : (
<EuiSuperSelect
aria-label={i18n.CONNECTOR_SELECTOR_TITLE}
css={styles.placeholder}
compressed={true}
data-test-subj="connector-selector"
disabled={isDisabled}
hasDividers={true}
onChange={onChangeConnector}
options={allConnectorOptions}
valueOfSelected={selectedId}
placeholder={i18n.CONNECTOR_SELECTOR_PLACEHOLDER}
popoverProps={{ panelMinWidth: 400, anchorPosition: 'downRight' }}
/>
)}
</div>
);
}
);

ConnectorSelector.displayName = 'ConnectorSelector';
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 const ADD_NEW_CONNECTOR = 'ADD_NEW_CONNECTOR';
36 changes: 36 additions & 0 deletions x-pack/solutions/security/packages/connectors/src/translations.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* 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 CONNECTOR_SELECTOR_TITLE = i18n.translate(
'securitySolutionPackages.connectors.connectorSelector.ariaLabel',
{
defaultMessage: 'Connector Selector',
}
);

export const ADD_NEW_CONNECTOR = i18n.translate(
'securitySolutionPackages.connectors.connectorSelector.newConnectorOptions',
{
defaultMessage: 'Add new Connector...',
}
);

export const ADD_CONNECTOR = i18n.translate(
'securitySolutionPackages.connectors.connectorSelector.addConnectorButtonLabel',
{
defaultMessage: 'Add connector',
}
);

export const CONNECTOR_SELECTOR_PLACEHOLDER = i18n.translate(
'securitySolutionPackages.connectors.connectorSelectorInline.connectorPlaceholder',
{
defaultMessage: 'Select a connector',
}
);
19 changes: 19 additions & 0 deletions x-pack/solutions/security/packages/connectors/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"extends": "../../../../../tsconfig.base.json",
"compilerOptions": {
"outDir": "target/types",
"types": [
"jest",
"node",
"react",
"@emotion/react/types/css-prop",
"@testing-library/jest-dom",
"@testing-library/react",
]
},
"include": ["**/*.ts", "**/*.tsx"],
"kbn_references": [
"@kbn/i18n",
],
"exclude": ["target/**/*"]
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const LocalStorageKey = {
selectedIntegrationTabId: 'securitySolution.onboarding.selectedIntegrationTabId',
selectedCardItemId: 'securitySolution.onboarding.selectedCardItem',
integrationSearchTerm: 'securitySolution.onboarding.integrationSearchTerm',
assistantConnectorId: 'securitySolution.onboarding.assistantCard.connectorId',
} as const;

/**
Expand Down Expand Up @@ -80,3 +81,9 @@ export const useStoredIntegrationSearchTerm = (spaceId: string) =>
`${LocalStorageKey.integrationSearchTerm}.${spaceId}`,
null
);

/**
* Stores the integration search term per space
*/
export const useStoredAssistantConnectorId = (spaceId: string) =>
useDefinedLocalStorage<string | null>(`${LocalStorageKey.assistantConnectorId}.${spaceId}`, null);
Loading

0 comments on commit bf9927c

Please sign in to comment.