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

[Controls] [PresentationUtil] QoL improvements to control creation form #162067

Merged
merged 16 commits into from
Aug 4, 2023
Merged
Show file tree
Hide file tree
Changes from all 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 @@ -259,7 +259,7 @@ export const ControlEditor = ({
<EuiFormRow
label={ControlGroupStrings.manageControl.displaySettings.getWidthInputTitle()}
>
<>
<div>
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Switched this to a div so that it can receive the id passed down from the EuiFormRow component - not sure why, but the React fragment ignored it 🤷

<EuiButtonGroup
color="primary"
legend={ControlGroupStrings.management.controlWidth.getWidthSwitchLegend()}
Expand All @@ -275,7 +275,7 @@ export const ControlEditor = ({
onChange={() => setCurrentGrow(!currentGrow)}
data-test-subj="control-editor-grow-switch"
/>
</>
</div>
</EuiFormRow>
)}
</EuiDescribedFormGroup>
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,12 @@
* Side Public License, v 1.
*/

import { i18n } from '@kbn/i18n';
import React, { useState } from 'react';
import { EuiPopover, EuiPopoverTitle, EuiSelectable, EuiSelectableProps } from '@elastic/eui';
import { EuiSelectable, EuiInputPopover, EuiSelectableProps } from '@elastic/eui';
import { DataViewListItem } from '@kbn/data-views-plugin/common';

import { ToolbarButton, ToolbarButtonProps } from '@kbn/kibana-react-plugin/public';

import './data_view_picker.scss';

export type DataViewTriggerProps = ToolbarButtonProps & {
label: string;
title?: string;
Expand All @@ -26,6 +23,7 @@ export function DataViewPicker({
onChangeDataViewId,
trigger,
selectableProps,
...other
}: {
dataViews: DataViewListItem[];
selectedDataViewId?: string;
Expand Down Expand Up @@ -61,20 +59,19 @@ export function DataViewPicker({
};

return (
<EuiPopover
button={createTrigger()}
isOpen={isPopoverOpen}
closePopover={() => setPopoverIsOpen(false)}
<EuiInputPopover
{...other}
ownFocus
fullWidth
display="block"
panelPaddingSize="s"
ownFocus
panelClassName="presDataViewPicker__panel"
isOpen={isPopoverOpen}
input={createTrigger()}
closePopover={() => setPopoverIsOpen(false)}
panelProps={{
'data-test-subj': 'data-view-picker-popover',
}}
>
<EuiPopoverTitle data-test-subj="data-view-picker-title">
{i18n.translate('presentationUtil.dataViewPicker.changeDataViewTitle', {
defaultMessage: 'Data view',
})}
</EuiPopoverTitle>
<EuiSelectable<{
key?: string;
label: string;
Expand Down Expand Up @@ -110,7 +107,7 @@ export function DataViewPicker({
</>
)}
</EuiSelectable>
</EuiPopover>
</EuiInputPopover>
);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@

.presFieldPickerFieldButtonActive {
background-color: transparentize($euiColorPrimary, .9);
}

.fieldPickerSelectable {
height: $euiSizeXXL * 9; // 40 * 9 = 360px

.presFieldPicker__fieldButton[aria-checked='true'] {
background-color: transparentize($euiColorPrimary, .9);
}

.euiSelectableMessage {
height: 100%;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

import classNames from 'classnames';
import { sortBy, uniq } from 'lodash';
import React, { useEffect, useMemo, useState } from 'react';
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';

import { i18n } from '@kbn/i18n';
import { FieldIcon } from '@kbn/react-field';
Expand Down Expand Up @@ -39,8 +39,12 @@ export const FieldPicker = ({
filterPredicate,
selectedFieldName,
selectableProps,
...other
}: FieldPickerProps) => {
const initialSelection = useRef(selectedFieldName);

const [typesFilter, setTypesFilter] = useState<string[]>([]);
const [searchRef, setSearchRef] = useState<HTMLInputElement | null>(null);
const [fieldSelectableOptions, setFieldSelectableOptions] = useState<EuiSelectableOption[]>([]);

const availableFields = useMemo(
Expand All @@ -50,7 +54,7 @@ export const FieldPicker = ({
.filter((f) => typesFilter.length === 0 || typesFilter.includes(f.type as string))
.filter((f) => (filterPredicate ? filterPredicate(f) : true)),
['name']
),
).sort((f) => (f.name === initialSelection.current ? -1 : 1)),
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This brings the selected field, if provided, to the top of the list on load.

[dataView, filterPredicate, typesFilter]
);

Expand All @@ -60,9 +64,8 @@ export const FieldPicker = ({
return {
key: field.name,
label: field.displayName ?? field.name,
className: classNames('presFieldPicker__fieldButton', {
presFieldPickerFieldButtonActive: field.name === selectedFieldName,
}),
className: 'presFieldPicker__fieldButton',
checked: field.name === selectedFieldName ? 'on' : undefined,
'data-test-subj': `field-picker-select-${field.name}`,
prepend: (
<FieldIcon
Expand All @@ -89,9 +92,14 @@ export const FieldPicker = ({
[dataView, filterPredicate]
);

const setFocusToSearch = useCallback(() => {
searchRef?.focus();
}, [searchRef]);

const fieldTypeFilter = (
<EuiFormRow fullWidth={true}>
<FieldTypeFilter
setFocusToSearch={setFocusToSearch}
onFieldTypesChange={(types) => setTypesFilter(types)}
fieldTypesValue={typesFilter}
availableFieldTypes={uniqueTypes}
Expand All @@ -102,6 +110,7 @@ export const FieldPicker = ({

return (
<EuiSelectable
{...other}
{...selectableProps}
className={classNames('fieldPickerSelectable', {
fieldPickerSelectableLoading: selectableProps?.isLoading,
Expand All @@ -126,6 +135,7 @@ export const FieldPicker = ({
defaultMessage: 'Search field names',
}),
disabled: Boolean(selectableProps?.isLoading),
inputRef: setSearchRef,
}}
listProps={{
isVirtualized: true,
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -7,35 +7,33 @@
*/

import React, { useState } from 'react';
import { i18n } from '@kbn/i18n';

import {
EuiFilterGroup,
EuiFlexGroup,
EuiFlexItem,
EuiPopover,
EuiInputPopover,
EuiContextMenuPanel,
EuiContextMenuItem,
EuiOutsideClickDetector,
EuiFilterButton,
EuiPopoverTitle,
EuiFilterButtonProps,
} from '@elastic/eui';
import { FieldIcon } from '@kbn/react-field';
import { FormattedMessage } from '@kbn/i18n-react';

import './field_type_filter.scss';

export interface Props {
onFieldTypesChange: (value: string[]) => void;
fieldTypesValue: string[];
availableFieldTypes: string[];
buttonProps?: Partial<EuiFilterButtonProps>;
setFocusToSearch: () => void;
availableFieldTypes: string[];
fieldTypesValue: string[];
}

export function FieldTypeFilter({
availableFieldTypes,
onFieldTypesChange,
setFocusToSearch,
fieldTypesValue,
availableFieldTypes,
buttonProps,
}: Props) {
const [isPopoverOpen, setPopoverOpen] = useState(false);
Expand Down Expand Up @@ -63,48 +61,45 @@ export function FieldTypeFilter({
);

return (
<EuiOutsideClickDetector onOutsideClick={() => {}} isDisabled={!isPopoverOpen}>
<EuiFilterGroup fullWidth>
<EuiPopover
panelClassName="presFilterByType__panel"
panelPaddingSize="none"
display="block"
isOpen={isPopoverOpen}
closePopover={() => {
setPopoverOpen(false);
}}
button={buttonContent}
>
<EuiPopoverTitle paddingSize="s">
{i18n.translate('presentationUtil.fieldSearch.filterByTypeLabel', {
defaultMessage: 'Filter by type',
})}
</EuiPopoverTitle>
<EuiContextMenuPanel
items={(availableFieldTypes as string[]).map((type) => (
<EuiContextMenuItem
key={type}
icon={fieldTypesValue.includes(type) ? 'check' : 'empty'}
data-test-subj={`typeFilter-${type}`}
onClick={() => {
if (fieldTypesValue.includes(type)) {
onFieldTypesChange(fieldTypesValue.filter((f) => f !== type));
} else {
onFieldTypesChange([...fieldTypesValue, type]);
}
}}
>
<EuiFlexGroup gutterSize="xs" responsive={false}>
<EuiFlexItem grow={false}>
<FieldIcon type={type} label={type} />
</EuiFlexItem>
<EuiFlexItem>{type}</EuiFlexItem>
</EuiFlexGroup>
</EuiContextMenuItem>
))}
/>
</EuiPopover>
</EuiFilterGroup>
</EuiOutsideClickDetector>
<EuiFilterGroup fullWidth>
<EuiInputPopover
panelPaddingSize="none"
display="block"
isOpen={isPopoverOpen}
closePopover={() => {
setPopoverOpen(false);
}}
fullWidth
input={buttonContent}
focusTrapProps={{
returnFocus: false, // we will be manually returning the focus to the search
onDeactivation: setFocusToSearch,
}}
Comment on lines +74 to +77
Copy link
Contributor Author

@Heenawter Heenawter Jul 31, 2023

Choose a reason for hiding this comment

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

This is the important change in this file - it ensures that, as described in elastic/eui#6627 (comment), the focus returns to the search field when the field type filter is closed (either via Esc or through the natural tab order).

>
<EuiContextMenuPanel
items={(availableFieldTypes as string[]).map((type) => (
<EuiContextMenuItem
key={type}
icon={fieldTypesValue.includes(type) ? 'check' : 'empty'}
data-test-subj={`typeFilter-${type}`}
onClick={() => {
if (fieldTypesValue.includes(type)) {
onFieldTypesChange(fieldTypesValue.filter((f) => f !== type));
} else {
onFieldTypesChange([...fieldTypesValue, type]);
}
}}
>
<EuiFlexGroup gutterSize="xs" responsive={false}>
<EuiFlexItem grow={false}>
<FieldIcon type={type} label={type} />
</EuiFlexItem>
<EuiFlexItem>{type}</EuiFlexItem>
</EuiFlexGroup>
</EuiContextMenuItem>
))}
/>
</EuiInputPopover>
</EuiFilterGroup>
);
}
2 changes: 1 addition & 1 deletion test/functional/page_objects/dashboard_page_controls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -599,7 +599,7 @@ export class DashboardPageControls extends FtrService {
this.log.debug(`Setting control data view to ${dataViewTitle}`);
await this.testSubjects.click('open-data-view-picker');
await this.retry.try(async () => {
await this.testSubjects.existOrFail('data-view-picker-title');
await this.testSubjects.existOrFail('data-view-picker-popover');
});
await this.testSubjects.click(`data-view-picker-${dataViewTitle}`);
}
Expand Down
2 changes: 0 additions & 2 deletions x-pack/plugins/translations/translations/fr-FR.json
Original file line number Diff line number Diff line change
Expand Up @@ -4793,11 +4793,9 @@
"presentationUtil.labs.components.enabledStatusMessage": "Par défaut : {status}",
"presentationUtil.labs.components.noProjectsinSolutionMessage": "Aucun atelier actuellement dans {solutionName}.",
"presentationUtil.dashboardPicker.searchDashboardPlaceholder": "Recherche dans les tableaux de bord…",
"presentationUtil.dataViewPicker.changeDataViewTitle": "Vue de données",
"presentationUtil.fieldPicker.noFieldsLabel": "Aucun champ correspondant",
"presentationUtil.fieldPicker.selectableAriaLabel": "Sélectionner un champ",
"presentationUtil.fieldSearch.fieldFilterButtonLabel": "Filtrer par type",
"presentationUtil.fieldSearch.filterByTypeLabel": "Filtrer par type",
"presentationUtil.fieldSearch.searchPlaceHolder": "Rechercher les noms de champs",
"presentationUtil.labs.components.browserSwitchHelp": "Active l'atelier pour ce navigateur et persiste après sa fermeture.",
"presentationUtil.labs.components.browserSwitchName": "Navigateur",
Expand Down
2 changes: 0 additions & 2 deletions x-pack/plugins/translations/translations/ja-JP.json
Original file line number Diff line number Diff line change
Expand Up @@ -4809,11 +4809,9 @@
"presentationUtil.labs.components.enabledStatusMessage": "デフォルト:{status}",
"presentationUtil.labs.components.noProjectsinSolutionMessage": "現在{solutionName}にラボはありません。",
"presentationUtil.dashboardPicker.searchDashboardPlaceholder": "ダッシュボードを検索...",
"presentationUtil.dataViewPicker.changeDataViewTitle": "データビュー",
"presentationUtil.fieldPicker.noFieldsLabel": "一致するがフィールドがありません",
"presentationUtil.fieldPicker.selectableAriaLabel": "フィールドを選択",
"presentationUtil.fieldSearch.fieldFilterButtonLabel": "タイプでフィルタリング",
"presentationUtil.fieldSearch.filterByTypeLabel": "タイプでフィルタリング",
"presentationUtil.fieldSearch.searchPlaceHolder": "検索フィールド名",
"presentationUtil.labs.components.browserSwitchHelp": "このブラウザーでラボを有効にします。ブラウザーを閉じた後も永続します。",
"presentationUtil.labs.components.browserSwitchName": "ブラウザー",
Expand Down
2 changes: 0 additions & 2 deletions x-pack/plugins/translations/translations/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -4808,11 +4808,9 @@
"presentationUtil.labs.components.enabledStatusMessage": "默认值:{status}",
"presentationUtil.labs.components.noProjectsinSolutionMessage": "{solutionName} 中当前没有实验。",
"presentationUtil.dashboardPicker.searchDashboardPlaceholder": "搜索仪表板......",
"presentationUtil.dataViewPicker.changeDataViewTitle": "数据视图",
"presentationUtil.fieldPicker.noFieldsLabel": "无匹配字段",
"presentationUtil.fieldPicker.selectableAriaLabel": "选择字段",
"presentationUtil.fieldSearch.fieldFilterButtonLabel": "按类型筛选",
"presentationUtil.fieldSearch.filterByTypeLabel": "按类型筛选",
"presentationUtil.fieldSearch.searchPlaceHolder": "搜索字段名称",
"presentationUtil.labs.components.browserSwitchHelp": "启用此浏览器的实验并在其关闭后继续保持。",
"presentationUtil.labs.components.browserSwitchName": "浏览器",
Expand Down