Skip to content

Commit

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

# Backport

This will backport the following commits from `main` to `8.13`:
- [fix: [Rules > Shared exception lists][AXE-CORE]: Interactive
controls must not be nested
(#178023)](#178023)

<!--- Backport version: 9.4.3 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT [{"author":{"name":"Alexey
Antonov","email":"[email protected]"},"sourceCommit":{"committedDate":"2024-03-14T16:02:50Z","message":"fix:
[Rules > Shared exception lists][AXE-CORE]: Interactive controls must
not be nested (#178023)\n\nCloses:
https://github.com/elastic/security-team/issues/8611\r\n\r\n##
Description\r\n\r\nThe [axe browser plugin](https://deque.com/axe) is
reporting a nested\r\nbutton (interactive control)in the shared
exception list accordion(s).\r\nThe accordion has a button to open/close
and a link to the list detail\r\nview. Screenshot attached
below.\r\n\r\n### Steps to recreate\r\n\r\n1. Open the Security
Dashboards, then click Rules > Shared Exception\r\nLists\r\n2. Run an
axe browser scan in Chrome, Edge, or Firefox\r\n3. Verify the nested
control error\r\n\r\n### What was done? \r\n\r\n1. Solution proposed by
EUI team was applied.
See\r\nhttps://github.com/elastic/eui/issues/7545\r\n2. The component
`ExceptionsListCard` has been slightly changed to make\r\nit more
responsive.\r\n Before: \r\n\r\n After:
\r\n\r\n\r\nhttps://github.com/elastic/kibana/assets/20072247/dd6c6681-980c-40ed-98cf-29a71f896bc2\r\n\r\n\r\n\r\n\r\n###
Screen\r\n\r\n#### AXE Report
\r\n\r\n\r\n![image](https://github.com/elastic/kibana/assets/20072247/43313005-b7ec-49d1-9eed-30bfacbb5ecc)","sha":"b90e215d9b4a1462c8cb3be692bcc44edda1e6b8","branchLabelMapping":{"^v8.14.0$":"main","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:skip","Team:Detections
and Resp","Team: SecuritySolution","Feature:Rule
Exceptions","Team:Detection Engine","v8.13.0","v8.14.0"],"title":"fix:
[Rules > Shared exception lists][AXE-CORE]: Interactive controls must
not be
nested","number":178023,"url":"https://github.com/elastic/kibana/pull/178023","mergeCommit":{"message":"fix:
[Rules > Shared exception lists][AXE-CORE]: Interactive controls must
not be nested (#178023)\n\nCloses:
https://github.com/elastic/security-team/issues/8611\r\n\r\n##
Description\r\n\r\nThe [axe browser plugin](https://deque.com/axe) is
reporting a nested\r\nbutton (interactive control)in the shared
exception list accordion(s).\r\nThe accordion has a button to open/close
and a link to the list detail\r\nview. Screenshot attached
below.\r\n\r\n### Steps to recreate\r\n\r\n1. Open the Security
Dashboards, then click Rules > Shared Exception\r\nLists\r\n2. Run an
axe browser scan in Chrome, Edge, or Firefox\r\n3. Verify the nested
control error\r\n\r\n### What was done? \r\n\r\n1. Solution proposed by
EUI team was applied.
See\r\nhttps://github.com/elastic/eui/issues/7545\r\n2. The component
`ExceptionsListCard` has been slightly changed to make\r\nit more
responsive.\r\n Before: \r\n\r\n After:
\r\n\r\n\r\nhttps://github.com/elastic/kibana/assets/20072247/dd6c6681-980c-40ed-98cf-29a71f896bc2\r\n\r\n\r\n\r\n\r\n###
Screen\r\n\r\n#### AXE Report
\r\n\r\n\r\n![image](https://github.com/elastic/kibana/assets/20072247/43313005-b7ec-49d1-9eed-30bfacbb5ecc)","sha":"b90e215d9b4a1462c8cb3be692bcc44edda1e6b8"}},"sourceBranch":"main","suggestedTargetBranches":["8.13"],"targetPullRequestStates":[{"branch":"8.13","label":"v8.13.0","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"},{"branch":"main","label":"v8.14.0","branchLabelMappingKey":"^v8.14.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/178023","number":178023,"mergeCommit":{"message":"fix:
[Rules > Shared exception lists][AXE-CORE]: Interactive controls must
not be nested (#178023)\n\nCloses:
https://github.com/elastic/security-team/issues/8611\r\n\r\n##
Description\r\n\r\nThe [axe browser plugin](https://deque.com/axe) is
reporting a nested\r\nbutton (interactive control)in the shared
exception list accordion(s).\r\nThe accordion has a button to open/close
and a link to the list detail\r\nview. Screenshot attached
below.\r\n\r\n### Steps to recreate\r\n\r\n1. Open the Security
Dashboards, then click Rules > Shared Exception\r\nLists\r\n2. Run an
axe browser scan in Chrome, Edge, or Firefox\r\n3. Verify the nested
control error\r\n\r\n### What was done? \r\n\r\n1. Solution proposed by
EUI team was applied.
See\r\nhttps://github.com/elastic/eui/issues/7545\r\n2. The component
`ExceptionsListCard` has been slightly changed to make\r\nit more
responsive.\r\n Before: \r\n\r\n After:
\r\n\r\n\r\nhttps://github.com/elastic/kibana/assets/20072247/dd6c6681-980c-40ed-98cf-29a71f896bc2\r\n\r\n\r\n\r\n\r\n###
Screen\r\n\r\n#### AXE Report
\r\n\r\n\r\n![image](https://github.com/elastic/kibana/assets/20072247/43313005-b7ec-49d1-9eed-30bfacbb5ecc)","sha":"b90e215d9b4a1462c8cb3be692bcc44edda1e6b8"}}]}]
BACKPORT-->

Co-authored-by: Alexey Antonov <[email protected]>
  • Loading branch information
kibanamachine and alexwizp authored Mar 14, 2024
1 parent f4e3f9f commit dfe85fd
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 dfe85fd

Please sign in to comment.