Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
3ec0455
Refactor to OptionList
qn895 Oct 2, 2024
4b7dd54
Refactor to OptionListWithStats
qn895 Oct 2, 2024
fcb84ba
Add styling for removal
qn895 Oct 2, 2024
0f6224a
Add for other rest of AD
qn895 Oct 2, 2024
cc0de0e
Update for geo field
qn895 Oct 2, 2024
57377aa
Update styling for DFA
qn895 Oct 2, 2024
e321f9d
Update types and styling
qn895 Oct 3, 2024
29658f7
Update width
qn895 Oct 3, 2024
585b2e3
Revert AIOps change
qn895 Oct 3, 2024
70399ee
Remove commented code
qn895 Oct 3, 2024
f9fd80f
[CI] Auto-commit changed files from 'node scripts/notice'
kibanamachine Oct 3, 2024
0915532
Update translations
qn895 Oct 3, 2024
711409f
Update component
qn895 Oct 3, 2024
a2efa16
FIx types
qn895 Oct 3, 2024
72bb627
Update layout
qn895 Oct 3, 2024
1e16d85
Use formcontrolledlayout instead
qn895 Oct 3, 2024
5a8269d
Update types
qn895 Oct 3, 2024
0636979
Merge branch 'ml-fields-empty-state-controls' into replace-combo-box
qn895 Oct 3, 2024
eecd220
Fix in advanced settings
qn895 Oct 3, 2024
2534c00
Update tests
qn895 Oct 4, 2024
2f75637
Update tests for AD
qn895 Oct 7, 2024
ddd93dd
Update for DFA
qn895 Oct 7, 2024
8d260a4
Update DFA tests
qn895 Oct 7, 2024
b32e595
Fix accessability test
qn895 Oct 7, 2024
0d5c2be
Merge remote-tracking branch 'upstream/main' into replace-combo-box
qn895 Oct 7, 2024
5e8355e
Update tests
qn895 Oct 8, 2024
139627c
Fix button showing more than needed
qn895 Oct 9, 2024
570880c
Merge remote-tracking branch 'upstream/main' into replace-combo-box
qn895 Oct 9, 2024
84fd32d
Fix undefined
qn895 Oct 9, 2024
17d1253
Fix type
qn895 Oct 9, 2024
5cebeb9
Merge remote-tracking branch 'upstream/main' into replace-combo-box
qn895 Oct 9, 2024
c7371cc
Fix timestamp or fields without aggs showing up
qn895 Oct 10, 2024
4c0c3ae
Update types, style, remove extra icons
qn895 Oct 14, 2024
ba1bd8c
Merge remote-tracking branch 'upstream/main' into replace-combo-box
qn895 Oct 14, 2024
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

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ export const FieldStatsFlyoutProvider: FC<FieldStatsFlyoutProviderProps> = (prop
// Get all field names for each returned doc and flatten it
// to a list of unique field names used across all docs.
const fieldsWithData = new Set(docs.map(Object.keys).flat(1));

manager.set(cacheKey, fieldsWithData);
if (!unmounted) {
setPopulatedFields(fieldsWithData);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ export const FieldStatsInfoButton: FC<FieldStatsInfoButtonProps> = (props) => {
defaultMessage: '(no data found in 1000 sample records)',
})
: '';

return (
<EuiFlexGroup gutterSize="none" alignItems="center">
<EuiFlexItem grow={false}>
Expand Down Expand Up @@ -135,22 +136,22 @@ export const FieldStatsInfoButton: FC<FieldStatsInfoButtonProps> = (props) => {
grow={false}
css={{
paddingRight: themeVars.euiTheme.euiSizeXS,
paddingBottom: themeVars.euiTheme.euiSizeXS,
}}
>
<FieldIcon
color={isEmpty ? themeVars.euiTheme.euiColorDisabled : undefined}
type={getKbnFieldIconType(field.type)}
fill="none"
/>
{!hideTrigger ? (
<FieldIcon
color={isEmpty ? themeVars.euiTheme.euiColorDisabled : undefined}
type={getKbnFieldIconType(field.type)}
fill="none"
/>
) : null}
</EuiFlexItem>
<EuiText
color={isEmpty ? 'subdued' : undefined}
size="s"
aria-label={label}
title={label}
className="euiComboBoxOption__content"
css={{ paddingBottom: themeVars.euiTheme.euiSizeXS }}
>
{label}
</EuiText>
Expand Down
7 changes: 3 additions & 4 deletions x-pack/packages/ml/field_stats_flyout/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ export {
type FieldStatsInfoButtonProps,
} from './field_stats_info_button';
export { useFieldStatsTrigger } from './use_field_stats_trigger';
export {
EuiComboBoxWithFieldStats,
type EuiComboBoxWithFieldStatsProps,
} from './eui_combo_box_with_field_stats';

export { OptionListWithFieldStats } from './options_list_with_stats/option_list_with_stats';
export type { DropDownLabel } from './options_list_with_stats/types';
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
/*
* 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 type { FC } from 'react';
import React, { useMemo, useState, useEffect } from 'react';
import { isDefined } from '@kbn/ml-is-defined';
import type {
EuiComboBoxOptionOption,
EuiComboBoxSingleSelectionShape,
EuiSelectableOption,
} from '@elastic/eui';
import { EuiFlexItem, EuiSelectable, htmlIdGenerator } from '@elastic/eui';
import { css } from '@emotion/react';
import { type DropDownLabel } from './types';
import { useFieldStatsFlyoutContext } from '../use_field_stats_flyout_context';
import { OptionsListPopoverFooter } from './option_list_popover_footer';

interface OptionsListPopoverProps {
options: DropDownLabel[];
renderOption: (option: DropDownLabel) => React.ReactNode;
singleSelection?: boolean | EuiComboBoxSingleSelectionShape;
onChange?:
| ((newSuggestions: DropDownLabel[]) => void)
| ((
newSuggestions: Array<EuiComboBoxOptionOption<string | number | string[] | undefined>>
) => void);
setPopoverOpen: (open: boolean) => void;
isLoading?: boolean;
}

interface OptionsListPopoverSuggestionsProps {
options: DropDownLabel[];
renderOption: (option: DropDownLabel) => React.ReactNode;
singleSelection?: boolean | EuiComboBoxSingleSelectionShape;
onChange?:
| ((newSuggestions: DropDownLabel[]) => void)
| ((
newSuggestions: Array<EuiComboBoxOptionOption<string | number | string[] | undefined>>
) => void);
setPopoverOpen: (open: boolean) => void;
}
const OptionsListPopoverSuggestions: FC<OptionsListPopoverSuggestionsProps> = ({
options,
renderOption,
singleSelection,
onChange,
setPopoverOpen,
}) => {
const [selectableOptions, setSelectableOptions] = useState<DropDownLabel[]>([]); // will be set in following useEffect
useEffect(() => {
/* This useEffect makes selectableOptions responsive to search, show only selected, and clear selections */
const _selectableOptions = (options ?? []).map((suggestion) => {
const key = suggestion.label ?? suggestion.field?.id;
return {
...suggestion,
key,
checked: undefined,
'data-test-subj': `optionsListControlSelection-${key}`,
};
});
setSelectableOptions(_selectableOptions);
}, [options]);

return (
<EuiSelectable
searchProps={{ 'data-test-subj': 'optionsListFilterInput' }}
singleSelection={Boolean(singleSelection)}
searchable
options={selectableOptions as Array<EuiSelectableOption<string>>}
renderOption={renderOption}
listProps={{ onFocusBadge: false }}
onChange={(opts, _, changedOption) => {
const option = changedOption as DropDownLabel;
if (singleSelection) {
if (onChange) {
onChange([option as EuiComboBoxOptionOption<string | number | string[] | undefined>]);
setPopoverOpen(false);
}
} else {
if (onChange) {
onChange([option as EuiComboBoxOptionOption<string | number | string[] | undefined>]);
setPopoverOpen(false);
}
}
}}
>
{(list, search) => (
<>
{search}
{list}
</>
)}
</EuiSelectable>
);
};

