TSVB less to sass - and some UI work (#24250)

Fixes #16483, Fixes #20115, Fixes #17852, Fixes #16768, Fixes #14348, Fixes #17842, closes #15580, closes #14938
This commit is contained in:
Caroline Horn 2018-11-14 11:10:08 -05:00 committed by GitHub
parent 8143d58ea4
commit 2b3bff34b8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
137 changed files with 4239 additions and 4229 deletions

View file

@ -27,7 +27,8 @@ export default function (kibana) {
uiExports: {
visTypes: [
'plugins/metrics/kbn_vis_types'
]
],
styleSheetPaths: `${__dirname}/public/index.scss`,
},
config(Joi) {

View file

@ -0,0 +1,128 @@
// EUITODO: Create our own Markdown styles
.tvbMarkdown__content {
color: rgba(0,0,0,1);
pre, code, tt {
border-radius: 0;
font: 1em/1.5em 'Andale Mono', 'Lucida Console', monospace;
}
h1 { font-size: 30px; }
h2 { font-size: 26px; }
h3 { font-size: 22px; }
h4 { font-size: 18px; }
h5 { font-size: 16px; }
h6 { font-size: 16px; }
h1, h2, h3, h4, h5, h6, b, strong {
margin:0 0 15px 0;
font-weight: bold;
}
em, i, dfn {
font-style: italic;
}
dfn {
font-weight:bold;
}
p, code, pre, kbd {
margin:0 0 15px 0;
}
blockquote {
margin:0 15px 15px 15px;
}
cite {
font-style: italic;
}
li ul, li ol {
margin:0 15px;
}
ul, ol {
margin:0 15px 15px 15px;
}
ul {
list-style-type:disc;
}
ol {
list-style-type:decimal;
}
ol ol {
list-style: upper-alpha;
}
ol ol ol {
list-style: lower-roman;
}
ol ol ol ol {
list-style: lower-alpha;
}
dl {
margin:0 0 15px 0;
}
dl dt {
font-weight:bold;
}
dd {
margin-left:1.5em;
}
table {
margin-bottom:1.4em;
width:100%;
}
th {
font-weight:bold;
}
th, td, caption {
padding:4px 15px 4px 5px;
}
tfoot {
font-style:italic;
}
sup, sub {
line-height:0;
}
abbr, acronym {
border-bottom: 1px dotted;
}
address {
margin:0 0 15px;
font-style:italic;
}
del {
text-decoration: line-through;
}
pre {
margin: 15px 0;
white-space: pre;
}
img {
max-width: 100%;
}
pre, code, tt {
background-color: rgba(0,0,0,0.1);
color: red;
code {
color: rgba(0,0,0,1);
background-color: transparent;
}
border: none;
}
.tvbMarkdown.reversed & {
.table > thead > tr > th {
color: rgba(255,255,255,0.5);
border-bottom: 2px solid rgba(255,255,255,0.2);
}
.table > tbody > tr > td {
border-top: 1px solid rgba(255,255,255,0.2);
}
color: rgba(255,255,255,1);
pre, code, tt {
background-color: rgba(255,255,255,0.2);
color: #ffa5a8;
code {
color: rgba(255,255,255,1);
background-color: transparent;
}
border: none;
}
}
}

View file

@ -0,0 +1,14 @@
@import 'node_modules/@elastic/eui/src/components/form/variables';
@import 'node_modules/@elastic/eui/src/components/form/mixins';
@mixin tvbEditor__repeatingRow {
background-color: $euiColorLightestShade;
padding: $euiSizeS;
margin-bottom: $euiSizeS;
}
// SASSTODO: These need to be converted to EUI,
// but they have type errors
@mixin tvbEditor__input {
@include euiFormControlStyle($borderOnly: false, $includeStates: true, $includeSizes: false);
}

View file

@ -0,0 +1,41 @@
// react-anything-sortable overrides
// Scoped to just the TSVB editor
.tvbEditor {
.ui-sortable {
display: block;
position: relative;
overflow: visible;
user-select: none;
&:before,
&:after {
content: " ";
display: table;
}
&:after {
clear: both;
}
.ui-sortable-item.ui-sortable-dragging {
position: absolute;
z-index: $euiZLevel2;
}
.ui-sortable-placeholder {
display: none;
&.visible {
display: block;
opacity: 0.5;
z-index: -1;
}
}
}
}

View file

@ -0,0 +1,11 @@
$tvbLineColor: transparentize($euiColorFullShade,0.8);
$tvbLineColorReversed: transparentize($euiColorEmptyShade,0.6);
$tvbTextColor: transparentize($euiColorFullShade,0.6);
$tvbTextColorReversed: transparentize($euiColorEmptyShade,0.4);
$tvbValueColor: transparentize($euiColorFullShade,0.3);
$tvbValueColorReversed: transparentize($euiColorEmptyShade,0.2);
$tvbHoverBackgroundColor: transparentize($euiColorFullShade,0.9);
$tvbHoverBackgroundColorReversed: transparentize($euiColorEmptyShade,0.9);

View file

@ -0,0 +1,13 @@
@import 'node_modules/@elastic/eui/src/components/panel/mixins';
.tvbAnnotationsEditor__container {
padding: $euiSize;
background-color: $euiColorLightestShade;
}
@include euiPanel('tvbAnnotationsEditor');
.tvbAnnotationsEditor {
margin-bottom: $euiSize;
padding: $euiSizeS;
}

View file

@ -0,0 +1,44 @@
// EUITODO: Convert to EuiColorPicker
@import 'node_modules/@elastic/eui/src/components/color_picker/index';
.tvbColorPicker {
display: flex;
align-items: center;
position: relative;
}
.tvbColorPicker__swatch-empty,
.tvbColorPicker__swatch {
@extend .euiColorPicker__swatch;
}
.tvbColorPicker__swatch-empty {
background-color: transparent;
background-size: 22px 22px;
background-image: repeating-linear-gradient(
-45deg,
$euiColorDanger,
$euiColorDanger 2px,
transparent 2px,
transparent $euiSize
);
}
.tvbColorPicker__clear {
margin-left: $euiSizeXS;
}
.tvbColorPicker__popover {
position: absolute;
top: $euiSizeL;
z-index: 2;
}
.tvbColorPicker__cover {
position: fixed;
top: 0px;
right: 0px;
left: 0px;
bottom: 0px;
}

View file

@ -0,0 +1,3 @@
.tvbColorRules__rule {
@include tvbEditor__repeatingRow;
}

View file

@ -0,0 +1,97 @@
// EUITODO: Convert to EuiColorPicker
// with additional support for alpha, saturation, swatches
// SASSTODO: This custom picker moved all styles from react-color inline styles
// to SASS, but it should be in EUI.
// Also, some pixel values were kept as is to match inline styles from react-color
.tvbColorPickerPopUp {
@include euiBottomShadowMedium();
background-color: $euiColorEmptyShade;
border-radius: $euiBorderRadius;
box-sizing: initial;
width: 275px;
font-family: 'Menlo';
}
.tvbColorPickerPopUp__saturation {
width: 100%;
padding-bottom: 55%;
position: relative;
border-radius: $euiBorderRadius $euiBorderRadius 0 0;
overflow: hidden;
}
.tvbColorPickerPopUp__body {
padding: $euiSize;
}
.tvbColorPickerPopUp__controls {
display: flex;
}
.tvbColorPickerPopUp__color {
width: $euiSizeXL;
// The color indicator doesn't work, hiding it until it does
display: none;
}
.tvbColorPickerPopUp__color-disableAlpha {
width: $euiSizeL;
}
.tvbColorPickerPopUp__swatch {
margin-top: 6px;
width: $euiSize;
height: $euiSize;
border-radius: $euiSizeS;
position: relative;
overflow: hidden;
}
.tvbColorPickerPopUp__swatch-disableAlpha {
width: 10px;
height: 10px;
margin: 0px;
}
.tvbColorPickerPopUp__active {
position: absolute;
top: 0px;
left: 0px;
right: 0px;
bottom: 0px;
border-radius: $euiSizeS;
box-shadow: inset 0 0 0 1px rgba(0,0,0,0.1);
z-index: 2;
}
.tvbColorPickerPopUp__toggles {
flex: 1
}
.tvbColorPickerPopUp__hue {
height: 10px;
position: relative;
margin-bottom: $euiSizeS;
}
.tvbColorPickerPopUp__hue-disableAlpha {
margin-bottom: 0px;
}
.tvbColorPickerPopUp__alpha {
height: 10px;
position: relative;
}
.tvbColorPickerPopUp__alpha-disableAlpha {
display: none;
}
.tvbColorPickerPopUp__swatches {
display: flex;
flex-wrap: wrap;
justify-content: center;
margin-top: $euiSize;
}

View file

@ -0,0 +1,33 @@
// EUITODO: Convert to EuiCallout
.tvbError {
padding: $euiSize;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
// Calculate colors similar to EuiCallout
$tempBackgroundColor: tintOrShade($euiColorDanger, 90%, 70%);
background-color: $tempBackgroundColor;
.tvbError__title {
@include euiTitle("xs");
color: makeHighContrastColor($euiColorDanger, $tempBackgroundColor);
}
}
.tvbError__additional,
.tvbError__stack {
margin-top: $euiSizeS;
}
// EUITODO: Convert to EuiCodeBlock
.tvbError__stack {
padding: $euiSizeS;
background: $euiCodeBlockBackgroundColor;
color: $euiCodeBlockColor;
line-height: $euiLineHeight;
font-family: $euiCodeFontFamily;
font-weight: $euiFontWeightRegular;
white-space: pre-wrap;
}

View file

@ -0,0 +1,16 @@
@import './annotations_editor';
@import './color_rules';
@import './color_picker';
@import './custom_color_picker';
@import './error';
@import './no_data';
@import './markdown_editor';
@import './series_editor';
@import './vis_editor';
@import './vis_editor_visualization';
@import './vis_picker';
@import './vis_with_splits';
@import './aggs/index';
@import './panel_config/index';
@import './vis_types/index';

View file

@ -0,0 +1,28 @@
.tvbMarkdownEditor {
display: flex;
background-color: $euiColorEmptyShade;
height: 500px;
}
.tvbMarkdownEditor__editor,
.tvbMarkdownEditor__variables {
width: 50%;
flex: 1 0 auto;
}
.tvbMarkdownEditor__editor {
border-right: 2px solid $euiColorLightestShade;
}
.tvbMarkdownEditor__variables {
padding: $euiSizeS;
overflow: auto;
}
.tvbMarkdownEditor__noVariables {
display: block;
padding-bottom: $euiSizeXXL;
padding-top: $euiSizeXXL - $euiSizeL;
text-align: center;
border-bottom: $euiBorderThin;
}

View file

@ -0,0 +1,17 @@
// EUITODO: Convert to EuiCallout
.tvbNoData {
padding: $euiSize;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
// Calculate colors similar to EuiCallout
$tempBackgroundColor: tintOrShade($euiColorPrimary, 90%, 70%);
background-color: $tempBackgroundColor;
.tvbNoData__title {
@include euiTitle("xs");
color: makeHighContrastColor($euiColorPrimary, $tempBackgroundColor);
}
}

View file

@ -0,0 +1,23 @@
@import 'node_modules/@elastic/eui/src/components/panel/mixins';
.tvbSeriesEditor__container {
padding: $euiSize;
background-color: $euiColorLightestShade;
}
@include euiPanel('tvbSeriesEditor');
.tvbSeriesEditor {
margin-bottom: $euiSize;
padding: $euiSizeS;
// When dragging a collapsed item, make sure the contents doesn't show
&.ui-sortable-dragging {
overflow: hidden;
}
}
.tvbSeries__body {
margin-left: $euiSizeXL;
margin-top: $euiSizeS;
}

View file

@ -0,0 +1,4 @@
.tvbEditor {
flex: 1;
background: $euiColorLightestShade;
}

View file

@ -0,0 +1,19 @@
.tvbEditorVisualization {
position: relative;
display: flex;
flex-direction: column;
flex: 1 0 auto;
width: 100%;
height: $euiSizeL * 10;
line-height: normal;
background-color: $euiColorEmptyShade;
overflow: auto;
}
.tvbEditorVisualization__apply {
padding: $euiSizeS;
}
.tvbEditorVisualization__draghandle {
@include kibana-resizer($size: ($euiSizeM + 2px), $direction: vertical);
}

View file

@ -0,0 +1,4 @@
.tvbVisPickerItem {
font-size: $euiFontSizeM;
font-weight: $euiFontWeightMedium;
}

View file

@ -1,16 +1,11 @@
.splitVis {
.tvbSplitVis {
display: flex;
flex: 1 0 auto;
}
.splitVis_split {
.tvbSplitVis__split {
display: flex;
flex: 1 0 0;
flex-direction: column;
overflow: hidden;
}
.splitVis_visualization {
position: relative;
flex: 1 0 auto;
}

View file

@ -19,7 +19,7 @@
import PropTypes from 'prop-types';
import React from 'react';
import { EuiToolTip } from '@elastic/eui';
import { EuiToolTip, EuiButtonIcon, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
function AddDeleteButtons(props) {
const { testSubj } = props;
@ -28,16 +28,17 @@ function AddDeleteButtons(props) {
return null;
}
return (
<EuiToolTip content={props.deleteTooltip}>
<button
data-test-subj={`${testSubj}DeleteBtn`}
aria-label={props.deleteTooltip}
className="thor__button-outlined-danger thor__button-delete sm"
onClick={props.onDelete}
>
<i className="fa fa-trash-o" />
</button>
</EuiToolTip>
<EuiFlexItem grow={false}>
<EuiToolTip content={props.deleteTooltip}>
<EuiButtonIcon
data-test-subj={`${testSubj}DeleteBtn`}
aria-label={props.deleteTooltip}
color="danger"
iconType="trash"
onClick={props.onDelete}
/>
</EuiToolTip>
</EuiFlexItem>
);
};
const createAdd = () => {
@ -45,16 +46,16 @@ function AddDeleteButtons(props) {
return null;
}
return (
<EuiToolTip content={props.addTooltip}>
<button
data-test-subj={`${testSubj}AddBtn`}
aria-label={props.addTooltip}
className="thor__button-outlined-default sm thor__button-add"
onClick={props.onAdd}
>
<i className="fa fa-plus" />
</button>
</EuiToolTip>
<EuiFlexItem grow={false}>
<EuiToolTip content={props.addTooltip}>
<EuiButtonIcon
data-test-subj={`${testSubj}AddBtn`}
aria-label={props.addTooltip}
iconType="plusInCircle"
onClick={props.onAdd}
/>
</EuiToolTip>
</EuiFlexItem>
);
};
const deleteBtn = createDelete();
@ -62,24 +63,24 @@ function AddDeleteButtons(props) {
let clone;
if (props.onClone && !props.disableAdd) {
clone = (
<EuiToolTip content={props.cloneTooltip}>
<button
data-test-subj={`${testSubj}CloneBtn`}
aria-label={props.cloneTooltip}
className="thor__button-outlined-default thor__button-clone sm"
onClick={props.onClone}
>
<i className="fa fa-files-o" />
</button>
</EuiToolTip>
<EuiFlexItem grow={false}>
<EuiToolTip content={props.cloneTooltip}>
<EuiButtonIcon
data-test-subj={`${testSubj}CloneBtn`}
aria-label={props.cloneTooltip}
iconType="copy"
onClick={props.onClone}
/>
</EuiToolTip>
</EuiFlexItem>
);
}
return (
<div className="add_delete__buttons">
<EuiFlexGroup gutterSize="s" responsive={props.responsive} justifyContent="flexEnd">
{ clone }
{ addBtn }
{ deleteBtn }
</div>
</EuiFlexGroup>
);
}
@ -98,7 +99,8 @@ AddDeleteButtons.propTypes = {
disableDelete: PropTypes.bool,
onClone: PropTypes.func,
onAdd: PropTypes.func,
onDelete: PropTypes.func
onDelete: PropTypes.func,
responsive: PropTypes.bool,
};
export default AddDeleteButtons;

View file

@ -29,7 +29,7 @@ describe('AddDeleteButtons', () => {
const wrapper = shallow(
<AddDeleteButtons onAdd={handleAdd} />
);
wrapper.find('button').at(0).simulate('click');
wrapper.find('EuiButtonIcon').at(0).simulate('click');
expect(handleAdd.calledOnce).to.equal(true);
});
@ -38,7 +38,7 @@ describe('AddDeleteButtons', () => {
const wrapper = shallow(
<AddDeleteButtons onDelete={handleDelete} />
);
wrapper.find('button').at(1).simulate('click');
wrapper.find('EuiButtonIcon').at(1).simulate('click');
expect(handleDelete.calledOnce).to.equal(true);
});
@ -47,7 +47,7 @@ describe('AddDeleteButtons', () => {
const wrapper = shallow(
<AddDeleteButtons onClone={handleClone} />
);
wrapper.find('button').at(0).simulate('click');
wrapper.find('EuiButtonIcon').at(0).simulate('click');
expect(handleClone.calledOnce).to.equal(true);
});

View file

@ -0,0 +1,23 @@
.tvbAggRow {
@include tvbEditor__repeatingRow;
}
.tvbAggRow--split {
padding-left: $euiSizeXL;
}
.tvbAggRow__visibilityIcon {
margin-top: $euiSizeXS;
}
.tvbAggRow__children {
padding-top: $euiSizeS - 2px;
}
.tvbAggRow__unavailable {
margin-top: -$euiSizeXS;
}
.tvbAgg__input {
@include euiFormControlStyle($borderOnly: false, $includeStates: true, $includeSizes: false);
}

View file

@ -0,0 +1 @@
@import './agg_row';

View file

@ -21,47 +21,49 @@ import PropTypes from 'prop-types';
import React from 'react';
import _ from 'lodash';
import AddDeleteButtons from '../add_delete_buttons';
import { EuiToolTip } from '@elastic/eui';
import { EuiToolTip, EuiButtonIcon, EuiIcon, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
function AggRow(props) {
let iconClassName = 'fa fa-eye-slash';
let iconRowClassName = 'vis_editor__agg_row-icon';
let iconType = 'eyeClosed';
let iconColor = 'subdued';
const last = _.last(props.siblings);
if (last.id === props.model.id) {
iconClassName = 'fa fa-eye';
iconRowClassName += ' last';
iconType = 'eye';
iconColor = 'text';
}
let dragHandle;
if (!props.disableDelete) {
dragHandle = (
<div>
<EuiToolTip content="Sort">
<div className="vis_editor__agg_sort thor__button-outlined-default sm">
<i className="fa fa-sort" />
</div>
<EuiFlexItem grow={false}>
<EuiToolTip content="Drag to sort">
<EuiButtonIcon className="tvbAggRow__sortHandle" aria-label="Drag to sort" iconType="grab" />
</EuiToolTip>
</div>
</EuiFlexItem>
);
}
return (
<div className="vis_editor__agg_row">
<div className="vis_editor__agg_row-item" data-test-subj="aggRow">
<div className={iconRowClassName}>
<i className={iconClassName} />
</div>
{props.children}
{ dragHandle }
<AddDeleteButtons
testSubj="addMetric"
addTooltip="Add Metric"
deleteTooltip="Delete Metric"
onAdd={props.onAdd}
onDelete={props.onDelete}
disableDelete={props.disableDelete}
/>
</div>
<div className="tvbAggRow">
<EuiFlexGroup data-test-subj="aggRow" gutterSize="s" alignItems="flexStart" responsive={false}>
<EuiFlexItem grow={false}>
<EuiIcon className="tvbAggRow__visibilityIcon" type={iconType} color={iconColor} />
</EuiFlexItem>
<EuiFlexItem className="tvbAggRow__children">
{props.children}
</EuiFlexItem>
{dragHandle}
<EuiFlexItem grow={false}>
<AddDeleteButtons
testSubj="addMetric"
addTooltip="Add metric"
deleteTooltip="Delete metric"
onAdd={props.onAdd}
onDelete={props.onDelete}
disableDelete={props.disableDelete}
/>
</EuiFlexItem>
</EuiFlexGroup>
</div>
);
}

View file

@ -80,7 +80,7 @@ function filterByPanelType(panelType) {
}
function AggSelect(props) {
const { siblings, panelType, value } = props;
const { siblings, panelType, value, onChange, ...rest } = props;
const selectedOption = allAggOptions.find(option => {
return value === option.value;
@ -120,18 +120,19 @@ function AggSelect(props) {
const handleChange = selectedOptions => {
if (!selectedOptions || selectedOptions.length <= 0) return;
props.onChange(selectedOptions);
onChange(selectedOptions);
};
return (
<div data-test-subj="aggSelector" className="vis_editor__row_item">
<div data-test-subj="aggSelector">
<EuiComboBox
isClearable={false}
placeholder="Select aggregation"
options={options}
selectedOptions={selectedOptions}
onChange={handleChange}
singleSelection={true}
singleSelection={{ asPlainText: true }}
{...rest}
/>
</div>
);

View file

@ -29,7 +29,15 @@ import createSelectHandler from '../lib/create_select_handler';
import createTextHandler from '../lib/create_text_handler';
import Vars from './vars';
import { htmlIdGenerator } from '@elastic/eui';
import {
htmlIdGenerator,
EuiFlexGroup,
EuiFlexItem,
EuiFormLabel,
EuiTextArea,
EuiFormRow,
EuiCode,
} from '@elastic/eui';
class CalculationAgg extends Component {
@ -61,40 +69,49 @@ class CalculationAgg extends Component {
onDelete={this.props.onDelete}
siblings={this.props.siblings}
>
<div className="vis_editor__row_item">
<div>
<div className="vis_editor__label">Aggregation</div>
<EuiFlexGroup direction="column" gutterSize="l">
<EuiFlexItem>
<EuiFormLabel htmlFor={htmlId('aggregation')}>Aggregation</EuiFormLabel>
<AggSelect
id={htmlId('aggregation')}
panelType={this.props.panel.type}
siblings={this.props.siblings}
value={model.type}
onChange={handleSelectChange('type')}
/>
<div className="vis_editor__variables">
<div className="vis_editor__label">Variables</div>
<Vars
metrics={siblings}
onChange={handleChange}
name="variables"
model={model}
/>
</div>
<div className="vis_editor__row_item">
<label className="vis_editor__label" htmlFor={htmlId('painless')}>
Painless Script - Variables are keys on the <code>params</code>
object, i.e. <code>params.&lt;name&gt;</code>.
To access the bucket interval (in milliseconds) use <code>params._interval</code>.
</label>
<input
id={htmlId('painless')}
className="vis_editor__input-grows-100"
type="text"
</EuiFlexItem>
<EuiFlexItem>
<EuiFormLabel htmlFor={htmlId('variables')}>Variables</EuiFormLabel>
<Vars
id={htmlId('variables')}
metrics={siblings}
onChange={handleChange}
name="variables"
model={model}
/>
</EuiFlexItem>
<EuiFlexItem>
<EuiFormRow
id={htmlId('painless')}
label="Painless Script"
fullWidth
helpText={
<div>
Variables are keys on the <EuiCode>params</EuiCode> object, i.e. <EuiCode>params.&lt;name&gt;</EuiCode>.
To access the bucket interval (in milliseconds) use <EuiCode>params._interval</EuiCode>.
</div>
}
>
<EuiTextArea
onChange={handleTextChange('script')}
value={model.script}
fullWidth
/>
</div>
</div>
</div>
</EuiFormRow>
</EuiFlexItem>
</EuiFlexGroup>
</AggRow>
);
}

View file

@ -24,9 +24,11 @@ import AggSelect from './agg_select';
import MetricSelect from './metric_select';
import createChangeHandler from '../lib/create_change_handler';
import createSelectHandler from '../lib/create_select_handler';
import { htmlIdGenerator, EuiFlexGroup, EuiFlexItem, EuiFormLabel, EuiFormRow } from '@elastic/eui';
function CumulativeSumAgg(props) {
const { model, siblings } = props;
const htmlId = htmlIdGenerator();
const handleChange = createChangeHandler(props.onChange, model);
const handleSelectChange = createSelectHandler(handleChange);
return (
@ -37,24 +39,31 @@ function CumulativeSumAgg(props) {
onDelete={props.onDelete}
siblings={props.siblings}
>
<div className="vis_editor__row_item">
<div className="vis_editor__label">Aggregation</div>
<AggSelect
panelType={props.panel.type}
siblings={props.siblings}
value={model.type}
onChange={handleSelectChange('type')}
/>
</div>
<div className="vis_editor__row_item">
<div className="vis_editor__label">Metric</div>
<MetricSelect
onChange={handleSelectChange('field')}
metrics={siblings}
metric={model}
value={model.field}
/>
</div>
<EuiFlexGroup gutterSize="s">
<EuiFlexItem>
<EuiFormLabel htmlFor={htmlId('aggregation')}>Aggregation</EuiFormLabel>
<AggSelect
id={htmlId('aggregation')}
panelType={props.panel.type}
siblings={props.siblings}
value={model.type}
onChange={handleSelectChange('type')}
/>
</EuiFlexItem>
<EuiFlexItem>
<EuiFormRow
id={htmlId('metric')}
label="Metric"
>
<MetricSelect
onChange={handleSelectChange('field')}
metrics={siblings}
metric={model}
value={model.field}
/>
</EuiFormRow>
</EuiFlexItem>
</EuiFlexGroup>
</AggRow>
);

View file

@ -25,7 +25,14 @@ import AggRow from './agg_row';
import createChangeHandler from '../lib/create_change_handler';
import createSelectHandler from '../lib/create_select_handler';
import createTextHandler from '../lib/create_text_handler';
import { htmlIdGenerator } from '@elastic/eui';
import {
htmlIdGenerator,
EuiFlexGroup,
EuiFlexItem,
EuiFormLabel,
EuiFieldText,
EuiFormRow,
} from '@elastic/eui';
export const DerivativeAgg = props => {
const { siblings } = props;
@ -47,36 +54,47 @@ export const DerivativeAgg = props => {
onDelete={props.onDelete}
siblings={props.siblings}
>
<div className="vis_editor__row_item">
<div className="vis_editor__label">Aggregation</div>
<AggSelect
panelType={props.panel.type}
siblings={props.siblings}
value={model.type}
onChange={handleSelectChange('type')}
/>
</div>
<div className="vis_editor__row_item">
<div className="vis_editor__label">Metric</div>
<MetricSelect
onChange={handleSelectChange('field')}
metrics={siblings}
metric={model}
value={model.field}
/>
</div>
<div>
<label className="vis_editor__label" htmlFor={htmlId('units')}>
Units (1s, 1m, etc)
</label>
<input
id={htmlId('units')}
className="vis_editor__input"
onChange={handleTextChange('unit')}
value={model.unit}
type="text"
/>
</div>
<EuiFlexGroup gutterSize="s">
<EuiFlexItem>
<EuiFormLabel htmlFor={htmlId('aggregation')}>Aggregation</EuiFormLabel>
<AggSelect
id={htmlId('aggregation')}
panelType={props.panel.type}
siblings={props.siblings}
value={model.type}
onChange={handleSelectChange('type')}
fullWidth
/>
</EuiFlexItem>
<EuiFlexItem>
<EuiFormRow
id={htmlId('metric')}
label="Metric"
fullWidth
>
<MetricSelect
onChange={handleSelectChange('field')}
metrics={siblings}
metric={model}
value={model.field}
fullWidth
/>
</EuiFormRow>
</EuiFlexItem>
<EuiFlexItem>
<EuiFormRow
id={htmlId('units')}
label="Units (1s, 1m, etc)"
fullWidth
>
<EuiFieldText
onChange={handleTextChange('unit')}
value={model.unit}
fullWidth
/>
</EuiFormRow>
</EuiFlexItem>
</EuiFlexGroup>
</AggRow>
);
};

View file

@ -25,12 +25,12 @@ import {
import generateByTypeFilter from '../lib/generate_by_type_filter';
function FieldSelect(props) {
const { type, fields, indexPattern, value, onChange, disabled } = props;
const { type, fields, indexPattern, value, onChange, disabled, restrict, ...rest } = props;
if (type === 'count') {
return null;
}
const options = (fields[indexPattern] || [])
.filter(generateByTypeFilter(props.restrict))
.filter(generateByTypeFilter(restrict))
.map(field => {
return { label: field.name, value: field.name };
});
@ -47,7 +47,8 @@ function FieldSelect(props) {
options={options}
selectedOptions={selectedOptions}
onChange={onChange}
singleSelection={true}
singleSelection={{ asPlainText: true }}
{...rest}
/>
);
}

View file

@ -25,7 +25,15 @@ import AggRow from './agg_row';
import createChangeHandler from '../lib/create_change_handler';
import createSelectHandler from '../lib/create_select_handler';
import createTextHandler from '../lib/create_text_handler';
import { htmlIdGenerator } from '@elastic/eui';
import {
htmlIdGenerator,
EuiFlexGroup,
EuiFlexItem,
EuiFormLabel,
EuiFieldText,
EuiSpacer,
EuiFormRow,
} from '@elastic/eui';
export const FilterRatioAgg = props => {
const {
@ -58,59 +66,58 @@ export const FilterRatioAgg = props => {
onDelete={props.onDelete}
siblings={props.siblings}
>
<div style={{ flex: '1 0 auto' }}>
<div style={{ flex: '1 0 auto', display: 'flex' }}>
<div className="vis_editor__row_item">
<div className="vis_editor__label">Aggregation</div>
<AggSelect
panelType={props.panel.type}
siblings={props.siblings}
value={model.type}
onChange={handleSelectChange('type')}
/>
</div>
<div className="vis_editor__row_item">
<label className="vis_editor__label" htmlFor={htmlId('numerator')}>
Numerator
</label>
<input
id={htmlId('numerator')}
className="vis_editor__input-grows-100"
<EuiFlexGroup gutterSize="s">
<EuiFlexItem>
<EuiFormLabel htmlFor={htmlId('aggregation')}>Aggregation</EuiFormLabel>
<AggSelect
id={htmlId('aggregation')}
panelType={props.panel.type}
siblings={props.siblings}
value={model.type}
onChange={handleSelectChange('type')}
/>
</EuiFlexItem>
<EuiFlexItem>
<EuiFormRow id={htmlId('numerator')} label="Numerator">
<EuiFieldText
onChange={handleTextChange('numerator')}
value={model.numerator}
type="text"
/>
</div>
<div className="vis_editor__row_item">
<label className="vis_editor__label" htmlFor={htmlId('denominator')}>
Denominator
</label>
<input
id={htmlId('denominator')}
className="vis_editor__input-grows-100"
</EuiFormRow>
</EuiFlexItem>
<EuiFlexItem>
<EuiFormRow id={htmlId('denominator')} label="Denominator">
<EuiFieldText
onChange={handleTextChange('denominator')}
value={model.denominator}
type="text"
/>
</div>
</div>
<div style={{ flex: '1 0 auto', display: 'flex', marginTop: '10px' }}>
<div className="vis_editor__row_item">
<div className="vis_editor__label">Metric Aggregation</div>
<AggSelect
siblings={props.siblings}
panelType="metrics"
value={model.metric_agg}
onChange={handleSelectChange('metric_agg')}
/>
</div>
{ model.metric_agg !== 'count' ? (
<div className="vis_editor__row_item">
<label className="vis_editor__label" htmlFor={htmlId('aggField')}>
Field
</label>
</EuiFormRow>
</EuiFlexItem>
</EuiFlexGroup>
<EuiSpacer size="m" />
<EuiFlexGroup gutterSize="s">
<EuiFlexItem>
<EuiFormLabel htmlFor={htmlId('metric')}>Metric Aggregation</EuiFormLabel>
<AggSelect
id={htmlId('metric')}
siblings={props.siblings}
panelType="metrics"
value={model.metric_agg}
onChange={handleSelectChange('metric_agg')}
/>
</EuiFlexItem>
{ model.metric_agg !== 'count' ? (
<EuiFlexItem>
<EuiFormRow id={htmlId('aggField')} label="Field">
<FieldSelect
id={htmlId('aggField')}
fields={fields}
type={model.metric_agg}
restrict={restrictMode}
@ -118,9 +125,10 @@ export const FilterRatioAgg = props => {
value={model.field}
onChange={handleSelectChange('field')}
/>
</div>) : null }
</div>
</div>
</EuiFormRow>
</EuiFlexItem>) : null }
</EuiFlexGroup>
</AggRow>
);
};

View file

@ -28,6 +28,16 @@ import createChangeHandler from '../lib/create_change_handler';
import createSelectHandler from '../lib/create_select_handler';
import createTextHandler from '../lib/create_text_handler';
import Vars from './vars';
import {
htmlIdGenerator,
EuiFlexGroup,
EuiFlexItem,
EuiFormLabel,
EuiTextArea,
EuiLink,
EuiFormRow,
EuiCode,
} from '@elastic/eui';
class MathAgg extends Component {
componentWillMount() {
@ -42,6 +52,7 @@ class MathAgg extends Component {
render() {
const { siblings } = this.props;
const htmlId = htmlIdGenerator();
const defaults = { script: '' };
const model = { ...defaults, ...this.props.model };
@ -58,60 +69,63 @@ class MathAgg extends Component {
onDelete={this.props.onDelete}
siblings={this.props.siblings}
>
<div className="vis_editor__row_item">
<div>
<div className="vis_editor__label">Aggregation</div>
<EuiFlexGroup direction="column" gutterSize="l">
<EuiFlexItem>
<EuiFormLabel htmlFor={htmlId('aggregation')}>Aggregation</EuiFormLabel>
<AggSelect
id={htmlId('aggregation')}
siblings={this.props.siblings}
value={model.type}
onChange={handleSelectChange('type')}
/>
<div className="vis_editor__variables">
<div className="vis_editor__label">Variables</div>
<Vars
metrics={siblings}
onChange={handleChange}
name="variables"
model={model}
includeSiblings={true}
/>
</div>
<div className="vis_editor__row_item">
<label
className="vis_editor__label"
htmlFor="mathExpressionInput"
>
Expression
</label>
<textarea
</EuiFlexItem>
<EuiFlexItem>
<EuiFormLabel htmlFor={htmlId('variables')}>Variables</EuiFormLabel>
<Vars
id={htmlId('variables')}
metrics={siblings}
onChange={handleChange}
name="variables"
model={model}
includeSiblings={true}
/>
</EuiFlexItem>
<EuiFlexItem>
<EuiFormRow
id="mathExpressionInput"
label="Expression"
fullWidth
helpText={
<div>
This field uses basic math expressions (see{' '}
<EuiLink
href="https://github.com/elastic/tinymath/blob/master/docs/functions.md"
target="_blank"
>
TinyMath
</EuiLink>) - Variables are keys on the <EuiCode>params</EuiCode> object,
i.e. <EuiCode>params.&lt;name&gt;</EuiCode> To access all the data use
<EuiCode>params._all.&lt;name&gt;.values</EuiCode> for an array of the
values and <EuiCode>params._all.&lt;name&gt;.timestamps</EuiCode>
for an array of the timestamps. <EuiCode>params._timestamp</EuiCode>
is available for the current bucket&apos;s timestamp,
<EuiCode>params._index</EuiCode> is available for the current
bucket&apos;s index, and <EuiCode>params._interval</EuiCode>s
available for the interval in milliseconds.
</div>
}
>
<EuiTextArea
data-test-subj="mathExpression"
id="mathExpressionInput"
aria-describedby="mathExpressionDescription"
className="vis_editor__input-grows-100"
onChange={handleTextChange('script')}
>
{model.script}
</textarea>
<div className="vis_editor__note" id="mathExpressionDescription">
This field uses basic math expressions (see{' '}
<a
href="https://github.com/elastic/tinymath/blob/master/docs/functions.md"
target="_blank"
>
TinyMath
</a>) - Variables are keys on the <code>params</code> object,
i.e. <code>params.&lt;name&gt;</code> To access all the data use
<code>params._all.&lt;name&gt;.values</code> for an array of the
values and <code>params._all.&lt;name&gt;.timestamps</code>
for an array of the timestamps. <code>params._timestamp</code>
is available for the current bucket&apos;s timestamp,
<code>params._index</code> is available for the current
bucket&apos;s index, and <code>params._interval</code>s
available for the interval in milliseconds.
</div>
</div>
</div>
</div>
fullWidth
value={model.script}
/>
</EuiFormRow>
</EuiFlexItem>
</EuiFlexGroup>
</AggRow>
);
}

View file

@ -59,11 +59,11 @@ export function filterRows(includeSiblings) {
}
function MetricSelect(props) {
const { restrict, metric, onChange, value, exclude, includeSiblings } = props;
const { additionalOptions, restrict, metric, metrics, onChange, value, exclude, includeSiblings, clearable, ...rest } = props;
const metrics = props.metrics.filter(createTypeFilter(restrict, exclude));
const calculatedMetrics = metrics.filter(createTypeFilter(restrict, exclude));
const siblings = calculateSiblings(metrics, metric);
const siblings = calculateSiblings(calculatedMetrics, metric);
// Percentiles need to be handled differently because one percentile aggregation
// could have multiple percentiles associated with it. So the user needs a way
@ -71,7 +71,7 @@ function MetricSelect(props) {
const percentileOptions = siblings
.filter(row => /^percentile/.test(row.type))
.reduce((acc, row) => {
const label = calculateLabel(row, metrics);
const label = calculateLabel(row, calculatedMetrics);
row.percentiles.forEach(p => {
if (p.value) {
const value = /\./.test(p.value) ? p.value : `${p.value}.0`;
@ -85,10 +85,10 @@ function MetricSelect(props) {
}, []);
const options = siblings.filter(filterRows(includeSiblings)).map(row => {
const label = calculateLabel(row, metrics);
const label = calculateLabel(row, calculatedMetrics);
return { value: row.id, label };
});
const allOptions = [...options, ...props.additionalOptions, ...percentileOptions];
const allOptions = [...options, ...additionalOptions, ...percentileOptions];
const selectedOption = allOptions.find(option => {
return value === option.value;
@ -101,7 +101,9 @@ function MetricSelect(props) {
options={allOptions}
selectedOptions={selectedOptions}
onChange={onChange}
singleSelection={true}
singleSelection={{ asPlainText: true }}
isClearable={clearable}
{...rest}
/>
);
}

View file

@ -28,7 +28,14 @@ import createTextHandler from '../lib/create_text_handler';
import createNumberHandler from '../lib/create_number_handler';
import {
htmlIdGenerator,
EuiFlexGroup,
EuiFlexItem,
EuiFormLabel,
EuiComboBox,
EuiSpacer,
EuiFormRow,
EuiCode,
EuiTextArea,
} from '@elastic/eui';
export const MovingAverageAgg = props => {
@ -71,91 +78,109 @@ export const MovingAverageAgg = props => {
onDelete={props.onDelete}
siblings={props.siblings}
>
<div className="vis_editor__row_item">
<div className="vis_editor__agg_row-item">
<div className="vis_editor__row_item">
<div className="vis_editor__label">Aggregation</div>
<AggSelect
panelType={props.panel.type}
siblings={props.siblings}
value={model.type}
onChange={handleSelectChange('type')}
/>
</div>
<div className="vis_editor__row_item">
<div className="vis_editor__label">Metric</div>
<EuiFlexGroup gutterSize="s">
<EuiFlexItem>
<EuiFormLabel htmlFor={htmlId('aggregation')}>Aggregation</EuiFormLabel>
<AggSelect
id={htmlId('aggregation')}
panelType={props.panel.type}
siblings={props.siblings}
value={model.type}
onChange={handleSelectChange('type')}
/>
</EuiFlexItem>
<EuiFlexItem>
<EuiFormRow
id={htmlId('metric')}
label="Metric"
>
<MetricSelect
onChange={handleSelectChange('field')}
metrics={siblings}
metric={model}
value={model.field}
/>
</div>
</div>
<div className="vis_editor__agg_row-item">
<div className="vis_editor__row_item">
<label className="vis_editor__label" htmlFor={htmlId('model')}>Model</label>
</EuiFormRow>
</EuiFlexItem>
</EuiFlexGroup>
<EuiSpacer size="m" />
<EuiFlexGroup gutterSize="s">
<EuiFlexItem>
<EuiFormRow
id={htmlId('model')}
label="Model"
>
<EuiComboBox
isClearable={false}
id={htmlId('model')}
placeholder="Select..."
placeholder="Select"
options={modelOptions}
selectedOptions={selectedModelOption ? [selectedModelOption] : []}
onChange={handleSelectChange('model')}
singleSelection={true}
singleSelection={{ asPlainText: true }}
/>
</div>
<div className="vis_editor__row_item">
<label className="vis_editor__label" htmlFor={htmlId('windowSize')}>
Window Size
</label>
</EuiFormRow>
</EuiFlexItem>
<EuiFlexItem>
<EuiFormRow id={htmlId('windowSize')} label="Window size">
{/*
EUITODO: The following input couldn't be converted to EUI because of type mis-match.
Should it be text or number?
*/}
<input
id={htmlId('windowSize')}
className="vis_editor__input-grows-100"
className="tvbAgg__input"
type="text"
onChange={handleNumberChange('window')}
value={model.window}
/>
</div>
<div className="vis_editor__row_item">
<label className="vis_editor__label" htmlFor={htmlId('minimize')}>Minimize</label>
</EuiFormRow>
</EuiFlexItem>
<EuiFlexItem>
<EuiFormRow id={htmlId('minimize')} label="Minimize">
<EuiComboBox
id={htmlId('minimize')}
placeholder="Select..."
placeholder="Select"
options={minimizeOptions}
selectedOptions={selectedMinimizeOption ? [selectedMinimizeOption] : []}
onChange={handleSelectChange('minimize')}
singleSelection={true}
singleSelection={{ asPlainText: true }}
/>
</div>
<div className="vis_editor__row_item">
<label className="vis_editor__label" htmlFor={htmlId('windowSize')}>
Predict
</label>
</EuiFormRow>
</EuiFlexItem>
<EuiFlexItem>
<EuiFormRow id={htmlId('predict')} label="Predict">
{/*
EUITODO: The following input couldn't be converted to EUI because of type mis-match.
Should it be text or number?
*/}
<input
id={htmlId('predict')}
className="vis_editor__input-grows-100"
className="tvbAgg__input"
type="text"
onChange={handleNumberChange('predict')}
value={model.predict}
/>
</div>
</div>
<div className="vis_editor__agg_row-item">
<div className="vis_editor__row_item">
<label className="vis_editor__label" htmlFor={htmlId('settings')}>
Settings (<code>Key=Value</code> space-delimited)
</label>
<input
id={htmlId('settings')}
className="vis_editor__input-grows-100"
type="text"
onChange={handleTextChange('settings')}
value={model.settings}
/>
</div>
</div>
</div>
</EuiFormRow>
</EuiFlexItem>
</EuiFlexGroup>
<EuiSpacer size="m" />
<EuiFlexItem>
<EuiFormRow
fullWidth
id={htmlId('settings')}
label="Settings"
helpText={
<span><EuiCode>Key=Value</EuiCode> space-delimited</span>
}
>
<EuiTextArea
onChange={handleTextChange('settings')}
value={model.settings}
fullWidth
/>
</EuiFormRow>
</EuiFlexItem>
</AggRow>
);
};

View file

@ -30,8 +30,15 @@ import createChangeHandler from '../lib/create_change_handler';
import createSelectHandler from '../lib/create_select_handler';
import {
htmlIdGenerator,
EuiSpacer,
EuiFlexGroup,
EuiFlexItem,
EuiFormLabel,
EuiComboBox,
EuiFieldNumber,
EuiFormRow,
} from '@elastic/eui';
const newPercentile = (opts) => {
return _.assign({ id: uuid.v1(), mode: 'line', shade: 0.2 }, opts);
};
@ -65,64 +72,74 @@ class Percentiles extends Component {
if (model.mode === 'line') {
optionsStyle.display = 'none';
}
const labelStyle = { marginBottom: 0 };
const htmlId = htmlIdGenerator(model.id);
const selectedModeOption = modeOptions.find(option => {
return model.mode === option.value;
});
return (
<div className="vis_editor__percentiles-row" key={model.id}>
<div className="vis_editor__percentiles-content">
<input
aria-label="Percentile"
placeholder="Percentile"
className="vis_editor__input-grows"
type="number"
step="1"
onChange={this.handleTextChange(model, 'value')}
value={model.value}
/>
<label className="vis_editor__label" htmlFor={htmlId('mode')}>Mode</label>
<div className="vis_editor__row_item">
<EuiFlexItem key={model.id}>
<EuiFlexGroup alignItems="center" responsive={false} gutterSize="s">
<EuiFlexItem grow={false}>
<EuiFieldNumber
aria-label="Percentile"
placeholder="Percentile"
step={1}
onChange={this.handleTextChange(model, 'value')}
value={Number(model.value)}
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiFormLabel style={labelStyle} htmlFor={htmlId('mode')}>Mode:</EuiFormLabel>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiComboBox
isClearable={false}
id={htmlId('mode')}
options={modeOptions}
selectedOptions={selectedModeOption ? [selectedModeOption] : []}
onChange={this.handleTextChange(model, 'mode')}
singleSelection={true}
singleSelection={{ asPlainText: true }}
/>
</div>
<label style={optionsStyle} className="vis_editor__label" htmlFor={htmlId('fillTo')}>
Fill To
</label>
<input
id={htmlId('fillTo')}
style={optionsStyle}
className="vis_editor__input-grows"
type="number"
step="1"
onChange={this.handleTextChange(model, 'percentile')}
value={model.percentile}
/>
<label style={optionsStyle} className="vis_editor__label" htmlFor={htmlId('shade')}>
Shade (0 to 1)
</label>
<input
id={htmlId('shade')}
style={optionsStyle}
className="vis_editor__input-grows"
type="number"
step="0.1"
onChange={this.handleTextChange(model, 'shade')}
value={model.shade}
/>
</div>
<AddDeleteButtons
onAdd={handleAdd}
onDelete={handleDelete}
disableDelete={items.length < 2}
/>
</div>
</EuiFlexItem>
<EuiFlexItem style={optionsStyle} grow={false}>
<EuiFormLabel style={labelStyle} htmlFor={htmlId('fillTo')}>Fill to:</EuiFormLabel>
</EuiFlexItem>
<EuiFlexItem style={optionsStyle} grow={false}>
<EuiFieldNumber
id={htmlId('fillTo')}
step={1}
onChange={this.handleTextChange(model, 'percentile')}
value={Number(model.percentile)}
/>
</EuiFlexItem>
<EuiFlexItem style={optionsStyle} grow={false}>
<EuiFormLabel style={labelStyle} htmlFor={htmlId('shade')}>Shade (0 to 1):</EuiFormLabel>
</EuiFlexItem>
<EuiFlexItem style={optionsStyle} grow={false}>
<EuiFieldNumber
id={htmlId('shade')}
style={optionsStyle}
step={0.1}
onChange={this.handleTextChange(model, 'shade')}
value={Number(model.shade)}
/>
</EuiFlexItem>
<EuiFlexItem>
<AddDeleteButtons
onAdd={handleAdd}
onDelete={handleDelete}
disableDelete={items.length < 2}
responsive={false}
/>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
);
}
@ -132,9 +149,9 @@ class Percentiles extends Component {
const rows = model[name].map(this.renderRow);
return (
<div className="vis_editor__percentiles">
<EuiFlexGroup direction="column" gutterSize="s">
{ rows }
</div>
</EuiFlexGroup>
);
}
}
@ -166,6 +183,7 @@ class PercentileAgg extends Component { // eslint-disable-line react/no-multi-co
const handleChange = createChangeHandler(this.props.onChange, model);
const handleSelectChange = createSelectHandler(handleChange);
const indexPattern = series.override_index_pattern && series.series_index_pattern || panel.index_pattern;
const htmlId = htmlIdGenerator();
return (
<AggRow
@ -175,19 +193,19 @@ class PercentileAgg extends Component { // eslint-disable-line react/no-multi-co
onDelete={this.props.onDelete}
siblings={this.props.siblings}
>
<div className="vis_editor__row_item">
<div className="vis_editor__agg_row-item">
<div className="vis_editor__row_item">
<div className="vis_editor__label">Aggregation</div>
<AggSelect
panelType={this.props.panel.type}
siblings={this.props.siblings}
value={model.type}
onChange={handleSelectChange('type')}
/>
</div>
<div className="vis_editor__row_item">
<div className="vis_editor__label">Field</div>
<EuiFlexGroup gutterSize="s">
<EuiFlexItem>
<EuiFormLabel htmlFor={htmlId('aggregation')}>Aggregation</EuiFormLabel>
<AggSelect
id={htmlId('aggregation')}
panelType={this.props.panel.type}
siblings={this.props.siblings}
value={model.type}
onChange={handleSelectChange('type')}
/>
</EuiFlexItem>
<EuiFlexItem>
<EuiFormRow id={htmlId('field')} label="Field">
<FieldSelect
fields={fields}
type={model.type}
@ -196,14 +214,18 @@ class PercentileAgg extends Component { // eslint-disable-line react/no-multi-co
value={model.field}
onChange={handleSelectChange('field')}
/>
</div>
</div>
<Percentiles
onChange={handleChange}
name="percentiles"
model={model}
/>
</div>
</EuiFormRow>
</EuiFlexItem>
</EuiFlexGroup>
<EuiSpacer size="m" />
<Percentiles
onChange={handleChange}
name="percentiles"
model={model}
/>
</AggRow>
);
}

View file

@ -25,7 +25,14 @@ import AggRow from './agg_row';
import createChangeHandler from '../lib/create_change_handler';
import createSelectHandler from '../lib/create_select_handler';
import createTextHandler from '../lib/create_text_handler';
import { htmlIdGenerator } from '@elastic/eui';
import {
htmlIdGenerator,
EuiFlexGroup,
EuiFlexItem,
EuiFormLabel,
EuiFieldText,
EuiFormRow,
} from '@elastic/eui';
export const PercentileRankAgg = props => {
const { series, panel, fields } = props;
@ -47,36 +54,38 @@ export const PercentileRankAgg = props => {
onDelete={props.onDelete}
siblings={props.siblings}
>
<div className="vis_editor__row_item">
<div className="vis_editor__label">Aggregation</div>
<AggSelect
panelType={props.panel.type}
siblings={props.siblings}
value={model.type}
onChange={handleSelectChange('type')}
/>
</div>
<div className="vis_editor__row_item">
<label className="vis_editor__label" htmlFor={htmlId('field')}>Field</label>
<FieldSelect
id={htmlId('field')}
fields={fields}
type={model.type}
restrict="numeric"
indexPattern={indexPattern}
value={model.field}
onChange={handleSelectChange('field')}
/>
</div>
<div className="vis_editor__percentile_rank_value">
<label className="vis_editor__label" htmlFor={htmlId('value')}>Value</label>
<input
id={htmlId('value')}
className="vis_editor__input-grows"
value={model.value}
onChange={handleTextChange('value')}
/>
</div>
<EuiFlexGroup gutterSize="s">
<EuiFlexItem>
<EuiFormLabel htmlFor={htmlId('aggregation')}>Aggregation</EuiFormLabel>
<AggSelect
id={htmlId('aggregation')}
panelType={props.panel.type}
siblings={props.siblings}
value={model.type}
onChange={handleSelectChange('type')}
/>
</EuiFlexItem>
<EuiFlexItem>
<EuiFormRow id={htmlId('field')} label="Field">
<FieldSelect
fields={fields}
type={model.type}
restrict="numeric"
indexPattern={indexPattern}
value={model.field}
onChange={handleSelectChange('field')}
/>
</EuiFormRow>
</EuiFlexItem>
<EuiFlexItem>
<EuiFormRow id={htmlId('value')} label="Value">
<EuiFieldText
value={model.value}
onChange={handleTextChange('value')}
/>
</EuiFormRow>
</EuiFlexItem>
</EuiFlexGroup>
</AggRow>
);
};

View file

@ -24,6 +24,7 @@ import MetricSelect from './metric_select';
import AggRow from './agg_row';
import createChangeHandler from '../lib/create_change_handler';
import createSelectHandler from '../lib/create_select_handler';
import { htmlIdGenerator, EuiFlexGroup, EuiFlexItem, EuiFormLabel, EuiFormRow } from '@elastic/eui';
export const PositiveOnlyAgg = props => {
const { siblings } = props;
@ -31,6 +32,7 @@ export const PositiveOnlyAgg = props => {
const defaults = { unit: '' };
const model = { ...defaults, ...props.model };
const htmlId = htmlIdGenerator();
const handleChange = createChangeHandler(props.onChange, model);
const handleSelectChange = createSelectHandler(handleChange);
@ -42,24 +44,31 @@ export const PositiveOnlyAgg = props => {
onDelete={props.onDelete}
siblings={props.siblings}
>
<div className="vis_editor__row_item">
<div className="vis_editor__label">Aggregation</div>
<AggSelect
panelType={props.panel.type}
siblings={props.siblings}
value={model.type}
onChange={handleSelectChange('type')}
/>
</div>
<div className="vis_editor__row_item">
<div className="vis_editor__label">Metric</div>
<MetricSelect
onChange={handleSelectChange('field')}
metrics={siblings}
metric={model}
value={model.field}
/>
</div>
<EuiFlexGroup gutterSize="s">
<EuiFlexItem>
<EuiFormLabel htmlFor={htmlId('aggregation')}>Aggregation</EuiFormLabel>
<AggSelect
id={htmlId('aggregation')}
panelType={props.panel.type}
siblings={props.siblings}
value={model.type}
onChange={handleSelectChange('type')}
/>
</EuiFlexItem>
<EuiFlexItem>
<EuiFormRow
id={htmlId('metric')}
label="Metric"
>
<MetricSelect
onChange={handleSelectChange('field')}
metrics={siblings}
metric={model}
value={model.field}
/>
</EuiFormRow>
</EuiFlexItem>
</EuiFlexGroup>
</AggRow>
);
};

View file

@ -25,7 +25,7 @@ import AggRow from './agg_row';
import createChangeHandler from '../lib/create_change_handler';
import createSelectHandler from '../lib/create_select_handler';
import createNumberHandler from '../lib/create_number_handler';
import { htmlIdGenerator } from '@elastic/eui';
import { htmlIdGenerator, EuiFlexGroup, EuiFlexItem, EuiFormLabel, EuiFormRow } from '@elastic/eui';
export const SerialDiffAgg = props => {
const { siblings } = props;
@ -46,34 +46,45 @@ export const SerialDiffAgg = props => {
onDelete={props.onDelete}
siblings={props.siblings}
>
<div className="vis_editor__row_item">
<div className="vis_editor__label">Aggregation</div>
<AggSelect
panelType={props.panel.type}
siblings={props.siblings}
value={model.type}
onChange={handleSelectChange('type')}
/>
</div>
<div className="vis_editor__row_item">
<div className="vis_editor__label">Metric</div>
<MetricSelect
onChange={handleSelectChange('field')}
metrics={siblings}
metric={model}
value={model.field}
/>
</div>
<div>
<label className="vis_editor__label" htmlFor={htmlId('lag')}>Lag</label>
<input
id={htmlId('lag')}
className="vis_editor__input"
onChange={handleNumberChange('lag')}
value={model.lag}
type="text"
/>
</div>
<EuiFlexGroup gutterSize="s">
<EuiFlexItem>
<EuiFormLabel htmlFor={htmlId('aggregation')}>Aggregation</EuiFormLabel>
<AggSelect
id={htmlId('aggregation')}
panelType={props.panel.type}
siblings={props.siblings}
value={model.type}
onChange={handleSelectChange('type')}
/>
</EuiFlexItem>
<EuiFlexItem>
<EuiFormRow
id={htmlId('metric')}
label="Metric"
>
<MetricSelect
onChange={handleSelectChange('field')}
metrics={siblings}
metric={model}
value={model.field}
/>
</EuiFormRow>
</EuiFlexItem>
<EuiFlexItem>
<EuiFormRow id={htmlId('lag')} label="Lag">
{/*
EUITODO: The following input couldn't be converted to EUI because of type mis-match.
Should it be text or number?
*/}
<input
className="tvbAgg__input"
onChange={handleNumberChange('lag')}
value={model.lag}
type="text"
/>
</EuiFormRow>
</EuiFlexItem>
</EuiFlexGroup>
</AggRow>
);
};

View file

@ -25,7 +25,12 @@ import createChangeHandler from '../lib/create_change_handler';
import createSelectHandler from '../lib/create_select_handler';
import {
htmlIdGenerator,
EuiFlexGroup,
EuiFlexItem,
EuiFormLabel,
EuiComboBox,
EuiTitle,
EuiFormRow,
} from '@elastic/eui';
function SeriesAgg(props) {
@ -60,11 +65,9 @@ function SeriesAgg(props) {
onDelete={props.onDelete}
siblings={props.siblings}
>
<div className="vis_editor__item">
<div className="vis_editor__label">
Series Agg is not compatible with the table visualization.
</div>
</div>
<EuiTitle className="tvbAggRow__unavailable" size="xxxs">
<span>Series Agg is not compatible with the table visualization.</span>
</EuiTitle>
</AggRow>
);
}
@ -77,25 +80,28 @@ function SeriesAgg(props) {
onDelete={props.onDelete}
siblings={props.siblings}
>
<div className="vis_editor__item">
<div className="vis_editor__label">Aggregation</div>
<AggSelect
panelType={panel.type}
siblings={props.siblings}
value={model.type}
onChange={handleSelectChange('type')}
/>
</div>
<div className="vis_editor__item">
<label className="vis_editor__label" htmlFor={htmlId('function')}>Function</label>
<EuiComboBox
id={htmlId('function')}
options={functionOptions}
selectedOptions={selectedFunctionOption ? [selectedFunctionOption] : []}
onChange={handleSelectChange('function')}
singleSelection={true}
/>
</div>
<EuiFlexGroup gutterSize="s">
<EuiFlexItem>
<EuiFormLabel htmlFor={htmlId('aggregation')}>Aggregation</EuiFormLabel>
<AggSelect
id={htmlId('aggregation')}
panelType={panel.type}
siblings={props.siblings}
value={model.type}
onChange={handleSelectChange('type')}
/>
</EuiFlexItem>
<EuiFlexItem>
<EuiFormRow id={htmlId('function')} label="Function">
<EuiComboBox
options={functionOptions}
selectedOptions={selectedFunctionOption ? [selectedFunctionOption] : []}
onChange={handleSelectChange('function')}
singleSelection={{ asPlainText: true }}
/>
</EuiFormRow>
</EuiFlexItem>
</EuiFlexGroup>
</AggRow>
);

View file

@ -24,7 +24,14 @@ import AggRow from './agg_row';
import createChangeHandler from '../lib/create_change_handler';
import createSelectHandler from '../lib/create_select_handler';
import createTextHandler from '../lib/create_text_handler';
import { htmlIdGenerator } from '@elastic/eui';
import {
htmlIdGenerator,
EuiFlexGroup,
EuiFlexItem,
EuiFormLabel,
EuiFieldNumber,
EuiFormRow,
} from '@elastic/eui';
export const Static = props => {
const handleChange = createChangeHandler(props.onChange, props.model);
@ -48,32 +55,27 @@ export const Static = props => {
onDelete={props.onDelete}
siblings={props.siblings}
>
<div style={{ flex: '1 0 auto' }}>
<div style={{ flex: '1 0 auto', display: 'flex' }}>
<div className="vis_editor__row_item">
<div className="vis_editor__label">Aggregation</div>
<AggSelect
panelType={props.panel.type}
siblings={props.siblings}
value={model.type}
onChange={handleSelectChange('type')}
/>
</div>
<div className="vis_editor__row_item">
<label className="vis_editor__label" htmlFor={htmlId('staticValue')}>
Static Value
</label>
<input
id={htmlId('staticValue')}
className="vis_editor__input-grows-100"
<EuiFlexGroup gutterSize="s">
<EuiFlexItem>
<EuiFormLabel htmlFor={htmlId('aggregation')}>Aggregation</EuiFormLabel>
<AggSelect
id={htmlId('aggregation')}
panelType={props.panel.type}
siblings={props.siblings}
value={model.type}
onChange={handleSelectChange('type')}
/>
</EuiFlexItem>
<EuiFlexItem>
<EuiFormRow id={htmlId('staticValue')} label="Static value">
<EuiFieldNumber
onChange={handleTextChange('value')}
value={model.value}
step="0.1"
type="number"
value={Number(model.value)}
step={0.1}
/>
</div>
</div>
</div>
</EuiFormRow>
</EuiFlexItem>
</EuiFlexGroup>
</AggRow>
);
};

View file

@ -24,7 +24,7 @@ import FieldSelect from './field_select';
import AggRow from './agg_row';
import createChangeHandler from '../lib/create_change_handler';
import createSelectHandler from '../lib/create_select_handler';
import { htmlIdGenerator } from '@elastic/eui';
import { htmlIdGenerator, EuiFlexGroup, EuiFlexItem, EuiFormRow, EuiFormLabel } from '@elastic/eui';
function StandardAgg(props) {
const { model, panel, series, fields } = props;
@ -47,32 +47,39 @@ function StandardAgg(props) {
onDelete={props.onDelete}
siblings={props.siblings}
>
<div className="vis_editor__item">
<div className="vis_editor__label">Aggregation</div>
<AggSelect
panelType={props.panel.type}
siblings={props.siblings}
value={model.type}
onChange={handleSelectChange('type')}
/>
</div>
{
model.type !== 'count'
? (
<div className="vis_editor__item">
<label className="vis_editor__label" htmlFor={htmlId('field')}>Field</label>
<FieldSelect
id={htmlId('field')}
fields={fields}
type={model.type}
restrict={restrict}
indexPattern={indexPattern}
value={model.field}
onChange={handleSelectChange('field')}
/>
</div>
) : null
}
<EuiFlexGroup gutterSize="s">
<EuiFlexItem>
<EuiFormLabel htmlFor={htmlId('aggregation')}>Aggregation</EuiFormLabel>
<AggSelect
id={htmlId('aggregation')}
panelType={props.panel.type}
siblings={props.siblings}
value={model.type}
onChange={handleSelectChange('type')}
fullWidth
/>
</EuiFlexItem>
{
model.type !== 'count'
? (
<EuiFlexItem>
<EuiFormRow id={htmlId('field')} label="Field" fullWidth>
<FieldSelect
fields={fields}
type={model.type}
restrict={restrict}
indexPattern={indexPattern}
value={model.field}
onChange={handleSelectChange('field')}
fullWidth
/>
</EuiFormRow>
</EuiFlexItem>
) : null
}
</EuiFlexGroup>
</AggRow>
);

View file

@ -27,7 +27,12 @@ import createSelectHandler from '../lib/create_select_handler';
import createTextHandler from '../lib/create_text_handler';
import {
htmlIdGenerator,
EuiFlexGroup,
EuiFlexItem,
EuiFormLabel,
EuiComboBox,
EuiFieldText,
EuiFormRow,
} from '@elastic/eui';
export const StandardDeviationAgg = props => {
@ -63,46 +68,48 @@ export const StandardDeviationAgg = props => {
onDelete={props.onDelete}
siblings={props.siblings}
>
<div className="vis_editor__row_item">
<div className="vis_editor__label">Aggregation</div>
<AggSelect
panelType={props.panel.type}
siblings={props.siblings}
value={model.type}
onChange={handleSelectChange('type')}
/>
</div>
<div className="vis_editor__std_deviation-field">
<label className="vis_editor__label" htmlFor={htmlId('field')}>Field</label>
<FieldSelect
id={htmlId('field')}
fields={fields}
type={model.type}
restrict="numeric"
indexPattern={indexPattern}
value={model.field}
onChange={handleSelectChange('field')}
/>
</div>
<div className="vis_editor__std_deviation-sigma_item">
<label className="vis_editor__label" htmlFor={htmlId('sigma')}>Sigma</label>
<input
id={htmlId('sigma')}
className="vis_editor__std_deviation-sigma"
value={model.sigma}
onChange={handleTextChange('sigma')}
/>
</div>
<div className="vis_editor__row_item">
<label className="vis_editor__label" htmlFor={htmlId('mode')}>Mode</label>
<EuiComboBox
id={htmlId('mode')}
options={modeOptions}
selectedOptions={selectedModeOption ? [selectedModeOption] : []}
onChange={handleSelectChange('mode')}
singleSelection={true}
/>
</div>
<EuiFlexGroup gutterSize="s">
<EuiFlexItem>
<EuiFormLabel htmlFor={htmlId('aggregation')}>Aggregation</EuiFormLabel>
<AggSelect
id={htmlId('aggregation')}
panelType={props.panel.type}
siblings={props.siblings}
value={model.type}
onChange={handleSelectChange('type')}
/>
</EuiFlexItem>
<EuiFlexItem>
<EuiFormRow id={htmlId('field')} label="Field">
<FieldSelect
fields={fields}
type={model.type}
restrict="numeric"
indexPattern={indexPattern}
value={model.field}
onChange={handleSelectChange('field')}
/>
</EuiFormRow>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiFormRow id={htmlId('sigma')} label="Sigma">
<EuiFieldText
value={model.sigma}
onChange={handleTextChange('sigma')}
/>
</EuiFormRow>
</EuiFlexItem>
<EuiFlexItem>
<EuiFormRow id={htmlId('mode')} label="Mode">
<EuiComboBox
options={modeOptions}
selectedOptions={selectedModeOption ? [selectedModeOption] : []}
onChange={handleSelectChange('mode')}
singleSelection={{ asPlainText: true }}
/>
</EuiFormRow>
</EuiFlexItem>
</EuiFlexGroup>
</AggRow>
);
};

View file

@ -27,7 +27,12 @@ import createSelectHandler from '../lib/create_select_handler';
import createTextHandler from '../lib/create_text_handler';
import {
htmlIdGenerator,
EuiFlexGroup,
EuiFlexItem,
EuiFieldText,
EuiComboBox,
EuiFormLabel,
EuiFormRow,
} from '@elastic/eui';
export const StandardSiblingAgg = props => {
@ -43,15 +48,14 @@ export const StandardSiblingAgg = props => {
const stdDev = {};
if (model.type === 'std_deviation_bucket') {
stdDev.sigma = (
<div className="vis_editor__std_deviation-sigma_item">
<label className="vis_editor__label" htmlFor={htmlId('sigma')}>Sigma</label>
<input
id={htmlId('sigma')}
className="vis_editor__std_deviation-sigma"
value={model.sigma}
onChange={handleTextChange('sigma')}
/>
</div>
<EuiFlexItem grow={false}>
<EuiFormRow id={htmlId('sigma')} label="Sigma">
<EuiFieldText
value={model.sigma}
onChange={handleTextChange('sigma')}
/>
</EuiFormRow>
</EuiFlexItem>
);
const modeOptions = [
@ -65,16 +69,16 @@ export const StandardSiblingAgg = props => {
});
stdDev.mode = (
<div className="vis_editor__row_item">
<label className="vis_editor__label" htmlFor={htmlId('mode')}>Mode</label>
<EuiComboBox
id={htmlId('mode')}
options={modeOptions}
selectedOptions={selectedModeOption ? [selectedModeOption] : []}
onChange={handleSelectChange('mode')}
singleSelection={true}
/>
</div>
<EuiFlexItem>
<EuiFormRow id={htmlId('mode')} label="Mode">
<EuiComboBox
options={modeOptions}
selectedOptions={selectedModeOption ? [selectedModeOption] : []}
onChange={handleSelectChange('mode')}
singleSelection={{ asPlainText: true }}
/>
</EuiFormRow>
</EuiFlexItem>
);
}
@ -86,27 +90,34 @@ export const StandardSiblingAgg = props => {
onDelete={props.onDelete}
siblings={props.siblings}
>
<div className="vis_editor__row_item">
<div className="vis_editor__label">Aggregation</div>
<AggSelect
panelType={props.panel.type}
siblings={props.siblings}
value={model.type}
onChange={handleSelectChange('type')}
/>
</div>
<div className="vis_editor__std_sibling-metric">
<div className="vis_editor__label">Metric</div>
<MetricSelect
onChange={handleSelectChange('field')}
exclude={['percentile']}
metrics={siblings}
metric={model}
value={model.field}
/>
</div>
{ stdDev.sigma }
{ stdDev.mode }
<EuiFlexGroup gutterSize="s">
<EuiFlexItem>
<EuiFormLabel htmlFor={htmlId('aggregation')}>Aggregation</EuiFormLabel>
<AggSelect
id={htmlId('aggregation')}
panelType={props.panel.type}
siblings={props.siblings}
value={model.type}
onChange={handleSelectChange('type')}
/>
</EuiFlexItem>
<EuiFlexItem>
<EuiFormRow
id={htmlId('metric')}
label="Metric"
>
<MetricSelect
onChange={handleSelectChange('field')}
exclude={['percentile']}
metrics={siblings}
metric={model}
value={model.field}
/>
</EuiFormRow>
</EuiFlexItem>
{ stdDev.sigma }
{ stdDev.mode }
</EuiFlexGroup>
</AggRow>
);
};

View file

@ -26,7 +26,12 @@ import createSelectHandler from '../lib/create_select_handler';
import createTextHandler from '../lib/create_text_handler';
import {
htmlIdGenerator,
EuiFlexGroup,
EuiFlexItem,
EuiFormLabel,
EuiComboBox,
EuiSpacer,
EuiFormRow,
} from '@elastic/eui';
export const TopHitAgg = props => {
@ -72,23 +77,20 @@ export const TopHitAgg = props => {
onDelete={props.onDelete}
siblings={props.siblings}
>
<div className="vis_editor__row_item">
<div className="vis_editor__agg_row-item">
<div className="vis_editor__row_item">
<div className="vis_editor__label">Aggregation</div>
<AggSelect
panelType={props.panel.type}
siblings={props.siblings}
value={model.type}
onChange={handleSelectChange('type')}
/>
</div>
<div className="vis_editor__row_item">
<label className="vis_editor__label" htmlFor={htmlId('field')}>
Field
</label>
<EuiFlexGroup gutterSize="s">
<EuiFlexItem>
<EuiFormLabel htmlFor={htmlId('aggregation')}>Aggregation</EuiFormLabel>
<AggSelect
id={htmlId('aggregation')}
panelType={props.panel.type}
siblings={props.siblings}
value={model.type}
onChange={handleSelectChange('type')}
/>
</EuiFlexItem>
<EuiFlexItem>
<EuiFormRow id={htmlId('field')} label="Field">
<FieldSelect
id={htmlId('field')}
fields={fields}
type={model.type}
restrict="numeric"
@ -96,63 +98,62 @@ export const TopHitAgg = props => {
value={model.field}
onChange={handleSelectChange('field')}
/>
</div>
</div>
<div className="vis_editor__agg_row-item">
<div className="vis_editor__row_item">
<label className="vis_editor__label" htmlFor={htmlId('size')}>
Size
</label>
</EuiFormRow>
</EuiFlexItem>
</EuiFlexGroup>
<EuiSpacer size="m" />
<EuiFlexGroup gutterSize="s">
<EuiFlexItem grow={false}>
<EuiFormRow id={htmlId('size')} label="Size">
{/*
EUITODO: The following input couldn't be converted to EUI because of type mis-match.
Should it be text or number?
*/}
<input
id={htmlId('size')}
className="vis_editor__input-grows-100"
className="tvbAgg__input"
value={model.size}
onChange={handleTextChange('size')}
/>
</div>
<div className="vis_editor__row_item">
<label className="vis_editor__label" htmlFor={htmlId('agg_with')}>
Aggregate with
</label>
</EuiFormRow>
</EuiFlexItem>
<EuiFlexItem>
<EuiFormRow id={htmlId('agg_with')} label="Aggregate with">
<EuiComboBox
isClearable={false}
id={htmlId('agg_with')}
placeholder="Select..."
placeholder="Select"
options={aggWithOptions}
selectedOptions={selectedAggWithOption ? [selectedAggWithOption] : []}
onChange={handleSelectChange('agg_with')}
singleSelection={true}
singleSelection={{ asPlainText: true }}
/>
</div>
<div className="vis_editor__row_item">
<label className="vis_editor__label" htmlFor={htmlId('order_by')}>
Order by
</label>
</EuiFormRow>
</EuiFlexItem>
<EuiFlexItem>
<EuiFormRow id={htmlId('order_by')} label="Order by">
<FieldSelect
id={htmlId('order_by')}
restrict="date"
value={model.order_by}
onChange={handleSelectChange('order_by')}
indexPattern={indexPattern}
fields={fields}
/>
</div>
<div className="vis_editor__row_item">
<label className="vis_editor__label" htmlFor={htmlId('order')}>
Order
</label>
</EuiFormRow>
</EuiFlexItem>
<EuiFlexItem>
<EuiFormRow id={htmlId('order')} label="Order">
<EuiComboBox
isClearable={false}
id={htmlId('order')}
placeholder="Select..."
options={orderOptions}
selectedOptions={selectedOrderOption ? [selectedOrderOption] : []}
onChange={handleSelectChange('order')}
singleSelection={true}
singleSelection={{ asPlainText: true }}
/>
</div>
</div>
</div>
</EuiFormRow>
</EuiFlexItem>
</EuiFlexGroup>
</AggRow>
);
};

View file

@ -19,6 +19,8 @@
import AggRow from './agg_row';
import React from 'react';
import { EuiCode, EuiTitle } from '@elastic/eui';
export function UnsupportedAgg(props) {
return (
<AggRow
@ -28,12 +30,9 @@ export function UnsupportedAgg(props) {
onDelete={props.onDelete}
siblings={props.siblings}
>
<div className="vis_editor__row_item">
<p>
The <code>{props.model.type}</code> aggregation is no longer
supported.
</p>
</div>
<EuiTitle className="tvbAggRow__unavailable" size="xxxs">
<span>The <EuiCode>{props.model.type}</EuiCode> aggregation is no longer supported.</span>
</EuiTitle>
</AggRow>
);
}

View file

@ -23,6 +23,7 @@ import _ from 'lodash';
import AddDeleteButtons from '../add_delete_buttons';
import * as collectionActions from '../lib/collection_actions';
import MetricSelect from './metric_select';
import { EuiFlexGroup, EuiFlexItem, EuiFieldText } from '@elastic/eui';
class CalculationVars extends Component {
@ -44,34 +45,36 @@ class CalculationVars extends Component {
const handleAdd = collectionActions.handleAdd.bind(null, this.props);
const handleDelete = collectionActions.handleDelete.bind(null, this.props, row);
return (
<div className="vis_editor__calc_vars-row" key={row.id} data-test-subj="varRow">
<div className="vis_editor__calc_vars-name">
<input
aria-label="Variable name"
placeholder="Variable Name"
className="vis_editor__input-grows-100"
type="text"
onChange={this.handleChange(row, 'name')}
value={row.name}
/>
</div>
<div className="vis_editor__calc_vars-var">
<MetricSelect
onChange={this.handleChange(row, 'field')}
metrics={this.props.metrics}
metric={this.props.model}
value={row.field}
includeSiblings={this.props.includeSiblings}
/>
</div>
<div className="vis_editor__calc_vars-control">
<AddDeleteButtons
onAdd={handleAdd}
onDelete={handleDelete}
disableDelete={items.length < 2}
/>
</div>
</div>
<EuiFlexItem key={row.id} data-test-subj="varRow">
<EuiFlexGroup alignItems="center" responsive={false} gutterSize="s">
<EuiFlexItem>
<EuiFieldText
className="tvbAggs__varName"
aria-label="Variable name"
placeholder="Variable name"
onChange={this.handleChange(row, 'name')}
value={row.name}
/>
</EuiFlexItem>
<EuiFlexItem className="tvbAggs__varMetricWrapper">
<MetricSelect
onChange={this.handleChange(row, 'field')}
metrics={this.props.metrics}
metric={this.props.model}
value={row.field}
includeSiblings={this.props.includeSiblings}
/>
</EuiFlexItem>
<EuiFlexItem>
<AddDeleteButtons
onAdd={handleAdd}
onDelete={handleDelete}
disableDelete={items.length < 2}
responsive={false}
/>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
);
}
@ -80,9 +83,9 @@ class CalculationVars extends Component {
if (!model[name]) return (<div/>);
const rows = model[name].map(this.renderRow);
return (
<div className="vis_editor__calc_vars">
<EuiFlexGroup direction="column" gutterSize="s">
{ rows }
</div>
</EuiFlexGroup>
);
}

View file

@ -30,6 +30,15 @@ import YesNo from './yes_no';
import {
htmlIdGenerator,
EuiFlexGroup,
EuiFlexItem,
EuiFormRow,
EuiFormLabel,
EuiSpacer,
EuiFieldText,
EuiTitle,
EuiButton,
EuiCode,
EuiText,
} from '@elastic/eui';
@ -74,118 +83,133 @@ class AnnotationsEditor extends Component {
const handleDelete = collectionActions.handleDelete
.bind(null, this.props, model);
return (
<div className="vis_editor__annotations-row" key={model.id}>
<div className="vis_editor__annotations-color">
<ColorPicker
disableTrash={true}
onChange={handleChange}
name="color"
value={model.color}
/>
</div>
<div className="vis_editor__annotations-content">
<div className="vis_editor__row">
<div className="vis_editor__row-item">
<label className="vis_editor__label" htmlFor={htmlId('indexPattern')}>
Index Pattern (required)
</label>
<input
id={htmlId('indexPattern')}
className="vis_editor__input-grows-100"
type="text"
onChange={this.handleChange(model, 'index_pattern')}
value={model.index_pattern}
/>
</div>
<div className="vis_editor__row-item">
<label className="vis_editor__label" htmlFor={htmlId('timeField')}>
Time Field (required)
</label>
<FieldSelect
id={htmlId('timeField')}
restrict="date"
value={model.time_field}
onChange={this.handleChange(model, 'time_field')}
indexPattern={model.index_pattern}
fields={this.props.fields}
/>
</div>
</div>
<div className="vis_editor__row">
<div className="vis_editor__row-item">
<label className="vis_editor__label" htmlFor={htmlId('queryString')}>
Query String
</label>
<input
id={htmlId('queryString')}
className="vis_editor__input-grows-100"
type="text"
onChange={this.handleChange(model, 'query_string')}
value={model.query_string}
/>
</div>
<fieldset className="vis_editor__row-item-small">
<legend className="vis_editor__label">Ignore Global Filters</legend>
<YesNo
value={model.ignore_global_filters}
name="ignore_global_filters"
onChange={handleChange}
/>
<div className="tvbAnnotationsEditor" key={model.id}>
<EuiFlexGroup responsive={false}>
<EuiFlexItem grow={false}>
<ColorPicker
disableTrash={true}
onChange={handleChange}
name="color"
value={model.color}
/>
</EuiFlexItem>
</fieldset>
<fieldset className="vis_editor__row-item-small">
<legend className="vis_editor__label">Ignore Panel Filters</legend>
<YesNo
value={model.ignore_panel_filters}
name="ignore_panel_filters"
onChange={handleChange}
/>
<EuiFlexItem className="tvbAggRow__children">
<EuiFlexGroup responsive={false} wrap={true} gutterSize="m">
<EuiFlexItem>
<EuiFormRow
id={htmlId('indexPattern')}
label="Index pattern (required)"
fullWidth
>
<EuiFieldText
onChange={this.handleChange(model, 'index_pattern')}
value={model.index_pattern}
fullWidth
/>
</EuiFormRow>
</EuiFlexItem>
<EuiFlexItem>
<EuiFormRow id={htmlId('timeField')} label="Time field (required)" fullWidth>
<FieldSelect
restrict="date"
value={model.time_field}
onChange={this.handleChange(model, 'time_field')}
indexPattern={model.index_pattern}
fields={this.props.fields}
fullWidth
/>
</EuiFormRow>
</EuiFlexItem>
</EuiFlexGroup>
</fieldset>
</div>
<div className="vis_editor__row">
<div className="vis_editor__row-item">
<label className="vis_editor__label" htmlFor={htmlId('icon')}>Icon (required)</label>
<div className="vis_editor__item">
<IconSelect
id={htmlId('icon')}
value={model.icon}
onChange={this.handleChange(model, 'icon')}
<EuiSpacer size="m" />
<EuiFlexGroup responsive={false} wrap={true} gutterSize="m">
<EuiFlexItem>
<EuiFormRow
id={htmlId('queryString')}
label="Query string"
fullWidth
>
<EuiFieldText
onChange={this.handleChange(model, 'query_string')}
value={model.query_string}
fullWidth
/>
</EuiFormRow>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiFormLabel>Ignore global filters?</EuiFormLabel>
<EuiSpacer size="s" />
<YesNo
value={model.ignore_global_filters}
name="ignore_global_filters"
onChange={handleChange}
/>
</div>
</div>
<div className="vis_editor__row-item">
<label className="vis_editor__label" htmlFor={htmlId('fields')}>
Fields (required - comma separated paths)
</label>
<input
id={htmlId('fields')}
className="vis_editor__input-grows-100"
type="text"
onChange={this.handleChange(model, 'fields')}
value={model.fields}
/>
</div>
<div className="vis_editor__row-item">
<label className="vis_editor__label" htmlFor={htmlId('rowTemplate')}>
Row Template (required - eg.<code>{'{{field}}'}</code>)
</label>
<input
id={htmlId('rowTemplate')}
className="vis_editor__input-grows-100"
type="text"
onChange={this.handleChange(model, 'template')}
value={model.template}
/>
</div>
</div>
</div>
<div className="vis_editor__annotations-controls">
<AddDeleteButtons
onAdd={handleAdd}
onDelete={handleDelete}
/>
</div>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiFormLabel>Ignore panel filters?</EuiFormLabel>
<EuiSpacer size="s" />
<YesNo
value={model.ignore_panel_filters}
name="ignore_panel_filters"
onChange={handleChange}
/>
</EuiFlexItem>
</EuiFlexGroup>
<EuiSpacer size="m" />
<EuiFlexGroup responsive={false} wrap={true} gutterSize="m">
<EuiFlexItem>
<EuiFormRow id={htmlId('icon')} label="Icon (required)">
<IconSelect
value={model.icon}
onChange={this.handleChange(model, 'icon')}
/>
</EuiFormRow>
</EuiFlexItem>
<EuiFlexItem>
<EuiFormRow
id={htmlId('fields')}
label="Fields (required - comma separated paths)"
fullWidth
>
<EuiFieldText
onChange={this.handleChange(model, 'fields')}
value={model.fields}
fullWidth
/>
</EuiFormRow>
</EuiFlexItem>
<EuiFlexItem>
<EuiFormRow
id={htmlId('rowTemplate')}
label="Row template (required)"
helpText={
<span>eg. <EuiCode>{'{{field}}'}</EuiCode></span>
}
fullWidth
>
<EuiFieldText
onChange={this.handleChange(model, 'template')}
value={model.template}
fullWidth
/>
</EuiFormRow>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<AddDeleteButtons
onAdd={handleAdd}
onDelete={handleDelete}
/>
</EuiFlexItem>
</EuiFlexGroup>
</div>
);
}
@ -197,36 +221,26 @@ class AnnotationsEditor extends Component {
const handleAdd = collectionActions.handleAdd
.bind(null, this.props, newAnnotation);
content = (
<div className="vis_editor__annotations-missing">
<EuiText>
<p>Click the button below to create an annotation data source.</p>
<button
className="thor__button-outlined-default large"
onClick={handleAdd}
>Add Data Source
</button>
</EuiText>
</div>
<EuiText textAlign="center">
<p>Click the button below to create an annotation data source.</p>
<EuiButton fill onClick={handleAdd}>Add data source</EuiButton>
</EuiText>
);
} else {
const annotations = model.annotations.map(this.renderRow);
content = (
<div className="vis_editor__annotations">
<div className="kbnTabs sm" role="tablist">
<button
role="tab"
aria-selected={true}
className="kbnTabs__tab-active"
>
Data Sources
</button>
</div>
<div>
<EuiTitle size="s">
<span>Data sources</span>
</EuiTitle>
<EuiSpacer size="m" />
{ annotations }
</div>
);
}
return(
<div className="vis_editor__container">
<div className="tvbAnnotationsEditor__container">
{ content }
</div>
);

View file

@ -22,7 +22,7 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { EuiToolTip, } from '@elastic/eui';
import { EuiIconTip, } from '@elastic/eui';
import Picker from './custom_color_picker';
class ColorPicker extends Component {
@ -66,7 +66,7 @@ class ColorPicker extends Component {
return (
<button
aria-label="Color picker, not accessible"
className="vis_editor__color_picker-swatch-empty"
className="tvbColorPicker__swatch-empty"
onClick={this.handleClick}
/>
);
@ -75,7 +75,7 @@ class ColorPicker extends Component {
<button
aria-label={`Color picker ({this.props.value}), not accessible`}
style={{ backgroundColor: this.props.value }}
className="vis_editor__color_picker-swatch"
className="tvbColorPicker__swatch"
onClick={this.handleClick}
/>
);
@ -87,23 +87,21 @@ class ColorPicker extends Component {
let clear;
if (!this.props.disableTrash) {
clear = (
<div className="vis_editor__color_picker-clear" onClick={this.handleClear}>
<EuiToolTip content="Clear">
<i className="fa fa-ban"/>
</EuiToolTip>
<div className="tvbColorPicker__clear" onClick={this.handleClear}>
<EuiIconTip size="s" type="cross" color="danger" content="Clear" />
</div>
);
}
return (
<div className="vis_editor__color_picker">
<div className="tvbColorPicker">
{ swatch }
{ clear }
{
this.state.displayPicker
? (
<div className="vis_editor__color_picker-popover">
<div className="tvbColorPicker__popover">
<div
className="vis_editor__color_picker-cover"
className="tvbColorPicker__cover"
onClick={this.handleClose}
/>
<Picker

View file

@ -18,7 +18,7 @@
*/
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import React, { Component, Fragment } from 'react';
import _ from 'lodash';
import AddDeleteButtons from './add_delete_buttons';
import * as collectionActions from './lib/collection_actions';
@ -26,6 +26,10 @@ import ColorPicker from './color_picker';
import {
htmlIdGenerator,
EuiComboBox,
EuiFieldNumber,
EuiFormLabel,
EuiFlexGroup,
EuiFlexItem,
} from '@elastic/eui';
class ColorRules extends Component {
@ -66,55 +70,72 @@ class ColorRules extends Component {
return model.operator === option.value;
});
const labelStyle = { marginBottom: 0 };
let secondary;
if (!this.props.hideSecondary) {
secondary = (
<div className="color_rules__secondary">
<div className="color_rules__label">and {this.props.secondaryName} to</div>
<ColorPicker
onChange={handleColorChange}
name={this.props.secondaryVarName}
value={model[this.props.secondaryVarName]}
/>
</div>
<Fragment>
<EuiFlexItem grow={false}>
<EuiFormLabel style={labelStyle}>and {this.props.secondaryName} to</EuiFormLabel>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<ColorPicker
onChange={handleColorChange}
name={this.props.secondaryVarName}
value={model[this.props.secondaryVarName]}
/>
</EuiFlexItem>
</Fragment>
);
}
return (
<div key={model.id} className="color_rules__rule">
<div className="color_rules__label">Set {this.props.primaryName} to</div>
<ColorPicker
onChange={handleColorChange}
name={this.props.primaryVarName}
value={model[this.props.primaryVarName]}
/>
<EuiFlexGroup wrap={true} responsive={false} gutterSize="s" key={model.id} alignItems="center" className="tvbColorRules__rule">
<EuiFlexItem grow={false}>
<EuiFormLabel style={labelStyle}>Set {this.props.primaryName} to</EuiFormLabel>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<ColorPicker
onChange={handleColorChange}
name={this.props.primaryVarName}
value={model[this.props.primaryVarName]}
/>
</EuiFlexItem>
{ secondary }
<label className="color_rules__label" htmlFor={htmlId('ifMetricIs')}>
if metric is
</label>
<div className="color_rules__item">
<EuiFlexItem grow={false}>
<EuiFormLabel style={labelStyle} htmlFor={htmlId('ifMetricIs')}>if metric is</EuiFormLabel>
</EuiFlexItem>
<EuiFlexItem>
<EuiComboBox
id={htmlId('ifMetricIs')}
options={operatorOptions}
selectedOptions={selectedOperatorOption ? [selectedOperatorOption] : []}
onChange={this.handleChange(model, 'operator')}
singleSelection={true}
fullWidth
/>
</div>
<input
aria-label="Value"
className="color_rules__input"
type="number"
value={model.value}
onChange={this.handleChange(model, 'value', Number)}
/>
<div className="color_rules__control">
</EuiFlexItem>
<EuiFlexItem>
<EuiFieldNumber
aria-label="Value"
value={model.value}
onChange={this.handleChange(model, 'value', Number)}
fullWidth
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<AddDeleteButtons
onAdd={handleAdd}
onDelete={handleDelete}
disableDelete={items.length < 2}
responsive={false}
/>
</div>
</div>
</EuiFlexItem>
</EuiFlexGroup>
);
}
@ -123,7 +144,7 @@ class ColorRules extends Component {
if (!model[name]) return (<div/>);
const rows = model[name].map(this.renderRow);
return (
<div className="color_rules">
<div>
{ rows }
</div>
);

View file

@ -82,8 +82,8 @@ export class CustomColorPicker extends Component {
});
return (
<div className="custom-picker color_picker">
<div className="color_picker__saturation">
<div className="tvbColorPickerPopUp">
<div className="tvbColorPickerPopUp__saturation">
<Saturation
style={styles.Saturation}
{...this.props}
@ -91,16 +91,16 @@ export class CustomColorPicker extends Component {
onChange={this.handleChange}
/>
</div>
<div className="color_picker__body">
<div className="color_picker__controls flexbox-fix">
<div className={this.props.disableAlpha ? 'color_picker__color-disable_alpha' : 'color_picker__color'}>
<div className={this.props.disableAlpha ? 'color_picker__swatch-disable_alpha' : 'color_picker__swatch'}>
<div className="color_picker__active" />
<div className="tvbColorPickerPopUp__body">
<div className="tvbColorPickerPopUp__controls">
<div className={this.props.disableAlpha ? 'tvbColorPickerPopUp__color-disableAlpha' : 'tvbColorPickerPopUp__color'}>
<div className={this.props.disableAlpha ? 'tvbColorPickerPopUp__swatch-disableAlpha' : 'tvbColorPickerPopUp__swatch'}>
<div className="tvbColorPickerPopUp__active" />
<Checkboard />
</div>
</div>
<div className="color_picker__toggles">
<div className={this.props.disableAlpha ? 'color_picker__hue-disable_alpha' : 'color_picker__hue'}>
<div className="tvbColorPickerPopUp__toggles">
<div className={this.props.disableAlpha ? 'tvbColorPickerPopUp__hue-disableAlpha' : 'tvbColorPickerPopUp__hue'}>
<Hue
style={styles.Hue}
{...this.props}
@ -108,7 +108,7 @@ export class CustomColorPicker extends Component {
onChange={this.handleChange}
/>
</div>
<div className={this.props.disableAlpha ? 'color_picker__alpha-disable_alpha' : 'color_picker__alpha'}>
<div className={this.props.disableAlpha ? 'tvbColorPickerPopUp__alpha-disableAlpha' : 'tvbColorPickerPopUp__alpha'}>
<Alpha
style={styles.Alpha}
{...this.props}
@ -123,7 +123,7 @@ export class CustomColorPicker extends Component {
onChange={this.handleChange}
disableAlpha={this.props.disableAlpha}
/>
<div className="color_picker__swatches flexbox-fix">
<div className="tvbColorPickerPopUp__swatches">
{swatches}
</div>
</div>

View file

@ -21,7 +21,7 @@ import PropTypes from 'prop-types';
import React, { Component } from 'react';
import _ from 'lodash';
import {
EuiComboBox,
htmlIdGenerator, EuiComboBox, EuiFlexGroup, EuiFlexItem, EuiFormRow, EuiFieldText, EuiLink,
} from '@elastic/eui';
import { durationOutputOptions, durationInputOptions } from './lib/durations';
const durationFormatTest = /[pnumshdwMY]+,[pnumshdwMY]+/;
@ -91,6 +91,7 @@ class DataFormatPicker extends Component {
}
render() {
const htmlId = htmlIdGenerator();
const value = this.props.value || '';
let defaultValue = value;
if (!_.includes(['bytes', 'number', 'percent'], value)) {
@ -120,11 +121,74 @@ class DataFormatPicker extends Component {
return to === option.value;
});
return (
<div className="vis_editor__data_format_picker-container">
<div className="vis_editor__label">
{this.props.label}
</div>
<div className="vis_editor__item">
<EuiFlexGroup responsive={false} gutterSize="s">
<EuiFlexItem grow={false}>
<EuiFormRow id={htmlId('date')} label={this.props.label}>
<EuiComboBox
isClearable={false}
options={options}
selectedOptions={selectedOption ? [selectedOption] : []}
onChange={this.handleChange}
singleSelection={true}
/>
</EuiFormRow>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiFormRow id={htmlId('from')} label="From">
<EuiComboBox
isClearable={false}
options={durationInputOptions}
selectedOptions={selectedFrom ? [selectedFrom] : []}
onChange={this.handleDurationChange('from')}
singleSelection={true}
/>
</EuiFormRow>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiFormRow id={htmlId('to')} label="To">
<EuiComboBox
isClearable={false}
options={durationOutputOptions}
selectedOptions={selectedTo ? [selectedTo] : []}
onChange={this.handleDurationChange('to')}
singleSelection={true}
/>
</EuiFormRow>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiFormRow id={htmlId('decimal')} label="Decimal places">
<EuiFieldText
defaultValue={decimals}
inputRef={(el) => this.decimals = el}
onChange={this.handleDurationChange('decimals')}
/>
</EuiFormRow>
</EuiFlexItem>
</EuiFlexGroup>
);
}
if (defaultValue === 'custom') {
custom = (
<EuiFlexItem grow={false}>
<EuiFormRow
label="Format string"
helpText={
<span>See <EuiLink href="http://numeraljs.com/#format" target="_BLANK">Numeral.js</EuiLink></span>
}
>
<EuiFieldText
defaultValue={value}
inputRef={(el) => this.custom = el}
onChange={this.handleCustomChange}
/>
</EuiFormRow>
</EuiFlexItem>
);
}
return (
<EuiFlexGroup responsive={false} gutterSize="s">
<EuiFlexItem grow={false}>
<EuiFormRow label={this.props.label}>
<EuiComboBox
isClearable={false}
options={options}
@ -132,72 +196,10 @@ class DataFormatPicker extends Component {
onChange={this.handleChange}
singleSelection={true}
/>
</div>
<div className="vis_editor__label">From</div>
<div className="vis_editor__item">
<EuiComboBox
isClearable={false}
options={durationInputOptions}
selectedOptions={selectedFrom ? [selectedFrom] : []}
onChange={this.handleDurationChange('from')}
singleSelection={true}
/>
</div>
<div className="vis_editor__label">To</div>
<div className="vis_editor__item">
<EuiComboBox
isClearable={false}
options={durationOutputOptions}
selectedOptions={selectedTo ? [selectedTo] : []}
onChange={this.handleDurationChange('to')}
singleSelection={true}
/>
</div>
<div className="vis_editor__label">Decimal Places</div>
<input
style={{ width: 60 }}
className="vis_editor__input"
defaultValue={decimals}
ref={(el) => this.decimals = el}
onChange={this.handleDurationChange('decimals')}
type="text"
/>
</div>
);
}
if (defaultValue === 'custom') {
custom = (
<div className="vis_editor__data_format_picker-custom_row">
<div className="vis_editor__label">
Format String (See <a href="http://numeraljs.com/#format" target="_BLANK">Numeral.js</a>)
</div>
<input
style={{ width: 100 }}
className="vis_editor__input"
defaultValue={value}
ref={(el) => this.custom = el}
onChange={this.handleCustomChange}
type="text"
/>
</div>
);
}
return (
<div className="vis_editor__data_format_picker-container">
<div className="vis_editor__label">
{this.props.label}
</div>
<div className="vis_editor__item">
<EuiComboBox
isClearable={false}
options={options}
selectedOptions={selectedOption ? [selectedOption] : []}
onChange={this.handleChange}
singleSelection={true}
/>
</div>
</EuiFormRow>
</EuiFlexItem>
{custom}
</div>
</EuiFlexGroup>
);
}

View file

@ -36,22 +36,22 @@ function ErrorComponent(props) {
const scriptStack = _.get(error, 'error.caused_by.script_stack');
reason = _.get(error, 'error.caused_by.caused_by.reason');
additionalInfo = (
<div className="metrics_error__additional">
<div className="metrics_error__reason">{reason}</div>
<div className="metrics_error__stack">{scriptStack.join('\n')}</div>
<div className="tvbError__additional">
<div className="tvbError__reason">{reason}</div>
<div className="tvbError__stack">{scriptStack.join('\n')}</div>
</div>
);
} else if (reason) {
additionalInfo = (
<div className="metrics_error__additional">
<div className="metrics_error__reason">{reason}</div>
<div className="tvbError__additional">
<div className="tvbError__reason">{reason}</div>
</div>
);
}
return (
<div className="metrics_error">
<div className="metrics_error__title">{title || 'The request for this panel failed.'}</div>
<div className="tvbError">
<div className="tvbError__title">{title || 'The request for this panel failed'}</div>
{additionalInfo}
</div>
);

View file

@ -27,15 +27,10 @@ function renderOption(option) {
const icon = option.value;
const label = option.label;
return (
<div className="Select-value">
<span
className="Select-value-label"
aria-label={`${label} icon`}
>
<span className={`vis_editor__icon_select-value kuiIcon ${icon}`} />
{ label }
</span>
</div>
<span>
<span className={`kuiIcon ${icon}`} aria-hidden="true" />
{` ${label}`}
</span>
);
}

View file

@ -23,7 +23,15 @@ import FieldSelect from './aggs/field_select';
import createSelectHandler from './lib/create_select_handler';
import createTextHandler from './lib/create_text_handler';
import YesNo from './yes_no';
import { htmlIdGenerator } from '@elastic/eui';
import {
htmlIdGenerator,
EuiFieldText,
EuiFlexGroup,
EuiFlexItem,
EuiFormRow,
EuiFormLabel,
EuiSpacer,
} from '@elastic/eui';
export const IndexPattern = props => {
const { fields, prefix } = props;
@ -45,48 +53,63 @@ export const IndexPattern = props => {
const model = { ...defaults, ...props.model };
return (
<div className={props.className}>
<label className="vis_editor__label" htmlFor={htmlId('indexPattern')}>
Index Pattern
</label>
<input
id={htmlId('indexPattern')}
className="vis_editor__input"
disabled={props.disabled}
onChange={handleTextChange(indexPatternName, '*')}
value={model[indexPatternName]}
data-test-subj="metricsIndexPatternInput"
/>
<label className="vis_editor__label" htmlFor={htmlId('timeField')}>
Time Field
</label>
<div className="vis_editor__index_pattern-fields">
<FieldSelect
id={htmlId('timeField')}
restrict="date"
value={model[timeFieldName]}
disabled={props.disabled}
onChange={handleSelectChange(timeFieldName)}
indexPattern={model[indexPatternName]}
fields={fields}
data-test-subj="metricsIndexPatternFieldsSelect"
/>
</div>
<label className="vis_editor__label" htmlFor={htmlId('interval')}>
Interval (auto, 1m, 1d, 7d, 1y, &gt;=1m)
</label>
<input
id={htmlId('interval')}
className="vis_editor__input"
disabled={props.disabled}
onChange={handleTextChange(intervalName, 'auto')}
value={model[intervalName]}
/>
<div className="vis_editor__label">Drop Last Bucket</div>
<YesNo
value={model[dropBucketName]}
name={dropBucketName}
onChange={props.onChange}
/>
<EuiFlexGroup responsive={false} wrap={true}>
<EuiFlexItem>
<EuiFormRow
id={htmlId('indexPattern')}
label="Index pattern"
fullWidth
>
<EuiFieldText
data-test-subj="metricsIndexPatternInput"
disabled={props.disabled}
onChange={handleTextChange(indexPatternName, '*')}
value={model[indexPatternName]}
fullWidth
/>
</EuiFormRow>
</EuiFlexItem>
<EuiFlexItem>
<EuiFormRow
id={htmlId('timeField')}
label="Time field"
fullWidth
>
<FieldSelect
data-test-subj="metricsIndexPatternFieldsSelect"
restrict="date"
value={model[timeFieldName]}
disabled={props.disabled}
onChange={handleSelectChange(timeFieldName)}
indexPattern={model[indexPatternName]}
fields={fields}
fullWidth
/>
</EuiFormRow>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiFormRow
id={htmlId('interval')}
label="Interval"
helpText="Examples: auto, 1m, 1d, 7d, 1y, >=1m"
>
<EuiFieldText
disabled={props.disabled}
onChange={handleTextChange(intervalName, 'auto')}
value={model[intervalName]}
/>
</EuiFormRow>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiFormLabel>Drop last bucket?</EuiFormLabel>
<EuiSpacer size="s" />
<YesNo
value={model[dropBucketName]}
name={dropBucketName}
onChange={props.onChange}
/>
</EuiFlexItem>
</EuiFlexGroup>
</div>
);
};
@ -94,7 +117,6 @@ export const IndexPattern = props => {
IndexPattern.defaultProps = {
prefix: '',
disabled: false,
className: 'vis_editor__row'
};
IndexPattern.propTypes = {

View file

@ -34,6 +34,8 @@ import 'brace/theme/github';
import {
EuiText,
EuiCodeBlock,
EuiSpacer,
EuiTitle,
} from '@elastic/eui';
class MarkdownEditor extends Component {
@ -131,8 +133,8 @@ class MarkdownEditor extends Component {
walk(variables);
return (
<div className="vis_editor__markdown">
<div className="vis_editor__markdown-editor">
<div className="tvbMarkdownEditor">
<div className="tvbMarkdownEditor__editor">
<KuiCodeEditor
onLoad={this.handleOnLoad}
mode="markdown"
@ -145,13 +147,15 @@ class MarkdownEditor extends Component {
onChange={this.handleChange}
/>
</div>
<div className="vis_editor__markdown-variables">
<div className="tvbMarkdownEditor__variables">
<EuiText>
The following variables can be used in the Markdown by using the Handlebar (mustache) syntax.{' '}
<a href="http://handlebarsjs.com/expressions.html" target="_BLANK">
Click here for documentation
</a>{' '}
on the available expressions.
<p>
The following variables can be used in the Markdown by using the Handlebar (mustache) syntax.{' '}
<a href="http://handlebarsjs.com/expressions.html" target="_BLANK">
Click here for documentation
</a>{' '}
on the available expressions.
</p>
</EuiText>
<table className="table">
<thead>
@ -162,25 +166,30 @@ class MarkdownEditor extends Component {
</thead>
<tbody>{rows}</tbody>
</table>
{rows.length === 0 && (
<div className="vis_editor__no-markdown-variables">No variables available for the selected data metrics.</div>
<EuiTitle size="xxs" className="tvbMarkdownEditor__noVariables">
<span>No variables available for the selected data metrics.</span>
</EuiTitle>
)}
<div className="vis_editor__markdown-code-desc">
<EuiText>
<p>
There is also a special variable named <code>_all</code> which you can use to access the entire tree. This is useful for
creating lists with data from a group by...
</p>
</EuiText>
</div>
<EuiSpacer />
<EuiText>
<p>
There is also a special variable named <code>_all</code> which you can use to access the entire tree. This is useful for
creating lists with data from a group by...
</p>
</EuiText>
<EuiSpacer />
<EuiCodeBlock>
{`# All servers:
{{#each _all}}
- {{ label }} {{ last.formatted }}
{{/each}}`}
{{#each _all}}
- {{ label }} {{ last.formatted }}
{{/each}}`}
</EuiCodeBlock>
</div>
</div>

View file

@ -21,8 +21,8 @@ import React from 'react';
function NoDataComponent() {
return (
<div className="metrics_issue" data-test-subj="noTSVBDataMessage">
<div className="metrics_issue__title">No data to display for the selected metrics .</div>
<div className="tvbNoData" data-test-subj="noTSVBDataMessage">
<div className="tvbNoData__title">No data to display for the selected metrics</div>
</div>
);
}

View file

@ -0,0 +1 @@
@import './panel_config';

View file

@ -0,0 +1,4 @@
.tvbPanelConfig__container {
padding: $euiSize;
background-color: $euiColorLightestShade;
}

View file

@ -30,6 +30,18 @@ import YesNo from '../yes_no';
import {
htmlIdGenerator,
EuiComboBox,
EuiTabs,
EuiTab,
EuiPanel,
EuiFlexGroup,
EuiFlexItem,
EuiFormRow,
EuiFormLabel,
EuiSpacer,
EuiFieldText,
EuiFieldNumber,
EuiTitle,
EuiHorizontalRule,
} from '@elastic/eui';
class GaugePanelConfig extends Component {
@ -90,92 +102,137 @@ class GaugePanelConfig extends Component {
);
} else {
view = (
<div className="vis_editor__container">
<IndexPattern
fields={this.props.fields}
model={this.props.model}
onChange={this.props.onChange}
/>
<div className="vis_editor__vis_config-row">
<label className="vis_editor__label" htmlFor={htmlId('panelFilter')}>
Panel Filter
</label>
<input
id={htmlId('panelFilter')}
className="vis_editor__input-grows"
type="text"
onChange={handleTextChange('filter')}
value={model.filter}
/>
<div className="vis_editor__label">Ignore Global Filter</div>
<YesNo
value={model.ignore_global_filter}
name="ignore_global_filter"
<div className="tvbPanelConfig__container">
<EuiPanel>
<EuiTitle size="s"><span>Data</span></EuiTitle>
<EuiSpacer size="m" />
<IndexPattern
fields={this.props.fields}
model={this.props.model}
onChange={this.props.onChange}
/>
</div>
<div className="vis_editor__vis_config-row">
<div className="vis_editor__label">Background Color</div>
<ColorPicker
onChange={this.props.onChange}
name="background_color"
value={model.background_color}
/>
<label className="vis_editor__label" htmlFor={htmlId('gaugeMax')}>
Gauge Max (empty for auto)
</label>
<input
id={htmlId('gaugeMax')}
className="vis_editor__input-grows"
type="number"
onChange={handleTextChange('gauge_max')}
value={model.gauge_max}
/>
<label className="vis_editor__label" htmlFor={htmlId('gaugeStyle')}>
Gauge Style
</label>
<EuiComboBox
isClearable={false}
id={htmlId('gaugeStyle')}
options={styleOptions}
selectedOptions={selectedGaugeStyleOption ? [selectedGaugeStyleOption] : []}
onChange={handleSelectChange('gauge_style')}
singleSelection={true}
/>
</div>
<div className="vis_editor__vis_config-row">
<div className="vis_editor__label">Inner Color</div>
<ColorPicker
onChange={this.props.onChange}
name="gauge_inner_color"
value={model.gauge_inner_color}
/>
<label className="vis_editor__label" htmlFor={htmlId('innerLine')}>
Inner Line Width
</label>
<input
id={htmlId('innerLine')}
className="vis_editor__input-grows"
type="number"
onChange={handleTextChange('gauge_inner_width')}
value={model.gauge_inner_width}
/>
<label className="vis_editor__label" htmlFor={htmlId('gaugeLine')}>
Gauge Line Width
</label>
<input
id={htmlId('gaugeLine')}
className="vis_editor__input-grows"
type="number"
onChange={handleTextChange('gauge_width')}
value={model.gauge_width}
/>
</div>
<div>
<div className="vis_editor__label">Color Rules</div>
</div>
<div className="vis_editor__vis_config-row">
<EuiHorizontalRule />
<EuiFlexGroup responsive={false} wrap={true}>
<EuiFlexItem>
<EuiFormRow
id={htmlId('panelFilter')}
label="Panel filter"
fullWidth
>
<EuiFieldText
onChange={handleTextChange('filter')}
value={model.filter}
fullWidth
/>
</EuiFormRow>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiFormLabel>Ignore global filter?</EuiFormLabel>
<EuiSpacer size="s" />
<YesNo
value={model.ignore_global_filter}
name="ignore_global_filter"
onChange={this.props.onChange}
/>
</EuiFlexItem>
</EuiFlexGroup>
</EuiPanel>
<EuiSpacer />
<EuiPanel>
<EuiTitle size="s"><span>Style</span></EuiTitle>
<EuiSpacer size="m" />
<EuiFlexGroup responsive={false} wrap={true}>
<EuiFlexItem>
<EuiFormRow
id={htmlId('gaugeMax')}
label="Gauge max (empty for auto)"
>
{/*
EUITODO: The following input couldn't be converted to EUI because of type mis-match.
It accepts a null value, but is passed a empty string.
*/}
<input
id={htmlId('gaugeMax')}
className="tvbAgg__input"
type="number"
onChange={handleTextChange('gauge_max')}
value={model.gauge_max}
/>
</EuiFormRow>
</EuiFlexItem>
<EuiFlexItem>
<EuiFormRow
id={htmlId('gaugeStyle')}
label="Gauge style"
>
<EuiComboBox
isClearable={false}
options={styleOptions}
selectedOptions={selectedGaugeStyleOption ? [selectedGaugeStyleOption] : []}
onChange={handleSelectChange('gauge_style')}
singleSelection={{ asPlainText: true }}
/>
</EuiFormRow>
</EuiFlexItem>
<EuiFlexItem>
<EuiFormRow
id={htmlId('innerLine')}
label="Inner line width"
>
<EuiFieldNumber
onChange={handleTextChange('gauge_inner_width')}
value={Number(model.gauge_inner_width)}
/>
</EuiFormRow>
</EuiFlexItem>
<EuiFlexItem>
<EuiFormRow
id={htmlId('gaugeLine')}
label="Gauge line width"
>
<EuiFieldNumber
onChange={handleTextChange('gauge_width')}
value={Number(model.gauge_width)}
/>
</EuiFormRow>
</EuiFlexItem>
</EuiFlexGroup>
<EuiHorizontalRule />
<EuiFlexGroup responsive={false} wrap={true} alignItems="center">
<EuiFlexItem grow={false}>
<EuiFormLabel style={{ marginBottom: 0 }}>Background color:</EuiFormLabel>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<ColorPicker
onChange={this.props.onChange}
name="background_color"
value={model.background_color}
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiFormLabel style={{ marginBottom: 0 }}>Inner color:</EuiFormLabel>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<ColorPicker
onChange={this.props.onChange}
name="gauge_inner_color"
value={model.gauge_inner_color}
/>
</EuiFlexItem>
</EuiFlexGroup>
<EuiHorizontalRule />
<EuiTitle size="xxs"><span>Color rules</span></EuiTitle>
<EuiSpacer size="s" />
<ColorRules
primaryName="gauge color"
primaryVarName="gauge"
@ -185,28 +242,26 @@ class GaugePanelConfig extends Component {
onChange={this.props.onChange}
name="gauge_color_rules"
/>
</div>
</EuiPanel>
</div>
);
}
return (
<div>
<div className="kbnTabs" role="tablist">
<button
role="tab"
aria-selected={selectedTab === 'data'}
className={`kbnTabs__tab${selectedTab === 'data' && '-active' || ''}`}
<EuiTabs size="s">
<EuiTab
isSelected={selectedTab === 'data'}
onClick={() => this.switchTab('data')}
>Data
</button>
<button
role="tab"
aria-selected={selectedTab === 'options'}
className={`kbnTabs__tab${selectedTab === 'options' && '-active' || ''}`}
>
Data
</EuiTab>
<EuiTab
isSelected={selectedTab === 'options'}
onClick={() => this.switchTab('options')}
>Panel Options
</button>
</div>
>
Panel options
</EuiTab>
</EuiTabs>
{view}
</div>
);

View file

@ -32,6 +32,17 @@ import { KuiCodeEditor } from '@kbn/ui-framework/components';
import {
htmlIdGenerator,
EuiComboBox,
EuiTabs,
EuiTab,
EuiPanel,
EuiFlexGroup,
EuiFlexItem,
EuiFormRow,
EuiFormLabel,
EuiSpacer,
EuiFieldText,
EuiTitle,
EuiHorizontalRule,
} from '@elastic/eui';
const lessC = less(window, { env: 'production' });
@ -93,61 +104,91 @@ class MarkdownPanelConfig extends Component {
);
} else {
view = (
<div className="vis_editor__container">
<IndexPattern
fields={this.props.fields}
model={this.props.model}
onChange={this.props.onChange}
/>
<div className="vis_editor__vis_config-row">
<div className="vis_editor__label">Background Color</div>
<ColorPicker
onChange={this.props.onChange}
name="background_color"
value={model.background_color}
/>
<label className="vis_editor__label" htmlFor={htmlId('panelFilter')}>
Panel Filter
</label>
<input
id={htmlId('panelFilter')}
className="vis_editor__input-grows"
type="text"
onChange={handleTextChange('filter')}
value={model.filter}
/>
<div className="vis_editor__label">Ignore Global Filter</div>
<YesNo
value={model.ignore_global_filter}
name="ignore_global_filter"
<div className="tvbPanelConfig__container">
<EuiPanel>
<EuiTitle size="s"><span>Data</span></EuiTitle>
<EuiSpacer size="m" />
<IndexPattern
fields={this.props.fields}
model={this.props.model}
onChange={this.props.onChange}
/>
</div>
<div className="vis_editor__vis_config-row">
<div className="vis_editor__label">Show Scrollbars</div>
<YesNo
value={model.markdown_scrollbars}
name="markdown_scrollbars"
onChange={this.props.onChange}
/>
<label className="vis_editor__label" htmlFor={htmlId('valign')}>
Vertical Alignment
</label>
<div className="vis_editor__row_item">
<EuiComboBox
isClearable={false}
id={htmlId('valign')}
options={alignOptions}
selectedOptions={selectedAlignOption ? [selectedAlignOption] : []}
onChange={handleSelectChange('markdown_vertical_align')}
singleSelection={true}
/>
</div>
</div>
<div className="vis_editor__vis_config-row">
<div className="vis_editor__label">Custom CSS (supports Less)</div>
</div>
<div className="vis_editor__ace-editor">
<EuiHorizontalRule />
<EuiFlexGroup responsive={false} wrap={true}>
<EuiFlexItem>
<EuiFormRow
id={htmlId('panelFilter')}
label="Panel filter"
fullWidth
>
<EuiFieldText
onChange={handleTextChange('filter')}
value={model.filter}
fullWidth
/>
</EuiFormRow>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiFormLabel>Ignore global filter?</EuiFormLabel>
<EuiSpacer size="s" />
<YesNo
value={model.ignore_global_filter}
name="ignore_global_filter"
onChange={this.props.onChange}
/>
</EuiFlexItem>
</EuiFlexGroup>
</EuiPanel>
<EuiSpacer />
<EuiPanel>
<EuiTitle size="s"><span>Style</span></EuiTitle>
<EuiSpacer size="m" />
<EuiFlexGroup responsive={false} wrap={true} alignItems="center">
<EuiFlexItem grow={false}>
<EuiFormLabel style={{ marginBottom: 0 }}>Background color:</EuiFormLabel>
</EuiFlexItem>
<EuiFlexItem>
<ColorPicker
onChange={this.props.onChange}
name="background_color"
value={model.background_color}
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiFormLabel style={{ marginBottom: 0 }}>Show scrollbars?</EuiFormLabel>
</EuiFlexItem>
<EuiFlexItem>
<YesNo
value={model.markdown_scrollbars}
name="markdown_scrollbars"
onChange={this.props.onChange}
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiFormLabel style={{ marginBottom: 0 }} htmlFor={htmlId('valign')}>Vertical alignment:</EuiFormLabel>
</EuiFlexItem>
<EuiFlexItem>
<EuiComboBox
id={htmlId('valign')}
isClearable={false}
options={alignOptions}
selectedOptions={selectedAlignOption ? [selectedAlignOption] : []}
onChange={handleSelectChange('markdown_vertical_align')}
singleSelection={{ asPlainText: true }}
/>
</EuiFlexItem>
</EuiFlexGroup>
<EuiHorizontalRule />
<EuiTitle size="xxs"><span>Custom CSS (supports Less)</span></EuiTitle>
<EuiSpacer size="s" />
<KuiCodeEditor
mode="less"
theme="github"
@ -157,36 +198,33 @@ class MarkdownPanelConfig extends Component {
value={model.markdown_less}
onChange={this.handleCSSChange}
/>
</div>
</EuiPanel>
</div>
);
}
return (
<div>
<div className="kbnTabs" role="tablist">
<button
role="tab"
aria-selected={selectedTab === 'markdown'}
className={`kbnTabs__tab${selectedTab === 'markdown' && '-active' || ''}`}
<EuiTabs size="s">
<EuiTab
isSelected={selectedTab === 'markdown'}
onClick={() => this.switchTab('markdown')}
>Markdown
</button>
<button
>
Markdown
</EuiTab>
<EuiTab
data-test-subj="markdownDataBtn"
role="tab"
aria-selected={selectedTab === 'data'}
className={`kbnTabs__tab${selectedTab === 'data' && '-active' || ''}`}
isSelected={selectedTab === 'data'}
onClick={() => this.switchTab('data')}
>Data
</button>
<button
role="tab"
aria-selected={selectedTab === 'options'}
className={`kbnTabs__tab${selectedTab === 'options' && '-active' || ''}`}
>
Data
</EuiTab>
<EuiTab
isSelected={selectedTab === 'options'}
onClick={() => this.switchTab('options')}
>Panel Options
</button>
</div>
>
Panel options
</EuiTab>
</EuiTabs>
{view}
</div>
);

View file

@ -25,7 +25,20 @@ import createTextHandler from '../lib/create_text_handler';
import ColorRules from '../color_rules';
import YesNo from '../yes_no';
import uuid from 'uuid';
import { htmlIdGenerator } from '@elastic/eui';
import {
htmlIdGenerator,
EuiTabs,
EuiTab,
EuiPanel,
EuiFlexGroup,
EuiFlexItem,
EuiFormRow,
EuiFormLabel,
EuiSpacer,
EuiFieldText,
EuiTitle,
EuiHorizontalRule,
} from '@elastic/eui';
class MetricPanelConfig extends Component {
@ -67,62 +80,77 @@ class MetricPanelConfig extends Component {
);
} else {
view = (
<div className="vis_editor__container">
<IndexPattern
fields={this.props.fields}
model={this.props.model}
onChange={this.props.onChange}
/>
<div className="vis_editor__vis_config-row">
<label className="vis_editor__label" htmlFor={htmlId('panelFilter')}>
Panel Filter
</label>
<input
id={htmlId('panelFilter')}
className="vis_editor__input-grows"
type="text"
onChange={handleTextChange('filter')}
value={model.filter}
/>
<div className="vis_editor__label">Ignore Global Filter</div>
<YesNo
value={model.ignore_global_filter}
name="ignore_global_filter"
<div className="tvbPanelConfig__container">
<EuiPanel>
<EuiTitle size="s"><span>Data</span></EuiTitle>
<EuiSpacer size="m" />
<IndexPattern
fields={this.props.fields}
model={this.props.model}
onChange={this.props.onChange}
/>
</div>
<div>
<div className="vis_editor__label">Color Rules</div>
</div>
<div className="vis_editor__vis_config-row">
<EuiHorizontalRule />
<EuiFlexGroup responsive={false} wrap={true}>
<EuiFlexItem>
<EuiFormRow
id={htmlId('panelFilter')}
label="Panel filter"
fullWidth
>
<EuiFieldText
onChange={handleTextChange('filter')}
value={model.filter}
fullWidth
/>
</EuiFormRow>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiFormLabel>Ignore global filter?</EuiFormLabel>
<EuiSpacer size="s" />
<YesNo
value={model.ignore_global_filter}
name="ignore_global_filter"
onChange={this.props.onChange}
/>
</EuiFlexItem>
</EuiFlexGroup>
</EuiPanel>
<EuiSpacer />
<EuiPanel>
<EuiTitle size="s"><span>Color rules</span></EuiTitle>
<EuiSpacer size="m" />
<ColorRules
model={model}
onChange={this.props.onChange}
name="background_color_rules"
/>
</div>
</EuiPanel>
</div>
);
}
return (
<div>
<div className="kbnTabs" role="tablist">
<button
role="tab"
aria-selected={selectedTab === 'data'}
className={`kbnTabs__tab${selectedTab === 'data' && '-active' || ''}`}
<EuiTabs size="s">
<EuiTab
isSelected={selectedTab === 'data'}
onClick={() => this.switchTab('data')}
>Data
</button>
<button
role="tab"
aria-selected={selectedTab === 'options'}
className={`kbnTabs__tab${selectedTab === 'options' && '-active' || ''}`}
>
Data
</EuiTab>
<EuiTab
isSelected={selectedTab === 'options'}
onClick={() => this.switchTab('options')}
data-test-subj="metricEditorPanelOptionsBtn"
>Panel Options
</button>
</div>
>
Panel options
</EuiTab>
</EuiTabs>
{view}
</div>
);

View file

@ -26,7 +26,22 @@ import createTextHandler from '../lib/create_text_handler';
import createSelectHandler from '../lib/create_select_handler';
import uuid from 'uuid';
import YesNo from '../yes_no';
import { htmlIdGenerator } from '@elastic/eui';
import {
htmlIdGenerator,
EuiTabs,
EuiTab,
EuiPanel,
EuiFlexGroup,
EuiFlexItem,
EuiFormRow,
EuiFormLabel,
EuiSpacer,
EuiFieldText,
EuiTitle,
EuiHorizontalRule,
EuiCode,
EuiText,
} from '@elastic/eui';
class TablePanelConfig extends Component {
@ -59,45 +74,63 @@ class TablePanelConfig extends Component {
if (selectedTab === 'data') {
view = (
<div>
<div className="vis_editor__table-pivot-fields">
<div className="vis_editor__container">
<div className="vis_editor__vis_config-row">
<div className="tvbPanelConfig__container">
<EuiPanel>
<EuiText>
<p>
For the table visualization you need to define a field to
group by using a terms aggregation.
</p>
</div>
<div className="vis_editor__vis_config-row">
<label className="vis_editor__label" htmlFor={htmlId('field')}>Group By Field</label>
<div className="vis_editor__row_item" data-test-subj="groupByField">
<FieldSelect
id={htmlId('field')}
fields={this.props.fields}
value={model.pivot_id}
indexPattern={model.index_pattern}
onChange={handleSelectChange('pivot_id')}
/>
</div>
<label className="vis_editor__label" htmlFor={htmlId('pivotLabelInput')}>Column Label</label>
<input
id={htmlId('pivotLabelInput')}
className="vis_editor__input-grows"
data-test-subj="columnLabelName"
type="text"
onChange={handleTextChange('pivot_label')}
value={model.pivot_label}
/>
<label className="vis_editor__label" htmlFor={htmlId('pivotRowsInput')}>Rows</label>
<input
id={htmlId('pivotRowsInput')}
className="vis_editor__input-number"
type="number"
onChange={handleTextChange('pivot_rows')}
value={model.pivot_rows}
/>
</div>
</div>
</EuiText>
<EuiSpacer size="m" />
<EuiFlexGroup responsive={false} wrap={true}>
<EuiFlexItem data-test-subj="groupByField">
<EuiFormRow id={htmlId('field')} label="Group by field">
<FieldSelect
fields={this.props.fields}
value={model.pivot_id}
indexPattern={model.index_pattern}
onChange={handleSelectChange('pivot_id')}
fullWidth
/>
</EuiFormRow>
</EuiFlexItem>
<EuiFlexItem>
<EuiFormRow
id={htmlId('pivotLabelInput')}
label="Column label"
fullWidth
>
<EuiFieldText
data-test-subj="columnLabelName"
onChange={handleTextChange('pivot_label')}
value={model.pivot_label}
fullWidth
/>
</EuiFormRow>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiFormRow
id={htmlId('pivotRowsInput')}
label="Rows"
>
{/*
EUITODO: The following input couldn't be converted to EUI because of type mis-match.
Should it be number or string?
*/}
<input
className="tvbAgg__input"
type="number"
onChange={handleTextChange('pivot_rows')}
value={model.pivot_rows}
/>
</EuiFormRow>
</EuiFlexItem>
</EuiFlexGroup>
</EuiPanel>
</div>
<SeriesEditor
fields={this.props.fields}
model={this.props.model}
@ -108,59 +141,81 @@ class TablePanelConfig extends Component {
);
} else {
view = (
<div className="vis_editor__container">
<div className="vis_editor__vis_config-row">
<label className="vis_editor__label" htmlFor={htmlId('drilldownInput')}>Item Url (This supports mustache templating.
<code>{'{{key}}'}</code> is set to the term)
</label>
<input
<div className="tvbPanelConfig__container">
<EuiPanel>
<EuiTitle size="s"><span>Data</span></EuiTitle>
<EuiSpacer size="m" />
<EuiFormRow
id={htmlId('drilldownInput')}
className="vis_editor__input-grows"
onChange={handleTextChange('drilldown_url')}
value={model.drilldown_url}
/>
</div>
<IndexPattern
fields={this.props.fields}
model={this.props.model}
onChange={this.props.onChange}
/>
<div className="vis_editor__vis_config-row">
<label className="vis_editor__label" htmlFor={htmlId('panelFilterInput')}>Panel Filter</label>
<input
id={htmlId('panelFilterInput')}
className="vis_editor__input-grows"
type="text"
onChange={handleTextChange('filter')}
value={model.filter}
/>
<label className="vis_editor__label" htmlFor={htmlId('globalFilterOption')}>Ignore Global Filter</label>
<YesNo
id={htmlId('globalFilterOption')}
value={model.ignore_global_filter}
name="ignore_global_filter"
label="Item url"
helpText={
<span>
This supports mustache templating.
<EuiCode>{'{{key}}'}</EuiCode> is set to the term.
</span>
}
>
<EuiFieldText
onChange={handleTextChange('drilldown_url')}
value={model.drilldown_url}
/>
</EuiFormRow>
<EuiHorizontalRule />
<IndexPattern
fields={this.props.fields}
model={this.props.model}
onChange={this.props.onChange}
/>
</div>
<EuiHorizontalRule />
<EuiFlexGroup responsive={false} wrap={true}>
<EuiFlexItem>
<EuiFormRow
id={htmlId('panelFilterInput')}
label="Panel filter"
fullWidth
>
<EuiFieldText
onChange={handleTextChange('filter')}
value={model.filter}
fullWidth
/>
</EuiFormRow>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiFormLabel htmlFor={htmlId('globalFilterOption')}>Ignore global filter?</EuiFormLabel>
<EuiSpacer size="s" />
<YesNo
id={htmlId('globalFilterOption')}
value={model.ignore_global_filter}
name="ignore_global_filter"
onChange={this.props.onChange}
/>
</EuiFlexItem>
</EuiFlexGroup>
</EuiPanel>
</div>
);
}
return (
<div>
<div className="kbnTabs">
<div
className={`kbnTabs__tab${selectedTab === 'data' && '-active' || ''}`}
<EuiTabs size="s">
<EuiTab
isSelected={selectedTab === 'data'}
onClick={() => this.switchTab('data')}
>
Columns
</div>
<div
className={`kbnTabs__tab${selectedTab === 'options' && '-active' || ''}`}
</EuiTab>
<EuiTab
isSelected={selectedTab === 'options'}
onClick={() => this.switchTab('options')}
>
Panel Options
</div>
</div>
Panel options
</EuiTab>
</EuiTabs>
{view}
</div>
);

View file

@ -29,6 +29,17 @@ import YesNo from '../yes_no';
import {
htmlIdGenerator,
EuiComboBox,
EuiTabs,
EuiTab,
EuiPanel,
EuiFlexGroup,
EuiFlexItem,
EuiFormRow,
EuiFormLabel,
EuiSpacer,
EuiFieldText,
EuiTitle,
EuiHorizontalRule,
} from '@elastic/eui';
class TimeseriesPanelConfig extends Component {
@ -99,127 +110,165 @@ class TimeseriesPanelConfig extends Component {
);
} else {
view = (
<div className="vis_editor__container">
<IndexPattern
fields={this.props.fields}
model={this.props.model}
onChange={this.props.onChange}
/>
<div className="vis_editor__vis_config-row">
<label className="vis_editor__label" htmlFor={htmlId('axisMin')}>Axis Min</label>
<input
id={htmlId('axisMin')}
className="vis_editor__input-grows"
type="text"
onChange={handleTextChange('axis_min')}
value={model.axis_min}
/>
<label className="vis_editor__label" htmlFor={htmlId('axisMax')}>Axis Max</label>
<input
id={htmlId('axisMax')}
className="vis_editor__input-grows"
type="text"
onChange={handleTextChange('axis_max')}
value={model.axis_max}
/>
<label className="vis_editor__label" htmlFor={htmlId('axisPos')}>Axis Position</label>
<div className="vis_editor__row_item">
<EuiComboBox
isClearable={false}
id={htmlId('axisPos')}
options={positionOptions}
selectedOptions={selectedPositionOption ? [selectedPositionOption] : []}
onChange={handleSelectChange('axis_position')}
singleSelection={true}
/>
</div>
<label className="vis_editor__label" htmlFor={htmlId('axisPos')}>Axis Scale</label>
<div className="vis_editor__row_item">
<EuiComboBox
isClearable={false}
id={htmlId('axisScale')}
options={scaleOptions}
selectedOptions={selectedAxisScaleOption ? [selectedAxisScaleOption] : []}
onChange={handleSelectChange('axis_scale')}
singleSelection={true}
/>
</div>
</div>
<div className="vis_editor__vis_config-row">
<div className="vis_editor__label">Background Color</div>
<ColorPicker
onChange={this.props.onChange}
name="background_color"
value={model.background_color}
/>
<div className="vis_editor__label">Show Legend</div>
<YesNo
value={model.show_legend}
name="show_legend"
<div className="tvbPanelConfig__container">
<EuiPanel>
<EuiTitle size="s"><span>Data</span></EuiTitle>
<EuiSpacer size="m" />
<IndexPattern
fields={this.props.fields}
model={this.props.model}
onChange={this.props.onChange}
/>
<label className="vis_editor__label" htmlFor={htmlId('legendPos')}>Legend Position</label>
<div className="vis_editor__row_item">
<EuiComboBox
isClearable={false}
id={htmlId('legendPos')}
options={legendPositionOptions}
selectedOptions={selectedLegendPosOption ? [selectedLegendPosOption] : []}
onChange={handleSelectChange('legend_position')}
singleSelection={true}
/>
</div>
<div className="vis_editor__label">Display Grid</div>
<YesNo
value={model.show_grid}
name="show_grid"
onChange={this.props.onChange}
/>
</div>
<div className="vis_editor__vis_config-row">
<label className="vis_editor__label" htmlFor={htmlId('panelFilter')}>Panel Filter</label>
<input
id={htmlId('panelFilter')}
className="vis_editor__input-grows"
type="text"
onChange={handleTextChange('filter')}
value={model.filter}
/>
<div className="vis_editor__label">Ignore Global Filter</div>
<YesNo
value={model.ignore_global_filter}
name="ignore_global_filter"
onChange={this.props.onChange}
/>
</div>
<EuiHorizontalRule />
<EuiFlexGroup responsive={false} wrap={true}>
<EuiFlexItem>
<EuiFormRow
id={htmlId('panelFilter')}
label="Panel filter"
fullWidth
>
<EuiFieldText
onChange={handleTextChange('filter')}
value={model.filter}
fullWidth
/>
</EuiFormRow>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiFormLabel>Ignore global filter?</EuiFormLabel>
<EuiSpacer size="s" />
<YesNo
value={model.ignore_global_filter}
name="ignore_global_filter"
onChange={this.props.onChange}
/>
</EuiFlexItem>
</EuiFlexGroup>
</EuiPanel>
<EuiSpacer />
<EuiPanel>
<EuiTitle size="s"><span>Style</span></EuiTitle>
<EuiSpacer size="m" />
<EuiFlexGroup responsive={false} wrap={true} alignItems="center">
<EuiFlexItem>
<EuiFormRow id={htmlId('axisMin')} label="Axis min">
<EuiFieldText
onChange={handleTextChange('axis_min')}
value={model.axis_min}
/>
</EuiFormRow>
</EuiFlexItem>
<EuiFlexItem>
<EuiFormRow id={htmlId('axisMax')} label="Axis max">
<EuiFieldText
onChange={handleTextChange('axis_max')}
value={model.axis_max}
/>
</EuiFormRow>
</EuiFlexItem>
<EuiFlexItem>
<EuiFormRow id={htmlId('axisPos')} label="Axis position">
<EuiComboBox
isClearable={false}
options={positionOptions}
selectedOptions={selectedPositionOption ? [selectedPositionOption] : []}
onChange={handleSelectChange('axis_position')}
singleSelection={{ asPlainText: true }}
/>
</EuiFormRow>
</EuiFlexItem>
<EuiFlexItem>
<EuiFormRow id={htmlId('axisScale')} label="Axis scale">
<EuiComboBox
isClearable={false}
options={scaleOptions}
selectedOptions={selectedAxisScaleOption ? [selectedAxisScaleOption] : []}
onChange={handleSelectChange('axis_scale')}
singleSelection={{ asPlainText: true }}
/>
</EuiFormRow>
</EuiFlexItem>
</EuiFlexGroup>
<EuiHorizontalRule />
<EuiFlexGroup responsive={false} wrap={true} alignItems="center">
<EuiFlexItem grow={false}>
<EuiFormLabel style={{ marginBottom: 0 }}>Background color:</EuiFormLabel>
</EuiFlexItem>
<EuiFlexItem>
<ColorPicker
onChange={this.props.onChange}
name="background_color"
value={model.background_color}
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiFormLabel style={{ marginBottom: 0 }}>Show legend?</EuiFormLabel>
</EuiFlexItem>
<EuiFlexItem>
<YesNo
value={model.show_legend}
name="show_legend"
onChange={this.props.onChange}
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiFormLabel style={{ marginBottom: 0 }} htmlFor={htmlId('legendPos')}>Legend position</EuiFormLabel>
</EuiFlexItem>
<EuiFlexItem>
<EuiComboBox
isClearable={false}
id={htmlId('legendPos')}
options={legendPositionOptions}
selectedOptions={selectedLegendPosOption ? [selectedLegendPosOption] : []}
onChange={handleSelectChange('legend_position')}
singleSelection={{ asPlainText: true }}
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiFormLabel style={{ marginBottom: 0 }}>Display grid</EuiFormLabel>
</EuiFlexItem>
<EuiFlexItem>
<YesNo
value={model.show_grid}
name="show_grid"
onChange={this.props.onChange}
/>
</EuiFlexItem>
</EuiFlexGroup>
</EuiPanel>
</div>
);
}
return (
<div>
<div className="kbnTabs" role="tablist">
<button
role="tab"
aria-selected={selectedTab === 'data'}
className={`kbnTabs__tab${selectedTab === 'data' && '-active' || ''}`}
<EuiTabs size="s">
<EuiTab
isSelected={selectedTab === 'data'}
onClick={() => this.switchTab('data')}
>Data
</button>
<button
role="tab"
aria-selected={selectedTab === 'options'}
className={`kbnTabs__tab${selectedTab === 'options' && '-active' || ''}`}
>
Data
</EuiTab>
<EuiTab
isSelected={selectedTab === 'options'}
onClick={() => this.switchTab('options')}
>Panel Options
</button>
<button
role="tab"
aria-selected={selectedTab === 'annotations'}
className={`kbnTabs__tab${selectedTab === 'annotations' && '-active' || ''}`}
>
Panel options
</EuiTab>
<EuiTab
isSelected={selectedTab === 'annotations'}
onClick={() => this.switchTab('annotations')}
>Annotations
</button>
</div>
>
Annotations
</EuiTab>
</EuiTabs>
{view}
</div>
);

View file

@ -26,7 +26,21 @@ import ColorRules from '../color_rules';
import ColorPicker from '../color_picker';
import uuid from 'uuid';
import YesNo from '../yes_no';
import { htmlIdGenerator } from '@elastic/eui';
import {
htmlIdGenerator,
EuiTabs,
EuiTab,
EuiPanel,
EuiFlexGroup,
EuiFlexItem,
EuiFormRow,
EuiFormLabel,
EuiSpacer,
EuiFieldText,
EuiTitle,
EuiHorizontalRule,
EuiCode,
} from '@elastic/eui';
class TopNPanelConfig extends Component {
@ -67,52 +81,85 @@ class TopNPanelConfig extends Component {
);
} else {
view = (
<div className="vis_editor__container">
<div className="vis_editor__vis_config-row">
<label className="vis_editor__label" htmlFor={htmlId('itemUrl')}>
Item Url (This supports mustache templating.
<code>{'{{key}}'}</code> is set to the term)
</label>
<input
<div className="tvbPanelConfig__container">
<EuiPanel>
<EuiTitle size="s"><span>Data</span></EuiTitle>
<EuiSpacer size="m" />
<EuiFormRow
id={htmlId('itemUrl')}
className="vis_editor__input-grows"
onChange={handleTextChange('drilldown_url')}
value={model.drilldown_url}
/>
</div>
<IndexPattern
fields={this.props.fields}
model={this.props.model}
onChange={this.props.onChange}
/>
<div className="vis_editor__vis_config-row">
<div className="vis_editor__label">Background Color</div>
<ColorPicker
onChange={this.props.onChange}
name="background_color"
value={model.background_color}
/>
<label className="vis_editor__label" htmlFor={htmlId('panelFilter')}>
Panel Filter
</label>
<input
id={htmlId('panelFilter')}
className="vis_editor__input-grows"
type="text"
onChange={handleTextChange('filter')}
value={model.filter}
/>
<div className="vis_editor__label">Ignore Global Filter</div>
<YesNo
value={model.ignore_global_filter}
name="ignore_global_filter"
label="Item url"
helpText={
<span>
This supports mustache templating.
<EuiCode>{'{{key}}'}</EuiCode> is set to the term.
</span>
}
>
<EuiFieldText
onChange={handleTextChange('drilldown_url')}
value={model.drilldown_url}
/>
</EuiFormRow>
<EuiHorizontalRule />
<IndexPattern
fields={this.props.fields}
model={this.props.model}
onChange={this.props.onChange}
/>
</div>
<div>
<div className="vis_editor__label">Color Rules</div>
</div>
<div className="vis_editor__vis_config-row">
<EuiHorizontalRule />
<EuiFlexGroup responsive={false} wrap={true}>
<EuiFlexItem>
<EuiFormRow
id={htmlId('panelFilter')}
label="Panel filter"
fullWidth
>
<EuiFieldText
onChange={handleTextChange('filter')}
value={model.filter}
fullWidth
/>
</EuiFormRow>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiFormLabel>Ignore global filter?</EuiFormLabel>
<EuiSpacer size="s" />
<YesNo
value={model.ignore_global_filter}
name="ignore_global_filter"
onChange={this.props.onChange}
/>
</EuiFlexItem>
</EuiFlexGroup>
</EuiPanel>
<EuiSpacer />
<EuiPanel>
<EuiTitle size="s"><span>Style</span></EuiTitle>
<EuiSpacer size="m" />
<EuiFlexGroup responsive={false} wrap={true} alignItems="center">
<EuiFlexItem grow={false}>
<EuiFormLabel style={{ marginBottom: 0 }}>Background color:</EuiFormLabel>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<ColorPicker
onChange={this.props.onChange}
name="background_color"
value={model.background_color}
/>
</EuiFlexItem>
</EuiFlexGroup>
<EuiHorizontalRule />
<EuiTitle size="xxs"><span>Color rules</span></EuiTitle>
<EuiSpacer size="s" />
<ColorRules
model={model}
primaryVarName="bar_color"
@ -121,28 +168,26 @@ class TopNPanelConfig extends Component {
onChange={this.props.onChange}
name="bar_color_rules"
/>
</div>
</EuiPanel>
</div>
);
}
return (
<div>
<div className="kbnTabs" role="tablist">
<button
role="tab"
aria-selected={selectedTab === 'data'}
className={`kbnTabs__tab${selectedTab === 'data' && '-active' || ''}`}
<EuiTabs size="s">
<EuiTab
isSelected={selectedTab === 'data'}
onClick={() => this.switchTab('data')}
>Data
</button>
<button
role="tab"
aria-selected={selectedTab === 'options'}
className={`kbnTabs__tab${selectedTab === 'options' && '-active' || ''}`}
>
Data
</EuiTab>
<EuiTab
isSelected={selectedTab === 'options'}
onClick={() => this.switchTab('options')}
>Panel Options
</button>
</div>
>
Panel options
</EuiTab>
</EuiTabs>
{view}
</div>
);

View file

@ -24,7 +24,17 @@ import createSelectHandler from './lib/create_select_handler';
import createTextHandler from './lib/create_text_handler';
import YesNo from './yes_no';
import { IndexPattern } from './index_pattern';
import { htmlIdGenerator } from '@elastic/eui';
import {
htmlIdGenerator,
EuiFlexGroup,
EuiFlexItem,
EuiFieldText,
EuiFormRow,
EuiCode,
EuiHorizontalRule,
EuiFormLabel,
EuiSpacer,
} from '@elastic/eui';
export const SeriesConfig = props => {
const defaults = { offset_time: '', value_template: '' };
@ -34,65 +44,81 @@ export const SeriesConfig = props => {
const htmlId = htmlIdGenerator();
return (
<div>
<div className="vis_editor__series_config-container">
<div className="vis_editor__series_config-row">
<DataFormatPicker
onChange={handleSelectChange('formatter')}
value={model.formatter}
/>
<label className="vis_editor__label" htmlFor={htmlId('template')}>
Template (eg.<code>{'{{value}}/s'}</code>)
</label>
<input
style={{ width: 100 }}
<div className="tvbAggRow">
<DataFormatPicker
onChange={handleSelectChange('formatter')}
value={model.formatter}
/>
<EuiHorizontalRule margin="s" />
<EuiFormRow
id={htmlId('series_filter')}
label="Filter"
fullWidth
>
<EuiFieldText
onChange={handleTextChange('filter')}
value={model.filter}
fullWidth
/>
</EuiFormRow>
<EuiHorizontalRule margin="s" />
<EuiFlexGroup gutterSize="s">
<EuiFlexItem>
<EuiFormRow
id={htmlId('template')}
className="vis_editor__input-grows"
onChange={handleTextChange('value_template')}
value={model.value_template}
/>
<label className="vis_editor__label" htmlFor={htmlId('offsetSeries')}>
Offset series time by (1m, 1h, 1w, 1d)
</label>
<input
data-test-subj="offsetTimeSeries"
style={{ width: 100 }}
label="Template"
helpText={<span>eg.<EuiCode>{'{{value}}/s'}</EuiCode></span>}
fullWidth
>
<EuiFieldText
onChange={handleTextChange('value_template')}
value={model.value_template}
fullWidth
/>
</EuiFormRow>
</EuiFlexItem>
<EuiFlexItem>
<EuiFormRow
id={htmlId('offsetSeries')}
className="vis_editor__input-grows"
type="text"
onChange={handleTextChange('offset_time')}
value={model.offset_time}
/>
</div>
<div className="vis_editor__series_config-row">
<div className="vis_editor__label">Override Index Pattern</div>
label="Offset series time by (1m, 1h, 1w, 1d)"
>
<EuiFieldText
data-test-subj="offsetTimeSeries"
onChange={handleTextChange('offset_time')}
value={model.offset_time}
/>
</EuiFormRow>
</EuiFlexItem>
</EuiFlexGroup>
<EuiHorizontalRule margin="s" />
<EuiFlexGroup gutterSize="s" responsive={false} wrap={true}>
<EuiFlexItem grow={false}>
<EuiFormLabel>Override Index Pattern?</EuiFormLabel>
<EuiSpacer size="s" />
<YesNo
value={model.override_index_pattern}
name="override_index_pattern"
onChange={props.onChange}
/>
</EuiFlexItem>
<EuiFlexItem>
<IndexPattern
onChange={props.onChange}
model={props.model}
fields={props.fields}
prefix="series_"
className="vis_editor__row_item vis_editor__row"
disabled={!model.override_index_pattern}
/>
</div>
<div className="vis_editor__series_config-row">
<label className="vis_editor__label" htmlFor={htmlId('series_filter')}>
Filter
</label>
<input
id={htmlId('series_filter')}
className="vis_editor__input-grows"
type="text"
onChange={handleTextChange('filter')}
value={model.filter}
/>
</div>
</div>
</EuiFlexItem>
</EuiFlexGroup>
</div>
);
};

View file

@ -61,6 +61,7 @@ class SeriesEditor extends Component {
const { fields, model, name, limit, colorPicker } = props;
return (
<Series
className="tvbSeriesEditor"
colorPicker={colorPicker}
disableAdd={model[name].length >= limit}
disableDelete={model[name].length < 2}
@ -88,12 +89,12 @@ class SeriesEditor extends Component {
this.props.onChange({ series });
};
return (
<div className="vis_editor__series_editor-container">
<div className="tvbSeriesEditor__container">
<Sortable
dynamic={true}
direction="vertical"
onSort={handleSort}
sortHandle="vis_editor__sort"
sortHandle="tvbSeries__sortHandle"
>
{ series }
</Sortable>

View file

@ -21,20 +21,23 @@ import createSelectHandler from '../lib/create_select_handler';
import GroupBySelect from './group_by_select';
import PropTypes from 'prop-types';
import React from 'react';
import { htmlIdGenerator, EuiFlexGroup, EuiFlexItem, EuiFormRow } from '@elastic/eui';
function SplitByEverything(props) {
const { onChange, model } = props;
const htmlId = htmlIdGenerator();
const handleSelectChange = createSelectHandler(onChange);
return (
<div className="vis_editor__split-container">
<div className="vis_editor__label">Group By</div>
<div className="vis_editor__split-selects">
<GroupBySelect
value={model.split_mode}
onChange={handleSelectChange('split_mode')}
/>
</div>
</div>
<EuiFlexGroup alignItems="center">
<EuiFlexItem>
<EuiFormRow id={htmlId('group')} label="Group by">
<GroupBySelect
value={model.split_mode}
onChange={handleSelectChange('split_mode')}
/>
</EuiFormRow>
</EuiFlexItem>
</EuiFlexGroup>
);
}

View file

@ -22,29 +22,34 @@ import createSelectHandler from '../lib/create_select_handler';
import GroupBySelect from './group_by_select';
import PropTypes from 'prop-types';
import React from 'react';
import { htmlIdGenerator, EuiFlexGroup, EuiFlexItem, EuiFormRow, EuiFieldText } from '@elastic/eui';
export const SplitByFilter = props => {
const { onChange } = props;
const defaults = { filter: '' };
const model = { ...defaults, ...props.model };
const htmlId = htmlIdGenerator();
const handleTextChange = createTextHandler(onChange);
const handleSelectChange = createSelectHandler(onChange);
return (
<div className="vis_editor__split-container">
<div className="vis_editor__label">Group By</div>
<div className="vis_editor__split-selects">
<GroupBySelect
value={model.split_mode}
onChange={handleSelectChange('split_mode')}
/>
</div>
<div className="vis_editor__label">Query String</div>
<input
className="vis_editor__split-filter"
value={model.filter}
onChange={handleTextChange('filter')}
/>
</div>
<EuiFlexGroup alignItems="center">
<EuiFlexItem>
<EuiFormRow id={htmlId('group')} label="Group by">
<GroupBySelect
value={model.split_mode}
onChange={handleSelectChange('split_mode')}
/>
</EuiFormRow>
</EuiFlexItem>
<EuiFlexItem>
<EuiFormRow id={htmlId('query')} label="Query string">
<EuiFieldText
value={model.filter}
onChange={handleTextChange('filter')}
/>
</EuiFormRow>
</EuiFlexItem>
</EuiFlexGroup>
);
};

View file

@ -24,6 +24,7 @@ import * as collectionActions from '../lib/collection_actions';
import AddDeleteButtons from '../add_delete_buttons';
import ColorPicker from '../color_picker';
import uuid from 'uuid';
import { EuiFieldText, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
class FilterItems extends Component {
constructor(props) {
@ -53,41 +54,42 @@ class FilterItems extends Component {
const handleDelete = collectionActions.handleDelete
.bind(null, this.props, model);
return (
<div className="vis_editor__split-filter-row" key={model.id}>
<div className="vis_editor__split-filter-color">
<EuiFlexGroup gutterSize="s" className="tvbAggRow" alignItems="center" key={model.id}>
<EuiFlexItem grow={false}>
<ColorPicker
disableTrash={true}
onChange={handleChange}
name="color"
value={model.color}
/>
</div>
<div className="vis_editor__split-filter-item">
<input
</EuiFlexItem>
<EuiFlexItem>
<EuiFieldText
placeholder="Filter"
className="vis_editor__input-grows-100"
type="text"
aria-label="Filter"
onChange={this.handleChange(model, 'filter')}
value={model.filter}
fullWidth
/>
</div>
<div className="vis_editor__split-filter-item">
<input
</EuiFlexItem>
<EuiFlexItem>
<EuiFieldText
placeholder="Label"
className="vis_editor__input-grows-100"
type="text"
aria-label="Label"
onChange={this.handleChange(model, 'label')}
value={model.label}
fullWidth
/>
</div>
<div className="vis_editor__split-filter-control">
</EuiFlexItem>
<EuiFlexItem grow={false}>
<AddDeleteButtons
onAdd={handleAdd}
onDelete={handleDelete}
disableDelete={items.length < 2}
responsive={false}
/>
</div>
</div>
</EuiFlexItem>
</EuiFlexGroup>
);
}
@ -96,7 +98,7 @@ class FilterItems extends Component {
if (!model[name]) return (<div/>);
const rows = model[name].map(this.renderRow);
return (
<div className="vis_editor__split-filters">
<div>
{ rows }
</div>
);

View file

@ -22,29 +22,30 @@ import GroupBySelect from './group_by_select';
import FilterItems from './filter_items';
import PropTypes from 'prop-types';
import React from 'react';
import { htmlIdGenerator, EuiFlexGroup, EuiFlexItem, EuiFormRow } from '@elastic/eui';
function SplitByFilters(props) {
const { onChange, model } = props;
const htmlId = htmlIdGenerator();
const handleSelectChange = createSelectHandler(onChange);
return(
<div className="vis_editor__item">
<div className="vis_editor__split-container">
<div className="vis_editor__label">Group By</div>
<div className="vis_editor__split-selects">
<GroupBySelect
value={model.split_mode}
onChange={handleSelectChange('split_mode')}
/>
</div>
</div>
<div className="vis_editor__split-container">
<div className="vis_editor__row vis_editor__item">
<FilterItems
name="split_filters"
model={model}
onChange={onChange}
/>
</div>
</div>
<div>
<EuiFlexGroup alignItems="center">
<EuiFlexItem>
<EuiFormRow id={htmlId('group')} label="Group by">
<GroupBySelect
value={model.split_mode}
onChange={handleSelectChange('split_mode')}
/>
</EuiFormRow>
</EuiFlexItem>
</EuiFlexGroup>
<FilterItems
name="split_filters"
model={model}
onChange={onChange}
/>
</div>
);
}

View file

@ -35,6 +35,7 @@ function GroupBySelect(props) {
});
return (
<EuiComboBox
id={props.id}
isClearable={false}
options={modeOptions}
selectedOptions={[selectedOption]}

View file

@ -24,11 +24,10 @@ import createTextHandler from '../lib/create_text_handler';
import createSelectHandler from '../lib/create_select_handler';
import FieldSelect from '../aggs/field_select';
import MetricSelect from '../aggs/metric_select';
import {
EuiComboBox,
} from '@elastic/eui';
import { htmlIdGenerator, EuiFlexGroup, EuiFlexItem, EuiFormRow, EuiFieldNumber, EuiComboBox, EuiSpacer } from '@elastic/eui';
export const SplitByTerms = props => {
const htmlId = htmlIdGenerator();
const handleTextChange = createTextHandler(props.onChange);
const handleSelectChange = createSelectHandler(props.onChange);
const { indexPattern } = props;
@ -47,52 +46,64 @@ export const SplitByTerms = props => {
});
return (
<div className="vis_editor__split-container">
<div className="vis_editor__label">Group By</div>
<div className="vis_editor__split-selects">
<GroupBySelect
value={model.split_mode}
onChange={handleSelectChange('split_mode')}
/>
</div>
<div className="vis_editor__label">By</div>
<div className="vis_editor__item">
<FieldSelect
indexPattern={indexPattern}
onChange={handleSelectChange('terms_field')}
value={model.terms_field}
fields={props.fields}
/>
</div>
<div className="vis_editor__label">Top</div>
<input
placeholder="Size..."
type="number"
value={model.terms_size}
className="vis_editor__split-term_count"
onChange={handleTextChange('terms_size')}
/>
<div className="vis_editor__label">Order By</div>
<div className="vis_editor__split-aggs">
<MetricSelect
metrics={metrics}
clearable={false}
additionalOptions={[defaultCount, terms]}
onChange={handleSelectChange('terms_order_by')}
restrict="basic"
value={model.terms_order_by}
/>
</div>
<div className="vis_editor__label">Direction</div>
<div className="vis_editor__split-aggs">
<EuiComboBox
isClearable={false}
options={dirOptions}
selectedOptions={selectedDirectionOption ? [selectedDirectionOption] : []}
onChange={handleSelectChange('terms_direction')}
singleSelection={true}
/>
</div>
<div>
<EuiFlexGroup alignItems="center">
<EuiFlexItem>
<EuiFormRow id={htmlId('group')} label="Group by">
<GroupBySelect
value={model.split_mode}
onChange={handleSelectChange('split_mode')}
/>
</EuiFormRow>
</EuiFlexItem>
<EuiFlexItem>
<EuiFormRow id={htmlId('by')} label="By">
<FieldSelect
indexPattern={indexPattern}
onChange={handleSelectChange('terms_field')}
value={model.terms_field}
fields={props.fields}
/>
</EuiFormRow>
</EuiFlexItem>
</EuiFlexGroup>
<EuiSpacer />
<EuiFlexGroup alignItems="center">
<EuiFlexItem>
<EuiFormRow id={htmlId('top')} label="Top">
<EuiFieldNumber
placeholder="Size"
value={Number(model.terms_size)}
onChange={handleTextChange('terms_size')}
/>
</EuiFormRow>
</EuiFlexItem>
<EuiFlexItem>
<EuiFormRow id={htmlId('order')} label="Order by">
<MetricSelect
metrics={metrics}
clearable={false}
additionalOptions={[defaultCount, terms]}
onChange={handleSelectChange('terms_order_by')}
restrict="basic"
value={model.terms_order_by}
/>
</EuiFormRow>
</EuiFlexItem>
<EuiFlexItem>
<EuiFormRow id={htmlId('direction')} label="Direction">
<EuiComboBox
isClearable={false}
options={dirOptions}
selectedOptions={selectedDirectionOption ? [selectedDirectionOption] : []}
onChange={handleSelectChange('terms_direction')}
singleSelection={true}
/>
</EuiFormRow>
</EuiFlexItem>
</EuiFlexGroup>
</div>
);
};

View file

@ -149,8 +149,8 @@ class VisEditor extends Component {
if (model) {
return (
<div className="vis_editor">
<div className="vis-editor-hide-for-reporting">
<div className="tvbEditor">
<div className="tvbEditor--hideForReporting">
<VisPicker model={model} onChange={this.handleChange} />
</div>
<VisEditorVisualization
@ -171,7 +171,7 @@ class VisEditor extends Component {
dateFormat={this.props.config.get('dateFormat')}
onDataChange={this.onDataChange}
/>
<div className="vis-editor-hide-for-reporting">
<div className="tvbEditor--hideForReporting">
<PanelConfig
fields={this.state.visFields}
model={model}

View file

@ -19,9 +19,7 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { keyCodes } from '@elastic/eui';
import Toggle from 'react-toggle';
import 'react-toggle/style.css';
import { keyCodes, EuiFlexGroup, EuiFlexItem, EuiButton, EuiText, EuiSwitch } from '@elastic/eui';
import { getVisualizeLoader } from 'ui/visualize/loader/visualize_loader';
const MIN_CHART_HEIGHT = 250;
@ -142,47 +140,41 @@ class VisEditorVisualization extends Component {
style.userSelect = 'none';
}
const applyButtonClassName = dirty ? 'thor__button-solid-default' : 'thor__button-outlined-grayLight';
let applyMessage = 'The latest changes have been applied.';
if (dirty) applyMessage = 'The changes to this visualization have not been applied.';
if (autoApply) applyMessage = 'The changes will be automatically applied.';
const applyButton = (
<div className="vis_editor__dirty_controls">
<label
className="vis_editor__dirty_controls-toggle-label"
id="tsvbAutoApply"
htmlFor="tsvbAutoApplyInput"
>
Auto Apply
</label>
<div className="vis_editor__dirty_controls-toggle">
<Toggle
<EuiFlexGroup className="tvbEditorVisualization__apply" alignItems="center">
<EuiFlexItem grow={true}>
<EuiSwitch
id="tsvbAutoApplyInput"
defaultChecked={autoApply}
icons={false}
label="Auto apply"
checked={autoApply}
onChange={this.props.onToggleAutoApply}
/>
</div>
<div className="vis_editor__dirty_controls-button">
<button
disabled={!dirty}
onClick={this.props.onCommit}
className={`${applyButtonClassName} md`}
>
<i className="fa fa-play" /> Apply Changes
</button>
</div>
<div className={`vis_editor__dirty_controls-message${dirty ? '-dirty' : ''}`}>
{applyMessage}
</div>
</div>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiText color={dirty ? 'default' : 'subdued'} size="xs">
<p>
{applyMessage}
</p>
</EuiText>
</EuiFlexItem>
{!autoApply &&
<EuiFlexItem grow={false}>
<EuiButton iconType="play" fill size="s" onClick={this.props.onCommit} disabled={!dirty}>Apply changes</EuiButton>
</EuiFlexItem>
}
</EuiFlexGroup>
);
return (
<div>
<div
style={style}
className="vis_editor__visualization"
className="tvbEditorVisualization"
data-shared-items-container
data-shared-item
data-title={this.props.title}
@ -190,10 +182,10 @@ class VisEditorVisualization extends Component {
data-render-complete="disabled"
ref={this._visEl}
/>
<div className="vis-editor-hide-for-reporting">
<div className="tvbEditor--hideForReporting">
{applyButton}
<button
className="vis_editor__visualization-draghandle"
className="tvbEditorVisualization__draghandle"
onMouseDown={this.handleMouseDown}
onMouseUp={this.handleMouseUp}
onKeyDown={this.onSizeHandleKeyDown}

View file

@ -19,36 +19,25 @@
import PropTypes from 'prop-types';
import React from 'react';
import { EuiTabs, EuiTab } from '@elastic/eui';
function VisPickerItem(props) {
const { label, icon, type } = props;
let itemClassName = 'vis_editor__vis_picker-item';
let iconClassName = 'vis_editor__vis_picker-icon';
let labelClassName = 'vis_editor__vis_picker-label';
if (props.selected) {
itemClassName += ' selected';
iconClassName += ' selected';
labelClassName += ' selected';
}
const { label, type, selected } = props;
const itemClassName = 'tvbVisPickerItem';
return (
<button
role="tab"
<EuiTab
className={itemClassName}
isSelected={selected}
onClick={() => props.onClick(type)}
data-test-subj={`${type}TsvbTypeBtn`}
>
<div className={iconClassName}>
<i className={`fa ${icon}`} />
</div>
<div className={labelClassName}>
{ label }
</div>
</button>
{ label }
</EuiTab>
);
}
VisPickerItem.propTypes = {
icon: PropTypes.string,
label: PropTypes.string,
onClick: PropTypes.func,
type: PropTypes.string,
@ -61,13 +50,13 @@ function VisPicker(props) {
};
const { model } = props;
const icons = [
{ type: 'timeseries', icon: 'fa-line-chart', label: 'Time Series' },
{ type: 'metric', icon: 'fa-superscript', label: 'Metric' },
{ type: 'top_n', icon: 'fa-bar-chart fa-rotate-90', label: 'Top N' },
{ type: 'gauge', icon: 'fa-circle-o-notch', label: 'Gauge' },
{ type: 'markdown', icon: 'fa-paragraph', label: 'Markdown' },
{ type: 'table', icon: 'fa-paragraph', label: 'Table' }
const tabs = [
{ type: 'timeseries', label: 'Time Series' },
{ type: 'metric', label: 'Metric' },
{ type: 'top_n', label: 'Top N' },
{ type: 'gauge', label: 'Gauge' },
{ type: 'markdown', label: 'Markdown' },
{ type: 'table', label: 'Table' }
].map(item => {
return (
<VisPickerItem
@ -80,9 +69,9 @@ function VisPicker(props) {
});
return (
<div className="vis_editor__vis_picker-container" role="tablist">
{ icons }
</div>
<EuiTabs>
{ tabs }
</EuiTabs>
);
}

View file

@ -0,0 +1,3 @@
@import './vis_types';
@import './markdown/markdown';

View file

@ -0,0 +1,6 @@
.tvbVis {
display: flex;
flex-direction: column;
flex: 1 1 100%;
padding: $euiSizeS;
}

View file

@ -24,7 +24,7 @@ import AddDeleteButtons from '../../add_delete_buttons';
import { SeriesConfig } from '../../series_config';
import Sortable from 'react-anything-sortable';
import Split from '../../split';
import { EuiToolTip } from '@elastic/eui';
import { EuiToolTip, EuiTabs, EuiTab, EuiFlexGroup, EuiFlexItem, EuiFieldText, EuiButtonIcon } from '@elastic/eui';
import createAggRowRender from '../../lib/create_agg_row_render';
import createTextHandler from '../../lib/create_text_handler';
import { createUpDownHandler } from '../../lib/sort_keyhandler';
@ -48,15 +48,11 @@ function GaugeSeries(props) {
const handleChange = createTextHandler(onChange);
const aggs = model.metrics.map(createAggRowRender(props));
let caretClassName = 'fa fa-caret-down';
if (!visible) caretClassName = 'fa fa-caret-right';
let caretIcon = 'arrowDown';
if (!visible) caretIcon = 'arrowRight';
let body = null;
if (visible) {
let metricsClassName = 'kbnTabs__tab';
let optionsClassname = 'kbnTabs__tab';
if (selectedTab === 'metrics') metricsClassName += '-active';
if (selectedTab === 'options') optionsClassname += '-active';
let seriesBody;
if (selectedTab === 'metrics') {
const handleSort = (data) => {
@ -70,19 +66,17 @@ function GaugeSeries(props) {
dynamic={true}
direction="vertical"
onSort={handleSort}
sortHandle="vis_editor__agg_sort"
sortHandle="tvbAggRow__sortHandle"
>
{ aggs }
</Sortable>
<div className="vis_editor__series_row">
<div className="vis_editor__series_row-item">
<Split
onChange={props.onChange}
fields={fields}
panel={panel}
model={model}
/>
</div>
<div className="tvbAggRow tvbAggRow--split">
<Split
onChange={props.onChange}
fields={fields}
panel={panel}
model={model}
/>
</div>
</div>
);
@ -96,24 +90,22 @@ function GaugeSeries(props) {
);
}
body = (
<div className="vis_editor__series-row">
<div className="kbnTabs sm" role="tablist">
<button
role="tab"
aria-selected={selectedTab === 'metrics'}
className={metricsClassName}
<div className="tvbSeries__body">
<EuiTabs size="s">
<EuiTab
isSelected={selectedTab === 'metrics'}
onClick={() => props.switchTab('metrics')}
>Metrics
</button>
<button
role="tab"
>
Metrics
</EuiTab>
<EuiTab
data-test-subj="seriesOptions"
aria-selected={selectedTab === 'options'}
className={optionsClassname}
isSelected={selectedTab === 'options'}
onClick={() => props.switchTab('options')}
>Options
</button>
</div>
>
Options
</EuiTab>
</EuiTabs>
{seriesBody}
</div>
);
@ -131,45 +123,54 @@ function GaugeSeries(props) {
let dragHandle;
if (!props.disableDelete) {
dragHandle = (
<EuiToolTip content="Sort">
<button
className="vis_editor__sort thor__button-outlined-default sm"
aria-label="Sort series by pressing up/down"
onKeyDown={createUpDownHandler(props.onShouldSortItem)}
>
<i className="fa fa-sort" />
</button>
</EuiToolTip>
<EuiFlexItem grow={false}>
<EuiToolTip content="Drag to sort">
<EuiButtonIcon
className="tvbSeries__sortHandle"
iconType="grab"
aria-label="Sort series by pressing up/down"
onKeyDown={createUpDownHandler(props.onShouldSortItem)}
/>
</EuiToolTip>
</EuiFlexItem>
);
}
return (
<div
className={`${props.className} vis_editor__series`}
className={`${props.className}`}
style={props.style}
onMouseDown={props.onMouseDown}
onTouchStart={props.onTouchStart}
>
<div className="vis_editor__container">
<div className="vis_editor__series-details">
<button
className="vis_editor__series-visibility-toggle"
<EuiFlexGroup responsive={false} gutterSize="s" alignItems="center">
<EuiFlexItem grow={false}>
<EuiButtonIcon
iconType={caretIcon}
color="text"
onClick={props.toggleVisible}
aria-label="Toggle series editor"
aria-expanded={props.visible}
>
<i className={caretClassName}/>
</button>
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
{ colorPicker }
<div className="vis_editor__row vis_editor__row_item">
<input
className="vis_editor__input-grows"
onChange={handleChange('label')}
placeholder="Label"
value={model.label}
/>
</div>
{ dragHandle }
</EuiFlexItem>
<EuiFlexItem>
<EuiFieldText
fullWidth
onChange={handleChange('label')}
placeholder="Label"
value={model.label}
/>
</EuiFlexItem>
{ dragHandle }
<EuiFlexItem grow={false}>
<AddDeleteButtons
addTooltip="Add Series"
deleteTooltip="Delete Series"
@ -179,9 +180,11 @@ function GaugeSeries(props) {
onAdd={onAdd}
disableDelete={disableDelete}
disableAdd={disableAdd}
responsive={false}
/>
</div>
</div>
</EuiFlexItem>
</EuiFlexGroup>
{ body }
</div>
);

View file

@ -83,7 +83,7 @@ function GaugeVisualization(props) {
const style = { backgroundColor: panelBackgroundColor };
return (
<div className="dashboard__visualization" style={style}>
<div className="tvbVis" style={style}>
<Gauge {...params} />
</div>
);

View file

@ -0,0 +1,30 @@
.tvbMarkdown {
display: flex;
flex-direction: column;
flex: 1 0 auto;
position: relative;
}
.tvbMarkdown__content {
display: flex;
flex-direction: column;
flex: 1 0 auto;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
overflow: hidden;
&.middle {
justify-content: center;
}
&.bottom {
justify-content: flex-end;
}
&.scrolling {
overflow: auto;
}
}

View file

@ -25,6 +25,7 @@ import Sortable from 'react-anything-sortable';
import Split from '../../split';
import createAggRowRender from '../../lib/create_agg_row_render';
import createTextHandler from '../../lib/create_text_handler';
import { EuiTabs, EuiTab, EuiFlexGroup, EuiFlexItem, EuiFieldText, EuiButtonIcon } from '@elastic/eui';
function MarkdownSeries(props) {
const {
@ -45,15 +46,11 @@ function MarkdownSeries(props) {
const handleChange = createTextHandler(onChange);
const aggs = model.metrics.map(createAggRowRender(props));
let caretClassName = 'fa fa-caret-down';
if (!visible) caretClassName = 'fa fa-caret-right';
let caretIcon = 'arrowDown';
if (!visible) caretIcon = 'arrowRight';
let body = null;
if (visible) {
let metricsClassName = 'kbnTabs__tab';
let optionsClassname = 'kbnTabs__tab';
if (selectedTab === 'metrics') metricsClassName += '-active';
if (selectedTab === 'options') optionsClassname += '-active';
let seriesBody;
if (selectedTab === 'metrics') {
const handleSort = (data) => {
@ -67,19 +64,17 @@ function MarkdownSeries(props) {
dynamic={true}
direction="vertical"
onSort={handleSort}
sortHandle="vis_editor__agg_sort"
sortHandle="tvbAggRow__sortHandle"
>
{ aggs }
</Sortable>
<div className="vis_editor__series_row">
<div className="vis_editor__series_row-item">
<Split
onChange={props.onChange}
fields={fields}
panel={panel}
model={model}
/>
</div>
<div className="tvbAggRow tvbAggRow--split">
<Split
onChange={props.onChange}
fields={fields}
panel={panel}
model={model}
/>
</div>
</div>
);
@ -93,24 +88,22 @@ function MarkdownSeries(props) {
);
}
body = (
<div className="vis_editor__series-row">
<div className="kbnTabs sm" role="tablist">
<button
role="tab"
aria-selected={selectedTab === 'metrics'}
className={metricsClassName}
<div className="tvbSeries__body">
<EuiTabs size="s">
<EuiTab
isSelected={selectedTab === 'metrics'}
onClick={() => props.switchTab('metrics')}
>Metrics
</button>
<button
role="tab"
>
Metrics
</EuiTab>
<EuiTab
data-test-subj="seriesOptions"
aria-selected={selectedTab === 'metrics'}
className={optionsClassname}
isSelected={selectedTab === 'options'}
onClick={() => props.switchTab('options')}
>Options
</button>
</div>
>
Options
</EuiTab>
</EuiTabs>
{seriesBody}
</div>
);
@ -118,47 +111,55 @@ function MarkdownSeries(props) {
return (
<div
className={`${props.className} vis_editor__series`}
className={`${props.className}`}
style={props.style}
onMouseDown={props.onMouseDown}
onTouchStart={props.onTouchStart}
>
<div className="vis_editor__container">
<div className="vis_editor__series-details">
<button
className="vis_editor__series-visibility-toggle"
<EuiFlexGroup responsive={false} gutterSize="s" alignItems="center">
<EuiFlexItem grow={false}>
<EuiButtonIcon
iconType={caretIcon}
color="text"
onClick={props.toggleVisible}
aria-label="Toggle series editor"
aria-expanded={props.visible}
>
<i className={caretClassName}/>
</button>
<div className="vis_editor__row vis_editor__row_item">
<input
className="vis_editor__input-grows vis_editor__row_item"
onChange={handleChange('label')}
placeholder="Label"
value={model.label}
/>
<input
className="vis_editor__input-grows"
onChange={handleChange('var_name')}
placeholder="Variable Name"
value={model.var_name}
/>
</div>
/>
</EuiFlexItem>
<EuiFlexItem>
<EuiFieldText
fullWidth
onChange={handleChange('label')}
placeholder="Label"
value={model.label}
/>
</EuiFlexItem>
<EuiFlexItem>
<EuiFieldText
fullWidth
onChange={handleChange('var_name')}
placeholder="Variable name"
value={model.var_name}
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<AddDeleteButtons
addTooltip="Add Series"
deleteTooltip="Delete Series"
cloneTooltip="Clone Series"
addTooltip="Add series"
deleteTooltip="Delete series"
cloneTooltip="Clone series"
onDelete={onDelete}
onClone={props.onClone}
onAdd={onAdd}
disableDelete={disableDelete}
disableAdd={disableAdd}
responsive={false}
/>
</div>
</div>
</EuiFlexItem>
</EuiFlexGroup>
{ body }
</div>
);

View file

@ -47,8 +47,8 @@ function MarkdownVisualization(props) {
...variables
}
);
let className = 'thorMarkdown';
let contentClassName = `thorMarkdown__content ${model.markdown_vertical_align}`;
let className = 'tvbMarkdown';
let contentClassName = `tvbMarkdown__content ${model.markdown_vertical_align}`;
if (model.markdown_scrollbars) contentClassName += ' scrolling';
if (reversed) className += ' reversed';
const markdownError = markdownSource instanceof Error ? markdownSource : null;
@ -63,7 +63,7 @@ function MarkdownVisualization(props) {
);
}
return (
<div className="dashboard__visualization" style={style}>
<div className="tvbVis" style={style}>
{markdown}
</div>
);

View file

@ -24,7 +24,7 @@ import AddDeleteButtons from '../../add_delete_buttons';
import { SeriesConfig } from '../../series_config';
import Sortable from 'react-anything-sortable';
import Split from '../../split';
import { EuiToolTip } from '@elastic/eui';
import { EuiToolTip, EuiTabs, EuiTab, EuiFlexGroup, EuiFlexItem, EuiFieldText, EuiButtonIcon } from '@elastic/eui';
import createAggRowRender from '../../lib/create_agg_row_render';
import createTextHandler from '../../lib/create_text_handler';
import { createUpDownHandler } from '../../lib/sort_keyhandler';
@ -48,15 +48,11 @@ function MetricSeries(props) {
const handleChange = createTextHandler(onChange);
const aggs = model.metrics.map(createAggRowRender(props));
let caretClassName = 'fa fa-caret-down';
if (!visible) caretClassName = 'fa fa-caret-right';
let caretIcon = 'arrowDown';
if (!visible) caretIcon = 'arrowRight';
let body = null;
if (visible) {
let metricsClassName = 'kbnTabs__tab';
let optionsClassname = 'kbnTabs__tab';
if (selectedTab === 'metrics') metricsClassName += '-active';
if (selectedTab === 'options') optionsClassname += '-active';
let seriesBody;
if (selectedTab === 'metrics') {
const handleSort = (data) => {
@ -70,19 +66,17 @@ function MetricSeries(props) {
dynamic={true}
direction="vertical"
onSort={handleSort}
sortHandle="vis_editor__agg_sort"
sortHandle="tvbAggRow__sortHandle"
>
{ aggs }
</Sortable>
<div className="vis_editor__series_row">
<div className="vis_editor__series_row-item">
<Split
onChange={props.onChange}
fields={fields}
panel={panel}
model={model}
/>
</div>
<div className="tvbAggRow tvbAggRow--split">
<Split
onChange={props.onChange}
fields={fields}
panel={panel}
model={model}
/>
</div>
</div>
);
@ -96,24 +90,22 @@ function MetricSeries(props) {
);
}
body = (
<div className="vis_editor__series-row">
<div className="kbnTabs sm" role="tablist">
<button
role="tab"
aria-selected={selectedTab === 'metrics'}
className={metricsClassName}
<div className="tvbSeries__body">
<EuiTabs size="s">
<EuiTab
isSelected={selectedTab === 'metrics'}
onClick={() => props.switchTab('metrics')}
>Metrics
</button>
<button
role="tab"
>
Metrics
</EuiTab>
<EuiTab
data-test-subj="seriesOptions"
aria-selected={selectedTab === 'options'}
className={optionsClassname}
isSelected={selectedTab === 'options'}
onClick={() => props.switchTab('options')}
>Options
</button>
</div>
>
Options
</EuiTab>
</EuiTabs>
{seriesBody}
</div>
);
@ -122,57 +114,65 @@ function MetricSeries(props) {
let colorPicker;
if (props.colorPicker) {
colorPicker = (
<ColorPicker
disableTrash={true}
onChange={props.onChange}
name="color"
value={model.color}
/>
<EuiFlexItem grow={false}>
<ColorPicker
disableTrash={true}
onChange={props.onChange}
name="color"
value={model.color}
/>
</EuiFlexItem>
);
}
let dragHandle;
if (!props.disableDelete) {
dragHandle = (
<EuiToolTip content="Sort">
<button
className="vis_editor__sort thor__button-outlined-default sm"
aria-label="Sort series by pressing up/down"
onKeyDown={createUpDownHandler(props.onShouldSortItem)}
>
<i className="fa fa-sort" />
</button>
</EuiToolTip>
<EuiFlexItem grow={false}>
<EuiToolTip content="Drag to sort">
<EuiButtonIcon
className="tvbSeries__sortHandle"
iconType="grab"
aria-label="Sort series by pressing up/down"
onKeyDown={createUpDownHandler(props.onShouldSortItem)}
/>
</EuiToolTip>
</EuiFlexItem>
);
}
return (
<div
className={`${props.className} vis_editor__series`}
className={`${props.className}`}
style={props.style}
onMouseDown={props.onMouseDown}
onTouchStart={props.onTouchStart}
>
<div className="vis_editor__container">
<div className="vis_editor__series-details">
<button
className="vis_editor__series-visibility-toggle"
<EuiFlexGroup responsive={false} gutterSize="s" alignItems="center">
<EuiFlexItem grow={false}>
<EuiButtonIcon
iconType={caretIcon}
color="text"
onClick={props.toggleVisible}
aria-label="Toggle series editor"
aria-expanded={props.visible}
>
<i className={caretClassName}/>
</button>
{ colorPicker }
<div className="vis_editor__row vis_editor__row_item">
<input
className="vis_editor__input-grows"
onChange={handleChange('label')}
placeholder="Label"
value={model.label}
/>
</div>
{ dragHandle }
/>
</EuiFlexItem>
{ colorPicker }
<EuiFlexItem>
<EuiFieldText
fullWidth
onChange={handleChange('label')}
placeholder="Label"
value={model.label}
/>
</EuiFlexItem>
{ dragHandle }
<EuiFlexItem grow={false}>
<AddDeleteButtons
addTooltip="Add Series"
deleteTooltip="Delete Series"
@ -182,9 +182,11 @@ function MetricSeries(props) {
onAdd={onAdd}
disableDelete={disableDelete}
disableAdd={disableAdd}
responsive={false}
/>
</div>
</div>
</EuiFlexItem>
</EuiFlexGroup>
{ body }
</div>
);

View file

@ -74,8 +74,10 @@ function MetricVisualization(props) {
params.reversed = color(panelBackgroundColor).luminosity() < 0.45;
}
const style = { backgroundColor: panelBackgroundColor };
params.backgroundColor = panelBackgroundColor;
return (
<div className="dashboard__visualization" style={style}>
<div className="tvbVis" style={style}>
<Metric {...params}/>
</div>
);

View file

@ -29,6 +29,15 @@ import ColorRules from '../../color_rules';
import {
htmlIdGenerator,
EuiComboBox,
EuiFlexGroup,
EuiFlexItem,
EuiFieldText,
EuiFormRow,
EuiCode,
EuiHorizontalRule,
EuiFormLabel,
EuiSpacer,
EuiTitle,
} from '@elastic/eui';
class TableSeriesConfig extends Component {
@ -65,67 +74,102 @@ class TableSeriesConfig extends Component {
});
return (
<div>
<div className="vis_editor__series_config-container">
<div className="vis_editor__series_config-row">
<div className="tvbAggRow">
<EuiFlexGroup gutterSize="s">
<EuiFlexItem grow={false}>
<DataFormatPicker
onChange={handleSelectChange('formatter')}
value={model.formatter}
/>
<label className="vis_editor__label" htmlFor={htmlId('valueTemplateInput')}>Template (eg.<code>{'{{value}}/s'}</code>)</label>
<input
id={htmlId('valueTemplateInput')}
className="vis_editor__input-grows"
onChange={handleTextChange('value_template')}
value={model.value_template}
/>
</div>
<div className="vis_editor__series_config-row">
<label className="vis_editor__label" htmlFor={htmlId('filterInput')}>Filter</label>
<input
</EuiFlexItem>
<EuiFlexItem>
<EuiFormRow
id={htmlId('template')}
label="Template"
helpText={<span>eg.<EuiCode>{'{{value}}/s'}</EuiCode></span>}
fullWidth
>
<EuiFieldText
onChange={handleTextChange('value_template')}
value={model.value_template}
fullWidth
/>
</EuiFormRow>
</EuiFlexItem>
</EuiFlexGroup>
<EuiHorizontalRule margin="s" />
<EuiFlexGroup responsive={false} wrap={true}>
<EuiFlexItem grow={true}>
<EuiFormRow
id={htmlId('filterInput')}
className="vis_editor__input-grows"
onChange={handleTextChange('filter')}
value={model.filter}
/>
<label className="vis_editor__label">Show Trend Arrows</label>
label="Filter"
fullWidth
>
<EuiFieldText
onChange={handleTextChange('filter')}
value={model.filter}
fullWidth
/>
</EuiFormRow>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiFormLabel>Show trend arrows?</EuiFormLabel>
<EuiSpacer size="s" />
<YesNo
value={model.trend_arrows}
name="trend_arrows"
onChange={this.props.onChange}
/>
</div>
<div className="vis_editor__series_config-row">
<div className="vis_editor__row_item">
</EuiFlexItem>
</EuiFlexGroup>
<EuiHorizontalRule margin="s" />
<EuiFlexGroup responsive={false} wrap={true}>
<EuiFlexItem grow={true}>
<EuiFormRow id={htmlId('field')} label="Field">
<FieldSelect
fields={this.props.fields}
indexPattern={this.props.panel.index_pattern}
value={model.aggregate_by}
onChange={handleSelectChange('aggregate_by')}
fullWidth
/>
</div>
<label className="vis_editor__label" htmlFor={htmlId('aggregateFunctionInput')}>Aggregate Function</label>
<div className="vis_editor__row_item">
</EuiFormRow>
</EuiFlexItem>
<EuiFlexItem grow={true}>
<EuiFormRow
id={htmlId('aggregateFunctionInput')}
label="Aggregate function"
fullWidth
>
<EuiComboBox
id={htmlId('aggregateFunctionInput')}
options={functionOptions}
selectedOptions={selectedAggFuncOption ? [selectedAggFuncOption] : []}
onChange={handleSelectChange('aggregate_function')}
singleSelection={true}
fullWidth
/>
</div>
</div>
<div className="vis_editor__series_config-row summarize__colorRules">
<ColorRules
primaryName="text"
primaryVarName="text"
hideSecondary={true}
model={model}
onChange={this.props.onChange}
name="color_rules"
/>
</div>
</div>
</EuiFormRow>
</EuiFlexItem>
</EuiFlexGroup>
<EuiHorizontalRule margin="s" />
<EuiTitle size="xxs"><span>Color rules</span></EuiTitle>
<EuiSpacer size="s" />
<ColorRules
primaryName="text"
primaryVarName="text"
hideSecondary={true}
model={model}
onChange={this.props.onChange}
name="color_rules"
/>
</div>
);
}

View file

@ -22,7 +22,7 @@ import PropTypes from 'prop-types';
import AddDeleteButtons from '../../add_delete_buttons';
import SeriesConfig from './config';
import Sortable from 'react-anything-sortable';
import { EuiToolTip } from '@elastic/eui';
import { EuiToolTip, EuiTabs, EuiTab, EuiFlexGroup, EuiFlexItem, EuiFieldText, EuiButtonIcon } from '@elastic/eui';
import createTextHandler from '../../lib/create_text_handler';
import createAggRowRender from '../../lib/create_agg_row_render';
import { createUpDownHandler } from '../../lib/sort_keyhandler';
@ -42,15 +42,11 @@ function TopNSeries(props) {
const handleChange = createTextHandler(onChange);
const aggs = model.metrics.map(createAggRowRender(props));
let caretClassName = 'fa fa-caret-down';
if (!visible) caretClassName = 'fa fa-caret-right';
let caretIcon = 'arrowDown';
if (!visible) caretIcon = 'arrowRight';
let body = null;
if (visible) {
let metricsClassName = 'kbnTabs__tab';
let optionsClassname = 'kbnTabs__tab';
if (selectedTab === 'metrics') metricsClassName += '-active';
if (selectedTab === 'options') optionsClassname += '-active';
let seriesBody;
if (selectedTab === 'metrics') {
const handleSort = (data) => {
@ -64,7 +60,7 @@ function TopNSeries(props) {
dynamic={true}
direction="vertical"
onSort={handleSort}
sortHandle="vis_editor__agg_sort"
sortHandle="tvbAggRow__sortHandle"
>
{ aggs }
</Sortable>
@ -81,22 +77,22 @@ function TopNSeries(props) {
);
}
body = (
<div className="vis_editor__series-row">
<div className="kbnTabs sm">
<div
className={metricsClassName}
<div className="tvbSeries__body">
<EuiTabs size="s">
<EuiTab
isSelected={selectedTab === 'metrics'}
onClick={() => props.switchTab('metrics')}
>
Metrics
</div>
<div
</EuiTab>
<EuiTab
data-test-subj="seriesOptions"
className={optionsClassname}
isSelected={selectedTab === 'options'}
onClick={() => props.switchTab('options')}
>
Options
</div>
</div>
</EuiTab>
</EuiTabs>
{seriesBody}
</div>
);
@ -105,40 +101,50 @@ function TopNSeries(props) {
let dragHandle;
if (!props.disableDelete) {
dragHandle = (
<EuiToolTip content="Sort">
<button
className="vis_editor__sort thor__button-outlined-default sm"
aria-label="Sort series by pressing up/down"
onKeyDown={createUpDownHandler(props.onShouldSortItem)}
>
<i className="fa fa-sort" />
</button>
</EuiToolTip>
<EuiFlexItem grow={false}>
<EuiToolTip content="Drag to sort">
<EuiButtonIcon
className="tvbSeries__sortHandle"
iconType="grab"
aria-label="Sort series by pressing up/down"
onKeyDown={createUpDownHandler(props.onShouldSortItem)}
/>
</EuiToolTip>
</EuiFlexItem>
);
}
return (
<div
className={`${props.className} vis_editor__series`}
className={`${props.className}`}
style={props.style}
onMouseDown={props.onMouseDown}
onTouchStart={props.onTouchStart}
>
<div className="vis_editor__container">
<div className="vis_editor__series-details">
<button className="vis_editor__series-visibility-toggle" onClick={props.toggleVisible}>
<i className={caretClassName}/>
</button>
<div className="vis_editor__row vis_editor__row_item">
<input
aria-label="Label"
className="vis_editor__input-grows"
onChange={handleChange('label')}
placeholder="Label"
value={model.label}
/>
</div>
{ dragHandle }
<EuiFlexGroup responsive={false} gutterSize="s" alignItems="center">
<EuiFlexItem grow={false}>
<EuiButtonIcon
iconType={caretIcon}
color="text"
onClick={props.toggleVisible}
aria-label="Toggle series editor"
aria-expanded={props.visible}
/>
</EuiFlexItem>
<EuiFlexItem>
<EuiFieldText
fullWidth
aria-label="Label"
onChange={handleChange('label')}
placeholder="Label"
value={model.label}
/>
</EuiFlexItem>
{ dragHandle }
<EuiFlexItem grow={false}>
<AddDeleteButtons
addTooltip="Add Series"
deleteTooltip="Delete Series"
@ -148,9 +154,11 @@ function TopNSeries(props) {
onAdd={onAdd}
disableDelete={disableDelete}
disableAdd={disableAdd}
responsive={false}
/>
</div>
</div>
</EuiFlexItem>
</EuiFlexGroup>
{ body }
</div>
);

View file

@ -23,7 +23,7 @@ import PropTypes from 'prop-types';
import tickFormatter from '../../lib/tick_formatter';
import calculateLabel from '../../../../common/calculate_label';
import { isSortable } from './is_sortable';
import { EuiToolTip } from '@elastic/eui';
import { EuiToolTip, EuiIcon } from '@elastic/eui';
import replaceVars from '../../lib/replace_vars';
function getColor(rules, colorKey, value) {
@ -62,24 +62,24 @@ class TableVis extends Component {
const value = formatter(item.last);
let trend;
if (column.trend_arrows) {
const trendClass = item.slope > 0 ? 'fa-long-arrow-up' : 'fa-long-arrow-down';
const trendIcon = item.slope > 0 ? 'sortUp' : 'sortDown';
trend = (
<span className="tsvb-table__trend">
<i className={`fa ${trendClass}`}/>
<span>
&nbsp; <EuiIcon type={trendIcon} color="subdued" />
</span>
);
}
const style = { color: getColor(column.color_rules, 'text', item.last) };
return (
<td key={`${rowId}-${item.id}`} className="tsvb-table__value" style={style}>
<span className="tsvb-table__value-display">{ value }</span>
<td key={`${rowId}-${item.id}`} data-test-subj="tvbTableVis__value" className="eui-textRight" style={style}>
<span>{ value }</span>
{trend}
</td>
);
});
return (
<tr key={rowId}>
<td className="tsvb-table__fieldName">{rowDisplay}</td>
<td>{rowDisplay}</td>
{columns}
</tr>
);
@ -109,12 +109,12 @@ class TableVis extends Component {
if (isSortable(metric)) {
let sortIcon;
if (sort.column === item.id) {
sortIcon = sort.order === 'asc' ? 'sort-asc' : 'sort-desc';
sortIcon = sort.order === 'asc' ? 'sortUp' : 'sortDown';
} else {
sortIcon = 'sort';
sortIcon = 'empty';
}
sortComponent = (
<i className={`fa fa-${sortIcon}`} />
<EuiIcon type={sortIcon} />
);
}
let headerContent = (
@ -122,13 +122,12 @@ class TableVis extends Component {
);
if (!isSortable(metric)) {
headerContent = (
<EuiToolTip content="This Column is Not Sortable">{headerContent}</EuiToolTip>
<EuiToolTip content="This column is not sortable">{headerContent}</EuiToolTip>
);
}
return (
<th
className="tsvb-table__columnName"
onClick={handleClick}
key={item.id}
scope="col"
@ -140,12 +139,12 @@ class TableVis extends Component {
const label = model.pivot_label || model.pivot_field || model.pivot_id;
let sortIcon;
if (sort.column === '_default_') {
sortIcon = sort.order === 'asc' ? 'sort-asc' : 'sort-desc';
sortIcon = sort.order === 'asc' ? 'sortUp' : 'sortDown';
} else {
sortIcon = 'sort';
sortIcon = 'empty';
}
const sortComponent = (
<i className={`fa fa-${sortIcon}`} />
<EuiIcon type={sortIcon} />
);
const handleSortClick = () => {
let order;
@ -158,7 +157,7 @@ class TableVis extends Component {
};
return (
<tr>
<th scope="col" onClick={handleSortClick}>{label} {sortComponent}</th>
<th className="eui-textLeft" scope="col" onClick={handleSortClick}>{label} {sortComponent}</th>
{ columns }
</tr>
);
@ -184,7 +183,6 @@ class TableVis extends Component {
rows = (
<tr>
<td
className="tsvb-table__noResults"
colSpan={model.series.length + 1}
>
{message}
@ -193,7 +191,7 @@ class TableVis extends Component {
);
}
return(
<div className={`dashboard__visualization ${reversedClass}`} data-test-subj="tableView">
<div className={`tvbVis ${reversedClass}`} data-test-subj="tableView">
<table className="table">
<thead>
{header}

View file

@ -27,6 +27,15 @@ import { IndexPattern } from '../../index_pattern';
import {
htmlIdGenerator,
EuiComboBox,
EuiFlexGroup,
EuiFlexItem,
EuiFieldText,
EuiFormRow,
EuiCode,
EuiHorizontalRule,
EuiFieldNumber,
EuiFormLabel,
EuiSpacer,
} from '@elastic/eui';
function TimeseriesConfig(props) {
@ -84,252 +93,311 @@ function TimeseriesConfig(props) {
let type;
if (model.chart_type === 'line') {
type = (
<div className="vis_editor__series_config-row">
<label className="vis_editor__label" htmlFor={htmlId('chartType')}>
Chart Type
</label>
<div className="vis_editor__item">
<EuiComboBox
isClearable={false}
<EuiFlexGroup gutterSize="s" responsive={false} wrap={true}>
<EuiFlexItem grow={false}>
<EuiFormRow
id={htmlId('chartType')}
options={chartTypeOptions}
selectedOptions={selectedChartTypeOption ? [selectedChartTypeOption] : []}
onChange={handleSelectChange('chart_type')}
singleSelection={true}
/>
</div>
<label className="vis_editor__label" htmlFor={htmlId('stacked')}>
Stacked
</label>
<div className="vis_editor__item">
<EuiComboBox
isClearable={false}
label="Chart type"
>
<EuiComboBox
isClearable={false}
options={chartTypeOptions}
selectedOptions={selectedChartTypeOption ? [selectedChartTypeOption] : []}
onChange={handleSelectChange('chart_type')}
singleSelection={true}
/>
</EuiFormRow>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiFormRow
id={htmlId('stacked')}
options={stackedOptions}
selectedOptions={selectedStackedOption ? [selectedStackedOption] : []}
onChange={handleSelectChange('stacked')}
singleSelection={true}
label="Stacked"
>
<EuiComboBox
isClearable={false}
options={stackedOptions}
selectedOptions={selectedStackedOption ? [selectedStackedOption] : []}
onChange={handleSelectChange('stacked')}
singleSelection={true}
/>
</EuiFormRow>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiFormRow
id={htmlId('fill')}
label="Fill (0 to 1)"
>
<EuiFieldNumber
step={0.1}
onChange={handleTextChange('fill')}
value={Number(model.fill)}
/>
</EuiFormRow>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiFormRow
id={htmlId('lineWidth')}
label="Line width"
>
<EuiFieldNumber
onChange={handleTextChange('line_width')}
value={Number(model.line_width)}
/>
</EuiFormRow>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiFormRow
id={htmlId('pointSize')}
label="Point size"
>
<EuiFieldNumber
onChange={handleTextChange('point_size')}
value={Number(model.point_size)}
/>
</EuiFormRow>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiFormLabel>Steps</EuiFormLabel>
<EuiSpacer size="s" />
<YesNo
value={model.steps}
name="steps"
onChange={props.onChange}
/>
</div>
<label className="vis_editor__label" htmlFor={htmlId('fill')}>
Fill (0 to 1)
</label>
<input
id={htmlId('fill')}
className="vis_editor__input-grows"
type="number"
step="0.1"
onChange={handleTextChange('fill')}
value={model.fill}
/>
<label className="vis_editor__label" htmlFor={htmlId('lineWidth')}>
Line Width
</label>
<input
id={htmlId('lineWidth')}
className="vis_editor__input-grows"
type="number"
onChange={handleTextChange('line_width')}
value={model.line_width}
/>
<label className="vis_editor__label" htmlFor={htmlId('pointSize')}>
Point Size
</label>
<input
id={htmlId('pointSize')}
className="vis_editor__input-grows"
type="number"
onChange={handleTextChange('point_size')}
value={model.point_size}
/>
<div className="vis_editor__label">Steps</div>
<YesNo
value={model.steps}
name="steps"
onChange={props.onChange}
/>
</div>
</EuiFlexItem>
</EuiFlexGroup>
);
}
if (model.chart_type === 'bar') {
type = (
<div className="vis_editor__series_config-row">
<label className="vis_editor__label" htmlFor={htmlId('chartType')}>
Chart Type
</label>
<div className="vis_editor__item">
<EuiComboBox
isClearable={false}
<EuiFlexGroup gutterSize="s" responsive={false} wrap={true}>
<EuiFlexItem grow={false}>
<EuiFormRow
id={htmlId('chartType')}
options={chartTypeOptions}
selectedOptions={selectedChartTypeOption ? [selectedChartTypeOption] : []}
onChange={handleSelectChange('chart_type')}
singleSelection={true}
/>
</div>
<label className="vis_editor__label" htmlFor={htmlId('stacked')}>
Stacked
</label>
<div className="vis_editor__item">
<EuiComboBox
isClearable={false}
label="Chart type"
>
<EuiComboBox
isClearable={false}
options={chartTypeOptions}
selectedOptions={selectedChartTypeOption ? [selectedChartTypeOption] : []}
onChange={handleSelectChange('chart_type')}
singleSelection={true}
/>
</EuiFormRow>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiFormRow
id={htmlId('stacked')}
options={stackedOptions}
selectedOptions={selectedStackedOption ? [selectedStackedOption] : []}
onChange={handleSelectChange('stacked')}
singleSelection={true}
/>
</div>
<label className="vis_editor__label" htmlFor={htmlId('fill')}>
Fill (0 to 1)
</label>
<input
id={htmlId('fill')}
className="vis_editor__input-grows"
type="number"
step="0.5"
onChange={handleTextChange('fill')}
value={model.fill}
/>
<label className="vis_editor__label" htmlFor={htmlId('lineWidth')}>
Line Width
</label>
<input
id={htmlId('lineWidth')}
className="vis_editor__input-grows"
type="number"
onChange={handleTextChange('line_width')}
value={model.line_width}
/>
</div>
label="Stacked"
>
<EuiComboBox
isClearable={false}
options={stackedOptions}
selectedOptions={selectedStackedOption ? [selectedStackedOption] : []}
onChange={handleSelectChange('stacked')}
singleSelection={true}
/>
</EuiFormRow>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiFormRow
id={htmlId('fill')}
label="Fill (0 to 1)"
>
<EuiFieldNumber
step={0.5}
onChange={handleTextChange('fill')}
value={Number(model.fill)}
/>
</EuiFormRow>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiFormRow
id={htmlId('lineWidth')}
label="Line width"
>
<EuiFieldNumber
onChange={handleTextChange('line_width')}
value={Number(model.line_width)}
/>
</EuiFormRow>
</EuiFlexItem>
</EuiFlexGroup>
);
}
const disableSeparateYaxis = model.separate_axis ? false : true;
return (
<div>
<div className="vis_editor__series_config-container">
<div className="vis_editor__series_config-row">
<div className="tvbAggRow">
<EuiFlexGroup gutterSize="s">
<EuiFlexItem grow={false}>
<DataFormatPicker
onChange={handleSelectChange('formatter')}
value={model.formatter}
/>
<label className="vis_editor__label" htmlFor={htmlId('template')}>
Template (eg.<code>{'{{value}}/s'}</code>)
</label>
<input
</EuiFlexItem>
<EuiFlexItem>
<EuiFormRow
id={htmlId('template')}
className="vis_editor__input-grows"
onChange={handleTextChange('value_template')}
value={model.value_template}
/>
</div>
{ type }
<div className="vis_editor__series_config-row">
<label className="vis_editor__label" htmlFor={htmlId('offset')}>
Offset series time by (1m, 1h, 1w, 1d)
</label>
<input
label="Template"
helpText={<span>eg.<EuiCode>{'{{value}}/s'}</EuiCode></span>}
fullWidth
>
<EuiFieldText
onChange={handleTextChange('value_template')}
value={model.value_template}
fullWidth
/>
</EuiFormRow>
</EuiFlexItem>
</EuiFlexGroup>
<EuiHorizontalRule margin="s" />
<EuiFormRow
id={htmlId('series_filter')}
label="Filter"
fullWidth
>
<EuiFieldText
onChange={handleTextChange('filter')}
value={model.filter}
fullWidth
/>
</EuiFormRow>
<EuiHorizontalRule margin="s" />
{ type }
<EuiHorizontalRule margin="s" />
<EuiFlexGroup responsive={false} wrap={true}>
<EuiFlexItem grow={true}>
<EuiFormRow
id={htmlId('offset')}
data-test-subj="offsetTimeSeries"
className="vis_editor__input-grows"
type="text"
onChange={handleTextChange('offset_time')}
value={model.offset_time}
/>
<div className="vis_editor__label">Hide in Legend</div>
label="Offset series time by (1m, 1h, 1w, 1d)"
>
<EuiFieldText
data-test-subj="offsetTimeSeries"
onChange={handleTextChange('offset_time')}
value={model.offset_time}
/>
</EuiFormRow>
</EuiFlexItem>
<EuiFlexItem grow={true}>
<EuiFormLabel>Hide in legend</EuiFormLabel>
<EuiSpacer size="s" />
<YesNo
value={model.hide_in_legend}
name="hide_in_legend"
onChange={props.onChange}
/>
<label className="vis_editor__label" htmlFor={htmlId('splitColor')}>
Split Color Theme
</label>
<div className="vis_editor__row_item">
</EuiFlexItem>
<EuiFlexItem grow={true}>
<EuiFormRow
id={htmlId('splitColor')}
label="Split color theme"
>
<EuiComboBox
isClearable={false}
id={htmlId('splitColor')}
options={splitColorOptions}
selectedOptions={selectedSplitColorOption ? [selectedSplitColorOption] : []}
onChange={handleSelectChange('split_color_mode')}
singleSelection={true}
/>
</div>
</div>
<div className="vis_editor__series_config-row">
<div className="vis_editor__label">Separate Axis</div>
</EuiFormRow>
</EuiFlexItem>
</EuiFlexGroup>
<EuiHorizontalRule margin="s" />
<EuiFlexGroup responsive={false} wrap={true}>
<EuiFlexItem grow={false}>
<EuiFormLabel>Separate axis?</EuiFormLabel>
<EuiSpacer size="s" />
<YesNo
value={model.separate_axis}
name="separate_axis"
onChange={props.onChange}
/>
<label className="vis_editor__label" htmlFor={htmlId('axisMin')}>
Axis Min
</label>
<input
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiFormRow
id={htmlId('axisMin')}
className="vis_editor__input-grows"
type="number"
disabled={disableSeparateYaxis}
onChange={handleTextChange('axis_min')}
value={model.axis_min}
/>
<label className="vis_editor__label" htmlFor={htmlId('axisMax')}>
Axis Max
</label>
<input
label="Axis min"
>
{/*
EUITODO: The following input couldn't be converted to EUI because of type mis-match.
It accepts a null value, but is passed a empty string.
*/}
<input
className="tvbAgg__input"
type="number"
disabled={disableSeparateYaxis}
onChange={handleTextChange('axis_min')}
value={model.axis_min}
/>
</EuiFormRow>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiFormRow
id={htmlId('axisMax')}
className="vis_editor__input-grows"
type="number"
disabled={disableSeparateYaxis}
onChange={handleTextChange('axis_max')}
value={model.axis_max}
/>
<label className="vis_editor__label" htmlFor={htmlId('axisPos')}>
Axis Position
</label>
<div className="vis_editor__row_item">
label="Axis max"
>
{/*
EUITODO: The following input couldn't be converted to EUI because of type mis-match.
It accepts a null value, but is passed a empty string.
*/}
<input
className="tvbAgg__input"
disabled={disableSeparateYaxis}
onChange={handleTextChange('axis_max')}
value={model.axis_max}
/>
</EuiFormRow>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiFormRow
id={htmlId('axisPos')}
label="Axis position"
>
<EuiComboBox
isClearable={false}
isDisabled={disableSeparateYaxis}
id={htmlId('axisPos')}
options={positionOptions}
selectedOptions={selectedAxisPosOption ? [selectedAxisPosOption] : []}
onChange={handleSelectChange('axis_position')}
singleSelection={true}
/>
</div>
</div>
<div className="vis_editor__series_config-row">
<div className="vis_editor__label">Override Index Pattern</div>
</EuiFormRow>
</EuiFlexItem>
</EuiFlexGroup>
<EuiHorizontalRule margin="s" />
<EuiFlexGroup gutterSize="s" responsive={false} wrap={true}>
<EuiFlexItem grow={false}>
<EuiFormLabel>Override Index Pattern?</EuiFormLabel>
<EuiSpacer size="s" />
<YesNo
value={model.override_index_pattern}
name="override_index_pattern"
onChange={props.onChange}
/>
</EuiFlexItem>
<EuiFlexItem>
<IndexPattern
{...props}
prefix="series_"
className="vis_editor__row_item vis_editor__row"
disabled={!model.override_index_pattern}
with-interval={true}
/>
</div>
<div className="vis_editor__series_config-row">
<label className="vis_editor__label" htmlFor={htmlId('series_filter')}>
Filter
</label>
<input
id={htmlId('series_filter')}
className="vis_editor__input-grows"
type="text"
onChange={handleTextChange('filter')}
value={model.filter}
/>
</div>
</div>
</EuiFlexItem>
</EuiFlexGroup>
</div>
);

View file

@ -23,7 +23,7 @@ import ColorPicker from '../../color_picker';
import AddDeleteButtons from '../../add_delete_buttons';
import SeriesConfig from './config';
import Sortable from 'react-anything-sortable';
import { EuiToolTip } from '@elastic/eui';
import { EuiToolTip, EuiTabs, EuiTab, EuiFlexGroup, EuiFlexItem, EuiFieldText, EuiButtonIcon } from '@elastic/eui';
import Split from '../../split';
import createAggRowRender from '../../lib/create_agg_row_render';
import createTextHandler from '../../lib/create_text_handler';
@ -48,15 +48,11 @@ function TimeseriesSeries(props) {
const handleChange = createTextHandler(onChange);
const aggs = model.metrics.map(createAggRowRender(props));
let caretClassName = 'fa fa-caret-down';
if (!visible) caretClassName = 'fa fa-caret-right';
let caretIcon = 'arrowDown';
if (!visible) caretIcon = 'arrowRight';
let body = null;
if (visible) {
let metricsClassName = 'kbnTabs__tab';
let optionsClassname = 'kbnTabs__tab';
if (selectedTab === 'metrics') metricsClassName += '-active';
if (selectedTab === 'options') optionsClassname += '-active';
let seriesBody;
if (selectedTab === 'metrics') {
const handleSort = (data) => {
@ -70,19 +66,17 @@ function TimeseriesSeries(props) {
dynamic={true}
direction="vertical"
onSort={handleSort}
sortHandle="vis_editor__agg_sort"
sortHandle="tvbAggRow__sortHandle"
>
{ aggs }
</Sortable>
<div className="vis_editor__series_row">
<div className="vis_editor__series_row-item">
<Split
onChange={props.onChange}
fields={fields}
panel={panel}
model={model}
/>
</div>
<div className="tvbAggRow tvbAggRow--split">
<Split
onChange={props.onChange}
fields={fields}
panel={panel}
model={model}
/>
</div>
</div>
);
@ -96,24 +90,22 @@ function TimeseriesSeries(props) {
);
}
body = (
<div className="vis_editor__series-row">
<div className="kbnTabs sm" role="tablist">
<button
role="tab"
aria-selected={selectedTab === 'metrics'}
className={metricsClassName}
<div className="tvbSeries__body">
<EuiTabs size="s">
<EuiTab
isSelected={selectedTab === 'metrics'}
onClick={() => props.switchTab('metrics')}
>Metrics
</button>
<button
role="tab"
>
Metrics
</EuiTab>
<EuiTab
data-test-subj="seriesOptions"
aria-selected={selectedTab === 'options'}
className={optionsClassname}
isSelected={selectedTab === 'options'}
onClick={() => props.switchTab('options')}
>Options
</button>
</div>
>
Options
</EuiTab>
</EuiTabs>
{seriesBody}
</div>
);
@ -131,45 +123,53 @@ function TimeseriesSeries(props) {
let dragHandle;
if (!props.disableDelete) {
dragHandle = (
<EuiToolTip content="Sort">
<button
className="vis_editor__sort thor__button-outlined-default sm"
aria-label="Sort series by pressing up/down"
onKeyDown={createUpDownHandler(props.onShouldSortItem)}
>
<i className="fa fa-sort" />
</button>
</EuiToolTip>
<EuiFlexItem grow={false}>
<EuiToolTip content="Drag to sort">
<EuiButtonIcon
className="tvbSeries__sortHandle"
iconType="grab"
aria-label="Sort series by pressing up/down"
onKeyDown={createUpDownHandler(props.onShouldSortItem)}
/>
</EuiToolTip>
</EuiFlexItem>
);
}
return (
<div
className={`${props.className} vis_editor__series`}
className={`${props.className}`}
style={props.style}
onMouseDown={props.onMouseDown}
onTouchStart={props.onTouchStart}
>
<div className="vis_editor__container">
<div className="vis_editor__series-details">
<button
className="vis_editor__series-visibility-toggle"
<EuiFlexGroup responsive={false} gutterSize="s" alignItems="center">
<EuiFlexItem grow={false}>
<EuiButtonIcon
iconType={caretIcon}
color="text"
onClick={props.toggleVisible}
aria-label="Toggle series editor"
aria-expanded={props.visible}
>
<i className={caretClassName}/>
</button>
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
{ colorPicker }
<div className="vis_editor__row vis_editor__row_item">
<input
className="vis_editor__input-grows"
onChange={handleChange('label')}
placeholder="Label"
value={model.label}
/>
</div>
{ dragHandle }
</EuiFlexItem>
<EuiFlexItem>
<EuiFieldText
fullWidth
onChange={handleChange('label')}
placeholder="Label"
value={model.label}
/>
</EuiFlexItem>
{ dragHandle }
<EuiFlexItem grow={false}>
<AddDeleteButtons
addTooltip="Add Series"
deleteTooltip="Delete Series"
@ -179,9 +179,11 @@ function TimeseriesSeries(props) {
onAdd={onAdd}
disableDelete={disableDelete}
disableAdd={disableAdd}
responsive={false}
/>
</div>
</div>
</EuiFlexItem>
</EuiFlexGroup>
{ body }
</div>
);

View file

@ -201,7 +201,7 @@ class TimeseriesVisualization extends Component {
params.reversed = color(panelBackgroundColor || backgroundColor).luminosity() < 0.45;
}
return (
<div className="dashboard__visualization" style={style}>
<div className="tvbVis" style={style}>
<Timeseries {...params}/>
</div>
);

View file

@ -24,7 +24,7 @@ import AddDeleteButtons from '../../add_delete_buttons';
import { SeriesConfig } from '../../series_config';
import Sortable from 'react-anything-sortable';
import Split from '../../split';
import { EuiToolTip } from '@elastic/eui';
import { EuiToolTip, EuiTabs, EuiTab, EuiFlexGroup, EuiFlexItem, EuiFieldText, EuiButtonIcon } from '@elastic/eui';
import createTextHandler from '../../lib/create_text_handler';
import createAggRowRender from '../../lib/create_agg_row_render';
import { createUpDownHandler } from '../../lib/sort_keyhandler';
@ -46,15 +46,11 @@ function TopNSeries(props) {
const handleChange = createTextHandler(onChange);
const aggs = model.metrics.map(createAggRowRender(props));
let caretClassName = 'fa fa-caret-down';
if (!visible) caretClassName = 'fa fa-caret-right';
let caretIcon = 'arrowDown';
if (!visible) caretIcon = 'arrowRight';
let body = null;
if (visible) {
let metricsClassName = 'kbnTabs__tab';
let optionsClassname = 'kbnTabs__tab';
if (selectedTab === 'metrics') metricsClassName += '-active';
if (selectedTab === 'options') optionsClassname += '-active';
let seriesBody;
if (selectedTab === 'metrics') {
const handleSort = (data) => {
@ -68,19 +64,17 @@ function TopNSeries(props) {
dynamic={true}
direction="vertical"
onSort={handleSort}
sortHandle="vis_editor__agg_sort"
sortHandle="tvbAggRow__sortHandle"
>
{ aggs }
</Sortable>
<div className="vis_editor__series_row">
<div className="vis_editor__series_row-item">
<Split
onChange={props.onChange}
fields={fields}
panel={panel}
model={model}
/>
</div>
<div className="tvbAggRow tvbAggRow--split">
<Split
onChange={props.onChange}
fields={fields}
panel={panel}
model={model}
/>
</div>
</div>
);
@ -94,24 +88,22 @@ function TopNSeries(props) {
);
}
body = (
<div className="vis_editor__series-row">
<div className="kbnTabs sm" role="tablist">
<button
role="tab"
aria-selected={selectedTab === 'metrics'}
className={metricsClassName}
<div className="tvbSeries__body">
<EuiTabs size="s">
<EuiTab
isSelected={selectedTab === 'metrics'}
onClick={() => props.switchTab('metrics')}
>Metrics
</button>
<button
role="tab"
>
Metrics
</EuiTab>
<EuiTab
data-test-subj="seriesOptions"
aria-selected={selectedTab === 'options'}
className={optionsClassname}
isSelected={selectedTab === 'options'}
onClick={() => props.switchTab('options')}
>Options
</button>
</div>
>
Options
</EuiTab>
</EuiTabs>
{seriesBody}
</div>
);
@ -129,45 +121,53 @@ function TopNSeries(props) {
let dragHandle;
if (!props.disableDelete) {
dragHandle = (
<EuiToolTip content="Sort">
<button
className="vis_editor__sort thor__button-outlined-default sm"
aria-label="Sort series by pressing up/down"
onKeyDown={createUpDownHandler(props.onShouldSortItem)}
>
<i className="fa fa-sort" />
</button>
</EuiToolTip>
<EuiFlexItem grow={false}>
<EuiToolTip content="Drag to sort">
<EuiButtonIcon
className="tvbSeries__sortHandle"
iconType="grab"
aria-label="Sort series by pressing up/down"
onKeyDown={createUpDownHandler(props.onShouldSortItem)}
/>
</EuiToolTip>
</EuiFlexItem>
);
}
return (
<div
className={`${props.className} vis_editor__series`}
className={`${props.className}`}
style={props.style}
onMouseDown={props.onMouseDown}
onTouchStart={props.onTouchStart}
>
<div className="vis_editor__container">
<div className="vis_editor__series-details">
<button
className="vis_editor__series-visibility-toggle"
<EuiFlexGroup responsive={false} gutterSize="s" alignItems="center">
<EuiFlexItem grow={false}>
<EuiButtonIcon
iconType={caretIcon}
color="text"
onClick={props.toggleVisible}
aria-label="Toggle series editor"
aria-expanded={props.visible}
>
<i className={caretClassName}/>
</button>
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
{ colorPicker }
<div className="vis_editor__row vis_editor__row_item">
<input
className="vis_editor__input-grows"
onChange={handleChange('label')}
placeholder="Label"
value={model.label}
/>
</div>
{ dragHandle }
</EuiFlexItem>
<EuiFlexItem>
<EuiFieldText
fullWidth
onChange={handleChange('label')}
placeholder="Label"
value={model.label}
/>
</EuiFlexItem>
{ dragHandle }
<EuiFlexItem grow={false}>
<AddDeleteButtons
addTooltip="Add Series"
deleteTooltip="Delete Series"
@ -177,9 +177,11 @@ function TopNSeries(props) {
onAdd={onAdd}
disableDelete={disableDelete}
disableAdd={disableAdd}
responsive={false}
/>
</div>
</div>
</EuiFlexItem>
</EuiFlexGroup>
{ body }
</div>
);

View file

@ -93,7 +93,7 @@ function TopNVisualization(props) {
}
const style = { backgroundColor: panelBackgroundColor };
return (
<div className="dashboard__visualization" style={style}>
<div className="tvbVis" style={style}>
<TopN {...params}/>
</div>
);

View file

@ -65,22 +65,20 @@ export function visWithSplits(WrappedComponent) {
}
};
return (
<div key={key} className="splitVis_split">
<div className="splitVis_visualization">
<WrappedComponent
model={model}
visData={newVisData}
onBrush={props.onBrush}
additionalLabel={label}
backgroundColor={props.backgroundColor}
/>
</div>
<div key={key} className="tvbSplitVis__split">
<WrappedComponent
model={model}
visData={newVisData}
onBrush={props.onBrush}
additionalLabel={label}
backgroundColor={props.backgroundColor}
/>
</div>
);
});
return (
<div className="splitVis">{rows}</div>
<div className="tvbSplitVis">{rows}</div>
);
}
SplitVisComponent.displayName = `SplitVisComponent(${getDisplayName(WrappedComponent)})`;

View file

@ -79,10 +79,6 @@ function Visualization(props) {
return <div className={props.className} />;
}
Visualization.defaultProps = {
className: 'thor__visualization'
};
Visualization.propTypes = {
backgroundColor: PropTypes.string,
className: PropTypes.string,
@ -97,4 +93,8 @@ Visualization.propTypes = {
getConfig: PropTypes.func
};
Visualization.defaultProps = {
className: 'tvbVis'
};
export default Visualization;

View file

@ -20,6 +20,7 @@
import PropTypes from 'prop-types';
import React from 'react';
import _ from 'lodash';
import { EuiRadio, htmlIdGenerator } from '@elastic/eui';
function YesNo(props) {
const { name, value } = props;
@ -30,29 +31,31 @@ function YesNo(props) {
props.onChange(parts);
};
};
const htmlId = htmlIdGenerator();
const inputName = name + _.uniqueId();
return (
<div className="thor__yes_no">
<label>
<input
type="radio"
name={inputName}
checked={Boolean(value)}
value="yes"
onChange={handleChange(1)}
/>
Yes
</label>
<label>
<input
type="radio"
name={inputName}
checked={!Boolean(value)}
value="no"
onChange={handleChange(0)}
/>
No
</label>
<div>
<EuiRadio
id={htmlId('yes')}
label="Yes"
className="eui-displayInlineBlock"
name={inputName}
checked={Boolean(value)}
value="yes"
onChange={handleChange(1)}
/>
&emsp;
<EuiRadio
id={htmlId('no')}
label="No"
className="eui-displayInlineBlock"
name={inputName}
checked={!Boolean(value)}
value="no"
onChange={handleChange(0)}
/>
</div>
);
}

View file

@ -29,7 +29,7 @@ describe('YesNo', () => {
const wrapper = shallow(
<YesNo name="test" onChange={handleChange} />
);
wrapper.find('input').first().simulate('change');
wrapper.find('EuiRadio').first().simulate('change');
expect(handleChange.calledOnce).to.equal(true);
expect(handleChange.firstCall.args[0]).to.eql({
test: 1
@ -41,7 +41,7 @@ describe('YesNo', () => {
const wrapper = shallow(
<YesNo name="test" onChange={handleChange} />
);
wrapper.find('input').last().simulate('change');
wrapper.find('EuiRadio').last().simulate('change');
expect(handleChange.calledOnce).to.equal(true);
expect(handleChange.firstCall.args[0]).to.eql({
test: 0

View file

@ -0,0 +1,23 @@
@import 'src/ui/public/styles/styling_constants';
// Prefix all styles with "tvb" to avoid conflicts.
// Examples
// tvbChart
// tvbChart__legend
// tvbChart__legend--small
// tvbChart__legend-isLoading
@import './variables';
@import './mixins';
// Hacks (mostly todo's)
@import './hacks';
// Library overrides
@import './ui_sortable';
// Components
@import './components/index';
// Visualizations
@import './visualizations/components/index';

View file

@ -17,8 +17,6 @@
* under the License.
*/
import '../visualizations/less/main.less';
import '../less/main.less';
import { MetricsRequestHandlerProvider } from './request_handler';
import { ReactEditorControllerProvider } from './editor_controller';
import { VisFactoryProvider } from 'ui/vis/vis_factory';

View file

@ -1,89 +0,0 @@
.color_picker {
background-color: #fff;
border-radius: 2px;
box-shadow: 0 0 2px rgba(0,0,0,0.3), 0 4px 8px rgba(0,0,0,0.3);
box-sizing: initial;
width: 275px;
font-family: 'Menlo';
}
.color_picker__saturation {
width: 100%;
padding-bottom: 55%;
position: relative;
border-radius: 2px 2px 0 0;
overflow: hidden;
}
.color_picker__body {
padding: 16px 16px 12px;
}
.color_picker__controls {
display: flex;
}
.color_picker__color {
width: 32px;
}
.color_picker__color-disable_alpha {
width: 22px;
}
.color_picker__swatch {
margin-top: 6px;
width: 16px;
height: 16px;
border-radius: 8px;
position: relative;
overflow: hidden;
}
.color_picker__swatch-disable_alpha {
width: 10px;
height: 10px;
margin: 0px;
}
.color_picker__active {
position: absolute;
top: 0px;
left: 0px;
right: 0px;
bottom: 0px;
border-radius: 8px;
box-shadow: inset 0 0 0 1px rgba(0,0,0,0.1);
z-index: 2;
}
.color_picker__toggles {
flex: 1
}
.color_picker__hue {
height: 10px;
position: relative;
margin-bottom: 8px;
}
.color_picker__hue-disable_alpha {
margin-bottom: 0px;
}
.color_picker__alpha {
height: 10px;
position: relative;
}
.color_picker__alpha-disable_alpha {
display: none;
}
.color_picker__swatches {
display: flex;
flex-wrap: wrap;
justify-content: center;
margin-top: 10px;
}

View file

@ -1,35 +0,0 @@
.color_rules {
flex: 1 0 auto;
}
.color_rules__rule {
background-color: @grayLightest;
padding: 10px;
margin-bottom: 5px;
display: flex;
align-items: center;
flex: 1 0 auto;
}
.color_rules__item {
flex: 1 0 auto;
margin-right: 10px;
}
.color_rules__label {
font-weight: normal;
margin: 0 10px;
}
.color_rules__input {
padding: 6px 10px;
border-radius: 4px;
border: 1px solid @grayLight;
flex: 1 0 auto;
margin-right: 10px;
}
.color_rules__secondary {
display: flex;
align-items: center;
}

View file

@ -1,607 +0,0 @@
@borderRadius: 4px;
.vis_editor {
flex: 1;
}
.vis_editor_container {
background: @pageColor;
}
// general styles
.vis_editor__title {
display: flex;
flex-grow: 1;
align-items: center;
font-size: 20px;
margin-bottom: 20px;
i.fa {
color: @grayLighter;
margin: 0 10px;
}
i.fa-pencil {
&:hover {
color: @gray;
}
}
i.fa-check-square {
color: @esGreen;
&:hover {
color: darken(@esGreen, 10%);
}
}
input {
padding: 0 10px;
border-radius: 4px;
border: 1px solid @grayLighter;
flex-grow: 1;
}
}
.vis_editor__container {
padding: 10px;
background-color: @white;
display: flex;
flex-direction: column;
flex: 1 1 auto;
}
.vis_editor__label {
display: block;
font-size: 1em;
font-weight: normal;
color: @gray;
margin: 0 10px;
flex-shrink: 0;
&:first-child {
margin: 0 10px 0 0;
}
}
.vis_editor__note {
.vis_editor__label;
font-style: italic;
}
.vis_editor__input {
padding: 8px 10px;
border-radius: @borderRadius;
border: 1px solid @grayLight;
font-size: 1.1em;
}
.vis_editor__input-grows {
.vis_editor__input;
flex-grow: 1;
}
.vis_editor__input-grows-100 {
.vis_editor__input-grows;
width: 100%;
}
.vis_editor__input-number {
.vis_editor__input;
width: 60px;
}
.vis_editor__row {
display: flex;
align-items: center;
}
.vis_editor__item {
flex-grow: 1;
}
.vis_editor__row_item {
flex-grow: 1;
margin-right: 10px;
}
.vis_editor__subhead {
font-size: 12px;
color: @gray;
margin: 5px 0;
}
.vis_editor__subhead-main {
font-size: 18px;
color: @gray;
margin: 10px 10px 5px;
}
.vis_editor__note {
font-size: 14px;
color: @gray;
margin: 5px 0 10px 0;
}
// color_picker.js
.vis_editor__color_picker {
display: flex;
align-items: center;
position: relative;
}
.vis_editor__color_picker-swatch {
border: 1px solid @grayDark;
width: 20px;
height: 20px;
border-radius: 4px;
}
.vis_editor__color_picker-swatch-empty {
.vis_editor__color_picker-swatch;
background-color: transparent;
background-size: 20px 20px;
background-image: repeating-linear-gradient(
-45deg,
#c00,
#c00 2px,
transparent 2px,
transparent 16px
);
}
.vis_editor__color_picker-clear {
margin-left: 5px;
color: #c00;
}
.vis_editor__color_picker-popover {
position: absolute;
top: 20px;
z-index: 2;
}
.vis_editor__color_picker-cover {
position: fixed;
top: 0px;
right: 0px;
left: 0px;
bottom: 0px;
}
// data_format_picker
.vis_editor__data_format_picker-container {
display: flex;
align-items: center;
flex-grow: 1;
}
.vis_editor__data_format_picker-custom_row {
display: flex;
align-items: center;
> .vis_editor__label {
margin-left: 10px;
}
}
// index_pattern.js
.vis_editor__index_pattern-fields {
margin-right: 10px;
flex-grow: 1;
}
// series.js
// mainRow == .vis_editor__container
.vis_editor__series {
background-color: @white;
padding: 10px;
border-top: 2px solid @lineColor;
&:first-child {
border-top: none;
}
}
.vis_editor__series-row {
.vis_editor__container;
padding: 0 10px 10px;
display: flex;
flex-direction: column;
flex: 1 1 auto;
}
.vis_editor__series-details {
.vis_editor__row;
flex-grow: 1;
> * {
margin-right: 10px;
}
> .vis_editor__sort {
cursor: move;
margin-right: 0px;
}
}
.vis_editor__series-visibility-toggle {
appearance: none;
background: none;
border: none;
}
// series_config.js
.vis_editor__series_config-subhead {
.vis_editor__subhead;
margin: 10px 0 5px;
}
// series_editor.js
.vis_editor__series_editor-container {
margin-bottom: 20px;
}
//split.js
.vis_editor__split-container {
.vis_editor__row;
flex-grow: 1;
}
.vis_editor__split-filter {
.vis_editor__input;
flex-grow: 1;
}
.vis_editor__split-selects {
.vis_editor__item;
}
.vis_editor__split-aggs {
flex-grow: 1;
}
.vis_editor__split-term_count {
.vis_editor__input;
width: 60px;
}
// vis_picker.js
.vis_editor__vis_picker-container {
display: flex;
align-items: center;
}
.vis_editor__vis_picker-item {
appearance: none;
background: none;
border: none;
justify-content: center;
display: flex;
align-items: center;
font-size: 18px;
padding: 5px 0;
margin: 0 10px;
&:hover {
border-bottom: 2px solid @grayDarker;
}
&.selected {
border-bottom: 2px solid @grayDarker;
}
}
.vis_editor__vis_picker-icon {
display: none;
margin-right: 5px;
color: @grayDark;
&:hover,
&.selected {
color: @grayDarker;
}
}
.vis_editor__vis_picker-label {
font-size: 18px;
color: @grayDark;
&:hover,
&.selected {
color: @grayDarker;
}
}
.vis_editor__vis_picker-controls {
flex: 1 0 auto;
text-align: right;
margin-right: 10px;
}
// visualization.js
.vis_editor__visualization {
position: relative;
display: flex;
flex-direction: column;
flex: 1 0 auto;
width: 100%;
height: 250px;
line-height: normal;
background-color: @white;
overflow: auto;
}
.vis_editor__visualization-draghandle {
text-align: center;
color: @grayLight;
cursor: row-resize;
width: 100%;
display: block;
appearance: none;
background: none;
border: none;
&:hover {
color: @gray;
}
&:focus {
color: @esBlue;
box-shadow: none;
}
}
.vis_editor__visualization-title {
color: @gray;
font-weight: 500;
overflow: hidden;
white-space: nowrap;
font-size: 16px;
margin-bottom: 10px;
}
// aggs/agg_row
.vis_editor__agg_row-icon {
margin-right: 10px;
color: @gray;
&.last {
color: @grayDark;
}
}
.vis_editor__series_row {
display: flex;
background-color: @grayLightest;
margin-bottom: 2px;
padding: 10px;
align-items: center;
.vis_editor__note,
.vis_editor__label {
margin-bottom: 5px;
font-size: 12px;
}
}
.vis_editor__agg_row {
.vis_editor__series_row;
}
.vis_editor__series_row-item {
display: flex;
flex-grow: 1;
}
.vis_editor__agg_row-item {
.vis_editor__series_row-item;
margin-bottom: 10px;
}
// aggs/std_deviation.js
.vis_editor__std_deviation-field {
.vis_editor__row_item;
flex-grow: 2;
}
.vis_editor__std_deviation-sigma_item {
margin-right: 10px;
}
.vis_editor__std_deviation-sigma {
.vis_editor__input;
width: 50px;
}
.vis_editor__percentile_rank_value {
margin-right: 10px;
}
// aggs/std_sibling.js
.vis_editor__std_sibling-metric {
.vis_editor__row_item;
flex-grow: 2;
}
// aggs/vis_config
.vis_editor__vis_config-row {
.vis_editor__row;
margin: 10px 0;
}
// aggs/series_config
.vis_editor__series_config-container {
background-color: @grayLightest;
padding: 10px;
}
.vis_editor__series_config-row {
.vis_editor__row;
padding: 5px 0;
font-size: 12px;
}
.vis_editor__percentiles,
.vis_editor__variables {
.vis_editor__row_item;
margin: 10px 0;
}
.vis_editor__calc_vars {
// background-color: @white;
// padding: 10px;
margin-right: 10px;
margin-bottom: 2px;
}
.vis_editor__percentiles-row,
.vis_editor__calc_vars-row {
display: flex;
margin-bottom: 10px;
align-items: center;
&:last-child {
margin-bottom: 0;
}
}
.vis_editor__calc_vars-name {
margin-right: 10px;
}
.vis_editor__calc_vars-var {
flex-grow: 1;
margin-right: 10px;
}
.vis_editor__percentiles-content {
display: flex;
align-items: center;
flex-grow: 1;
margin-right: 10px;
}
.vis_editor__markdown {
display: flex;
background-color: @white;
min-height: 500px;
}
.vis_editor__markdown-editor {
border: 2px solid @lineColor;
width: 50%;
flex: 1 0 auto;
}
.vis_editor__markdown-variables {
padding: 10px;
flex: 1 0 auto;
max-height: 500px;
overflow: auto;
width: 50%;
.table a {
text-decoration: none;
}
pre {
border-radius: 0;
border: none;
}
}
.vis_editor__no-markdown-variables {
margin-top: 60px;
margin-bottom: 10px;
padding-bottom: 60px;
text-align: center;
font-weight: bold;
border-bottom: 1px solid #d9d9d9;
}
.vis_editor__markdown-code-desc {
margin-bottom: 10px;
}
.vis_editor__ace-editor {
border: 2px solid @lineColor;
}
.vis_editor__split-filters {
flex-grow: 1;
display: flex;
padding: 10px 20px;
flex-direction: column;
}
.vis_editor__split-filter-row {
display: flex;
flex-grow: 1;
margin-bottom: 10px;
align-items: center;
&:last-child {
margin-bottom: 0;
}
}
.vis_editor__split-filter-item {
flex-grow: 1;
margin-right: 10px;
}
.vis_editor__split-filter-color {
margin-right: 10px;
}
.vis_editor__annotations-color,
.vis_editor__annotations-controls {
flex-shrink: 0;
}
.vis_editor__annotations-row {
display: flex;
padding: 10px;
background-color: @lineColor;
margin-bottom: 2px;
}
.vis_editor__annotations-missing {
padding: 30px 10px;
font-size: 16px;
p {
font-size: 16px;
}
text-align: center;
}
.vis_editor__annotations-content {
margin: 0 10px;
flex-grow: 1;
.vis_editor__row {
margin-bottom: 10px;
}
.vis_editor__row-item {
flex-grow: 1;
margin-left: 10px;
&:first-child {
margin-left: 0;
}
.vis_editor__label {
margin-bottom: 4px;
}
}
.vis_editor__row-item-small {
.vis_editor__row-item;
flex-grow: 0;
}
}
.vis_editor__icon_select-option {
margin: 0 5px;
}
.vis_editor__icon_select-value {
margin: 0 5px 0 0;
}
.vis_editor__agg_select-heading {
color: @black;
cursor: default;
}
.vis_editor__agg_select-note {
margin-left: 10px;
color: @gray;
font-size: 0.9em;
}
.vis_editor__dirty_controls {
padding: 8px 6px;
background-color: @grayLightest;
display: flex;
align-content: center;
}
.vis_editor__dirty_controls-button {
flex: 0 0 auto;
}
.vis_editor__dirty_controls-message {
flex: 1 0 auto;
color: @grayLight;
padding: 4px 10px 0;
}
.vis_editor__dirty_controls-message-dirty {
flex: 1 0 auto;
color: @gray;
padding: 4px 10px 0;
}
.vis_editor__dirty_controls-toggle-label {
padding: 4px 10px 0;
flex: 0 0 auto;
color: @gray;
font-weight: normal;
}
.vis_editor__dirty_controls-toggle {
flex: 0 0 auto;
}
.vis_editor__table-pivot-fields {
border-bottom: 2px solid @lineColor;
}

View file

@ -1,52 +0,0 @@
.metrics_issue {
display: flex;
flex-direction: column;
flex: 1 0 auto;
background-color: #b3ccd5;
color: #0079a5;
justify-content: center;
padding: 20px;
}
.metrics_issue__title {
text-align: center;
font-size: 18px;
font-weight: bold;
}
.metrics_error {
display: flex;
flex-direction: column;
flex: 1 0 auto;
background-color: #ffd9d9;
color: #c00;
justify-content: center;
padding: 20px;
height: 100%;
width: 100%;
}
.metrics_error__title {
text-align: center;
font-size: 18px;
font-weight: bold;
}
.metrics_error__additional {
margin-top: 10px;
padding: 0 20px;
}
.metrics_error__reason {
text-align: center;
}
.metrics_error__stack {
margin-top: 10px;
color: #fff;
border: 10px solid #fff;
background-color: #000;
font-family: "Courier New", Courier, monospace;
white-space: pre;
padding: 10px;
}

Some files were not shown because too many files have changed in this diff Show more