[Security Solution][Endpoint] Console UI fixes (#136964)

* Increases back button size to S

* Increases space between back button and responder title

* Moves read more link into line 3

* Make help text bold

* Fixes help panel texts and commands order

* Follow mock design for help command response

* Help button centered aligned

* Fixes old multilang files

* Fixes text for help output and fixes failing unit test
This commit is contained in:
David Sánchez 2022-07-25 16:45:25 +02:00 committed by GitHub
parent 5107282b01
commit 2880c66823
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 102 additions and 50 deletions

View file

@ -12,7 +12,6 @@ import {
EuiBadge,
EuiBasicTable,
EuiButtonIcon,
EuiCode,
EuiDescriptionList,
EuiFlexGroup,
EuiFlexGrid,
@ -31,6 +30,7 @@ import { useDataTestSubj } from '../hooks/state_selectors/use_data_test_subj';
import { useConsoleStateDispatch } from '../hooks/state_selectors/use_console_state_dispatch';
import { COMMON_ARGS, HELP_GROUPS } from '../service/builtin_commands';
import { getCommandNameWithArgs } from '../service/utils';
import { ConsoleCodeBlock } from './console_code_block';
// @ts-expect-error TS2769
const StyledEuiBasicTable = styled(EuiBasicTable)`
@ -55,6 +55,17 @@ const StyledEuiFlexGroup = styled(EuiFlexGroup)`
padding-left: ${({ theme: { eui } }) => eui.euiSizeS};
`;
const StyledEuiFlexGrid = styled(EuiFlexGrid)`
max-width: 50%;
`;
const StyledEuiBadge = styled(EuiBadge)`
font-size: 10px !important;
span {
color: ${({ theme: { eui } }) => eui.euiShadowColor} !important;
}
`;
export interface CommandListProps {
commands: CommandDefinition[];
display?: 'default' | 'table';
@ -66,13 +77,27 @@ export const CommandList = memo<CommandListProps>(({ commands, display = 'defaul
const footerMessage = useMemo(() => {
return (
<FormattedMessage
id="xpack.securitySolution.console.commandList.footerText"
defaultMessage="For more details on the commands above use the {helpOption} argument. Example: {cmdExample}"
values={{
helpOption: <EuiCode>{'--help'}</EuiCode>,
cmdExample: <EuiCode>{'some-command --help'}</EuiCode>,
}}
<EuiDescriptionList
compressed
listItems={[
{
title: (
<StyledEuiBadge>
<ConsoleCodeBlock inline bold>
{COMMON_ARGS.find((current) => current.name === '--help')?.name}
</ConsoleCodeBlock>
</StyledEuiBadge>
),
description: (
<EuiText color="subdued" size="xs">
<FormattedMessage
id="xpack.securitySolution.console.commandList.footerText"
defaultMessage="For more help with the individual commands use the --help argument. Ex: processes --help"
/>
</EuiText>
),
},
]}
/>
);
}, []);
@ -111,7 +136,7 @@ export const CommandList = memo<CommandListProps>(({ commands, display = 'defaul
acc[current[0].helpGroupPosition] = sortBy(current, 'helpCommandPosition');
} else if (current.length) {
acc.push(current);
acc.push(sortBy(current, 'helpCommandPosition'));
}
return acc;
},
@ -191,6 +216,14 @@ export const CommandList = memo<CommandListProps>(({ commands, display = 'defaul
[getTestId, otherCommandsGroupLabel, updateInputText]
);
const getFilteredCommands = useCallback(
(commandsByGroup): CommandDefinition[] =>
commandsByGroup.filter(
(current: CommandDefinition) => current.name !== 'help' && current.name !== 'cls'
),
[]
);
if (display === 'table') {
const calloutItems = [
<FormattedMessage
@ -203,7 +236,17 @@ export const CommandList = memo<CommandListProps>(({ commands, display = 'defaul
/>,
<FormattedMessage
id="xpack.securitySolution.console.commandList.callout.visitSupportSections"
defaultMessage="Visit support section to read more about manual response actions."
defaultMessage="{readMore} about manual response actions."
values={{
readMore: (
<EuiLink>
<FormattedMessage
id="xpack.securitySolution.console.commandList.callout.readMoreLink"
defaultMessage="Read more"
/>
</EuiLink>
),
}}
/>,
];
@ -223,13 +266,6 @@ export const CommandList = memo<CommandListProps>(({ commands, display = 'defaul
</li>
))}
</ol>
{/* //TODO: Add link to the read more page */}
<EuiLink>
<FormattedMessage
id="xpack.securitySolution.console.commandList.callout.readMoreLink"
defaultMessage="Read more"
/>
</EuiLink>
</StyledEuiCallOut>
);
@ -248,22 +284,37 @@ export const CommandList = memo<CommandListProps>(({ commands, display = 'defaul
return (
<>
<EuiSpacer />
<EuiSpacer size="s" />
{commandsByGroups.map((commandsByGroup) => {
const groupLabel = commandsByGroup[0].helpGroupLabel;
const groupedCommands =
groupLabel === HELP_GROUPS.supporting.label ? [...COMMON_ARGS] : commandsByGroup;
const filteredCommands = getFilteredCommands(commandsByGroup);
return (
<EuiFlexGrid columns={3} responsive={false} gutterSize="m" key={groupLabel}>
{groupedCommands.map((command) => {
<StyledEuiFlexGrid
columns={3}
responsive={false}
gutterSize="l"
key={groupLabel}
direction="column"
>
{filteredCommands.map((command) => {
return (
<EuiFlexItem key={command.name}>
<EuiDescriptionList
compressed
listItems={[
{
title: <EuiBadge>{getCommandNameWithArgs(command)}</EuiBadge>,
description: <>{command.about}</>,
title: (
<StyledEuiBadge>
<ConsoleCodeBlock inline bold>
{getCommandNameWithArgs(command)}{' '}
</ConsoleCodeBlock>
</StyledEuiBadge>
),
description: (
<EuiText color="subdued" size="xs">
{command.about}
</EuiText>
),
},
]}
data-test-subj={getTestId('commandList-command')}
@ -271,13 +322,11 @@ export const CommandList = memo<CommandListProps>(({ commands, display = 'defaul
</EuiFlexItem>
);
})}
</EuiFlexGrid>
</StyledEuiFlexGrid>
);
})}
<EuiSpacer />
<EuiText size="s" color="subdued">
{footerMessage}
</EuiText>
<EuiSpacer size="xl" />
{footerMessage}
</>
);
});

View file

@ -22,9 +22,6 @@ const StyledEuiButtonEmpty = styled(EuiButtonEmpty)`
margin-left: auto;
height: inherit;
`;
const StyledEuiFlexItem = styled(EuiFlexItem)`
align-self: flex-start;
`;
export type ConsoleHeaderProps = Pick<ConsoleProps, 'TitleComponent'>;
@ -51,7 +48,7 @@ export const ConsoleHeader = memo<ConsoleHeaderProps>(({ TitleComponent }) => {
{TitleComponent ? <TitleComponent /> : ''}
</EuiFlexItem>
{!isHelpOpen && (
<StyledEuiFlexItem grow={1}>
<EuiFlexItem grow={1}>
<StyledEuiButtonEmpty
style={{ marginLeft: 'auto' }}
onClick={handleHelpButtonOnClick}
@ -65,7 +62,7 @@ export const ConsoleHeader = memo<ConsoleHeaderProps>(({ TitleComponent }) => {
defaultMessage="Help"
/>
</StyledEuiButtonEmpty>
</StyledEuiFlexItem>
</EuiFlexItem>
)}
</EuiFlexGroup>
);

View file

@ -53,7 +53,7 @@ export const ConsolePageOverlay = memo<ConsolePageOverlayProps>(
headerBackComponent: (
<EuiButtonEmpty
flush="left"
size="xs"
size="s"
iconType="arrowLeft"
onClick={handleCloseOverlayOnClick}
data-test-subj={getTestId('header-back-link')}

View file

@ -95,6 +95,7 @@ export const PageLayout = memo<PageLayoutProps>(
responsive={false}
>
{headerBackComponent && <EuiFlexItem grow={false}>{headerBackComponent}</EuiFlexItem>}
<EuiSpacer size="m" />
<EuiFlexItem grow={false}>
<EuiTitle size="l">
<span data-test-subj={getTestId('titleHolder')}>{pageTitle}</span>
@ -128,6 +129,7 @@ export const PageLayout = memo<PageLayoutProps>(
rightSideItems={headerRightSideItems}
rightSideGroupProps={headerRightSideGroupProps}
restrictWidth={restrictWidth}
alignItems="bottom"
data-test-subj={getTestId('header')}
/>
<EuiSpacer size="l" />

View file

@ -32,10 +32,7 @@ describe('When a Console command is entered by the user', () => {
expect(renderResult.getByTestId('test-helpOutput')).toBeTruthy();
await waitFor(() => {
expect(renderResult.getAllByTestId('test-commandList-command')).toHaveLength(
// `+2` to account for builtin generic args
commands.length + 2
);
expect(renderResult.getAllByTestId('test-commandList-command')).toHaveLength(commands.length);
});
});

View file

@ -22,7 +22,7 @@ export const UnknownCommand = memo<CommandExecutionComponentProps>(({ command, s
<ConsoleCodeBlock>
<FormattedMessage
id="xpack.securitySolution.console.unknownCommand.helpMessage"
defaultMessage="The text you entered {userInput} is unsupported! Click {helpIcon} Help or type {helpCmd} for assistance."
defaultMessage="The text you entered {userInput} is unsupported! Click {helpIcon} {boldHelp} or type {helpCmd} for assistance."
values={{
userInput: (
<ConsoleCodeBlock bold inline>
@ -35,6 +35,14 @@ export const UnknownCommand = memo<CommandExecutionComponentProps>(({ command, s
{'help'}
</ConsoleCodeBlock>
),
boldHelp: (
<strong>
<FormattedMessage
id="xpack.securitySolution.console.unknownCommand.helpMessage.help"
defaultMessage="Help"
/>
</strong>
),
}}
/>
</ConsoleCodeBlock>

View file

@ -13,7 +13,7 @@ import type { CommandDefinition } from '../types';
export const HELP_GROUPS = Object.freeze({
supporting: {
label: i18n.translate('xpack.securitySolution.console.builtInCommands.groups.supporting', {
defaultMessage: 'Supporting commands & syntaxes',
defaultMessage: 'Supporting commands & parameters',
}),
},
});
@ -38,18 +38,20 @@ export const getBuiltinCommands = (): CommandDefinition[] => {
{
name: 'help',
about: i18n.translate('xpack.securitySolution.console.builtInCommands.helpAbout', {
defaultMessage: 'View list of available commands',
defaultMessage: 'List all available commands',
}),
RenderComponent: HelpCommand,
helpGroupLabel: HELP_GROUPS.supporting.label,
helpCommandPosition: 1,
},
{
name: 'cls',
about: i18n.translate('xpack.securitySolution.console.builtInCommands.clearAbout', {
defaultMessage: 'Clear the console buffer',
defaultMessage: 'Clear console screen',
}),
RenderComponent: ClearCommand,
helpGroupLabel: HELP_GROUPS.supporting.label,
helpCommandPosition: 0,
},
];
};

View file

@ -113,7 +113,7 @@ export const getEndpointResponseActionsConsoleCommands = (
{
name: 'kill-process',
about: i18n.translate('xpack.securitySolution.endpointConsoleCommands.killProcess.about', {
defaultMessage: 'Kill a running process. Accepts either a PID or an entity id.',
defaultMessage: 'Kill/terminate a process',
}),
RenderComponent: KillProcessActionResult,
meta: {
@ -157,7 +157,7 @@ export const getEndpointResponseActionsConsoleCommands = (
{
name: 'suspend-process',
about: i18n.translate('xpack.securitySolution.endpointConsoleCommands.suspendProcess.about', {
defaultMessage: 'Suspend a running process. Accepts either a PID or an entity id.',
defaultMessage: 'Temporarily suspend a process',
}),
RenderComponent: SuspendProcessActionResult,
meta: {
@ -204,7 +204,7 @@ export const getEndpointResponseActionsConsoleCommands = (
{
name: 'status',
about: i18n.translate('xpack.securitySolution.endpointConsoleCommands.status.about', {
defaultMessage: 'Display the latest status information for the Endpoint',
defaultMessage: 'Show host status information',
}),
RenderComponent: EndpointStatusActionResult,
meta: {
@ -217,7 +217,7 @@ export const getEndpointResponseActionsConsoleCommands = (
{
name: 'processes',
about: i18n.translate('xpack.securitySolution.endpointConsoleCommands.processes.about', {
defaultMessage: 'Display the processes on the endpoint',
defaultMessage: 'Show all running processes',
}),
RenderComponent: GetProcessesActionResult,
meta: {

View file

@ -24460,7 +24460,6 @@
"xpack.securitySolution.console.builtInCommands.clearAbout": "Purger la mémoire tampon de la console",
"xpack.securitySolution.console.builtInCommands.help.helpTitle": "Commandes disponibles",
"xpack.securitySolution.console.builtInCommands.helpAbout": "Afficher la liste des commandes disponibles",
"xpack.securitySolution.console.commandList.footerText": "Pour plus d'informations sur les commandes ci-dessus, utilisez l'argument {helpOption}. Exemple : {cmdExample}",
"xpack.securitySolution.console.commandUsage.inputUsage": "Utilisation :",
"xpack.securitySolution.console.commandValidation.argSupportedOnlyOnce": "cet argument ne peut être utilisé quune fois : {argName}.",
"xpack.securitySolution.console.commandValidation.invalidArgValue": "valeur d'argument non valide : {argName}. {error}",

View file

@ -24540,7 +24540,6 @@
"xpack.securitySolution.console.builtInCommands.clearAbout": "コンソールバッファーを消去",
"xpack.securitySolution.console.builtInCommands.help.helpTitle": "使用可能なコマンド",
"xpack.securitySolution.console.builtInCommands.helpAbout": "使用可能なコマンドのリストを表示",
"xpack.securitySolution.console.commandList.footerText": "上記のコマンドの詳細については、{helpOption}引数を使用してください。例:{cmdExample}",
"xpack.securitySolution.console.commandUsage.inputUsage": "使用方法:",
"xpack.securitySolution.console.commandValidation.argSupportedOnlyOnce": "引数{argName}は一度だけ使用できます",
"xpack.securitySolution.console.commandValidation.invalidArgValue": "無効な引数値:{argName}。{error}",

View file

@ -24566,7 +24566,6 @@
"xpack.securitySolution.console.builtInCommands.clearAbout": "清除控制台缓冲区",
"xpack.securitySolution.console.builtInCommands.help.helpTitle": "可用命令",
"xpack.securitySolution.console.builtInCommands.helpAbout": "查看可用命令列表",
"xpack.securitySolution.console.commandList.footerText": "有关上述命令的更多详情,请使用 {helpOption} 参数。示例:{cmdExample}",
"xpack.securitySolution.console.commandUsage.inputUsage": "用法:",
"xpack.securitySolution.console.commandValidation.argSupportedOnlyOnce": "参数只能使用一次:{argName}",
"xpack.securitySolution.console.commandValidation.invalidArgValue": "无效的参数值:{argName}。{error}",