[Dashboards] Flyouts design cleanup (#199408)

## Summary

Updates the styles of the following flyouts to achieve consistency: 

- Create control: This one has been simplified from two-column layout to
single column layout after the design process for ES|QL controls. I've
also changed the setting "expand width to fill" to `off` by default
after feedback from Graham and Nino (I also think it's the better
default).
- Control settings
- Add panel
- Create links panel
- Add link
- Add image
- Add from library (to-do: `Search` input, `Types` input and `Tags`
input should be switched to `compressed`)


This PR tries to standardize buttons in flyout footers according to
these
[guidelines](https://www.figma.com/design/y6thIbIHWSXl3v2GzOTtrE/Dashboard-new-panel-guidelines?node-id=51-364&t=rdiJ19w3v5GKYjrx-1)
which considers what we do in other applications such as Discover. For
example, this PR replaces the label `Save and close` with `Save`. The
behavior in Discover (see “Add field” flyout) is that clicking on `Save`
will `Save AND close` the flyout. I think it’s safe to assume users will
expect that behavior upon saving a flyout.

<img width="2236" alt="Frame 2"
src="https://github.com/user-attachments/assets/6df8d50a-0612-48a8-9215-25625dd40a6e">

<img width="1218" alt="Frame 3"
src="https://github.com/user-attachments/assets/abfef6d4-285a-47f8-8ba8-ab32101cec55">

<img width="1188" alt="Frame 4"
src="https://github.com/user-attachments/assets/3f76b3b5-07a3-4e0f-a837-2ae945003736">

<img width="1258" alt="Frame 5"
src="https://github.com/user-attachments/assets/9fd734b3-f35a-46a4-9340-6af75874ddfd">

<img width="1258" alt="Frame 6"
src="https://github.com/user-attachments/assets/907b362a-058c-44b5-8329-27e7c9d6ad7a">

<img width="1258" alt="Frame 7"
src="https://github.com/user-attachments/assets/42e6e3a6-fc48-4dff-9575-baf27cf1120e">

<img width="1258" alt="Frame 8"
src="https://github.com/user-attachments/assets/48eb2f9c-3a26-4589-a180-726ec701d087">

### Other changes

Additionally I've modified the behavior of the actions (`delete`,
`edit`) for `links`. Instead of always occupying a column, now they're
absolutely positioned and will appear on top of each link on hover. This
allows us to make better use of the space.

![CleanShot 2024-11-07 at 18 30
44](https://github.com/user-attachments/assets/85c07e17-06fd-4c38-a37b-8ec63d764972)


### Checklist

- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
- [x] Any UI touched in this PR is usable by keyboard only (learn more
about [keyboard accessibility](https://webaim.org/techniques/keyboard/))
- [x] This renders correctly on smaller devices using a responsive
layout. (You can test this [in your
browser](https://www.browserstack.com/guide/responsive-testing-on-local-server))
- [x] This was checked for [cross-browser
compatibility](https://www.elastic.co/support/matrix#matrix_browsers)


### Risk Matrix

Delete this section if it is not applicable to this PR.

Before closing this PR, invite QA, stakeholders, and other developers to
identify risks that should be tested prior to the change/feature
release.

When forming the risk matrix, consider some of the following examples
and how they may potentially impact the change:

| Risk | Probability | Severity | Mitigation/Notes |

|---------------------------|-------------|----------|-------------------------|
| Multiple Spaces&mdash;unexpected behavior in non-default Kibana Space.
| Low | High | Integration tests will verify that all features are still
supported in non-default Kibana Space and when user switches between
spaces. |
| Multiple nodes&mdash;Elasticsearch polling might have race conditions
when multiple Kibana nodes are polling for the same tasks. | High | Low
| Tasks are idempotent, so executing them multiple times will not result
in logical error, but will degrade performance. To test for this case we
add plenty of unit tests around this logic and document manual testing
procedure. |
| Code should gracefully handle cases when feature X or plugin Y are
disabled. | Medium | High | Unit tests will verify that any feature flag
or plugin combination still results in our service operational. |
| [See more potential risk
examples](https://github.com/elastic/kibana/blob/main/RISK_MATRIX.mdx) |


### For maintainers

- [ ] This was checked for breaking API changes and was [labeled
appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#_add_your_labels)
- [ ] This will appear in the **Release Notes** and follow the
[guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)

---------

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
Andrea Del Rio 2024-11-11 12:07:06 -08:00 committed by GitHub
parent 160e626ab5
commit 0e1021afbf
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
33 changed files with 272 additions and 298 deletions

View file

@ -16,7 +16,7 @@ export const CONTROL_CHAINING_OPTIONS = { NONE: 'NONE', HIERARCHICAL: 'HIERARCHI
export const DEFAULT_CONTROL_WIDTH: ControlWidth = CONTROL_WIDTH_OPTIONS.MEDIUM;
export const DEFAULT_CONTROL_LABEL_POSITION: ControlLabelPosition =
CONTROL_LABEL_POSITION_OPTIONS.ONE_LINE;
export const DEFAULT_CONTROL_GROW: boolean = true;
export const DEFAULT_CONTROL_GROW: boolean = false;
export const DEFAULT_CONTROL_CHAINING: ControlGroupChainingSystem =
CONTROL_CHAINING_OPTIONS.HIERARCHICAL;
export const DEFAULT_IGNORE_PARENT_SETTINGS = {

View file

@ -72,7 +72,7 @@ export const ControlGroupEditor = ({ onCancel, onSave, onDeleteAll, stateManager
return (
<>
<EuiFlyoutHeader hasBorder>
<EuiTitle size="m">
<EuiTitle size="s">
<h2>{ControlGroupStrings.management.getFlyoutTitle()}</h2>
</EuiTitle>
</EuiFlyoutHeader>
@ -80,7 +80,7 @@ export const ControlGroupEditor = ({ onCancel, onSave, onDeleteAll, stateManager
<EuiForm component="form" fullWidth>
<EuiFormRow label={ControlGroupStrings.management.labelPosition.getLabelPositionTitle()}>
<EuiButtonGroup
color="primary"
buttonSize="compressed"
options={CONTROL_LAYOUT_OPTIONS}
data-test-subj="control-group-layout-options"
idSelected={selectedLabelPosition}
@ -193,7 +193,7 @@ export const ControlGroupEditor = ({ onCancel, onSave, onDeleteAll, stateManager
<EuiFlexItem grow={false}>
<EuiButtonEmpty
aria-label={`cancel-editing-group`}
iconType="cross"
flush="left"
onClick={() => {
onCancel();
}}
@ -204,7 +204,7 @@ export const ControlGroupEditor = ({ onCancel, onSave, onDeleteAll, stateManager
<EuiFlexItem grow={false}>
<EuiButton
aria-label={`save-group`}
iconType="check"
fill
color="primary"
data-test-subj="control-group-editor-save"
onClick={() => {

View file

@ -12,7 +12,7 @@ import { i18n } from '@kbn/i18n';
export const ControlGroupStrings = {
getSaveChangesTitle: () =>
i18n.translate('controls.controlGroup.manageControl.saveChangesTitle', {
defaultMessage: 'Save and close',
defaultMessage: 'Save',
}),
getCancelTitle: () =>
i18n.translate('controls.controlGroup.manageControl.cancelTitle', {

View file

@ -263,7 +263,7 @@ describe('getNewControlState', () => {
test('should contain defaults when there are no existing controls', () => {
const controlsManager = initControlsManager({}, new BehaviorSubject<ControlPanelsState>({}));
expect(controlsManager.getNewControlState()).toEqual({
grow: true,
grow: false,
width: 'medium',
dataViewId: undefined,
});
@ -284,7 +284,7 @@ describe('getNewControlState', () => {
new BehaviorSubject<ControlPanelsState>(intialControlsState)
);
expect(controlsManager.getNewControlState()).toEqual({
grow: true,
grow: false,
width: 'medium',
dataViewId: 'myOtherDataViewId',
});

View file

@ -101,6 +101,9 @@ export const openEditControlGroupFlyout = (
'aria-label': i18n.translate('controls.controlGroup.manageControl', {
defaultMessage: 'Edit control settings',
}),
size: 'm',
maxWidth: 500,
paddingSize: 'm',
outsideClickCloses: false,
onClose: () => closeOverlay(overlay),
}

View file

@ -21,14 +21,6 @@ export const DataControlEditorStrings = {
defaultMessage: 'Edit control',
}),
dataSource: {
getFormGroupTitle: () =>
i18n.translate('controls.controlGroup.manageControl.dataSource.formGroupTitle', {
defaultMessage: 'Data source',
}),
getFormGroupDescription: () =>
i18n.translate('controls.controlGroup.manageControl.dataSource.formGroupDescription', {
defaultMessage: 'Select the data view and field that you want to create a control for.',
}),
getSelectDataViewMessage: () =>
i18n.translate('controls.controlGroup.manageControl.dataSource.selectDataViewMessage', {
defaultMessage: 'Please select a data view',
@ -95,14 +87,6 @@ export const DataControlEditorStrings = {
},
},
displaySettings: {
getFormGroupTitle: () =>
i18n.translate('controls.controlGroup.manageControl.displaySettings.formGroupTitle', {
defaultMessage: 'Display settings',
}),
getFormGroupDescription: () =>
i18n.translate('controls.controlGroup.manageControl.displaySettings.formGroupDescription', {
defaultMessage: 'Change how the control appears on your dashboard.',
}),
getTitleInputTitle: () =>
i18n.translate('controls.controlGroup.manageControl.displaySettings.titleInputTitle', {
defaultMessage: 'Label',
@ -133,7 +117,7 @@ export const DataControlEditorStrings = {
},
getSaveChangesTitle: () =>
i18n.translate('controls.controlGroup.manageControl.saveChangesTitle', {
defaultMessage: 'Save and close',
defaultMessage: 'Save',
}),
getCancelTitle: () =>
i18n.translate('controls.controlGroup.manageControl.cancelTitle', {

View file

@ -15,7 +15,6 @@ import {
EuiButtonEmpty,
EuiButtonGroup,
EuiCallOut,
EuiDescribedFormGroup,
EuiFieldText,
EuiFlexGroup,
EuiFlexItem,
@ -250,20 +249,8 @@ export const DataControlEditor = <State extends DefaultDataControlState = Defaul
if (!CustomSettings) return;
return (
<EuiDescribedFormGroup
ratio="third"
title={
<h2>
{DataControlEditorStrings.manageControl.controlTypeSettings.getFormGroupTitle(
controlFactory.getDisplayName()
)}
</h2>
}
description={DataControlEditorStrings.manageControl.controlTypeSettings.getFormGroupDescription(
controlFactory.getDisplayName()
)}
data-test-subj="control-editor-custom-settings"
>
<div data-test-subj="control-editor-custom-settings">
<EuiSpacer size="m" />
<CustomSettings
initialState={initialState}
field={fieldRegistry[editorState.fieldName].field}
@ -271,14 +258,14 @@ export const DataControlEditor = <State extends DefaultDataControlState = Defaul
setControlEditorValid={setControlOptionsValid}
controlGroupApi={controlGroupApi}
/>
</EuiDescribedFormGroup>
</div>
);
}, [fieldRegistry, controlFactory, initialState, editorState, controlGroupApi]);
return (
<>
<EuiFlyoutHeader hasBorder>
<EuiTitle size="m">
<EuiTitle size="s">
<h2>
{!controlId // if no ID, then we are creating a new control
? DataControlEditorStrings.manageControl.getFlyoutCreateTitle()
@ -288,156 +275,144 @@ export const DataControlEditor = <State extends DefaultDataControlState = Defaul
</EuiFlyoutHeader>
<EuiFlyoutBody data-test-subj="control-editor-flyout">
<EuiForm fullWidth>
<EuiDescribedFormGroup
ratio="third"
title={<h2>{DataControlEditorStrings.manageControl.dataSource.getFormGroupTitle()}</h2>}
description={DataControlEditorStrings.manageControl.dataSource.getFormGroupDescription()}
>
{!editorConfig?.hideDataViewSelector && (
<EuiFormRow
data-test-subj="control-editor-data-view-picker"
label={DataControlEditorStrings.manageControl.dataSource.getDataViewTitle()}
>
{dataViewListError ? (
<EuiCallOut
color="danger"
iconType="error"
title={DataControlEditorStrings.manageControl.dataSource.getDataViewListErrorTitle()}
>
<p>{dataViewListError.message}</p>
</EuiCallOut>
) : (
<DataViewPicker
dataViews={dataViewListItems}
selectedDataViewId={editorState.dataViewId}
onChangeDataViewId={(newDataViewId) => {
setEditorState({ ...editorState, dataViewId: newDataViewId });
setSelectedControlType(undefined);
}}
trigger={{
label:
selectedDataView?.getName() ??
DataControlEditorStrings.manageControl.dataSource.getSelectDataViewMessage(),
}}
selectableProps={{ isLoading: dataViewListLoading }}
/>
)}
</EuiFormRow>
)}
<EuiFormRow label={DataControlEditorStrings.manageControl.dataSource.getFieldTitle()}>
{fieldListError ? (
{!editorConfig?.hideDataViewSelector && (
<EuiFormRow
data-test-subj="control-editor-data-view-picker"
label={DataControlEditorStrings.manageControl.dataSource.getDataViewTitle()}
>
{dataViewListError ? (
<EuiCallOut
color="danger"
iconType="error"
title={DataControlEditorStrings.manageControl.dataSource.getFieldListErrorTitle()}
title={DataControlEditorStrings.manageControl.dataSource.getDataViewListErrorTitle()}
>
<p>{fieldListError.message}</p>
<p>{dataViewListError.message}</p>
</EuiCallOut>
) : (
<FieldPicker
filterPredicate={(field: DataViewField) => {
const customPredicate = editorConfig?.fieldFilterPredicate?.(field) ?? true;
return Boolean(fieldRegistry?.[field.name]) && customPredicate;
<DataViewPicker
dataViews={dataViewListItems}
selectedDataViewId={editorState.dataViewId}
onChangeDataViewId={(newDataViewId) => {
setEditorState({ ...editorState, dataViewId: newDataViewId });
setSelectedControlType(undefined);
}}
selectedFieldName={editorState.fieldName}
dataView={selectedDataView}
onSelectField={(field) => {
setEditorState({ ...editorState, fieldName: field.name });
/**
* make sure that the new field is compatible with the selected control type and, if it's not,
* reset the selected control type to the **first** compatible control type
*/
const newCompatibleControlTypes =
fieldRegistry?.[field.name]?.compatibleControlTypes ?? [];
if (
!selectedControlType ||
!newCompatibleControlTypes.includes(selectedControlType!)
) {
setSelectedControlType(newCompatibleControlTypes[0]);
}
/**
* set the control title (i.e. the one set by the user) + default title (i.e. the field display name)
*/
const newDefaultTitle = field.displayName ?? field.name;
setDefaultPanelTitle(newDefaultTitle);
const currentTitle = editorState.title;
if (!currentTitle || currentTitle === newDefaultTitle) {
setPanelTitle(newDefaultTitle);
}
setControlOptionsValid(true); // reset options state
trigger={{
label:
selectedDataView?.getName() ??
DataControlEditorStrings.manageControl.dataSource.getSelectDataViewMessage(),
}}
selectableProps={{ isLoading: dataViewListLoading || dataViewLoading }}
selectableProps={{ isLoading: dataViewListLoading }}
/>
)}
</EuiFormRow>
)}
<EuiFormRow label={DataControlEditorStrings.manageControl.dataSource.getFieldTitle()}>
{fieldListError ? (
<EuiCallOut
color="danger"
iconType="error"
title={DataControlEditorStrings.manageControl.dataSource.getFieldListErrorTitle()}
>
<p>{fieldListError.message}</p>
</EuiCallOut>
) : (
<FieldPicker
filterPredicate={(field: DataViewField) => {
const customPredicate = editorConfig?.fieldFilterPredicate?.(field) ?? true;
return Boolean(fieldRegistry?.[field.name]) && customPredicate;
}}
selectedFieldName={editorState.fieldName}
dataView={selectedDataView}
onSelectField={(field) => {
setEditorState({ ...editorState, fieldName: field.name });
/**
* make sure that the new field is compatible with the selected control type and, if it's not,
* reset the selected control type to the **first** compatible control type
*/
const newCompatibleControlTypes =
fieldRegistry?.[field.name]?.compatibleControlTypes ?? [];
if (
!selectedControlType ||
!newCompatibleControlTypes.includes(selectedControlType!)
) {
setSelectedControlType(newCompatibleControlTypes[0]);
}
/**
* set the control title (i.e. the one set by the user) + default title (i.e. the field display name)
*/
const newDefaultTitle = field.displayName ?? field.name;
setDefaultPanelTitle(newDefaultTitle);
const currentTitle = editorState.title;
if (!currentTitle || currentTitle === newDefaultTitle) {
setPanelTitle(newDefaultTitle);
}
setControlOptionsValid(true); // reset options state
}}
selectableProps={{ isLoading: dataViewListLoading || dataViewLoading }}
/>
)}
</EuiFormRow>
<EuiFormRow
label={DataControlEditorStrings.manageControl.dataSource.getControlTypeTitle()}
>
{/* wrapping in `div` so that focus gets passed properly to the form row */}
<div>
<CompatibleControlTypesComponent
fieldRegistry={fieldRegistry}
selectedFieldName={editorState.fieldName}
selectedControlType={selectedControlType}
setSelectedControlType={setSelectedControlType}
/>
</div>
</EuiFormRow>
<EuiFormRow
label={DataControlEditorStrings.manageControl.displaySettings.getTitleInputTitle()}
>
<EuiFieldText
data-test-subj="control-editor-title-input"
placeholder={defaultPanelTitle}
value={panelTitle}
compressed
onChange={(e) => {
setPanelTitle(e.target.value ?? '');
setEditorState({
...editorState,
title: e.target.value === '' ? undefined : e.target.value,
});
}}
/>
</EuiFormRow>
{!editorConfig?.hideWidthSettings && (
<EuiFormRow
label={DataControlEditorStrings.manageControl.dataSource.getControlTypeTitle()}
data-test-subj="control-editor-width-settings"
label={DataControlEditorStrings.manageControl.displaySettings.getWidthInputTitle()}
>
{/* wrapping in `div` so that focus gets passed properly to the form row */}
<div>
<CompatibleControlTypesComponent
fieldRegistry={fieldRegistry}
selectedFieldName={editorState.fieldName}
selectedControlType={selectedControlType}
setSelectedControlType={setSelectedControlType}
<EuiButtonGroup
buttonSize="compressed"
legend={DataControlEditorStrings.management.controlWidth.getWidthSwitchLegend()}
options={CONTROL_WIDTH_OPTIONS}
idSelected={editorState.width ?? DEFAULT_CONTROL_WIDTH}
onChange={(newWidth: string) =>
setEditorState({ ...editorState, width: newWidth as ControlWidth })
}
/>
<EuiSpacer size="s" />
<EuiSwitch
compressed
label={DataControlEditorStrings.manageControl.displaySettings.getGrowSwitchTitle()}
color="primary"
checked={editorState.grow ?? DEFAULT_CONTROL_GROW}
onChange={() => setEditorState({ ...editorState, grow: !editorState.grow })}
data-test-subj="control-editor-grow-switch"
/>
</div>
</EuiFormRow>
</EuiDescribedFormGroup>
<EuiDescribedFormGroup
ratio="third"
title={
<h2>{DataControlEditorStrings.manageControl.displaySettings.getFormGroupTitle()}</h2>
}
description={DataControlEditorStrings.manageControl.displaySettings.getFormGroupDescription()}
>
<EuiFormRow
label={DataControlEditorStrings.manageControl.displaySettings.getTitleInputTitle()}
>
<EuiFieldText
data-test-subj="control-editor-title-input"
placeholder={defaultPanelTitle}
value={panelTitle}
onChange={(e) => {
setPanelTitle(e.target.value ?? '');
setEditorState({
...editorState,
title: e.target.value === '' ? undefined : e.target.value,
});
}}
/>
</EuiFormRow>
{!editorConfig?.hideWidthSettings && (
<EuiFormRow
data-test-subj="control-editor-width-settings"
label={DataControlEditorStrings.manageControl.displaySettings.getWidthInputTitle()}
>
<div>
<EuiButtonGroup
color="primary"
legend={DataControlEditorStrings.management.controlWidth.getWidthSwitchLegend()}
options={CONTROL_WIDTH_OPTIONS}
idSelected={editorState.width ?? DEFAULT_CONTROL_WIDTH}
onChange={(newWidth: string) =>
setEditorState({ ...editorState, width: newWidth as ControlWidth })
}
/>
<EuiSpacer size="s" />
<EuiSwitch
label={DataControlEditorStrings.manageControl.displaySettings.getGrowSwitchTitle()}
color="primary"
checked={editorState.grow ?? DEFAULT_CONTROL_GROW}
onChange={() => setEditorState({ ...editorState, grow: !editorState.grow })}
data-test-subj="control-editor-grow-switch"
/>
</div>
</EuiFormRow>
)}
</EuiDescribedFormGroup>
)}
{!editorConfig?.hideAdditionalSettings && CustomSettingsComponent}
{controlId && (
<>
@ -464,7 +439,6 @@ export const DataControlEditor = <State extends DefaultDataControlState = Defaul
<EuiButtonEmpty
aria-label={`cancel-${editorState.title ?? editorState.fieldName}`}
data-test-subj="control-editor-cancel"
iconType="cross"
onClick={() => {
onCancel(editorState);
}}
@ -476,7 +450,7 @@ export const DataControlEditor = <State extends DefaultDataControlState = Defaul
<EuiButton
aria-label={`save-${editorState.title ?? editorState.fieldName}`}
data-test-subj="control-editor-save"
iconType="check"
fill
color="primary"
disabled={
!(

View file

@ -97,6 +97,9 @@ export const openDataControlEditor = <
}
),
{
size: 'm',
maxWidth: 500,
paddingSize: 'm',
onClose: () => closeOverlay(overlay),
}
);

View file

@ -131,6 +131,7 @@ export const OptionsListEditorOptions = ({
data-test-subj="optionsListControl__selectionOptionsRadioGroup"
>
<EuiRadioGroup
compressed
options={selectionOptions}
idSelected={singleSelect ? 'single' : 'multi'}
onChange={(id) => {
@ -146,6 +147,7 @@ export const OptionsListEditorOptions = ({
data-test-subj="optionsListControl__searchOptionsRadioGroup"
>
<EuiRadioGroup
compressed
options={searchOptions}
idSelected={searchTechnique}
onChange={(id) => {
@ -158,6 +160,7 @@ export const OptionsListEditorOptions = ({
)}
<EuiFormRow label={OptionsListStrings.editor.getAdditionalSettingsTitle()}>
<EuiSwitch
compressed
label={
<ControlSettingTooltipLabel
label={OptionsListStrings.editor.getRunPastTimeoutTitle()}

View file

@ -43,6 +43,7 @@ export const getRangesliderControlFactory = (): DataControlFactory<
<>
<EuiFormRow fullWidth label={RangeSliderStrings.editor.getStepTitle()}>
<EuiFieldNumber
compressed
value={step}
onChange={(event) => {
const newStep = event.target.valueAsNumber;

View file

@ -125,7 +125,7 @@ export const DashboardPanelSelectionListFlyout: React.FC<
return (
<>
<EuiFlyoutHeader hasBorder>
<EuiTitle size="m">
<EuiTitle size="s">
<h1 id="addPanelsFlyout">
<FormattedMessage
id="dashboard.solutionToolbar.addPanelFlyout.headingText"
@ -148,6 +148,7 @@ export const DashboardPanelSelectionListFlyout: React.FC<
<EuiForm component="form" fullWidth>
<EuiFormRow css={{ backgroundColor: euiTheme.colors.emptyShade }}>
<EuiFieldSearch
compressed
autoFocus
value={searchTerm}
onChange={(e) => {
@ -281,7 +282,7 @@ export const DashboardPanelSelectionListFlyout: React.FC<
<EuiButtonEmpty onClick={close} data-test-subj="dashboardPanelSelectionCloseBtn">
<FormattedMessage
id="dashboard.solutionToolbar.addPanelFlyout.cancelButtonText"
defaultMessage="Close"
defaultMessage="Cancel"
/>
</EuiButtonEmpty>
</EuiFlexItem>

View file

@ -43,7 +43,7 @@ export const EditorMenu = ({ createNewVisType, isDisabled }: EditorMenuProps) =>
function openDashboardPanelSelectionFlyout() {
const flyoutPanelPaddingSize: ComponentProps<
typeof DashboardPanelSelectionListFlyout
>['paddingSize'] = 'l';
>['paddingSize'] = 'm';
const mount = toMountPoint(
React.createElement(function () {

View file

@ -189,7 +189,7 @@ export const AddPanelFlyout = ({
return (
<>
<EuiFlyoutHeader hasBorder>
<EuiTitle size="m">
<EuiTitle size="s">
<h2 id={modalTitleId}>
{i18n.translate('embeddableApi.addPanel.Title', { defaultMessage: 'Add from library' })}
</h2>

View file

@ -52,6 +52,9 @@ export const openAddPanelFlyout = ({
if (onClose) onClose();
overlayRef.close();
},
size: 'm',
maxWidth: 500,
paddingSize: 'm',
'data-test-subj': 'dashboardAddPanel',
'aria-labelledby': modalTitleId,
}

View file

@ -43,11 +43,11 @@ const ImageEditor = (props: Partial<ImageEditorFlyoutProps>) => {
);
};
test('should call onCancel when "Close" clicked', async () => {
test('should call onCancel when "Cancel" clicked', async () => {
const onCancel = jest.fn();
const { getByText } = render(<ImageEditor onCancel={onCancel} />);
expect(getByText('Close')).toBeVisible();
await userEvent.click(getByText('Close'));
expect(getByText('Cancel')).toBeVisible();
await userEvent.click(getByText('Cancel'));
expect(onCancel).toBeCalled();
});

View file

@ -121,7 +121,7 @@ export function ImageEditorFlyout(props: ImageEditorFlyoutProps) {
return (
<>
<EuiFlyoutHeader hasBorder={true}>
<EuiTitle size="m">
<EuiTitle size="s">
<h2>
{isEditing ? (
<FormattedMessage
@ -136,8 +136,9 @@ export function ImageEditorFlyout(props: ImageEditorFlyoutProps) {
)}
</h2>
</EuiTitle>
<EuiSpacer size={'s'} />
<EuiTabs style={{ marginBottom: '-25px' }}>
</EuiFlyoutHeader>
<EuiFlyoutBody>
<EuiTabs size="s" bottomBorder={false}>
<EuiTab onClick={() => setSrcType('file')} isSelected={srcType === 'file'}>
<FormattedMessage
id="imageEmbeddable.imageEditor.uploadTabLabel"
@ -151,8 +152,7 @@ export function ImageEditorFlyout(props: ImageEditorFlyoutProps) {
/>
</EuiTab>
</EuiTabs>
</EuiFlyoutHeader>
<EuiFlyoutBody>
<EuiSpacer size="m" />
{srcType === 'file' && (
<>
{isDraftImageConfigValid ? (
@ -238,7 +238,7 @@ export function ImageEditorFlyout(props: ImageEditorFlyoutProps) {
/>
</p>
}
titleSize={'s'}
titleSize={'xs'}
/>
) : (
<ImageViewer
@ -257,8 +257,7 @@ export function ImageEditorFlyout(props: ImageEditorFlyoutProps) {
`}
/>
)}
<EuiSpacer />
<EuiSpacer size="m" />
<EuiFormRow
label={
<FormattedMessage
@ -300,7 +299,6 @@ export function ImageEditorFlyout(props: ImageEditorFlyoutProps) {
</EuiFormRow>
</>
)}
<EuiSpacer />
<EuiFormRow
label={
<FormattedMessage
@ -311,6 +309,7 @@ export function ImageEditorFlyout(props: ImageEditorFlyoutProps) {
fullWidth
>
<EuiSelect
compressed
fullWidth
options={[
{
@ -344,8 +343,6 @@ export function ImageEditorFlyout(props: ImageEditorFlyoutProps) {
}
/>
</EuiFormRow>
<EuiSpacer />
<EuiFormRow
label={
<FormattedMessage
@ -358,6 +355,7 @@ export function ImageEditorFlyout(props: ImageEditorFlyoutProps) {
error={colorErrors}
>
<EuiColorPicker
compressed
fullWidth
onChange={setColor}
color={color}
@ -371,8 +369,6 @@ export function ImageEditorFlyout(props: ImageEditorFlyoutProps) {
)}
/>
</EuiFormRow>
<EuiSpacer />
<EuiFormRow
label={
<FormattedMessage
@ -403,10 +399,10 @@ export function ImageEditorFlyout(props: ImageEditorFlyoutProps) {
<EuiFlyoutFooter>
<EuiFlexGroup justifyContent="spaceBetween">
<EuiFlexItem grow={false}>
<EuiButtonEmpty iconType="cross" onClick={props.onCancel} flush="left">
<EuiButtonEmpty onClick={props.onCancel} flush="left">
<FormattedMessage
id="imageEmbeddable.imageEditor.imageBackgroundCloseButtonText"
defaultMessage="Close"
defaultMessage="Cancel"
/>
</EuiButtonEmpty>
</EuiFlexItem>

View file

@ -79,6 +79,9 @@ export const openImageEditor = async ({
onClose: () => {
onCancel();
},
size: 'm',
maxWidth: 500,
paddingSize: 'm',
ownFocus: true,
'data-test-subj': 'createImageEmbeddableFlyout',
}

View file

@ -119,6 +119,7 @@ export const DashboardLinkDestinationPicker = ({
return (
<EuiComboBox
{...other}
compressed
async
fullWidth
className={'linksDashboardPicker'}

View file

@ -100,7 +100,7 @@ export const LinkEditor = ({
iconType={'arrowLeft'}
onClick={() => onClose()}
>
<EuiTitle size="m" aria-label={LinksStrings.editor.linkEditor.getGoBackAriaLabel()}>
<EuiTitle size="s" aria-label={LinksStrings.editor.linkEditor.getGoBackAriaLabel()}>
<h2>
{link
? LinksStrings.editor.getEditLinkTitle()
@ -113,6 +113,7 @@ export const LinkEditor = ({
<EuiForm component="form" fullWidth>
<EuiFormRow label={LinksStrings.editor.linkEditor.getLinkTypePickerLabel()}>
<EuiRadioGroup
compressed
options={linkTypes}
idSelected={selectedLinkType}
onChange={(id) => {
@ -131,6 +132,7 @@ export const LinkEditor = ({
/>
<EuiFormRow label={LinksStrings.editor.linkEditor.getLinkTextLabel()}>
<EuiFieldText
compressed
placeholder={
(linkDestination ? defaultLinkLabel : '') ||
LinksStrings.editor.linkEditor.getLinkTextPlaceholder()
@ -151,8 +153,8 @@ export const LinkEditor = ({
<EuiFlexGroup responsive={false} justifyContent="spaceBetween">
<EuiFlexItem grow={false}>
<EuiButtonEmpty
flush="left"
onClick={() => onClose()}
iconType="cross"
data-test-subj="links--linkEditor--closeBtn"
>
{LinksStrings.editor.getCancelButtonLabel()}
@ -160,6 +162,7 @@ export const LinkEditor = ({
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButton
fill
disabled={!linkDestination}
onClick={() => {
// this check should always be true, since the button is disabled otherwise - this is just for type safety

View file

@ -3,7 +3,7 @@
.linksPanelEditor {
.linkEditor {
@include euiFlyout;
max-inline-size: $euiSizeXXL * 18; // 40px * 18 = 720px
max-inline-size: $euiSizeXS * 125; // 4px * 125 = 500px
&.in {
animation: euiFlyoutOpenAnimation $euiAnimSpeedNormal $euiAnimSlightResistance;
@ -59,6 +59,9 @@
}
.links_hoverActions {
background-color: $euiColorEmptyShade;
position: absolute;
right: $euiSizeL;
opacity: 0;
visibility: hidden;
transition: visibility $euiAnimSpeedNormal, opacity $euiAnimSpeedNormal;

View file

@ -167,7 +167,7 @@ const LinksEditor = ({
<EuiFlyoutHeader hasBorder>
<EuiFlexGroup alignItems="center">
<EuiFlexItem grow={false}>
<EuiTitle size="m" data-test-subj="links--panelEditor--title">
<EuiTitle size="s" data-test-subj="links--panelEditor--title">
<h2>
{isEditingExisting
? LinksStrings.editor.panelEditor.getEditFlyoutTitle()
@ -251,7 +251,6 @@ const LinksEditor = ({
<EuiFlexItem grow={false}>
<EuiButtonEmpty
onClick={onClose}
iconType="cross"
flush="left"
data-test-subj="links--panelEditor--closeBtn"
>
@ -268,6 +267,7 @@ const LinksEditor = ({
data-test-subj="links--panelEditor--saveByReferenceTooltip"
>
<EuiSwitch
compressed
label={LinksStrings.editor.panelEditor.getSaveToLibrarySwitchLabel()}
checked={saveByReference}
disabled={hasZeroLinks}

View file

@ -106,33 +106,31 @@ export const LinksEditorSingleLink = ({
<LinkLabel />
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiFlexGroup gutterSize="none" responsive={false} className="links_hoverActions">
<EuiFlexItem>
<EuiToolTip content={LinksStrings.editor.getEditLinkTitle()}>
<EuiButtonIcon
size="xs"
iconType="pencil"
onClick={editLink}
aria-label={LinksStrings.editor.getEditLinkTitle(link.title)}
data-test-subj="panelEditorLink--editBtn"
/>
</EuiToolTip>
</EuiFlexItem>
<EuiFlexItem>
<EuiToolTip content={LinksStrings.editor.getDeleteLinkTitle()}>
<EuiButtonIcon
size="xs"
iconType="trash"
aria-label={LinksStrings.editor.getDeleteLinkTitle(link.title)}
color="danger"
onClick={deleteLink}
data-test-subj="panelEditorLink--deleteBtn"
/>
</EuiToolTip>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
<EuiFlexGroup gutterSize="none" responsive={false} className="links_hoverActions">
<EuiFlexItem>
<EuiToolTip content={LinksStrings.editor.getEditLinkTitle()}>
<EuiButtonIcon
size="xs"
iconType="pencil"
onClick={editLink}
aria-label={LinksStrings.editor.getEditLinkTitle(link.title)}
data-test-subj="panelEditorLink--editBtn"
/>
</EuiToolTip>
</EuiFlexItem>
<EuiFlexItem>
<EuiToolTip content={LinksStrings.editor.getDeleteLinkTitle()}>
<EuiButtonIcon
size="xs"
iconType="trash"
aria-label={LinksStrings.editor.getDeleteLinkTitle(link.title)}
color="danger"
onClick={deleteLink}
data-test-subj="panelEditorLink--deleteBtn"
/>
</EuiToolTip>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexGroup>
</EuiPanel>
);

View file

@ -54,6 +54,7 @@ export const ExternalLinkDestinationPicker = ({
return (
<div {...other}>
<EuiFieldText
compressed
value={currentUrl}
placeholder={ExternalLinkStrings.getPlaceholder()}
isInvalid={!validUrl}

View file

@ -41,7 +41,7 @@ export const LinksStrings = {
}),
getCancelButtonLabel: () =>
i18n.translate('links.editor.cancelButtonLabel', {
defaultMessage: 'Close',
defaultMessage: 'Cancel',
}),
panelEditor: {
getLinksTitle: () =>

View file

@ -137,7 +137,8 @@ export async function openEditorFlyout({
),
{
id: flyoutId,
maxWidth: 720,
maxWidth: 500,
paddingSize: 'm',
ownFocus: true,
onClose: onCancel,
outsideClickCloses: false,

View file

@ -8,7 +8,7 @@
*/
import React from 'react';
import { EuiFormRow, EuiSwitch } from '@elastic/eui';
import { EuiFormRow, EuiSpacer, EuiSwitch } from '@elastic/eui';
import { DashboardDrilldownOptions } from './types';
import { dashboardDrilldownConfigStrings } from '../../i18n/dashboard_drilldown_config';
@ -24,32 +24,35 @@ export const DashboardDrilldownOptionsComponent = ({
}: DashboardDrilldownOptionsProps) => {
return (
<>
<EuiFormRow hasChildLabel={false}>
<EuiSwitch
name="useCurrentFilters"
label={dashboardDrilldownConfigStrings.component.getUseCurrentFiltersLabel()}
checked={options.useCurrentFilters}
onChange={() => onOptionChange({ useCurrentFilters: !options.useCurrentFilters })}
data-test-subj="dashboardDrillDownOptions--useCurrentFilters--checkbox"
/>
</EuiFormRow>
<EuiFormRow hasChildLabel={false}>
<EuiSwitch
name="useCurrentDateRange"
label={dashboardDrilldownConfigStrings.component.getUseCurrentDateRange()}
checked={options.useCurrentDateRange}
onChange={() => onOptionChange({ useCurrentDateRange: !options.useCurrentDateRange })}
data-test-subj="dashboardDrillDownOptions--useCurrentDateRange--checkbox"
/>
</EuiFormRow>
<EuiFormRow hasChildLabel={false}>
<EuiSwitch
name="openInNewTab"
label={dashboardDrilldownConfigStrings.component.getOpenInNewTab()}
checked={options.openInNewTab}
onChange={() => onOptionChange({ openInNewTab: !options.openInNewTab })}
data-test-subj="dashboardDrillDownOptions--openInNewTab--checkbox"
/>
<EuiFormRow>
<div>
<EuiSwitch
compressed
name="useCurrentFilters"
label={dashboardDrilldownConfigStrings.component.getUseCurrentFiltersLabel()}
checked={options.useCurrentFilters}
onChange={() => onOptionChange({ useCurrentFilters: !options.useCurrentFilters })}
data-test-subj="dashboardDrillDownOptions--useCurrentFilters--checkbox"
/>
<EuiSpacer size="s" />
<EuiSwitch
compressed
name="useCurrentDateRange"
label={dashboardDrilldownConfigStrings.component.getUseCurrentDateRange()}
checked={options.useCurrentDateRange}
onChange={() => onOptionChange({ useCurrentDateRange: !options.useCurrentDateRange })}
data-test-subj="dashboardDrillDownOptions--useCurrentDateRange--checkbox"
/>
<EuiSpacer size="s" />
<EuiSwitch
compressed
name="openInNewTab"
label={dashboardDrilldownConfigStrings.component.getOpenInNewTab()}
checked={options.openInNewTab}
onChange={() => onOptionChange({ openInNewTab: !options.openInNewTab })}
data-test-subj="dashboardDrillDownOptions--openInNewTab--checkbox"
/>
</div>
</EuiFormRow>
</>
);

View file

@ -52,6 +52,7 @@ export function DataViewPicker({
data-test-subj="open-data-view-picker"
onClick={() => setPopoverIsOpen(!isPopoverOpen)}
label={label}
size="s"
fullWidth
{...colorProp}
{...rest}

View file

@ -140,6 +140,7 @@ export const FieldPicker = ({
placeholder: i18n.translate('presentationUtil.fieldSearch.searchPlaceHolder', {
defaultMessage: 'Search field names',
}),
compressed: true,
disabled: Boolean(selectableProps?.isLoading),
inputRef: setSearchRef,
}}

View file

@ -63,7 +63,7 @@ export function FieldTypeFilter({
);
return (
<EuiFilterGroup fullWidth>
<EuiFilterGroup compressed fullWidth>
<EuiInputPopover
panelPaddingSize="none"
display="block"

View file

@ -28,31 +28,34 @@ export const UrlDrilldownOptionsComponent = ({
}: UrlDrilldownOptionsProps) => {
return (
<>
<EuiFormRow hasChildLabel={false}>
<EuiSwitch
id="openInNewTab"
name="openInNewTab"
label={txtUrlTemplateOpenInNewTab}
checked={options.openInNewTab}
onChange={() => onOptionChange({ openInNewTab: !options.openInNewTab })}
data-test-subj="urlDrilldownOpenInNewTab"
/>
</EuiFormRow>
<EuiFormRow hasChildLabel={false} fullWidth>
<EuiSwitch
id="encodeUrl"
name="encodeUrl"
label={
<>
{txtUrlTemplateEncodeUrl}
<EuiSpacer size={'s'} />
<EuiTextColor color="subdued">{txtUrlTemplateEncodeDescription}</EuiTextColor>
</>
}
checked={options.encodeUrl}
onChange={() => onOptionChange({ encodeUrl: !options.encodeUrl })}
data-test-subj="urlDrilldownEncodeUrl"
/>
<EuiFormRow>
<div>
<EuiSwitch
compressed
id="openInNewTab"
name="openInNewTab"
label={txtUrlTemplateOpenInNewTab}
checked={options.openInNewTab}
onChange={() => onOptionChange({ openInNewTab: !options.openInNewTab })}
data-test-subj="urlDrilldownOpenInNewTab"
/>
<EuiSpacer size="s" />
<EuiSwitch
compressed
id="encodeUrl"
name="encodeUrl"
label={
<>
{txtUrlTemplateEncodeUrl}
<EuiSpacer size={'s'} />
<EuiTextColor color="subdued">{txtUrlTemplateEncodeDescription}</EuiTextColor>
</>
}
checked={options.encodeUrl}
onChange={() => onOptionChange({ encodeUrl: !options.encodeUrl })}
data-test-subj="urlDrilldownEncodeUrl"
/>
</div>
</EuiFormRow>
</>
);

View file

@ -862,11 +862,7 @@
"controls.controlGroup.manageControl.dataSource.dataViewTitle": "Vue de données",
"controls.controlGroup.manageControl.dataSource.fieldListErrorTitle": "Erreur lors du chargement de la liste des champs",
"controls.controlGroup.manageControl.dataSource.fieldTitle": "Champ",
"controls.controlGroup.manageControl.dataSource.formGroupDescription": "Sélectionnez la vue de données et le champ pour lesquels vous voulez créer un contrôle.",
"controls.controlGroup.manageControl.dataSource.formGroupTitle": "Source de données",
"controls.controlGroup.manageControl.dataSource.selectDataViewMessage": "Veuillez sélectionner une vue de données",
"controls.controlGroup.manageControl.displaySettings.formGroupDescription": "Changez la manière dont le contrôle apparaît sur votre tableau de bord.",
"controls.controlGroup.manageControl.displaySettings.formGroupTitle": "Paramètres d'affichage",
"controls.controlGroup.manageControl.displaySettings.growSwitchTitle": "Augmenter la largeur en fonction de l'espace disponible",
"controls.controlGroup.manageControl.displaySettings.titleInputTitle": "Étiquette",
"controls.controlGroup.manageControl.displaySettings.widthInputTitle": "Largeur minimale",

View file

@ -864,11 +864,7 @@
"controls.controlGroup.manageControl.dataSource.dataViewTitle": "データビュー",
"controls.controlGroup.manageControl.dataSource.fieldListErrorTitle": "フィールドリストの読み込みエラー",
"controls.controlGroup.manageControl.dataSource.fieldTitle": "フィールド",
"controls.controlGroup.manageControl.dataSource.formGroupDescription": "コントロールを作成するデータビューとフィールドを選択します。",
"controls.controlGroup.manageControl.dataSource.formGroupTitle": "データソース",
"controls.controlGroup.manageControl.dataSource.selectDataViewMessage": "データビューを選択してください",
"controls.controlGroup.manageControl.displaySettings.formGroupDescription": "ダッシュボードにコントロールを表示する方法を変更します。",
"controls.controlGroup.manageControl.displaySettings.formGroupTitle": "表示設定",
"controls.controlGroup.manageControl.displaySettings.growSwitchTitle": "空きスペースに合わせて幅を拡大",
"controls.controlGroup.manageControl.displaySettings.titleInputTitle": "ラベル",
"controls.controlGroup.manageControl.displaySettings.widthInputTitle": "最小幅",

View file

@ -856,11 +856,7 @@
"controls.controlGroup.manageControl.dataSource.dataViewTitle": "数据视图",
"controls.controlGroup.manageControl.dataSource.fieldListErrorTitle": "加载字段列表时出错",
"controls.controlGroup.manageControl.dataSource.fieldTitle": "字段",
"controls.controlGroup.manageControl.dataSource.formGroupDescription": "选择要为其创建控件的数据视图和字段。",
"controls.controlGroup.manageControl.dataSource.formGroupTitle": "数据源",
"controls.controlGroup.manageControl.dataSource.selectDataViewMessage": "请选择数据视图",
"controls.controlGroup.manageControl.displaySettings.formGroupDescription": "更改控件在仪表板上的显示方式。",
"controls.controlGroup.manageControl.displaySettings.formGroupTitle": "显示设置",
"controls.controlGroup.manageControl.displaySettings.growSwitchTitle": "扩大宽度以适应可用空间",
"controls.controlGroup.manageControl.displaySettings.titleInputTitle": "标签",
"controls.controlGroup.manageControl.displaySettings.widthInputTitle": "最小宽度",