Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Cases] Filter Overflow When To Much Filters Active #172860

Merged
merged 5 commits into from
Dec 11, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* 2.0.
*/

import { EuiFilterButton } from '@elastic/eui';
import { EuiFilterButton, EuiFilterGroup } from '@elastic/eui';
import { UserProfilesPopover } from '@kbn/user-profile-components';
import { isEmpty } from 'lodash';
import React, { useCallback, useMemo, useState } from 'react';
Expand Down Expand Up @@ -91,44 +91,46 @@ const AssigneesFilterPopoverComponent: React.FC<AssigneesFilterPopoverProps> = (
const isLoadingData = isLoading || isLoadingSuggest;

return (
<UserProfilesPopover
isOpen={isPopoverOpen}
closePopover={togglePopover}
panelStyle={{
minWidth: 520,
}}
button={
<EuiFilterButton
data-test-subj="options-filter-popover-button-assignees"
iconType="arrowDown"
onClick={togglePopover}
isLoading={isLoadingData}
isSelected={isPopoverOpen}
hasActiveFilters={selectedAssignees.length > 0}
numActiveFilters={selectedAssignees.length}
aria-label={i18n.FILTER_ASSIGNEES_ARIA_LABEL}
>
{i18n.ASSIGNEES}
</EuiFilterButton>
}
selectableProps={{
onChange,
onSearchChange,
selectedStatusMessage,
options: searchResultProfiles,
selectedOptions: selectedAssignees,
isLoading: isLoadingData || isUserTyping,
height: 'full',
searchPlaceholder: i18n.SEARCH_USERS,
clearButtonLabel: i18n.CLEAR_FILTERS,
emptyMessage: <EmptyMessage />,
noMatchesMessage: !isUserTyping && !isLoadingData ? <NoMatches /> : <EmptyMessage />,
limit: MAX_ASSIGNEES_FILTER_LENGTH,
limitReachedMessage,
singleSelection: false,
nullOptionLabel: i18n.NO_ASSIGNEES,
}}
/>
<EuiFilterGroup>
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I need to include this because, without it, the selectable styling breaks. It means that we are wrapping every single selectable in a filter group (a one filter only filter group) which might not be ideal semantically

<UserProfilesPopover
isOpen={isPopoverOpen}
closePopover={togglePopover}
panelStyle={{
minWidth: 520,
}}
button={
<EuiFilterButton
data-test-subj="options-filter-popover-button-assignees"
iconType="arrowDown"
onClick={togglePopover}
isLoading={isLoadingData}
isSelected={isPopoverOpen}
hasActiveFilters={selectedAssignees.length > 0}
numActiveFilters={selectedAssignees.length}
aria-label={i18n.FILTER_ASSIGNEES_ARIA_LABEL}
>
{i18n.ASSIGNEES}
</EuiFilterButton>
}
selectableProps={{
onChange,
onSearchChange,
selectedStatusMessage,
options: searchResultProfiles,
selectedOptions: selectedAssignees,
isLoading: isLoadingData || isUserTyping,
height: 'full',
searchPlaceholder: i18n.SEARCH_USERS,
clearButtonLabel: i18n.CLEAR_FILTERS,
emptyMessage: <EmptyMessage />,
noMatchesMessage: !isUserTyping && !isLoadingData ? <NoMatches /> : <EmptyMessage />,
limit: MAX_ASSIGNEES_FILTER_LENGTH,
limitReachedMessage,
singleSelection: false,
nullOptionLabel: i18n.NO_ASSIGNEES,
}}
/>
</EuiFilterGroup>
);
};
AssigneesFilterPopoverComponent.displayName = 'AssigneesFilterPopover';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ import {
EuiTextColor,
EuiSpacer,
useEuiTheme,
EuiFilterGroup,
EuiTextTruncate,
EuiText,
} from '@elastic/eui';
import { isEqual } from 'lodash/fp';
import * as i18n from './translations';
Expand Down Expand Up @@ -63,16 +66,17 @@ const getEuiSelectableCheckedOptions = <T extends string, K extends string>(
) => options.filter((option) => option.checked === 'on') as Array<FilterOption<T, K>>;

