[Lens] 7.9 design cleanup (#71444)

* Fix dimension popover layout and color picker “Auto”

* Created ToolbarButton

* Move disabled help text to tooltip for missing values

* Darker side panel backgrounds

* Adding to .asciidoc about where to put the SASS import

* Moving `SASS` guidelines to STYLEGUIDE.md

* Fix keyboard focus of XY settings popover

* Fix dark mode
This commit is contained in:
Caroline Horn 2020-07-13 20:06:58 -04:00 committed by GitHub
parent 82562a8e25
commit ddd8fa8947
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 284 additions and 204 deletions

View file

@ -3,11 +3,18 @@
This guide applies to all development within the Kibana project and is This guide applies to all development within the Kibana project and is
recommended for the development of all Kibana plugins. recommended for the development of all Kibana plugins.
- [General](#general)
- [HTML](#html)
- [API endpoints](#api-endpoints)
- [TypeScript/JavaScript](#typeScript/javaScript)
- [SASS files](#sass-files)
- [React](#react)
Besides the content in this style guide, the following style guides may also apply Besides the content in this style guide, the following style guides may also apply
to all development within the Kibana project. Please make sure to also read them: to all development within the Kibana project. Please make sure to also read them:
- [Accessibility style guide](https://elastic.github.io/eui/#/guidelines/accessibility) - [Accessibility style guide (EUI Docs)](https://elastic.github.io/eui/#/guidelines/accessibility)
- [SASS style guide](https://elastic.github.io/eui/#/guidelines/sass) - [SASS style guide (EUI Docs)](https://elastic.github.io/eui/#/guidelines/sass)
## General ## General
@ -582,6 +589,39 @@ Do not use setters, they cause more problems than they can solve.
[sideeffect]: http://en.wikipedia.org/wiki/Side_effect_(computer_science) [sideeffect]: http://en.wikipedia.org/wiki/Side_effect_(computer_science)
## SASS files
When writing a new component, create a sibling SASS file of the same name and import directly into the **top** of the JS/TS component file. Doing so ensures the styles are never separated or lost on import and allows for better modularization (smaller individual plugin asset footprint).
All SASS (.scss) files will automatically build with the [EUI](https://elastic.github.io/eui/#/guidelines/sass) & Kibana invisibles (SASS variables, mixins, functions) from the [`globals_[theme].scss` file](src/legacy/ui/public/styles/_globals_v7light.scss).
While the styles for this component will only be loaded if the component exists on the page,
the styles **will** be global and so it is recommended to use a three letter prefix on your
classes to ensure proper scope.
**Example:**
```tsx
// component.tsx
import './component.scss';
// All other imports below the SASS import
export const Component = () => {
return (
<div className="plgComponent" />
);
}
```
```scss
// component.scss
.plgComponent { ... }
```
Do not use the underscore `_` SASS file naming pattern when importing directly into a javascript file.
## React ## React
The following style guide rules are specific for working with the React framework. The following style guide rules are specific for working with the React framework.

View file

@ -29,7 +29,7 @@ you can switch to the correct version when using nvm by running:
---- ----
nvm use nvm use
---- ----
Install the latest version of https://yarnpkg.com[yarn]. Install the latest version of https://yarnpkg.com[yarn].
Bootstrap {kib} and install all the dependencies: Bootstrap {kib} and install all the dependencies:
@ -93,13 +93,13 @@ yarn es snapshot --license trial
`trial` will give you access to all capabilities. `trial` will give you access to all capabilities.
Read about more options for <<running-elasticsearch>>, like connecting to a remote host, running from source, Read about more options for <<running-elasticsearch>>, like connecting to a remote host, running from source,
preserving data inbetween runs, running remote cluster, etc. preserving data inbetween runs, running remote cluster, etc.
[float] [float]
=== Run {kib} === Run {kib}
In another terminal window, start up {kib}. Include developer examples by adding an optional `--run-examples` flag. In another terminal window, start up {kib}. Include developer examples by adding an optional `--run-examples` flag.
[source,bash] [source,bash]
---- ----
@ -125,8 +125,6 @@ cause the {kib} server to reboot.
* <<kibana-debugging>> * <<kibana-debugging>>
* <<kibana-sass>>
* <<building-kibana>> * <<building-kibana>>
* <<development-plugin-resources>> * <<development-plugin-resources>>
@ -137,8 +135,6 @@ include::sample-data.asciidoc[]
include::debugging.asciidoc[] include::debugging.asciidoc[]
include::sass.asciidoc[]
include::building-kibana.asciidoc[] include::building-kibana.asciidoc[]
include::development-plugin-resources.asciidoc[] include::development-plugin-resources.asciidoc[]

View file

@ -1,36 +0,0 @@
[[kibana-sass]]
=== Styling with SASS
When writing a new component, create a sibling SASS file of the same
name and import directly into the JS/TS component file. Doing so ensures
the styles are never separated or lost on import and allows for better
modularization (smaller individual plugin asset footprint).
All SASS (.scss) files will automatically build with the
https://elastic.github.io/eui/#/guidelines/sass[EUI] & {kib} invisibles (SASS variables, mixins, functions) from
the {kib-repo}tree/{branch}/src/legacy/ui/public/styles/_globals_v7light.scss[globals_THEME.scss] file.
*Example:*
[source,tsx]
----
// component.tsx
import './component.scss';
export const Component = () => {
return (
<div className="plgComponent" />
);
}
----
[source,scss]
----
// component.scss
.plgComponent { ... }
----
Do not use the underscore `_` SASS file naming pattern when importing
directly into a javascript file.

View file

@ -1,6 +1,7 @@
.lnsDataPanelWrapper { .lnsDataPanelWrapper {
flex: 1 0 100%; flex: 1 0 100%;
overflow: hidden; overflow: hidden;
background-color: lightOrDarkTheme($euiColorLightestShade, $euiColorInk);
} }
.lnsDataPanelWrapper__switchSource { .lnsDataPanelWrapper__switchSource {

View file

@ -22,7 +22,7 @@
// Leave out bottom padding so the suggestions scrollbar stays flush to window edge // Leave out bottom padding so the suggestions scrollbar stays flush to window edge
// Leave out left padding so the left sidebar's focus states are visible outside of content bounds // Leave out left padding so the left sidebar's focus states are visible outside of content bounds
// This also means needing to add same amount of margin to page content and suggestion items // This also means needing to add same amount of margin to page content and suggestion items
padding: $euiSize $euiSize 0 0; padding: $euiSize $euiSize 0;
&:first-child { &:first-child {
padding-left: $euiSize; padding-left: $euiSize;
@ -40,9 +40,10 @@
.lnsFrameLayout__sidebar--right { .lnsFrameLayout__sidebar--right {
@include euiScrollBar; @include euiScrollBar;
min-width: $lnsPanelMinWidth + $euiSize; background-color: lightOrDarkTheme($euiColorLightestShade, $euiColorInk);
min-width: $lnsPanelMinWidth + $euiSizeXL;
overflow-x: hidden; overflow-x: hidden;
overflow-y: scroll; overflow-y: scroll;
padding-top: $euiSize; padding: $euiSize 0 $euiSize $euiSize;
max-height: 100%; max-height: 100%;
} }

View file

@ -2,6 +2,10 @@
margin-bottom: $euiSizeS; margin-bottom: $euiSizeS;
} }
.lnsLayerPanel__sourceFlexItem {
max-width: calc(100% - #{$euiSize * 3.625});
}
.lnsLayerPanel__row { .lnsLayerPanel__row {
background: $euiColorLightestShade; background: $euiColorLightestShade;
padding: $euiSizeS; padding: $euiSizeS;
@ -32,5 +36,6 @@
} }
.lnsLayerPanel__styleEditor { .lnsLayerPanel__styleEditor {
width: $euiSize * 28; width: $euiSize * 30;
padding: $euiSizeS;
} }

View file

@ -40,8 +40,7 @@ export function DimensionPopover({
}} }}
button={trigger} button={trigger}
anchorPosition="leftUp" anchorPosition="leftUp"
withTitle panelPaddingSize="none"
panelPaddingSize="s"
> >
{panel} {panel}
</EuiPopover> </EuiPopover>

View file

@ -103,7 +103,7 @@ export function LayerPanel(
</EuiFlexItem> </EuiFlexItem>
{layerDatasource && ( {layerDatasource && (
<EuiFlexItem className="eui-textTruncate"> <EuiFlexItem className="lnsLayerPanel__sourceFlexItem">
<NativeRenderer <NativeRenderer
render={layerDatasource.renderLayerPanel} render={layerDatasource.renderLayerPanel}
nativeProps={{ nativeProps={{
@ -170,18 +170,15 @@ export function LayerPanel(
defaultMessage: 'Quick functions', defaultMessage: 'Quick functions',
}), }),
content: ( content: (
<> <NativeRenderer
<EuiSpacer size="s" /> render={props.datasourceMap[datasourceId].renderDimensionEditor}
<NativeRenderer nativeProps={{
render={props.datasourceMap[datasourceId].renderDimensionEditor} ...layerDatasourceConfigProps,
nativeProps={{ core: props.core,
...layerDatasourceConfigProps, columnId: accessor,
core: props.core, filterOperations: group.filterOperations,
columnId: accessor, }}
filterOperations: group.filterOperations, />
}}
/>
</>
), ),
}, },
]; ];
@ -194,7 +191,6 @@ export function LayerPanel(
}), }),
content: ( content: (
<div className="lnsLayerPanel__styleEditor"> <div className="lnsLayerPanel__styleEditor">
<EuiSpacer size="s" />
<NativeRenderer <NativeRenderer
render={activeVisualization.renderDimensionEditor} render={activeVisualization.renderDimensionEditor}
nativeProps={{ nativeProps={{

View file

@ -5,10 +5,11 @@
*/ */
import React, { useState } from 'react'; import React, { useState } from 'react';
import { EuiPopover, EuiButtonIcon, EuiToolTip } from '@elastic/eui'; import { EuiPopover, EuiToolTip } from '@elastic/eui';
import { i18n } from '@kbn/i18n'; import { i18n } from '@kbn/i18n';
import { NativeRenderer } from '../../../native_renderer'; import { NativeRenderer } from '../../../native_renderer';
import { Visualization, VisualizationLayerWidgetProps } from '../../../types'; import { Visualization, VisualizationLayerWidgetProps } from '../../../types';
import { ToolbarButton } from '../../../toolbar_button';
export function LayerSettings({ export function LayerSettings({
layerId, layerId,
@ -25,6 +26,10 @@ export function LayerSettings({
return null; return null;
} }
const a11yText = i18n.translate('xpack.lens.editLayerSettings', {
defaultMessage: 'Edit layer settings',
});
return ( return (
<EuiPopover <EuiPopover
id={`lnsLayerPopover_${layerId}`} id={`lnsLayerPopover_${layerId}`}
@ -36,11 +41,11 @@ export function LayerSettings({
defaultMessage: 'Edit layer settings', defaultMessage: 'Edit layer settings',
})} })}
> >
<EuiButtonIcon <ToolbarButton
size="s"
iconType={activeVisualization.getLayerContextMenuIcon?.(layerConfigProps) || 'gear'} iconType={activeVisualization.getLayerContextMenuIcon?.(layerConfigProps) || 'gear'}
aria-label={i18n.translate('xpack.lens.editLayerSettings', { aria-label={a11yText}
defaultMessage: 'Edit layer settings', title={a11yText}
})}
onClick={() => setIsOpen(!isOpen)} onClick={() => setIsOpen(!isOpen)}
data-test-subj="lns_layer_settings" data-test-subj="lns_layer_settings"
/> />

View file

@ -5,15 +5,9 @@
} }
} }
.lnsChartSwitch__triggerButton {
@include euiTitle('xs');
background-color: $euiColorEmptyShade;
border-color: $euiColorLightShade;
}
.lnsChartSwitch__summaryIcon { .lnsChartSwitch__summaryIcon {
margin-right: $euiSizeS; margin-right: $euiSizeS;
transform: translateY(-2px); transform: translateY(-1px);
} }
// Targeting img as this won't target normal EuiIcon's only the custom svgs's // Targeting img as this won't target normal EuiIcon's only the custom svgs's

View file

@ -4,6 +4,7 @@
* you may not use this file except in compliance with the Elastic License. * you may not use this file except in compliance with the Elastic License.
*/ */
import './chart_switch.scss';
import React, { useState, useMemo } from 'react'; import React, { useState, useMemo } from 'react';
import { import {
EuiIcon, EuiIcon,
@ -11,7 +12,6 @@ import {
EuiPopoverTitle, EuiPopoverTitle,
EuiKeyPadMenu, EuiKeyPadMenu,
EuiKeyPadMenuItem, EuiKeyPadMenuItem,
EuiButton,
} from '@elastic/eui'; } from '@elastic/eui';
import { flatten } from 'lodash'; import { flatten } from 'lodash';
import { i18n } from '@kbn/i18n'; import { i18n } from '@kbn/i18n';
@ -19,6 +19,7 @@ import { Visualization, FramePublicAPI, Datasource } from '../../../types';
import { Action } from '../state_management'; import { Action } from '../state_management';
import { getSuggestions, switchToSuggestion, Suggestion } from '../suggestion_helpers'; import { getSuggestions, switchToSuggestion, Suggestion } from '../suggestion_helpers';
import { trackUiEvent } from '../../../lens_ui_telemetry'; import { trackUiEvent } from '../../../lens_ui_telemetry';
import { ToolbarButton } from '../../../toolbar_button';
interface VisualizationSelection { interface VisualizationSelection {
visualizationId: string; visualizationId: string;
@ -72,8 +73,6 @@ function VisualizationSummary(props: Props) {
); );
} }
import './chart_switch.scss';
export function ChartSwitch(props: Props) { export function ChartSwitch(props: Props) {
const [flyoutOpen, setFlyoutOpen] = useState<boolean>(false); const [flyoutOpen, setFlyoutOpen] = useState<boolean>(false);
@ -202,16 +201,13 @@ export function ChartSwitch(props: Props) {
panelClassName="lnsChartSwitch__popoverPanel" panelClassName="lnsChartSwitch__popoverPanel"
panelPaddingSize="s" panelPaddingSize="s"
button={ button={
<EuiButton <ToolbarButton
className="lnsChartSwitch__triggerButton"
onClick={() => setFlyoutOpen(!flyoutOpen)} onClick={() => setFlyoutOpen(!flyoutOpen)}
data-test-subj="lnsChartSwitchPopover" data-test-subj="lnsChartSwitchPopover"
iconSide="right" fontWeight="bold"
iconType="arrowDown"
color="text"
> >
<VisualizationSummary {...props} /> <VisualizationSummary {...props} />
</EuiButton> </ToolbarButton>
} }
isOpen={flyoutOpen} isOpen={flyoutOpen}
closePopover={() => setFlyoutOpen(false)} closePopover={() => setFlyoutOpen(false)}

View file

@ -15,6 +15,7 @@ import {
EuiText, EuiText,
EuiBetaBadge, EuiBetaBadge,
EuiButtonEmpty, EuiButtonEmpty,
EuiLink,
} from '@elastic/eui'; } from '@elastic/eui';
import { CoreStart, CoreSetup } from 'kibana/public'; import { CoreStart, CoreSetup } from 'kibana/public';
import { import {
@ -208,18 +209,20 @@ export function InnerWorkspacePanel({
/>{' '} />{' '}
<EuiBetaBadge label="Beta" tooltipContent={tooltipContent} /> <EuiBetaBadge label="Beta" tooltipContent={tooltipContent} />
</p> </p>
<EuiButtonEmpty <p>
href="https://www.elastic.co/products/kibana/feedback" <small>
iconType="popout" <EuiLink
iconSide="right" href="https://www.elastic.co/products/kibana/feedback"
size="xs" target="_blank"
target="_blank" external
> >
<FormattedMessage <FormattedMessage
id="xpack.lens.editorFrame.goToForums" id="xpack.lens.editorFrame.goToForums"
defaultMessage="Make requests and give feedback" defaultMessage="Make requests and give feedback"
/> />
</EuiButtonEmpty> </EuiLink>
</small>
</p>
</EuiText> </EuiText>
</div> </div>
); );

View file

@ -6,18 +6,13 @@
import { i18n } from '@kbn/i18n'; import { i18n } from '@kbn/i18n';
import React, { useState } from 'react'; import React, { useState } from 'react';
import { import { EuiPopover, EuiPopoverTitle, EuiSelectable } from '@elastic/eui';
EuiButtonEmpty,
EuiPopover,
EuiPopoverTitle,
EuiSelectable,
EuiButtonEmptyProps,
} from '@elastic/eui';
import { EuiSelectableProps } from '@elastic/eui/src/components/selectable/selectable'; import { EuiSelectableProps } from '@elastic/eui/src/components/selectable/selectable';
import { IndexPatternRef } from './types'; import { IndexPatternRef } from './types';
import { trackUiEvent } from '../lens_ui_telemetry'; import { trackUiEvent } from '../lens_ui_telemetry';
import { ToolbarButtonProps, ToolbarButton } from '../toolbar_button';
export type ChangeIndexPatternTriggerProps = EuiButtonEmptyProps & { export type ChangeIndexPatternTriggerProps = ToolbarButtonProps & {
label: string; label: string;
title?: string; title?: string;
}; };
@ -40,29 +35,24 @@ export function ChangeIndexPattern({
const createTrigger = function () { const createTrigger = function () {
const { label, title, ...rest } = trigger; const { label, title, ...rest } = trigger;
return ( return (
<EuiButtonEmpty <ToolbarButton
className="eui-textTruncate"
flush="left"
color="text"
iconSide="right"
iconType="arrowDown"
title={title} title={title}
onClick={() => setPopoverIsOpen(!isPopoverOpen)} onClick={() => setPopoverIsOpen(!isPopoverOpen)}
fullWidth
{...rest} {...rest}
> >
{label} {label}
</EuiButtonEmpty> </ToolbarButton>
); );
}; };
return ( return (
<> <>
<EuiPopover <EuiPopover
style={{ width: '100%' }}
button={createTrigger()} button={createTrigger()}
isOpen={isPopoverOpen} isOpen={isPopoverOpen}
closePopover={() => setPopoverIsOpen(false)} closePopover={() => setPopoverIsOpen(false)}
className="eui-textTruncate"
anchorClassName="eui-textTruncate"
display="block" display="block"
panelPaddingSize="s" panelPaddingSize="s"
ownFocus ownFocus

View file

@ -7,13 +7,7 @@
.lnsInnerIndexPatternDataPanel__header { .lnsInnerIndexPatternDataPanel__header {
display: flex; display: flex;
align-items: center; align-items: center;
height: $euiSize * 3; margin-bottom: $euiSizeS;
margin-top: -$euiSizeS;
}
.lnsInnerIndexPatternDataPanel__triggerButton {
@include euiTitle('xs');
line-height: $euiSizeXXL;
} }
/** /**

View file

@ -424,7 +424,7 @@ export const InnerIndexPatternDataPanel = function InnerIndexPatternDataPanel({
label: currentIndexPattern.title, label: currentIndexPattern.title,
title: currentIndexPattern.title, title: currentIndexPattern.title,
'data-test-subj': 'indexPattern-switch-link', 'data-test-subj': 'indexPattern-switch-link',
className: 'lnsInnerIndexPatternDataPanel__triggerButton', fontWeight: 'bold',
}} }}
indexPatternId={currentIndexPatternId} indexPatternId={currentIndexPatternId}
indexPatternRefs={indexPatternRefs} indexPatternRefs={indexPatternRefs}

View file

@ -1,7 +1,6 @@
.lnsIndexPatternDimensionEditor { .lnsIndexPatternDimensionEditor {
flex-grow: 1; width: $euiSize * 30;
line-height: 0; padding: $euiSizeS;
overflow: hidden;
} }
.lnsIndexPatternDimensionEditor__left, .lnsIndexPatternDimensionEditor__left,
@ -11,10 +10,7 @@
.lnsIndexPatternDimensionEditor__left { .lnsIndexPatternDimensionEditor__left {
background-color: $euiPageBackgroundColor; background-color: $euiPageBackgroundColor;
} width: $euiSize * 8;
.lnsIndexPatternDimensionEditor__right {
width: $euiSize * 20;
} }
.lnsIndexPatternDimensionEditor__operation > button { .lnsIndexPatternDimensionEditor__operation > button {

View file

@ -299,25 +299,31 @@ export function PopoverEditor(props: PopoverEditorProps) {
</EuiFlexItem> </EuiFlexItem>
<EuiFlexItem grow={true} className="lnsIndexPatternDimensionEditor__right"> <EuiFlexItem grow={true} className="lnsIndexPatternDimensionEditor__right">
{incompatibleSelectedOperationType && selectedColumn && ( {incompatibleSelectedOperationType && selectedColumn && (
<EuiCallOut <>
data-test-subj="indexPattern-invalid-operation" <EuiCallOut
title={i18n.translate('xpack.lens.indexPattern.invalidOperationLabel', { data-test-subj="indexPattern-invalid-operation"
defaultMessage: 'To use this function, select a different field.', title={i18n.translate('xpack.lens.indexPattern.invalidOperationLabel', {
})} defaultMessage: 'To use this function, select a different field.',
color="warning" })}
size="s" color="warning"
iconType="sortUp" size="s"
/> iconType="sortUp"
/>
<EuiSpacer size="m" />
</>
)} )}
{incompatibleSelectedOperationType && !selectedColumn && ( {incompatibleSelectedOperationType && !selectedColumn && (
<EuiCallOut <>
size="s" <EuiCallOut
data-test-subj="indexPattern-fieldless-operation" size="s"
title={i18n.translate('xpack.lens.indexPattern.fieldlessOperationLabel', { data-test-subj="indexPattern-fieldless-operation"
defaultMessage: 'To use this function, select a field.', title={i18n.translate('xpack.lens.indexPattern.fieldlessOperationLabel', {
})} defaultMessage: 'To use this function, select a field.',
iconType="sortUp" })}
/> iconType="sortUp"
/>
<EuiSpacer size="m" />
</>
)} )}
{!incompatibleSelectedOperationType && ParamEditor && ( {!incompatibleSelectedOperationType && ParamEditor && (
<> <>

View file

@ -27,7 +27,8 @@ export function LayerPanel({ state, layerId, onChangeIndexPattern }: IndexPatter
label: state.indexPatterns[layer.indexPatternId].title, label: state.indexPatterns[layer.indexPatternId].title,
title: state.indexPatterns[layer.indexPatternId].title, title: state.indexPatterns[layer.indexPatternId].title,
'data-test-subj': 'lns_layerIndexPatternLabel', 'data-test-subj': 'lns_layerIndexPatternLabel',
size: 'xs', size: 's',
fontWeight: 'normal',
}} }}
indexPatternId={layer.indexPatternId} indexPatternId={layer.indexPatternId}
indexPatternRefs={state.indexPatternRefs} indexPatternRefs={state.indexPatternRefs}

View file

@ -0,0 +1,7 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
export { ToolbarButtonProps, ToolbarButton } from './toolbar_button';

View file

@ -0,0 +1,30 @@
.lnsToolbarButton {
line-height: $euiButtonHeight; // Keeps alignment of text and chart icon
background-color: $euiColorEmptyShade;
border-color: $euiBorderColor;
// Some toolbar buttons are just icons, but EuiButton comes with margin and min-width that need to be removed
min-width: 0;
.lnsToolbarButton__text:empty {
margin: 0;
}
// Toolbar buttons don't look good with centered text when fullWidth
&[class*='fullWidth'] {
text-align: left;
.lnsToolbarButton__content {
justify-content: space-between;
}
}
}
.lnsToolbarButton--bold {
font-weight: $euiFontWeightBold;
}
.lnsToolbarButton--s {
box-shadow: none !important; // sass-lint:disable-line no-important
font-size: $euiFontSizeS;
}

View file

@ -0,0 +1,53 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import './toolbar_button.scss';
import React from 'react';
import classNames from 'classnames';
import { EuiButton, PropsOf, EuiButtonProps } from '@elastic/eui';
export type ToolbarButtonProps = PropsOf<typeof EuiButton> & {
/**
* Determines prominence
*/
fontWeight?: 'normal' | 'bold';
/**
* Smaller buttons also remove extra shadow for less prominence
*/
size?: EuiButtonProps['size'];
};
export const ToolbarButton: React.FunctionComponent<ToolbarButtonProps> = ({
children,
className,
fontWeight = 'normal',
size = 'm',
...rest
}) => {
const classes = classNames(
'lnsToolbarButton',
[`lnsToolbarButton--${fontWeight}`, `lnsToolbarButton--${size}`],
className
);
return (
<EuiButton
className={classes}
iconSide="right"
iconType="arrowDown"
color="text"
contentProps={{
className: 'lnsToolbarButton__content',
}}
textProps={{
className: 'lnsToolbarButton__text',
}}
{...rest}
size={size}
>
{children}
</EuiButton>
);
};

View file

@ -4,11 +4,11 @@
* you may not use this file except in compliance with the Elastic License. * you may not use this file except in compliance with the Elastic License.
*/ */
import './xy_config_panel.scss';
import React, { useState } from 'react'; import React, { useState } from 'react';
import { i18n } from '@kbn/i18n'; import { i18n } from '@kbn/i18n';
import { debounce } from 'lodash'; import { debounce } from 'lodash';
import { import {
EuiButtonEmpty,
EuiButtonGroup, EuiButtonGroup,
EuiFlexGroup, EuiFlexGroup,
EuiFlexItem, EuiFlexItem,
@ -32,8 +32,7 @@ import { State, SeriesType, visualizationTypes, YAxisMode } from './types';
import { isHorizontalChart, isHorizontalSeries, getSeriesColor } from './state_helpers'; import { isHorizontalChart, isHorizontalSeries, getSeriesColor } from './state_helpers';
import { trackUiEvent } from '../lens_ui_telemetry'; import { trackUiEvent } from '../lens_ui_telemetry';
import { fittingFunctionDefinitions } from './fitting_functions'; import { fittingFunctionDefinitions } from './fitting_functions';
import { ToolbarButton } from '../toolbar_button';
import './xy_config_panel.scss';
type UnwrapArray<T> = T extends Array<infer P> ? P : T; type UnwrapArray<T> = T extends Array<infer P> ? P : T;
@ -101,17 +100,16 @@ export function XyToolbar(props: VisualizationToolbarProps<State>) {
<EuiFlexItem grow={false}> <EuiFlexItem grow={false}>
<EuiPopover <EuiPopover
panelClassName="lnsXyToolbar__popover" panelClassName="lnsXyToolbar__popover"
ownFocus
button={ button={
<EuiButtonEmpty <ToolbarButton
color="text" fontWeight="normal"
iconType="arrowDown"
iconSide="right"
onClick={() => { onClick={() => {
setOpen(!open); setOpen(!open);
}} }}
> >
{i18n.translate('xpack.lens.xyChart.settingsLabel', { defaultMessage: 'Settings' })} {i18n.translate('xpack.lens.xyChart.settingsLabel', { defaultMessage: 'Settings' })}
</EuiButtonEmpty> </ToolbarButton>
} }
isOpen={open} isOpen={open}
closePopover={() => { closePopover={() => {
@ -119,12 +117,9 @@ export function XyToolbar(props: VisualizationToolbarProps<State>) {
}} }}
anchorPosition="downRight" anchorPosition="downRight"
> >
<EuiFormRow <EuiToolTip
display="columnCompressed" anchorClassName="eui-displayBlock"
label={i18n.translate('xpack.lens.xyChart.fittingLabel', { content={
defaultMessage: 'Fill missing values',
})}
helpText={
!hasNonBarSeries && !hasNonBarSeries &&
i18n.translate('xpack.lens.xyChart.fittingDisabledHelpText', { i18n.translate('xpack.lens.xyChart.fittingDisabledHelpText', {
defaultMessage: defaultMessage:
@ -132,29 +127,36 @@ export function XyToolbar(props: VisualizationToolbarProps<State>) {
}) })
} }
> >
<EuiSuperSelect <EuiFormRow
compressed display="columnCompressed"
disabled={!hasNonBarSeries} label={i18n.translate('xpack.lens.xyChart.fittingLabel', {
options={fittingFunctionDefinitions.map(({ id, title, description }) => { defaultMessage: 'Fill missing values',
return {
value: id,
dropdownDisplay: (
<>
<strong>{title}</strong>
<EuiText size="xs" color="subdued">
<p>{description}</p>
</EuiText>
</>
),
inputDisplay: title,
};
})} })}
valueOfSelected={props.state?.fittingFunction || 'None'} >
onChange={(value) => props.setState({ ...props.state, fittingFunction: value })} <EuiSuperSelect
itemLayoutAlign="top" compressed
hasDividers disabled={!hasNonBarSeries}
/> options={fittingFunctionDefinitions.map(({ id, title, description }) => {
</EuiFormRow> return {
value: id,
dropdownDisplay: (
<>
<strong>{title}</strong>
<EuiText size="xs" color="subdued">
<p>{description}</p>
</EuiText>
</>
),
inputDisplay: title,
};
})}
valueOfSelected={props.state?.fittingFunction || 'None'}
onChange={(value) => props.setState({ ...props.state, fittingFunction: value })}
itemLayoutAlign="top"
hasDividers
/>
</EuiFormRow>
</EuiToolTip>
</EuiPopover> </EuiPopover>
</EuiFlexItem> </EuiFlexItem>
</EuiFlexGroup> </EuiFlexGroup>
@ -183,12 +185,12 @@ export function DimensionEditor(props: VisualizationDimensionEditorProps<State>)
})} })}
> >
<EuiButtonGroup <EuiButtonGroup
isFullWidth
legend={i18n.translate('xpack.lens.xyChart.axisSide.label', { legend={i18n.translate('xpack.lens.xyChart.axisSide.label', {
defaultMessage: 'Axis side', defaultMessage: 'Axis side',
})} })}
name="axisSide" name="axisSide"
buttonSize="compressed" buttonSize="compressed"
className="eui-displayInlineBlock"
options={[ options={[
{ {
id: `${idPrefix}auto`, id: `${idPrefix}auto`,
@ -241,7 +243,7 @@ const tooltipContent = {
}), }),
disabled: i18n.translate('xpack.lens.configPanel.color.tooltip.disabled', { disabled: i18n.translate('xpack.lens.configPanel.color.tooltip.disabled', {
defaultMessage: defaultMessage:
'Individual series cannot be custom colored when the layer includes a “Break down by“', 'Individual series cannot be custom colored when the layer includes a “Break down by.“',
}), }),
}; };
@ -286,6 +288,22 @@ const ColorPicker = ({
[state, layer, accessor, index] [state, layer, accessor, index]
); );
const colorPicker = (
<EuiColorPicker
compressed
isClearable
onChange={handleColor}
color={disabled ? '' : color}
disabled={disabled}
placeholder={i18n.translate('xpack.lens.xyChart.seriesColor.auto', {
defaultMessage: 'Auto',
})}
aria-label={i18n.translate('xpack.lens.xyChart.seriesColor.label', {
defaultMessage: 'Series color',
})}
/>
);
return ( return (
<EuiFormRow <EuiFormRow
display="columnCompressed" display="columnCompressed"
@ -312,25 +330,10 @@ const ColorPicker = ({
delay="long" delay="long"
anchorClassName="eui-displayBlock" anchorClassName="eui-displayBlock"
> >
<EuiColorPicker {colorPicker}
compressed
onChange={handleColor}
color=""
disabled
aria-label={i18n.translate('xpack.lens.xyChart.seriesColor.label', {
defaultMessage: 'Series color',
})}
/>
</EuiToolTip> </EuiToolTip>
) : ( ) : (
<EuiColorPicker colorPicker
compressed
onChange={handleColor}
color={color}
aria-label={i18n.translate('xpack.lens.xyChart.seriesColor.label', {
defaultMessage: 'Series color',
})}
/>
)} )}
</EuiFormRow> </EuiFormRow>
); );