Skip to content

Commit

Permalink
fix: [Rules > Shared exception lists][AXE-CORE]: Interactive controls…
Browse files Browse the repository at this point in the history
… must not be nested (#178023)

Closes: elastic/security-team#8611

## Description

The [axe browser plugin](https://deque.com/axe) is reporting a nested
button (interactive control)in the shared exception list accordion(s).
The accordion has a button to open/close and a link to the list detail
view. Screenshot attached below.

### Steps to recreate

1. Open the Security Dashboards, then click Rules > Shared Exception
Lists
2. Run an axe browser scan in Chrome, Edge, or Firefox
3. Verify the nested control error

### What was done? 

1. Solution proposed by EUI team was applied. See
elastic/eui#7545
2. The component `ExceptionsListCard` has been slightly changed to make
it more responsive.
       Before: 

       After: 


https://github.com/elastic/kibana/assets/20072247/dd6c6681-980c-40ed-98cf-29a71f896bc2




### Screen

#### AXE Report 


![image](https://github.com/elastic/kibana/assets/20072247/43313005-b7ec-49d1-9eed-30bfacbb5ecc)
  • Loading branch information
alexwizp authored Mar 14, 2024
1 parent a36a370 commit b90e215
Show file tree
Hide file tree
Showing 2 changed files with 120 additions and 117 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* 2.0.
*/

import React, { memo } from 'react';
import React, { memo, useMemo } from 'react';

import {
EuiLink,
Expand All @@ -15,7 +15,8 @@ import {
EuiPanel,
EuiText,
EuiAccordion,
EuiButtonIcon,
useEuiTheme,
useEuiShadow,
} from '@elastic/eui';
import { css } from '@emotion/css';
import { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types';
Expand Down Expand Up @@ -72,13 +73,7 @@ interface ExceptionsListCardProps {
}) => () => Promise<void>;
readOnly: boolean;
}
const buttonCss = css`
z-index: 100;
.euiAccordion__buttonContent {
cursor: pointer;
width: 100%;
}
`;

const ExceptionPanel = styled(EuiPanel)`
margin: -${euiThemeVars.euiSizeS} ${euiThemeVars.euiSizeM} 0 ${euiThemeVars.euiSizeM};
`;
Expand All @@ -98,6 +93,25 @@ export const ExceptionsListCard = memo<ExceptionsListCardProps>(
onCancelManageRules,
onRuleSelectionChange,
} = useListDetailsView(exceptionsList.list_id);
const { euiTheme } = useEuiTheme();
const panelShadow = useEuiShadow();

const euiAccordionStyles = useMemo(
() => css`
.euiAccordion__buttonContent {
flex: 1 1 auto;
cursor: pointer;
}
.euiAccordion__triggerWrapper {
z-index: 100;
position: relative;
border-radius: ${euiTheme.border.radius.medium};
padding: ${euiTheme.size.base};
${panelShadow}
}
`,
[euiTheme.border.radius.medium, euiTheme.size.base, panelShadow]
);

const {
listId,
Expand Down Expand Up @@ -140,104 +154,86 @@ export const ExceptionsListCard = memo<ExceptionsListCardProps>(
});

return (
<EuiFlexGroup gutterSize="none">
<EuiFlexItem>
<EuiPanel hasShadow={false}>
<EuiAccordion
// Note: this uses `className` instead of the `css` prop, because a plugin
// cannot be set up for styled-components and `@emotion/react` at the same time
// @see https://github.com/elastic/eui/discussions/6828#discussioncomment-6076157
buttonProps={{ className: buttonCss }}
id={openAccordionId}
arrowDisplay="none"
onToggle={() => setToggleAccordion(!toggleAccordion)}
buttonContent={
<EuiPanel>
<ListHeaderContainer gutterSize="m" alignItems="flexStart">
<EuiFlexItem grow={false}>
<EuiButtonIcon
iconType={toggleAccordion ? 'arrowDown' : 'arrowRight'}
aria-label="Next"
/>
</EuiFlexItem>
<EuiFlexItem>
<EuiFlexGroup
direction="column"
key={listId}
alignItems="flexStart"
gutterSize="none"
>
<EuiFlexItem grow>
<EuiText size="m">
<EuiLink
data-test-subj="exception-list-name"
onClick={goToExceptionDetail}
>
{listName}
</EuiLink>
</EuiText>
</EuiFlexItem>
<EuiFlexItem grow>
<EuiText size="xs">
<EuiTextColor color="subdued">{listDescription}</EuiTextColor>
</EuiText>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
<>
<EuiAccordion
// Note: this uses `className` instead of the `css` prop, because a plugin
// cannot be set up for styled-components and `@emotion/react` at the same time
// @see https://github.com/elastic/eui/discussions/6828#discussioncomment-6076157
className={euiAccordionStyles}
id={openAccordionId}
buttonElement="div"
onToggle={() => setToggleAccordion(!toggleAccordion)}
buttonContent={
<ListHeaderContainer gutterSize="m" alignItems="center" justifyContent="spaceBetween">
<EuiFlexItem grow={false}>
<EuiFlexGroup
direction="column"
key={listId}
alignItems="flexStart"
gutterSize="none"
>
<EuiFlexItem grow>
<EuiText size="m">
<EuiLink data-test-subj="exception-list-name" onClick={goToExceptionDetail}>
{listName}
</EuiLink>
</EuiText>
</EuiFlexItem>
<EuiFlexItem grow>
<EuiText size="xs">
<EuiTextColor color="subdued">{listDescription}</EuiTextColor>
</EuiText>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>

<EuiFlexItem>
<EuiFlexGroup alignItems="center">
<EuiFlexItem>
<TitleBadge title={i18n.DATE_CREATED} badgeString={createdAt} />
</EuiFlexItem>
<EuiFlexItem>
<TitleBadge title={i18n.CREATED_BY} badgeString={createdBy} />
</EuiFlexItem>
<EuiFlexItem>
<TitleBadge title={i18n.EXCEPTIONS} badgeString={exceptionItemsCount} />
</EuiFlexItem>
<EuiFlexItem data-test-subj="exceptionListCardLinkedRulesBadge">
<TitleBadge
title={i18n.RULES}
badgeString={linkedRules.length.toString()}
/>
</EuiFlexItem>
<EuiFlexItem>
<HeaderMenu
disableActions={readOnly}
dataTestSubj="sharedListOverflowCard"
actions={menuActionItems}
/>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
</ListHeaderContainer>
</EuiPanel>
}
data-test-subj={`exceptionsManagementListCard-${listId}`}
>
<ExceptionPanel hasBorder>
<ListExceptionItems
isReadOnly={readOnly}
exceptions={exceptions}
listType={exceptionsList.type as ExceptionListTypeEnum}
pagination={pagination}
hideUtility
viewerStatus={exceptionViewerStatus}
ruleReferences={ruleReferences}
onDeleteException={onDeleteException}
onEditExceptionItem={onEditExceptionItem}
onPaginationChange={onPaginationChange}
onCreateExceptionListItem={onAddExceptionClick}
lastUpdated={null}
emptyViewerTitle={emptyViewerTitle}
emptyViewerBody={emptyViewerBody}
emptyViewerButtonText={emptyViewerButtonText}
<EuiFlexItem>
<EuiFlexGroup alignItems="center" justifyContent="flexEnd" wrap responsive>
<EuiFlexItem grow={false}>
<TitleBadge title={i18n.DATE_CREATED} badgeString={createdAt} />
</EuiFlexItem>
<EuiFlexItem grow={false}>
<TitleBadge title={i18n.CREATED_BY} badgeString={createdBy} />
</EuiFlexItem>
<EuiFlexItem grow={false}>
<TitleBadge title={i18n.EXCEPTIONS} badgeString={exceptionItemsCount} />
</EuiFlexItem>
<EuiFlexItem data-test-subj="exceptionListCardLinkedRulesBadge" grow={false}>
<TitleBadge title={i18n.RULES} badgeString={linkedRules.length.toString()} />
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<HeaderMenu
disableActions={readOnly}
dataTestSubj="sharedListOverflowCard"
actions={menuActionItems}
/>
</ExceptionPanel>
</EuiAccordion>
</EuiPanel>
</EuiFlexItem>
</EuiFlexItem>
</ListHeaderContainer>
}
data-test-subj={`exceptionsManagementListCard-${listId}`}
>
<ExceptionPanel hasBorder>
<ListExceptionItems
isReadOnly={readOnly}
exceptions={exceptions}
listType={exceptionsList.type as ExceptionListTypeEnum}
pagination={pagination}
hideUtility
viewerStatus={exceptionViewerStatus}
ruleReferences={ruleReferences}
onDeleteException={onDeleteException}
onEditExceptionItem={onEditExceptionItem}
onPaginationChange={onPaginationChange}
onCreateExceptionListItem={onAddExceptionClick}
lastUpdated={null}
emptyViewerTitle={emptyViewerTitle}
emptyViewerBody={emptyViewerBody}
emptyViewerButtonText={emptyViewerButtonText}
/>
</ExceptionPanel>
</EuiAccordion>
{showAddExceptionFlyout ? (
<AddExceptionFlyout
rules={null}
Expand Down Expand Up @@ -278,7 +274,7 @@ export const ExceptionsListCard = memo<ExceptionsListCardProps>(
action={showIncludeExpiredExceptionsModal}
/>
) : null}
</EuiFlexGroup>
</>
);
}
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ import { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types';
import { useApi, useExceptionLists } from '@kbn/securitysolution-list-hooks';
import { EmptyViewerState, ViewerStatus } from '@kbn/securitysolution-exception-list-components';

import styled from 'styled-components';
import { euiThemeVars } from '@kbn/ui-theme';
import { AutoDownload } from '../../../common/components/auto_download/auto_download';
import { useKibana } from '../../../common/lib/kibana';
import { useAppToasts } from '../../../common/hooks/use_app_toasts';
Expand Down Expand Up @@ -79,6 +81,10 @@ const SORT_FIELDS: Array<{ field: string; label: string; defaultOrder: 'asc' | '
},
];

const ExceptionsTable = styled(EuiFlexGroup)`
padding: ${euiThemeVars.euiSizeL} 0;
`;

export const SharedLists = React.memo(() => {
const [{ loading: userInfoLoading, canUserCRUD, canUserREAD }] = useUserData();

Expand Down Expand Up @@ -587,19 +593,20 @@ export const SharedLists = React.memo(() => {
sortFields={SORT_FIELDS}
/>
{exceptionListsWithRuleRefs.length > 0 && (
<div data-test-subj="exceptionsTable">
<ExceptionsTable data-test-subj="exceptionsTable" direction="column">
{exceptionListsWithRuleRefs.map((excList) => (
<ExceptionsListCard
key={excList.list_id}
data-test-subj="exceptionsListCard"
readOnly={isReadOnly}
exceptionsList={excList}
handleDelete={handleDelete}
handleExport={handleExport}
handleDuplicate={handleDuplicate}
/>
<EuiFlexItem key={excList.list_id}>
<ExceptionsListCard
data-test-subj="exceptionsListCard"
readOnly={isReadOnly}
exceptionsList={excList}
handleDelete={handleDelete}
handleExport={handleExport}
handleDuplicate={handleDuplicate}
/>
</EuiFlexItem>
))}
</div>
</ExceptionsTable>
)}
</>
)}
Expand Down

0 comments on commit b90e215

Please sign in to comment.