interface UseFilterParams<T extends string, K extends string = string> {
buttonLabel?: string;
buttonIconType?: string;
buttonLabel?: string;
hideActiveOptionsNumber?: boolean;
id: string;
limit?: number;
limitReachedMessage?: string;
onChange: (params: { filterId: string; selectedOptionKeys: string[] }) => void;
options: Array<FilterOption<T, K>>;
selectedOptionKeys?: string[];
renderOption?: (option: FilterOption<T, K>) => React.ReactNode;
selectedOptionKeys?: string[];
transparentBackground?: boolean;
}
export const MultiSelectFilter = <T extends string, K extends string = string>({
buttonLabel,
Expand All @@ -85,6 +89,7 @@ export const MultiSelectFilter = <T extends string, K extends string = string>({
options: rawOptions,
selectedOptionKeys = [],
renderOption,
transparentBackground,
}: UseFilterParams<T, K>) => {
const { euiTheme } = useEuiTheme();
const [isPopoverOpen, setIsPopoverOpen] = useState(false);
Expand Down Expand Up @@ -118,74 +123,87 @@ export const MultiSelectFilter = <T extends string, K extends string = string>({
};

return (
<EuiPopover
ownFocus
button={
<EuiFilterButton
data-test-subj={`options-filter-popover-button-${id}`}
iconType={buttonIconType || 'arrowDown'}
onClick={toggleIsPopoverOpen}
isSelected={isPopoverOpen}
numFilters={showActiveOptionsNumber ? options.length : undefined}
hasActiveFilters={showActiveOptionsNumber ? selectedOptionKeys.length > 0 : undefined}
numActiveFilters={showActiveOptionsNumber ? selectedOptionKeys.length : undefined}
aria-label={buttonLabel}
>
{buttonLabel}
</EuiFilterButton>
}
isOpen={isPopoverOpen}
closePopover={() => setIsPopoverOpen(false)}
panelPaddingSize="none"
repositionOnScroll
<EuiFilterGroup
css={css`
${transparentBackground && 'background-color: transparent;'};
`}
>
{isInvalid && (
<>
<EuiHorizontalRule margin="none" />
<EuiCallOut
title={limitReachedMessage}
color="warning"
size="s"
data-test-subj="maximum-length-warning"
/>
<EuiHorizontalRule margin="none" />
</>
)}
<EuiSelectable<FilterOption<T, K>>
options={options}
searchable
searchProps={{
placeholder: buttonLabel,
compressed: false,
'data-test-subj': `${id}-search-input`,
}}
emptyMessage={i18n.EMPTY_FILTER_MESSAGE}
onChange={_onChange}
singleSelection={false}
renderOption={renderOption}
>
{(list, search) => (
<div
<EuiPopover
ownFocus
button={
<EuiFilterButton
css={css`
width: 400px;
max-width: 186px;
`}
data-test-subj={`options-filter-popover-button-${id}`}
iconType={buttonIconType || 'arrowDown'}
onClick={toggleIsPopoverOpen}
isSelected={isPopoverOpen}
numFilters={showActiveOptionsNumber ? options.length : undefined}
hasActiveFilters={showActiveOptionsNumber ? selectedOptionKeys.length > 0 : undefined}
numActiveFilters={showActiveOptionsNumber ? selectedOptionKeys.length : undefined}
aria-label={buttonLabel}
>
<EuiPopoverTitle paddingSize="s">{search}</EuiPopoverTitle>
<EuiTextTruncate text={buttonLabel || ''} truncation={'end'} width={128}>
{(truncatedText: string) => (
<EuiText size="s" dangerouslySetInnerHTML={{ __html: truncatedText }} />
jcger marked this conversation as resolved.
Show resolved Hide resolved
)}
</EuiTextTruncate>
</EuiFilterButton>
}
isOpen={isPopoverOpen}
closePopover={() => setIsPopoverOpen(false)}
panelPaddingSize="none"
repositionOnScroll
>
{isInvalid && (
<>
<EuiHorizontalRule margin="none" />
<EuiCallOut
title={limitReachedMessage}
color="warning"
size="s"
data-test-subj="maximum-length-warning"
/>
<EuiHorizontalRule margin="none" />
</>
)}
<EuiSelectable<FilterOption<T, K>>
options={options}
searchable
searchProps={{
placeholder: buttonLabel,
compressed: false,
'data-test-subj': `${id}-search-input`,
}}
emptyMessage={i18n.EMPTY_FILTER_MESSAGE}
onChange={_onChange}
singleSelection={false}
renderOption={renderOption}
>
{(list, search) => (
<div
css={css`
line-height: ${euiTheme.size.xl};
padding-left: ${euiTheme.size.m};
border-bottom: ${euiTheme.border.thin};
width: 400px;
`}
>
<EuiTextColor color="subdued">{i18n.OPTIONS(options.length)}</EuiTextColor>
<EuiPopoverTitle paddingSize="s">{search}</EuiPopoverTitle>
<div
css={css`
line-height: ${euiTheme.size.xl};
padding-left: ${euiTheme.size.m};
border-bottom: ${euiTheme.border.thin};
`}
>
<EuiTextColor color="subdued">{i18n.OPTIONS(options.length)}</EuiTextColor>
</div>
<EuiSpacer size="xs" />
{list}
</div>
<EuiSpacer size="xs" />
{list}
</div>
)}
</EuiSelectable>
</EuiPopover>
)}
</EuiSelectable>
</EuiPopover>
</EuiFilterGroup>
);
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export const MoreFiltersSelectable = ({
onChange={onChange}
options={options}
selectedOptionKeys={activeFilters}
transparentBackground={true}
/>
);
};
Expand Down
33 changes: 17 additions & 16 deletions x-pack/plugins/cases/public/components/all_cases/table_filters.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
*/

import React, { useCallback, useState } from 'react';
import { EuiFlexGroup, EuiFlexItem, EuiFieldSearch, EuiFilterGroup, EuiButton } from '@elastic/eui';
import { EuiFlexGroup, EuiFlexItem, EuiFieldSearch, EuiButton } from '@elastic/eui';
import { mergeWith, isEqual } from 'lodash';
import { MoreFiltersSelectable } from './table_filter_config/more_filters_selectable';
import type { CaseStatuses } from '../../../common/types/domain';
Expand Down Expand Up @@ -126,7 +126,7 @@ const CasesTableFiltersComponent = ({
}, [onCreateCasePressed]);

return (
<EuiFlexGroup gutterSize="s" justifyContent="flexStart">
<EuiFlexGroup gutterSize="s" justifyContent="flexStart" wrap={true}>
{isSelectorView && onCreateCasePressed ? (
<EuiFlexItem grow={false}>
<EuiButton
Expand All @@ -149,20 +149,21 @@ const CasesTableFiltersComponent = ({
onSearch={handleOnSearch}
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiFilterGroup data-test-subj="cases-table-filters-group">
{activeFilters.map((filter) => (
<React.Fragment key={filter.key}>{filter.render({ filterOptions })}</React.Fragment>
))}
{isSelectorView || (
<MoreFiltersSelectable
options={selectableOptions}
activeFilters={activeSelectableOptionKeys}
onChange={onFilterConfigChange}
/>
)}
</EuiFilterGroup>
</EuiFlexItem>
{activeFilters.map((filter) => (
<EuiFlexItem grow={false} key={filter.key}>
{filter.render({ filterOptions })}
</EuiFlexItem>
))}

{isSelectorView || (
<EuiFlexItem grow={false}>
<MoreFiltersSelectable
options={selectableOptions}
activeFilters={activeSelectableOptionKeys}
onChange={onFilterConfigChange}
/>
</EuiFlexItem>
)}
</EuiFlexGroup>
);
};
Expand Down