export const OptionsListPopover = ({
options,
renderOption,
singleSelection,
onChange,
setPopoverOpen,
isLoading,
}: OptionsListPopoverProps) => {
const { populatedFields } = useFieldStatsFlyoutContext();

const [showEmptyFields, setShowEmptyFields] = useState(false);
const id = useMemo(() => htmlIdGenerator()(), []);

const filteredOptions = useMemo(() => {
return showEmptyFields
? options
: options.filter((option) => {
if (isDefined(option['data-is-empty'])) {
return !option['data-is-empty'];
}
if (
Object.hasOwn(option, 'isGroupLabel') ||
Object.hasOwn(option, 'isGroupLabelOption')
) {
const key = option.key ?? option.searchableLabel;
return key ? populatedFields?.has(key) : false;
}
if (option.field) {
return populatedFields?.has(option.field.id);
}
return true;
});
}, [options, showEmptyFields, populatedFields]);
return (
<div
id={`control-popover-${id}`}
className={'optionsList__popover'}
data-test-subj={`optionsListControlPopover`}
>
<EuiFlexItem
data-test-subj={`optionsListControlAvailableOptions`}
css={css({ width: '100%', height: '100%' })}
>
<OptionsListPopoverSuggestions
renderOption={renderOption}
options={filteredOptions}
singleSelection={singleSelection}
onChange={onChange}
setPopoverOpen={setPopoverOpen}
/>
</EuiFlexItem>
<OptionsListPopoverFooter
showEmptyFields={showEmptyFields}
setShowEmptyFields={setShowEmptyFields}
isLoading={isLoading}
/>
</div>
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The vertical spacing in these dropdowns as always been a bit off, but with this PR they fields now have different kind of uneven spacing where the field looks like it needs some padding above it or maybe moved closer to the detector function below.
image

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Updated here 4c0c3ae (#186670)

);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* 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 React from 'react';
import type { FC } from 'react';
import { EuiPopoverFooter, EuiSwitch, EuiProgress, useEuiBackgroundColor } from '@elastic/eui';
import { css } from '@emotion/react';
import { i18n } from '@kbn/i18n';
import { euiThemeVars } from '@kbn/ui-theme';

export const OptionsListPopoverFooter: FC<{
showEmptyFields: boolean;
setShowEmptyFields: (showEmptyFields: boolean) => void;
isLoading?: boolean;
}> = ({ showEmptyFields, setShowEmptyFields, isLoading }) => {
return (
<EuiPopoverFooter
paddingSize="none"
css={css({
height: euiThemeVars.euiButtonHeight,
backgroundColor: useEuiBackgroundColor('subdued'),
alignItems: 'center',
display: 'flex',
paddingLeft: euiThemeVars.euiSizeS,
})}
>
{isLoading ? (
// @ts-expect-error css should be ok
<div css={css({ position: 'absolute', width: '100%' })}>
<EuiProgress
data-test-subj="optionsList-control-popover-loading"
size="xs"
color="accent"
/>
</div>
) : null}

<EuiSwitch
data-test-subj="optionsListIncludeEmptyFields"
label={i18n.translate('xpack.ml.controls.optionsList.popover.includeEmptyFieldsLabel', {
defaultMessage: 'Include empty fields',
})}
checked={showEmptyFields}
onChange={(e) => setShowEmptyFields(e.target.checked)}
/>
</EuiPopoverFooter>
);
};
Loading