mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[Canvas] Add simple variables to workpads (#66139)
* Add simple variables to Canvas workpads * Fix type for workpad variable action and clarify comment * Fix types in fixtures and templates * Fixing type check errors on actions * Addressing pr feedback and refactoring canvas sidebar accordions * Render true/false instead of Yes/no on variables * add warning callout when editing a variable * Address review feedback * More feedback * updating storyshot with new edit mode callout * Some animation tweaks for the panel * one more panel tweak * Removing the slide transition for now Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
parent
85d42535ea
commit
4d6ad89194
44 changed files with 2698 additions and 170 deletions
|
@ -84,6 +84,10 @@ import { RenderedElement } from '../shareable_runtime/components/rendered_elemen
|
|||
jest.mock('../shareable_runtime/components/rendered_element');
|
||||
RenderedElement.mockImplementation(() => 'RenderedElement');
|
||||
|
||||
import { EuiObserver } from '@elastic/eui/test-env/components/observer/observer';
|
||||
jest.mock('@elastic/eui/test-env/components/observer/observer');
|
||||
EuiObserver.mockImplementation(() => 'EuiObserver');
|
||||
|
||||
addSerializer(styleSheetSerializer);
|
||||
|
||||
// Initialize Storyshots and build the Jest Snapshots
|
||||
|
|
|
@ -25,6 +25,7 @@ const BaseWorkpad: CanvasWorkpad = {
|
|||
pages: [],
|
||||
colors: [],
|
||||
isWriteable: true,
|
||||
variables: [],
|
||||
};
|
||||
|
||||
const BasePage: CanvasPage = {
|
||||
|
|
|
@ -107,7 +107,7 @@ const EsdocsDatasource = ({ args, updateArgs, defaultIndex }) => {
|
|||
<EuiAccordion
|
||||
id="accordionAdvancedSettings"
|
||||
buttonContent="Advanced settings"
|
||||
className="canvasArg__accordion"
|
||||
className="canvasSidebar__accordion"
|
||||
>
|
||||
<EuiSpacer size="s" />
|
||||
<EuiFormRow label={strings.getSortFieldTitle()} display="columnCompressed">
|
||||
|
|
|
@ -545,7 +545,7 @@ export const ComponentStrings = {
|
|||
}),
|
||||
getTitle: () =>
|
||||
i18n.translate('xpack.canvas.pageConfig.title', {
|
||||
defaultMessage: 'Page styles',
|
||||
defaultMessage: 'Page settings',
|
||||
}),
|
||||
getTransitionLabel: () =>
|
||||
i18n.translate('xpack.canvas.pageConfig.transitionLabel', {
|
||||
|
@ -899,6 +899,144 @@ export const ComponentStrings = {
|
|||
defaultMessage: 'Close tray',
|
||||
}),
|
||||
},
|
||||
VarConfig: {
|
||||
getAddButtonLabel: () =>
|
||||
i18n.translate('xpack.canvas.varConfig.addButtonLabel', {
|
||||
defaultMessage: 'Add a variable',
|
||||
}),
|
||||
getAddTooltipLabel: () =>
|
||||
i18n.translate('xpack.canvas.varConfig.addTooltipLabel', {
|
||||
defaultMessage: 'Add a variable',
|
||||
}),
|
||||
getCopyActionButtonLabel: () =>
|
||||
i18n.translate('xpack.canvas.varConfig.copyActionButtonLabel', {
|
||||
defaultMessage: 'Copy snippet',
|
||||
}),
|
||||
getCopyActionTooltipLabel: () =>
|
||||
i18n.translate('xpack.canvas.varConfig.copyActionTooltipLabel', {
|
||||
defaultMessage: 'Copy variable syntax to clipboard',
|
||||
}),
|
||||
getCopyNotificationDescription: () =>
|
||||
i18n.translate('xpack.canvas.varConfig.copyNotificationDescription', {
|
||||
defaultMessage: 'Variable syntax copied to clipboard',
|
||||
}),
|
||||
getDeleteActionButtonLabel: () =>
|
||||
i18n.translate('xpack.canvas.varConfig.deleteActionButtonLabel', {
|
||||
defaultMessage: 'Delete variable',
|
||||
}),
|
||||
getDeleteNotificationDescription: () =>
|
||||
i18n.translate('xpack.canvas.varConfig.deleteNotificationDescription', {
|
||||
defaultMessage: 'Variable successfully deleted',
|
||||
}),
|
||||
getEditActionButtonLabel: () =>
|
||||
i18n.translate('xpack.canvas.varConfig.editActionButtonLabel', {
|
||||
defaultMessage: 'Edit variable',
|
||||
}),
|
||||
getEmptyDescription: () =>
|
||||
i18n.translate('xpack.canvas.varConfig.emptyDescription', {
|
||||
defaultMessage:
|
||||
'This workpad has no variables currently. You may add variables to store and edit common values. These variables can then be used in elements or within the expression editor.',
|
||||
}),
|
||||
getTableNameLabel: () =>
|
||||
i18n.translate('xpack.canvas.varConfig.tableNameLabel', {
|
||||
defaultMessage: 'Name',
|
||||
}),
|
||||
getTableTypeLabel: () =>
|
||||
i18n.translate('xpack.canvas.varConfig.tableTypeLabel', {
|
||||
defaultMessage: 'Type',
|
||||
}),
|
||||
getTableValueLabel: () =>
|
||||
i18n.translate('xpack.canvas.varConfig.tableValueLabel', {
|
||||
defaultMessage: 'Value',
|
||||
}),
|
||||
getTitle: () =>
|
||||
i18n.translate('xpack.canvas.varConfig.titleLabel', {
|
||||
defaultMessage: 'Variables',
|
||||
}),
|
||||
getTitleTooltip: () =>
|
||||
i18n.translate('xpack.canvas.varConfig.titleTooltip', {
|
||||
defaultMessage: 'Add variables to store and edit common values',
|
||||
}),
|
||||
},
|
||||
VarConfigDeleteVar: {
|
||||
getCancelButtonLabel: () =>
|
||||
i18n.translate('xpack.canvas.varConfigDeleteVar.cancelButtonLabel', {
|
||||
defaultMessage: 'Cancel',
|
||||
}),
|
||||
getDeleteButtonLabel: () =>
|
||||
i18n.translate('xpack.canvas.varConfigDeleteVar.deleteButtonLabel', {
|
||||
defaultMessage: 'Delete variable',
|
||||
}),
|
||||
getTitle: () =>
|
||||
i18n.translate('xpack.canvas.varConfigDeleteVar.titleLabel', {
|
||||
defaultMessage: 'Delete variable?',
|
||||
}),
|
||||
getWarningDescription: () =>
|
||||
i18n.translate('xpack.canvas.varConfigDeleteVar.warningDescription', {
|
||||
defaultMessage:
|
||||
'Deleting this variable may adversely affect the workpad. Are you sure you wish to continue?',
|
||||
}),
|
||||
},
|
||||
VarConfigEditVar: {
|
||||
getAddTitle: () =>
|
||||
i18n.translate('xpack.canvas.varConfigEditVar.addTitleLabel', {
|
||||
defaultMessage: 'Add variable',
|
||||
}),
|
||||
getCancelButtonLabel: () =>
|
||||
i18n.translate('xpack.canvas.varConfigEditVar.cancelButtonLabel', {
|
||||
defaultMessage: 'Cancel',
|
||||
}),
|
||||
getDuplicateNameError: () =>
|
||||
i18n.translate('xpack.canvas.varConfigEditVar.duplicateNameError', {
|
||||
defaultMessage: 'Variable name already in use',
|
||||
}),
|
||||
getEditTitle: () =>
|
||||
i18n.translate('xpack.canvas.varConfigEditVar.editTitleLabel', {
|
||||
defaultMessage: 'Edit variable',
|
||||
}),
|
||||
getEditWarning: () =>
|
||||
i18n.translate('xpack.canvas.varConfigEditVar.editWarning', {
|
||||
defaultMessage: 'Editing a variable in use may adversely affect your workpad',
|
||||
}),
|
||||
getNameFieldLabel: () =>
|
||||
i18n.translate('xpack.canvas.varConfigEditVar.nameFieldLabel', {
|
||||
defaultMessage: 'Name',
|
||||
}),
|
||||
getSaveButtonLabel: () =>
|
||||
i18n.translate('xpack.canvas.varConfigEditVar.saveButtonLabel', {
|
||||
defaultMessage: 'Save changes',
|
||||
}),
|
||||
getTypeBooleanLabel: () =>
|
||||
i18n.translate('xpack.canvas.varConfigEditVar.typeBooleanLabel', {
|
||||
defaultMessage: 'Boolean',
|
||||
}),
|
||||
getTypeFieldLabel: () =>
|
||||
i18n.translate('xpack.canvas.varConfigEditVar.typeFieldLabel', {
|
||||
defaultMessage: 'Type',
|
||||
}),
|
||||
getTypeNumberLabel: () =>
|
||||
i18n.translate('xpack.canvas.varConfigEditVar.typeNumberLabel', {
|
||||
defaultMessage: 'Number',
|
||||
}),
|
||||
getTypeStringLabel: () =>
|
||||
i18n.translate('xpack.canvas.varConfigEditVar.typeStringLabel', {
|
||||
defaultMessage: 'String',
|
||||
}),
|
||||
getValueFieldLabel: () =>
|
||||
i18n.translate('xpack.canvas.varConfigEditVar.valueFieldLabel', {
|
||||
defaultMessage: 'Value',
|
||||
}),
|
||||
},
|
||||
VarConfigVarValueField: {
|
||||
getFalseOption: () =>
|
||||
i18n.translate('xpack.canvas.varConfigVarValueField.falseOption', {
|
||||
defaultMessage: 'False',
|
||||
}),
|
||||
getTrueOption: () =>
|
||||
i18n.translate('xpack.canvas.varConfigVarValueField.trueOption', {
|
||||
defaultMessage: 'True',
|
||||
}),
|
||||
},
|
||||
WorkpadConfig: {
|
||||
getApplyStylesheetButtonLabel: () =>
|
||||
i18n.translate('xpack.canvas.workpadConfig.applyStylesheetButtonLabel', {
|
||||
|
|
|
@ -120,7 +120,7 @@ class ArgFormComponent extends PureComponent {
|
|||
);
|
||||
|
||||
return (
|
||||
<div className={`canvasArg ${expandableLabel ? 'canvasArg--expandable' : null}`}>
|
||||
<div className={`canvasArg ${expandableLabel ? 'canvasSidebar__expandable' : null}`}>
|
||||
<ArgLabel
|
||||
className="resolved"
|
||||
argId={argId}
|
||||
|
|
|
@ -1,27 +1,3 @@
|
|||
.canvasArg--expandable + .canvasArg--expandable {
|
||||
margin-top: 0;
|
||||
|
||||
.canvasArg__accordion:before {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.canvasSidebar__panel {
|
||||
.canvasArg--expandable:last-child {
|
||||
.canvasArg__accordion {
|
||||
margin-bottom: (-$euiSizeS);
|
||||
}
|
||||
|
||||
.canvasArg__accordion:after {
|
||||
content: none;
|
||||
}
|
||||
|
||||
.canvasArg__accordion.euiAccordion-isOpen:after {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.canvasArg {
|
||||
margin-top: $euiSizeS;
|
||||
}
|
||||
|
@ -31,10 +7,6 @@
|
|||
padding: $euiSizeXS 0;
|
||||
}
|
||||
|
||||
.canvasArg__content {
|
||||
padding-top: $euiSizeS;
|
||||
}
|
||||
|
||||
.canvasArg__form {
|
||||
position: relative;
|
||||
}
|
||||
|
@ -43,38 +15,11 @@
|
|||
margin-left: -$euiSizeXL;
|
||||
}
|
||||
|
||||
.canvasArg__accordion {
|
||||
padding: $euiSizeS $euiSizeM;
|
||||
margin: 0 (-$euiSizeM);
|
||||
background: $euiColorLightestShade;
|
||||
position: relative;
|
||||
|
||||
.canvasSidebar__accordion {
|
||||
// don't let remove button position here if this is nested in an accordion
|
||||
.canvasArg__form {
|
||||
position: static;
|
||||
}
|
||||
|
||||
&.euiAccordion-isOpen {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
&:before,
|
||||
&:after {
|
||||
content: '';
|
||||
height: 1px;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
background: $euiColorLightShade;
|
||||
}
|
||||
|
||||
&:before {
|
||||
top: 0;
|
||||
}
|
||||
|
||||
&:after {
|
||||
bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
// this is a workaround since an EuiFormRow label cannot be passed in toggle.js
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { EuiFormRow, EuiAccordion, EuiText, EuiToolTip, EuiIcon } from '@elastic/eui';
|
||||
import { EuiFormRow, EuiAccordion, EuiToolTip, EuiIcon } from '@elastic/eui';
|
||||
// This is what is being generated by render() from the Arg class. It is called in FunctionForm
|
||||
|
||||
export const ArgLabel = (props) => {
|
||||
|
@ -17,18 +17,16 @@ export const ArgLabel = (props) => {
|
|||
{expandable ? (
|
||||
<EuiAccordion
|
||||
id={`accordion-${argId}`}
|
||||
className="canvasArg__accordion"
|
||||
className="canvasSidebar__accordion"
|
||||
buttonContent={
|
||||
<EuiToolTip content={help} position="left" className="canvasArg__tooltip">
|
||||
<EuiText size="s" color="subdued" htmlFor={`accordion-${argId}`}>
|
||||
{label}
|
||||
</EuiText>
|
||||
<span>{label}</span>
|
||||
</EuiToolTip>
|
||||
}
|
||||
extraAction={simpleArg}
|
||||
initialIsOpen={initialIsOpen}
|
||||
>
|
||||
<div className="canvasArg__content">{children}</div>
|
||||
<div className="canvasSidebar__accordionContent">{children}</div>
|
||||
</EuiAccordion>
|
||||
) : (
|
||||
simpleArg && (
|
||||
|
|
|
@ -15,10 +15,13 @@ export const DatasourcePreview = compose(
|
|||
withState('datatable', 'setDatatable'),
|
||||
lifecycle({
|
||||
componentDidMount() {
|
||||
interpretAst({
|
||||
type: 'expression',
|
||||
chain: [this.props.function],
|
||||
}).then(this.props.setDatatable);
|
||||
interpretAst(
|
||||
{
|
||||
type: 'expression',
|
||||
chain: [this.props.function],
|
||||
},
|
||||
{}
|
||||
).then(this.props.setDatatable);
|
||||
},
|
||||
}),
|
||||
branch(({ datatable }) => !datatable, renderComponent(Loading))
|
||||
|
|
|
@ -1,73 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { EuiFlexGroup, EuiFlexItem, EuiStat, EuiAccordion, EuiText, EuiSpacer } from '@elastic/eui';
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import { ComponentStrings } from '../../../i18n';
|
||||
|
||||
const { ElementConfig: strings } = ComponentStrings;
|
||||
|
||||
export const ElementConfig = ({ elementStats }) => {
|
||||
if (!elementStats) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { total, ready, error } = elementStats;
|
||||
const progress = total > 0 ? Math.round(((ready + error) / total) * 100) : 100;
|
||||
|
||||
return (
|
||||
<EuiAccordion
|
||||
id="canvas-element-stats"
|
||||
buttonContent={
|
||||
<EuiText size="s" color="subdued">
|
||||
{strings.getTitle()}
|
||||
</EuiText>
|
||||
}
|
||||
initialIsOpen={false}
|
||||
>
|
||||
<EuiSpacer size="s" />
|
||||
<EuiFlexGroup gutterSize="none">
|
||||
<EuiFlexItem>
|
||||
<EuiStat
|
||||
title={total}
|
||||
description={strings.getTotalLabel()}
|
||||
titleSize="xs"
|
||||
textAlign="center"
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<EuiStat
|
||||
title={ready}
|
||||
description={strings.getLoadedLabel()}
|
||||
titleSize="xs"
|
||||
textAlign="center"
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<EuiStat
|
||||
title={error}
|
||||
description={strings.getFailedLabel()}
|
||||
titleSize="xs"
|
||||
textAlign="center"
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<EuiStat
|
||||
title={progress + '%'}
|
||||
description={strings.getProgressLabel()}
|
||||
titleSize="xs"
|
||||
textAlign="center"
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiAccordion>
|
||||
);
|
||||
};
|
||||
|
||||
ElementConfig.propTypes = {
|
||||
elementStats: PropTypes.object,
|
||||
};
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { EuiFlexGroup, EuiFlexItem, EuiStat, EuiAccordion } from '@elastic/eui';
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import { ComponentStrings } from '../../../i18n';
|
||||
import { State } from '../../../types';
|
||||
|
||||
const { ElementConfig: strings } = ComponentStrings;
|
||||
|
||||
interface Props {
|
||||
elementStats: State['transient']['elementStats'];
|
||||
}
|
||||
|
||||
export const ElementConfig = ({ elementStats }: Props) => {
|
||||
if (!elementStats) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { total, ready, error } = elementStats;
|
||||
const progress = total > 0 ? Math.round(((ready + error) / total) * 100) : 100;
|
||||
|
||||
return (
|
||||
<div className="canvasSidebar__expandable">
|
||||
<EuiAccordion
|
||||
id="canvas-element-stats"
|
||||
buttonContent={strings.getTitle()}
|
||||
initialIsOpen={false}
|
||||
className="canvasSidebar__accordion"
|
||||
>
|
||||
<div className="canvasSidebar__accordionContent">
|
||||
<EuiFlexGroup gutterSize="none">
|
||||
<EuiFlexItem>
|
||||
<EuiStat title={total} description={strings.getTotalLabel()} titleSize="xs" />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<EuiStat title={ready} description={strings.getLoadedLabel()} titleSize="xs" />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<EuiStat title={error} description={strings.getFailedLabel()} titleSize="xs" />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<EuiStat
|
||||
title={progress + '%'}
|
||||
description={strings.getProgressLabel()}
|
||||
titleSize="xs"
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</div>
|
||||
</EuiAccordion>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
ElementConfig.propTypes = {
|
||||
elementStats: PropTypes.object,
|
||||
};
|
|
@ -30,7 +30,7 @@ export const PageConfig = ({
|
|||
}) => {
|
||||
return (
|
||||
<Fragment>
|
||||
<EuiTitle size="xxxs" className="canvasSidebar__panelTitleHeading">
|
||||
<EuiTitle size="xs" className="canvasSidebar__panelTitleHeading">
|
||||
<h4>{strings.getTitle()}</h4>
|
||||
</EuiTitle>
|
||||
<EuiSpacer size="s" />
|
||||
|
|
|
@ -17,8 +17,6 @@ export const GlobalConfig: FunctionComponent = () => (
|
|||
<Fragment>
|
||||
<SidebarSection>
|
||||
<WorkpadConfig />
|
||||
</SidebarSection>
|
||||
<SidebarSection>
|
||||
<ElementConfig />
|
||||
</SidebarSection>
|
||||
<SidebarSection>
|
||||
|
|
|
@ -31,12 +31,68 @@
|
|||
&--isEmpty {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.canvasSidebar__expandable:last-child {
|
||||
.canvasSidebar__accordion {
|
||||
margin-bottom: (-$euiSizeS);
|
||||
}
|
||||
|
||||
.canvasSidebar__accordion:after {
|
||||
content: none;
|
||||
}
|
||||
|
||||
.canvasSidebar__accordion.euiAccordion-isOpen:after {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.canvasSidebar__panel-noMinWidth .euiButton {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.canvasSidebar__expandable + .canvasSidebar__expandable {
|
||||
margin-top: 0;
|
||||
|
||||
.canvasSidebar__accordion:before {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.canvasSidebar__accordion {
|
||||
padding: $euiSizeM;
|
||||
margin: 0 (-$euiSizeM);
|
||||
background: $euiColorLightestShade;
|
||||
position: relative;
|
||||
|
||||
&.euiAccordion-isOpen {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
&:before,
|
||||
&:after {
|
||||
content: '';
|
||||
height: 1px;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
background: $euiColorLightShade;
|
||||
}
|
||||
|
||||
&:before {
|
||||
top: 0;
|
||||
}
|
||||
|
||||
&:after {
|
||||
bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.canvasSidebar__accordionContent {
|
||||
padding-top: $euiSize;
|
||||
padding-left: $euiSizeXS + $euiSizeS + $euiSize;
|
||||
}
|
||||
|
||||
@keyframes sidebarPop {
|
||||
0% {
|
||||
opacity: 0;
|
||||
|
|
|
@ -0,0 +1,109 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Storyshots components/Variables/DeleteVar default 1`] = `
|
||||
Array [
|
||||
<div
|
||||
className="canvasVarHeader__triggerWrapper"
|
||||
>
|
||||
<button
|
||||
className="canvasVarHeader__button"
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
className="canvasVarHeader__iconWrapper"
|
||||
>
|
||||
<div
|
||||
data-euiicon-type="sortLeft"
|
||||
style={
|
||||
Object {
|
||||
"verticalAlign": "top",
|
||||
}
|
||||
}
|
||||
/>
|
||||
</span>
|
||||
<span>
|
||||
<span
|
||||
className="canvasVarHeader__anchor"
|
||||
>
|
||||
Delete variable?
|
||||
</span>
|
||||
</span>
|
||||
</button>
|
||||
</div>,
|
||||
<div
|
||||
className="canvasSidebar__accordionContent"
|
||||
>
|
||||
<div>
|
||||
<div
|
||||
className="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--directionRow euiFlexGroup--responsive"
|
||||
>
|
||||
<div
|
||||
className="euiFlexItem euiFlexItem--flexGrowZero"
|
||||
>
|
||||
<div
|
||||
className="euiText euiText--small"
|
||||
>
|
||||
<div
|
||||
className="euiTextColor euiTextColor--subdued"
|
||||
>
|
||||
Deleting this variable may adversely affect the workpad. Are you sure you wish to continue?
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="euiSpacer euiSpacer--m"
|
||||
/>
|
||||
<div
|
||||
className="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow euiFlexGroup--responsive"
|
||||
>
|
||||
<div
|
||||
className="euiFlexItem euiFlexItem--flexGrowZero"
|
||||
>
|
||||
<button
|
||||
className="euiButton euiButton--danger euiButton--small euiButton--fill"
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
className="euiButton__content"
|
||||
>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
className="euiButton__icon"
|
||||
data-euiicon-type="trash"
|
||||
size="m"
|
||||
/>
|
||||
<span
|
||||
className="euiButton__text"
|
||||
>
|
||||
Delete variable
|
||||
</span>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
className="euiFlexItem euiFlexItem--flexGrowZero"
|
||||
>
|
||||
<button
|
||||
className="euiButtonEmpty euiButtonEmpty--primary euiButtonEmpty--small"
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
className="euiButtonEmpty__content"
|
||||
>
|
||||
<span
|
||||
className="euiButtonEmpty__text"
|
||||
>
|
||||
Cancel
|
||||
</span>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>,
|
||||
]
|
||||
`;
|
1236
x-pack/plugins/canvas/public/components/var_config/__examples__/__snapshots__/edit_var.stories.storyshot
generated
Normal file
1236
x-pack/plugins/canvas/public/components/var_config/__examples__/__snapshots__/edit_var.stories.storyshot
generated
Normal file
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,87 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Storyshots components/Variables/VarConfig default 1`] = `
|
||||
<div
|
||||
className="canvasSidebar__expandable canvasVarConfig__container "
|
||||
>
|
||||
<div
|
||||
className="canvasVarConfig__innerContainer"
|
||||
>
|
||||
<div
|
||||
className="euiAccordion canvasVarConfig__listView canvasSidebar__accordion"
|
||||
>
|
||||
<div
|
||||
className="euiAccordion__triggerWrapper"
|
||||
>
|
||||
<button
|
||||
aria-controls="accordion-variables"
|
||||
aria-expanded={false}
|
||||
className="euiAccordion__button"
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
className="euiAccordion__iconWrapper"
|
||||
>
|
||||
<div
|
||||
className="euiAccordion__icon"
|
||||
data-euiicon-type="arrowRight"
|
||||
size="m"
|
||||
/>
|
||||
</span>
|
||||
<span
|
||||
className="euiIEFlexWrapFix"
|
||||
>
|
||||
<span
|
||||
className="euiToolTipAnchor"
|
||||
onKeyUp={[Function]}
|
||||
onMouseOut={[Function]}
|
||||
onMouseOver={[Function]}
|
||||
>
|
||||
<span
|
||||
onBlur={[Function]}
|
||||
onFocus={[Function]}
|
||||
>
|
||||
Variables
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</button>
|
||||
<div
|
||||
className="euiAccordion__optionalAction"
|
||||
>
|
||||
<span
|
||||
className="euiToolTipAnchor"
|
||||
onKeyUp={[Function]}
|
||||
onMouseOut={[Function]}
|
||||
onMouseOver={[Function]}
|
||||
>
|
||||
<button
|
||||
aria-label="Add a variable"
|
||||
className="euiButtonIcon euiButtonIcon--primary"
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
className="euiButtonIcon__icon"
|
||||
data-euiicon-type="plusInCircle"
|
||||
size="m"
|
||||
/>
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="euiAccordion__childWrapper"
|
||||
id="accordion-variables"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className="canvasVarConfig__editView canvasSidebar__accordion"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import { storiesOf } from '@storybook/react';
|
||||
import React from 'react';
|
||||
|
||||
import { CanvasVariable } from '../../../../types';
|
||||
|
||||
import { DeleteVar } from '../delete_var';
|
||||
|
||||
const variable: CanvasVariable = {
|
||||
name: 'homeUrl',
|
||||
value: 'https://elastic.co',
|
||||
type: 'string',
|
||||
};
|
||||
|
||||
storiesOf('components/Variables/DeleteVar', module).add('default', () => (
|
||||
<DeleteVar selectedVar={variable} onDelete={action('onDelete')} onCancel={action('onCancel')} />
|
||||
));
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import { storiesOf } from '@storybook/react';
|
||||
import React from 'react';
|
||||
|
||||
import { CanvasVariable } from '../../../../types';
|
||||
|
||||
import { EditVar } from '../edit_var';
|
||||
|
||||
const variables: CanvasVariable[] = [
|
||||
{
|
||||
name: 'homeUrl',
|
||||
value: 'https://elastic.co',
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
name: 'bigNumber',
|
||||
value: 1000,
|
||||
type: 'number',
|
||||
},
|
||||
{
|
||||
name: 'zenMode',
|
||||
value: true,
|
||||
type: 'boolean',
|
||||
},
|
||||
];
|
||||
|
||||
storiesOf('components/Variables/EditVar', module)
|
||||
.add('new variable', () => (
|
||||
<EditVar
|
||||
variables={variables}
|
||||
selectedVar={null}
|
||||
onSave={action('onSave')}
|
||||
onCancel={action('onCancel')}
|
||||
/>
|
||||
))
|
||||
.add('edit variable (string)', () => (
|
||||
<EditVar
|
||||
variables={variables}
|
||||
selectedVar={variables[0]}
|
||||
onSave={action('onSave')}
|
||||
onCancel={action('onCancel')}
|
||||
/>
|
||||
))
|
||||
.add('edit variable (number)', () => (
|
||||
<EditVar
|
||||
variables={variables}
|
||||
selectedVar={variables[1]}
|
||||
onSave={action('onSave')}
|
||||
onCancel={action('onCancel')}
|
||||
/>
|
||||
))
|
||||
.add('edit variable (boolean)', () => (
|
||||
<EditVar
|
||||
variables={variables}
|
||||
selectedVar={variables[2]}
|
||||
onSave={action('onSave')}
|
||||
onCancel={action('onCancel')}
|
||||
/>
|
||||
));
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import { storiesOf } from '@storybook/react';
|
||||
import React from 'react';
|
||||
|
||||
import { CanvasVariable } from '../../../../types';
|
||||
|
||||
import { VarConfig } from '../var_config';
|
||||
|
||||
const variables: CanvasVariable[] = [
|
||||
{
|
||||
name: 'homeUrl',
|
||||
value: 'https://elastic.co',
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
name: 'bigNumber',
|
||||
value: 1000,
|
||||
type: 'number',
|
||||
},
|
||||
{
|
||||
name: 'zenMode',
|
||||
value: true,
|
||||
type: 'boolean',
|
||||
},
|
||||
];
|
||||
|
||||
storiesOf('components/Variables/VarConfig', module).add('default', () => (
|
||||
<VarConfig
|
||||
variables={variables}
|
||||
onCopyVar={action('onCopyVar')}
|
||||
onDeleteVar={action('onDeleteVar')}
|
||||
onAddVar={action('onAddVar')}
|
||||
onEditVar={action('onEditVar')}
|
||||
/>
|
||||
));
|
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React, { FC } from 'react';
|
||||
import {
|
||||
EuiIcon,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiButton,
|
||||
EuiButtonEmpty,
|
||||
EuiSpacer,
|
||||
EuiText,
|
||||
} from '@elastic/eui';
|
||||
import { CanvasVariable } from '../../../types';
|
||||
|
||||
import { ComponentStrings } from '../../../i18n';
|
||||
const { VarConfigDeleteVar: strings } = ComponentStrings;
|
||||
|
||||
import './var_panel.scss';
|
||||
|
||||
interface Props {
|
||||
selectedVar: CanvasVariable;
|
||||
onDelete: (v: CanvasVariable) => void;
|
||||
onCancel: () => void;
|
||||
}
|
||||
|
||||
export const DeleteVar: FC<Props> = ({ selectedVar, onCancel, onDelete }) => {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<div className="canvasVarHeader__triggerWrapper">
|
||||
<button className="canvasVarHeader__button" type="button" onClick={() => onCancel()}>
|
||||
<span className="canvasVarHeader__iconWrapper">
|
||||
<EuiIcon type="sortLeft" style={{ verticalAlign: 'top' }} />
|
||||
</span>
|
||||
<span>
|
||||
<span className="canvasVarHeader__anchor">{strings.getTitle()}</span>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div className="canvasSidebar__accordionContent">
|
||||
<div>
|
||||
<EuiFlexGroup>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiText color="subdued" size="s">
|
||||
{strings.getWarningDescription()}
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
|
||||
<EuiSpacer size="m" />
|
||||
|
||||
<EuiFlexGroup alignItems="center">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButton
|
||||
color="danger"
|
||||
size="s"
|
||||
fill
|
||||
onClick={() => onDelete(selectedVar)}
|
||||
iconType="trash"
|
||||
>
|
||||
{strings.getDeleteButtonLabel()}
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButtonEmpty size="s" onClick={() => onCancel()}>
|
||||
{strings.getCancelButtonLabel()}
|
||||
</EuiButtonEmpty>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</div>
|
||||
</div>
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,8 @@
|
|||
.canvasEditVar__typeOption {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.canvasEditVar__tokenIcon {
|
||||
margin-right: 15px;
|
||||
}
|
||||
}
|
189
x-pack/plugins/canvas/public/components/var_config/edit_var.tsx
Normal file
189
x-pack/plugins/canvas/public/components/var_config/edit_var.tsx
Normal file
|
@ -0,0 +1,189 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React, { useState, FC } from 'react';
|
||||
import {
|
||||
EuiIcon,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiToken,
|
||||
EuiSuperSelect,
|
||||
EuiForm,
|
||||
EuiFormRow,
|
||||
EuiFieldText,
|
||||
EuiButton,
|
||||
EuiButtonEmpty,
|
||||
EuiSpacer,
|
||||
EuiCallOut,
|
||||
} from '@elastic/eui';
|
||||
import { CanvasVariable } from '../../../types';
|
||||
|
||||
import { VarValueField } from './var_value_field';
|
||||
|
||||
import { ComponentStrings } from '../../../i18n';
|
||||
const { VarConfigEditVar: strings } = ComponentStrings;
|
||||
|
||||
import './edit_var.scss';
|
||||
import './var_panel.scss';
|
||||
|
||||
interface Props {
|
||||
selectedVar: CanvasVariable | null;
|
||||
variables: CanvasVariable[];
|
||||
onSave: (v: CanvasVariable) => void;
|
||||
onCancel: () => void;
|
||||
}
|
||||
|
||||
const checkDupeName = (newName: string, oldName: string | null, variables: CanvasVariable[]) => {
|
||||
const match = variables.find((v) => {
|
||||
// If the new name matches an existing variable and that
|
||||
// matched variable name isn't the old name, then there
|
||||
// is a duplicate
|
||||
return newName === v.name && (!oldName || v.name !== oldName);
|
||||
});
|
||||
|
||||
return !!match;
|
||||
};
|
||||
|
||||
export const EditVar: FC<Props> = ({ variables, selectedVar, onCancel, onSave }) => {
|
||||
// If there isn't a selected variable, we're creating a new var
|
||||
const isNew = selectedVar === null;
|
||||
|
||||
const [type, setType] = useState(isNew ? 'string' : selectedVar!.type);
|
||||
const [name, setName] = useState(isNew ? '' : selectedVar!.name);
|
||||
const [value, setValue] = useState(isNew ? '' : selectedVar!.value);
|
||||
|
||||
const hasDupeName = checkDupeName(name, selectedVar && selectedVar.name, variables);
|
||||
|
||||
const typeOptions = [
|
||||
{
|
||||
value: 'string',
|
||||
inputDisplay: (
|
||||
<div className="canvasEditVar__typeOption">
|
||||
<EuiToken iconType="tokenString" className="canvasEditVar__tokenIcon" />{' '}
|
||||
<span>{strings.getTypeStringLabel()}</span>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
value: 'number',
|
||||
inputDisplay: (
|
||||
<div className="canvasEditVar__typeOption">
|
||||
<EuiToken iconType="tokenNumber" className="canvasEditVar__tokenIcon" />{' '}
|
||||
<span>{strings.getTypeNumberLabel()}</span>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
value: 'boolean',
|
||||
inputDisplay: (
|
||||
<div className="canvasEditVar__typeOption">
|
||||
<EuiToken iconType="tokenBoolean" className="canvasEditVar__tokenIcon" />{' '}
|
||||
<span>{strings.getTypeBooleanLabel()}</span>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="canvasVarHeader__triggerWrapper">
|
||||
<button className="canvasVarHeader__button" type="button" onClick={() => onCancel()}>
|
||||
<span className="canvasVarHeader__iconWrapper">
|
||||
<EuiIcon type="sortLeft" style={{ verticalAlign: 'top' }} />
|
||||
</span>
|
||||
<span>
|
||||
<span className="canvasVarHeader__anchor">
|
||||
{isNew ? strings.getAddTitle() : strings.getEditTitle()}
|
||||
</span>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div className="canvasSidebar__accordionContent">
|
||||
{!isNew && (
|
||||
<div>
|
||||
<EuiCallOut
|
||||
title={strings.getEditWarning()}
|
||||
color="warning"
|
||||
iconType="alert"
|
||||
size="s"
|
||||
/>
|
||||
<EuiSpacer size="m" />
|
||||
</div>
|
||||
)}
|
||||
|
||||
<EuiForm component="form">
|
||||
<EuiFormRow label={strings.getTypeFieldLabel()} display="rowCompressed">
|
||||
<EuiSuperSelect
|
||||
options={typeOptions}
|
||||
valueOfSelected={type}
|
||||
onChange={(v) => {
|
||||
// Only have these types possible in the dropdown
|
||||
setType(v as CanvasVariable['type']);
|
||||
|
||||
// Reset default value
|
||||
if (v === 'boolean') {
|
||||
// Just setting a default value
|
||||
setValue(true);
|
||||
} else if (v === 'number') {
|
||||
// Setting default number
|
||||
setValue(0);
|
||||
} else {
|
||||
setValue('');
|
||||
}
|
||||
}}
|
||||
compressed={true}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
<EuiFormRow
|
||||
label={strings.getNameFieldLabel()}
|
||||
display="rowCompressed"
|
||||
isInvalid={hasDupeName}
|
||||
error={hasDupeName && strings.getDuplicateNameError()}
|
||||
>
|
||||
<EuiFieldText
|
||||
name="name"
|
||||
value={name}
|
||||
compressed={true}
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
isInvalid={hasDupeName}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
<EuiFormRow label={strings.getValueFieldLabel()} display="rowCompressed">
|
||||
<VarValueField type={type} value={value} onChange={(v) => setValue(v)} />
|
||||
</EuiFormRow>
|
||||
|
||||
<EuiSpacer size="m" />
|
||||
|
||||
<EuiFlexGroup alignItems="center">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButton
|
||||
color="secondary"
|
||||
size="s"
|
||||
fill
|
||||
onClick={() =>
|
||||
onSave({
|
||||
name,
|
||||
value,
|
||||
type,
|
||||
})
|
||||
}
|
||||
disabled={hasDupeName || !name}
|
||||
iconType="save"
|
||||
>
|
||||
{strings.getSaveButtonLabel()}
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButtonEmpty size="s" onClick={() => onCancel()}>
|
||||
{strings.getCancelButtonLabel()}
|
||||
</EuiButtonEmpty>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiForm>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
66
x-pack/plugins/canvas/public/components/var_config/index.tsx
Normal file
66
x-pack/plugins/canvas/public/components/var_config/index.tsx
Normal file
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React, { FC } from 'react';
|
||||
import copy from 'copy-to-clipboard';
|
||||
import { VarConfig as ChildComponent } from './var_config';
|
||||
import {
|
||||
withKibana,
|
||||
KibanaReactContextValue,
|
||||
KibanaServices,
|
||||
} from '../../../../../../src/plugins/kibana_react/public';
|
||||
import { CanvasServices } from '../../services';
|
||||
|
||||
import { ComponentStrings } from '../../../i18n';
|
||||
|
||||
import { CanvasVariable } from '../../../types';
|
||||
|
||||
const { VarConfig: strings } = ComponentStrings;
|
||||
|
||||
interface Props {
|
||||
kibana: KibanaReactContextValue<{ canvas: CanvasServices } & KibanaServices>;
|
||||
|
||||
variables: CanvasVariable[];
|
||||
setVariables: (variables: CanvasVariable[]) => void;
|
||||
}
|
||||
|
||||
const WrappedComponent: FC<Props> = ({ kibana, variables, setVariables }) => {
|
||||
const onDeleteVar = (v: CanvasVariable) => {
|
||||
const index = variables.findIndex((targetVar: CanvasVariable) => {
|
||||
return targetVar.name === v.name;
|
||||
});
|
||||
if (index !== -1) {
|
||||
const newVars = [...variables];
|
||||
newVars.splice(index, 1);
|
||||
setVariables(newVars);
|
||||
|
||||
kibana.services.canvas.notify.success(strings.getDeleteNotificationDescription());
|
||||
}
|
||||
};
|
||||
|
||||
const onCopyVar = (v: CanvasVariable) => {
|
||||
const snippetStr = `{var "${v.name}"}`;
|
||||
copy(snippetStr, { debug: true });
|
||||
kibana.services.canvas.notify.success(strings.getCopyNotificationDescription());
|
||||
};
|
||||
|
||||
const onAddVar = (v: CanvasVariable) => {
|
||||
setVariables([...variables, v]);
|
||||
};
|
||||
|
||||
const onEditVar = (oldVar: CanvasVariable, newVar: CanvasVariable) => {
|
||||
const existingVarIndex = variables.findIndex((v) => oldVar.name === v.name);
|
||||
|
||||
const newVars = [...variables];
|
||||
newVars[existingVarIndex] = newVar;
|
||||
|
||||
setVariables(newVars);
|
||||
};
|
||||
|
||||
return <ChildComponent {...{ variables, onCopyVar, onDeleteVar, onAddVar, onEditVar }} />;
|
||||
};
|
||||
|
||||
export const VarConfig = withKibana(WrappedComponent);
|
|
@ -0,0 +1,66 @@
|
|||
.canvasVarConfig__container {
|
||||
width: 100%;
|
||||
position: relative;
|
||||
|
||||
&.canvasVarConfig-isEditMode {
|
||||
.canvasVarConfig__innerContainer {
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.canvasVarConfig__list {
|
||||
table {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
thead tr th,
|
||||
thead tr td {
|
||||
border-bottom: none;
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
tbody tr td {
|
||||
border-top: none;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
tbody tr:hover {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
tbody tr:last-child td {
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
|
||||
.canvasVarConfig__innerContainer {
|
||||
width: calc(200% + 48px); // Account for the extra padding
|
||||
|
||||
position: relative;
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-content: stretch;
|
||||
|
||||
.canvasVarConfig__editView {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.canvasVarConfig__listView {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.canvasVarConfig__editView {
|
||||
width: 50%;
|
||||
height: 100%;
|
||||
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.canvasVarConfig__listView {
|
||||
width: 50%;
|
||||
|
||||
flex-shrink: 0;
|
||||
}
|
|
@ -0,0 +1,230 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React, { useState, FC } from 'react';
|
||||
import {
|
||||
EuiAccordion,
|
||||
EuiButtonIcon,
|
||||
EuiToken,
|
||||
EuiToolTip,
|
||||
EuiText,
|
||||
EuiInMemoryTable,
|
||||
EuiBasicTableColumn,
|
||||
EuiTableActionsColumnType,
|
||||
EuiSpacer,
|
||||
EuiButton,
|
||||
} from '@elastic/eui';
|
||||
|
||||
import { CanvasVariable } from '../../../types';
|
||||
import { ComponentStrings } from '../../../i18n';
|
||||
|
||||
import { EditVar } from './edit_var';
|
||||
import { DeleteVar } from './delete_var';
|
||||
|
||||
import './var_config.scss';
|
||||
|
||||
const { VarConfig: strings } = ComponentStrings;
|
||||
|
||||
enum PanelMode {
|
||||
List,
|
||||
Edit,
|
||||
Delete,
|
||||
}
|
||||
|
||||
const typeToToken = {
|
||||
number: 'tokenNumber',
|
||||
boolean: 'tokenBoolean',
|
||||
string: 'tokenString',
|
||||
};
|
||||
|
||||
interface Props {
|
||||
variables: CanvasVariable[];
|
||||
onCopyVar: (v: CanvasVariable) => void;
|
||||
onDeleteVar: (v: CanvasVariable) => void;
|
||||
onAddVar: (v: CanvasVariable) => void;
|
||||
onEditVar: (oldVar: CanvasVariable, newVar: CanvasVariable) => void;
|
||||
}
|
||||
|
||||
export const VarConfig: FC<Props> = ({
|
||||
variables,
|
||||
onCopyVar,
|
||||
onDeleteVar,
|
||||
onAddVar,
|
||||
onEditVar,
|
||||
}) => {
|
||||
const [panelMode, setPanelMode] = useState<PanelMode>(PanelMode.List);
|
||||
const [selectedVar, setSelectedVar] = useState<CanvasVariable | null>(null);
|
||||
|
||||
const selectAndEditVar = (v: CanvasVariable) => {
|
||||
setSelectedVar(v);
|
||||
setPanelMode(PanelMode.Edit);
|
||||
};
|
||||
|
||||
const selectAndDeleteVar = (v: CanvasVariable) => {
|
||||
setSelectedVar(v);
|
||||
setPanelMode(PanelMode.Delete);
|
||||
};
|
||||
|
||||
const actions: EuiTableActionsColumnType<CanvasVariable>['actions'] = [
|
||||
{
|
||||
type: 'icon',
|
||||
name: strings.getCopyActionButtonLabel(),
|
||||
description: strings.getCopyActionTooltipLabel(),
|
||||
icon: 'copyClipboard',
|
||||
onClick: onCopyVar,
|
||||
isPrimary: true,
|
||||
},
|
||||
{
|
||||
type: 'icon',
|
||||
name: strings.getEditActionButtonLabel(),
|
||||
description: '',
|
||||
icon: 'pencil',
|
||||
onClick: selectAndEditVar,
|
||||
},
|
||||
{
|
||||
type: 'icon',
|
||||
name: strings.getDeleteActionButtonLabel(),
|
||||
description: '',
|
||||
icon: 'trash',
|
||||
color: 'danger',
|
||||
onClick: selectAndDeleteVar,
|
||||
},
|
||||
];
|
||||
|
||||
const varColumns: Array<EuiBasicTableColumn<CanvasVariable>> = [
|
||||
{
|
||||
field: 'type',
|
||||
name: strings.getTableTypeLabel(),
|
||||
sortable: true,
|
||||
render: (varType: CanvasVariable['type'], _v: CanvasVariable) => {
|
||||
return <EuiToken iconType={typeToToken[varType]} />;
|
||||
},
|
||||
width: '50px',
|
||||
},
|
||||
{
|
||||
field: 'name',
|
||||
name: strings.getTableNameLabel(),
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
field: 'value',
|
||||
name: strings.getTableValueLabel(),
|
||||
sortable: true,
|
||||
truncateText: true,
|
||||
render: (value: CanvasVariable['value'], _v: CanvasVariable) => {
|
||||
return '' + value;
|
||||
},
|
||||
},
|
||||
{
|
||||
actions,
|
||||
width: '60px',
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`canvasSidebar__expandable canvasVarConfig__container ${
|
||||
panelMode !== PanelMode.List ? 'canvasVarConfig-isEditMode' : ''
|
||||
}`}
|
||||
>
|
||||
<div className="canvasVarConfig__innerContainer">
|
||||
<EuiAccordion
|
||||
id="accordion-variables"
|
||||
className="canvasVarConfig__listView canvasSidebar__accordion"
|
||||
buttonContent={
|
||||
<EuiToolTip
|
||||
content={strings.getTitleTooltip()}
|
||||
position="left"
|
||||
className="canvasArg__tooltip"
|
||||
>
|
||||
<span>{strings.getTitle()}</span>
|
||||
</EuiToolTip>
|
||||
}
|
||||
extraAction={
|
||||
<EuiToolTip position="top" content={strings.getAddTooltipLabel()}>
|
||||
<EuiButtonIcon
|
||||
color="primary"
|
||||
iconType="plusInCircle"
|
||||
aria-label={strings.getAddTooltipLabel()}
|
||||
onClick={() => {
|
||||
setSelectedVar(null);
|
||||
setPanelMode(PanelMode.Edit);
|
||||
}}
|
||||
/>
|
||||
</EuiToolTip>
|
||||
}
|
||||
>
|
||||
{variables.length !== 0 && (
|
||||
<div className="canvasSidebar__accordionContent">
|
||||
<EuiInMemoryTable
|
||||
className="canvasVarConfig__list"
|
||||
items={variables}
|
||||
columns={varColumns}
|
||||
hasActions={true}
|
||||
pagination={false}
|
||||
sorting={true}
|
||||
compressed
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{variables.length === 0 && (
|
||||
<div className="canvasSidebar__accordionContent">
|
||||
<EuiText color="subdued" size="s">
|
||||
{strings.getEmptyDescription()}
|
||||
</EuiText>
|
||||
<EuiSpacer size="m" />
|
||||
<EuiButton
|
||||
size="s"
|
||||
iconType="plusInCircle"
|
||||
onClick={() => setPanelMode(PanelMode.Edit)}
|
||||
>
|
||||
{strings.getAddButtonLabel()}
|
||||
</EuiButton>
|
||||
</div>
|
||||
)}
|
||||
</EuiAccordion>
|
||||
<div className="canvasVarConfig__editView canvasSidebar__accordion">
|
||||
{panelMode === PanelMode.Edit && (
|
||||
<EditVar
|
||||
variables={variables}
|
||||
selectedVar={selectedVar}
|
||||
onSave={(newVar: CanvasVariable) => {
|
||||
if (!selectedVar) {
|
||||
onAddVar(newVar);
|
||||
} else {
|
||||
onEditVar(selectedVar, newVar);
|
||||
}
|
||||
|
||||
setSelectedVar(null);
|
||||
setPanelMode(PanelMode.List);
|
||||
}}
|
||||
onCancel={() => {
|
||||
setSelectedVar(null);
|
||||
setPanelMode(PanelMode.List);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
{panelMode === PanelMode.Delete && selectedVar && (
|
||||
<DeleteVar
|
||||
selectedVar={selectedVar}
|
||||
onDelete={(v: CanvasVariable) => {
|
||||
onDeleteVar(v);
|
||||
|
||||
setSelectedVar(null);
|
||||
setPanelMode(PanelMode.List);
|
||||
}}
|
||||
onCancel={() => {
|
||||
setSelectedVar(null);
|
||||
setPanelMode(PanelMode.List);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,31 @@
|
|||
.canvasVarHeader__triggerWrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.canvasVarHeader__button {
|
||||
@include euiFontSize;
|
||||
text-align: left;
|
||||
|
||||
width: 100%;
|
||||
flex-grow: 1;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.canvasVarHeader__iconWrapper {
|
||||
width: $euiSize;
|
||||
height: $euiSize;
|
||||
|
||||
border-radius: $euiBorderRadius;
|
||||
|
||||
margin-right: $euiSizeS;
|
||||
margin-left: $euiSizeXS;
|
||||
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.canvasVarHeader__anchor {
|
||||
display: inline-block;
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React, { FC } from 'react';
|
||||
import { EuiFieldText, EuiFieldNumber, EuiButtonGroup } from '@elastic/eui';
|
||||
import { htmlIdGenerator } from '@elastic/eui';
|
||||
|
||||
import { CanvasVariable } from '../../../types';
|
||||
|
||||
import { ComponentStrings } from '../../../i18n';
|
||||
const { VarConfigVarValueField: strings } = ComponentStrings;
|
||||
|
||||
interface Props {
|
||||
type: CanvasVariable['type'];
|
||||
value: CanvasVariable['value'];
|
||||
onChange: (v: CanvasVariable['value']) => void;
|
||||
}
|
||||
|
||||
export const VarValueField: FC<Props> = ({ type, value, onChange }) => {
|
||||
const idPrefix = htmlIdGenerator()();
|
||||
|
||||
const options = [
|
||||
{
|
||||
id: `${idPrefix}-true`,
|
||||
label: strings.getTrueOption(),
|
||||
},
|
||||
{
|
||||
id: `${idPrefix}-false`,
|
||||
label: strings.getFalseOption(),
|
||||
},
|
||||
];
|
||||
|
||||
if (type === 'number') {
|
||||
return (
|
||||
<EuiFieldNumber
|
||||
compressed
|
||||
name="value"
|
||||
value={value as number}
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
/>
|
||||
);
|
||||
} else if (type === 'boolean') {
|
||||
return (
|
||||
<EuiButtonGroup
|
||||
name="value"
|
||||
options={options}
|
||||
idSelected={`${idPrefix}-${value}`}
|
||||
onChange={(id) => {
|
||||
const val = id.replace(`${idPrefix}-`, '') === 'true';
|
||||
onChange(val);
|
||||
}}
|
||||
buttonSize="compressed"
|
||||
isFullWidth
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<EuiFieldText
|
||||
compressed
|
||||
name="value"
|
||||
value={String(value)}
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
/>
|
||||
);
|
||||
};
|
|
@ -7,11 +7,17 @@
|
|||
import { connect } from 'react-redux';
|
||||
|
||||
import { get } from 'lodash';
|
||||
import { sizeWorkpad as setSize, setName, setWorkpadCSS } from '../../state/actions/workpad';
|
||||
import {
|
||||
sizeWorkpad as setSize,
|
||||
setName,
|
||||
setWorkpadCSS,
|
||||
updateWorkpadVariables,
|
||||
} from '../../state/actions/workpad';
|
||||
|
||||
import { getWorkpad } from '../../state/selectors/workpad';
|
||||
import { DEFAULT_WORKPAD_CSS } from '../../../common/lib/constants';
|
||||
import { WorkpadConfig as Component } from './workpad_config';
|
||||
import { State } from '../../../types';
|
||||
import { State, CanvasVariable } from '../../../types';
|
||||
|
||||
const mapStateToProps = (state: State) => {
|
||||
const workpad = getWorkpad(state);
|
||||
|
@ -23,6 +29,7 @@ const mapStateToProps = (state: State) => {
|
|||
height: get(workpad, 'height'),
|
||||
},
|
||||
css: get(workpad, 'css', DEFAULT_WORKPAD_CSS),
|
||||
variables: get(workpad, 'variables', []),
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -30,6 +37,7 @@ const mapDispatchToProps = {
|
|||
setSize,
|
||||
setName,
|
||||
setWorkpadCSS,
|
||||
setWorkpadVariables: (vars: CanvasVariable[]) => updateWorkpadVariables(vars),
|
||||
};
|
||||
|
||||
export const WorkpadConfig = connect(mapStateToProps, mapDispatchToProps)(Component);
|
||||
|
|
|
@ -19,10 +19,13 @@ import {
|
|||
EuiToolTip,
|
||||
EuiTextArea,
|
||||
EuiAccordion,
|
||||
EuiText,
|
||||
EuiButton,
|
||||
} from '@elastic/eui';
|
||||
|
||||
import { VarConfig } from '../var_config';
|
||||
|
||||
import { DEFAULT_WORKPAD_CSS } from '../../../common/lib/constants';
|
||||
import { CanvasVariable } from '../../../types';
|
||||
import { ComponentStrings } from '../../../i18n';
|
||||
|
||||
const { WorkpadConfig: strings } = ComponentStrings;
|
||||
|
@ -34,14 +37,16 @@ interface Props {
|
|||
};
|
||||
name: string;
|
||||
css?: string;
|
||||
variables: CanvasVariable[];
|
||||
setSize: ({ height, width }: { height: number; width: number }) => void;
|
||||
setName: (name: string) => void;
|
||||
setWorkpadCSS: (css: string) => void;
|
||||
setWorkpadVariables: (vars: CanvasVariable[]) => void;
|
||||
}
|
||||
|
||||
export const WorkpadConfig: FunctionComponent<Props> = (props) => {
|
||||
const [css, setCSS] = useState(props.css);
|
||||
const { size, name, setSize, setName, setWorkpadCSS } = props;
|
||||
const { size, name, setSize, setName, setWorkpadCSS, variables, setWorkpadVariables } = props;
|
||||
const rotate = () => setSize({ width: size.height, height: size.width });
|
||||
|
||||
const badges = [
|
||||
|
@ -129,23 +134,25 @@ export const WorkpadConfig: FunctionComponent<Props> = (props) => {
|
|||
</div>
|
||||
|
||||
<EuiSpacer size="m" />
|
||||
<div className="canvasArg--expandable">
|
||||
|
||||
<VarConfig variables={variables} setVariables={setWorkpadVariables} />
|
||||
|
||||
<div className="canvasSidebar__expandable">
|
||||
<EuiAccordion
|
||||
id="accordion-global-css"
|
||||
className="canvasArg__accordion"
|
||||
className="canvasSidebar__accordion"
|
||||
style={{ marginBottom: 0 }}
|
||||
buttonContent={
|
||||
<EuiToolTip
|
||||
content={strings.getGlobalCSSTooltip()}
|
||||
position="left"
|
||||
className="canvasArg__tooltip"
|
||||
>
|
||||
<EuiText size="s" color="subdued">
|
||||
{strings.getGlobalCSSLabel()}
|
||||
</EuiText>
|
||||
<span>{strings.getGlobalCSSLabel()}</span>
|
||||
</EuiToolTip>
|
||||
}
|
||||
>
|
||||
<div className="canvasArg__content">
|
||||
<div className="canvasSidebar__accordionContent">
|
||||
<EuiTextArea
|
||||
aria-label={strings.getGlobalCSSTooltip()}
|
||||
value={css}
|
||||
|
@ -169,7 +176,9 @@ WorkpadConfig.propTypes = {
|
|||
size: PropTypes.object.isRequired,
|
||||
name: PropTypes.string.isRequired,
|
||||
css: PropTypes.string,
|
||||
variables: PropTypes.array,
|
||||
setSize: PropTypes.func.isRequired,
|
||||
setName: PropTypes.func.isRequired,
|
||||
setWorkpadCSS: PropTypes.func.isRequired,
|
||||
setWorkpadVariables: PropTypes.func.isRequired,
|
||||
};
|
||||
|
|
|
@ -10,7 +10,7 @@ import { ExpressionFunctionDefinition } from 'src/plugins/expressions/public';
|
|||
import { interpretAst } from '../lib/run_interpreter';
|
||||
// @ts-expect-error untyped local
|
||||
import { getState } from '../state/store';
|
||||
import { getGlobalFilters } from '../state/selectors/workpad';
|
||||
import { getGlobalFilters, getWorkpadVariablesAsObject } from '../state/selectors/workpad';
|
||||
import { ExpressionValueFilter } from '../../types';
|
||||
import { getFunctionHelp } from '../../i18n';
|
||||
import { InitializeArguments } from '.';
|
||||
|
@ -79,7 +79,7 @@ export function filtersFunctionFactory(initialize: InitializeArguments): () => F
|
|||
if (filterList && filterList.length) {
|
||||
const filterExpression = filterList.join(' | ');
|
||||
const filterAST = fromExpression(filterExpression);
|
||||
return interpretAst(filterAST);
|
||||
return interpretAst(filterAST, getWorkpadVariablesAsObject(getState()));
|
||||
} else {
|
||||
const filterType = initialize.typesRegistry.get('filter');
|
||||
return filterType?.from(null, {});
|
||||
|
|
|
@ -15,8 +15,12 @@ interface Options {
|
|||
/**
|
||||
* Meant to be a replacement for plugins/interpreter/interpretAST
|
||||
*/
|
||||
export async function interpretAst(ast: ExpressionAstExpression): Promise<ExpressionValue> {
|
||||
return await expressionsService.getService().execute(ast).getData();
|
||||
export async function interpretAst(
|
||||
ast: ExpressionAstExpression,
|
||||
variables: Record<string, any>
|
||||
): Promise<ExpressionValue> {
|
||||
const context = { variables };
|
||||
return await expressionsService.getService().execute(ast, null, context).getData();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -24,6 +28,7 @@ export async function interpretAst(ast: ExpressionAstExpression): Promise<Expres
|
|||
*
|
||||
* @param {object} ast - Executable AST
|
||||
* @param {any} input - Initial input for AST execution
|
||||
* @param {object} variables - Variables to pass in to the intrepreter context
|
||||
* @param {object} options
|
||||
* @param {boolean} options.castToRender - try to cast to a type: render object?
|
||||
* @returns {promise}
|
||||
|
@ -31,17 +36,20 @@ export async function interpretAst(ast: ExpressionAstExpression): Promise<Expres
|
|||
export async function runInterpreter(
|
||||
ast: ExpressionAstExpression,
|
||||
input: ExpressionValue,
|
||||
variables: Record<string, any>,
|
||||
options: Options = {}
|
||||
): Promise<ExpressionValue> {
|
||||
const context = { variables };
|
||||
|
||||
try {
|
||||
const renderable = await expressionsService.getService().execute(ast, input).getData();
|
||||
const renderable = await expressionsService.getService().execute(ast, input, context).getData();
|
||||
|
||||
if (getType(renderable) === 'render') {
|
||||
return renderable;
|
||||
}
|
||||
|
||||
if (options.castToRender) {
|
||||
return runInterpreter(fromExpression('render'), renderable, {
|
||||
return runInterpreter(fromExpression('render'), renderable, variables, {
|
||||
castToRender: false,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ const validKeys = [
|
|||
'assets',
|
||||
'colors',
|
||||
'css',
|
||||
'variables',
|
||||
'height',
|
||||
'id',
|
||||
'isWriteable',
|
||||
|
@ -61,6 +62,7 @@ export function create(workpad) {
|
|||
return fetch.post(getApiPath(), {
|
||||
...sanitizeWorkpad({ ...workpad }),
|
||||
assets: workpad.assets || {},
|
||||
variables: workpad.variables || [],
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -73,7 +75,7 @@ export async function createFromTemplate(templateId) {
|
|||
export function get(workpadId) {
|
||||
return fetch.get(`${getApiPath()}/${workpadId}`).then(({ data: workpad }) => {
|
||||
// shim old workpads with new properties
|
||||
return { css: DEFAULT_WORKPAD_CSS, ...workpad };
|
||||
return { css: DEFAULT_WORKPAD_CSS, variables: [], ...workpad };
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,13 @@ import immutable from 'object-path-immutable';
|
|||
import { get, pick, cloneDeep, without } from 'lodash';
|
||||
import { toExpression, safeElementFromExpression } from '@kbn/interpreter/common';
|
||||
import { createThunk } from '../../lib/create_thunk';
|
||||
import { getPages, getNodeById, getNodes, getSelectedPageIndex } from '../selectors/workpad';
|
||||
import {
|
||||
getPages,
|
||||
getWorkpadVariablesAsObject,
|
||||
getNodeById,
|
||||
getNodes,
|
||||
getSelectedPageIndex,
|
||||
} from '../selectors/workpad';
|
||||
import { getValue as getResolvedArgsValue } from '../selectors/resolved_args';
|
||||
import { getDefaultElement } from '../defaults';
|
||||
import { ErrorStrings } from '../../../i18n';
|
||||
|
@ -96,13 +102,15 @@ export const fetchContext = createThunk(
|
|||
return i < index;
|
||||
});
|
||||
|
||||
const variables = getWorkpadVariablesAsObject(getState());
|
||||
|
||||
// get context data from a partial AST
|
||||
return interpretAst(
|
||||
{
|
||||
...element.ast,
|
||||
chain: astChain,
|
||||
},
|
||||
prevContextValue
|
||||
variables
|
||||
).then((value) => {
|
||||
dispatch(
|
||||
args.setValue({
|
||||
|
@ -114,7 +122,7 @@ export const fetchContext = createThunk(
|
|||
}
|
||||
);
|
||||
|
||||
const fetchRenderableWithContextFn = ({ dispatch }, element, ast, context) => {
|
||||
const fetchRenderableWithContextFn = ({ dispatch, getState }, element, ast, context) => {
|
||||
const argumentPath = [element.id, 'expressionRenderable'];
|
||||
dispatch(
|
||||
args.setLoading({
|
||||
|
@ -128,7 +136,9 @@ const fetchRenderableWithContextFn = ({ dispatch }, element, ast, context) => {
|
|||
value: renderable,
|
||||
});
|
||||
|
||||
return runInterpreter(ast, context, { castToRender: true })
|
||||
const variables = getWorkpadVariablesAsObject(getState());
|
||||
|
||||
return runInterpreter(ast, context, variables, { castToRender: true })
|
||||
.then((renderable) => {
|
||||
dispatch(getAction(renderable));
|
||||
})
|
||||
|
@ -172,7 +182,9 @@ export const fetchAllRenderables = createThunk(
|
|||
const ast = element.ast || safeElementFromExpression(element.expression);
|
||||
const argumentPath = [element.id, 'expressionRenderable'];
|
||||
|
||||
return runInterpreter(ast, null, { castToRender: true })
|
||||
const variables = getWorkpadVariablesAsObject(getState());
|
||||
|
||||
return runInterpreter(ast, null, variables, { castToRender: true })
|
||||
.then((renderable) => ({ path: argumentPath, value: renderable }))
|
||||
.catch((err) => {
|
||||
services.notify.getService().error(err);
|
||||
|
|
|
@ -10,7 +10,7 @@ import { createThunk } from '../../lib/create_thunk';
|
|||
import { getWorkpadColors } from '../selectors/workpad';
|
||||
// @ts-expect-error
|
||||
import { fetchAllRenderables } from './elements';
|
||||
import { CanvasWorkpad } from '../../../types';
|
||||
import { CanvasWorkpad, CanvasVariable } from '../../../types';
|
||||
|
||||
export const sizeWorkpad = createAction<{ height: number; width: number }>('sizeWorkpad');
|
||||
export const setName = createAction<string>('setName');
|
||||
|
@ -18,6 +18,7 @@ export const setWriteable = createAction<boolean>('setWriteable');
|
|||
export const setColors = createAction<string[]>('setColors');
|
||||
export const setRefreshInterval = createAction<number>('setRefreshInterval');
|
||||
export const setWorkpadCSS = createAction<string>('setWorkpadCSS');
|
||||
export const setWorkpadVariables = createAction<CanvasVariable[]>('setWorkpadVariables');
|
||||
export const enableAutoplay = createAction<boolean>('enableAutoplay');
|
||||
export const setAutoplayInterval = createAction<number>('setAutoplayInterval');
|
||||
export const resetWorkpad = createAction<void>('resetWorkpad');
|
||||
|
@ -38,6 +39,14 @@ export const removeColor = createThunk('removeColor', ({ dispatch, getState }, c
|
|||
dispatch(setColors(without(getWorkpadColors(getState()), color)));
|
||||
});
|
||||
|
||||
export const updateWorkpadVariables = createThunk(
|
||||
'updateWorkpadVariables',
|
||||
({ dispatch }, vars) => {
|
||||
dispatch(setWorkpadVariables(vars));
|
||||
dispatch(fetchAllRenderables());
|
||||
}
|
||||
);
|
||||
|
||||
export const setWorkpad = createThunk(
|
||||
'setWorkpad',
|
||||
(
|
||||
|
|
|
@ -81,6 +81,7 @@ export const getDefaultWorkpad = () => {
|
|||
'#FFFFFF',
|
||||
'rgba(255,255,255,0)', // 'transparent'
|
||||
],
|
||||
variables: [],
|
||||
isWriteable: true,
|
||||
};
|
||||
};
|
||||
|
|
|
@ -14,6 +14,7 @@ import {
|
|||
setName,
|
||||
setWriteable,
|
||||
setWorkpadCSS,
|
||||
setWorkpadVariables,
|
||||
resetWorkpad,
|
||||
} from '../actions/workpad';
|
||||
|
||||
|
@ -59,6 +60,10 @@ export const workpadReducer = handleActions(
|
|||
return { ...workpadState, css: payload };
|
||||
},
|
||||
|
||||
[setWorkpadVariables]: (workpadState, { payload }) => {
|
||||
return { ...workpadState, variables: payload };
|
||||
},
|
||||
|
||||
[resetWorkpad]: () => ({ ...getDefaultWorkpad() }),
|
||||
},
|
||||
{}
|
||||
|
|
|
@ -10,7 +10,14 @@ import { safeElementFromExpression, fromExpression } from '@kbn/interpreter/comm
|
|||
// @ts-expect-error untyped local
|
||||
import { append } from '../../lib/modify_path';
|
||||
import { getAssets } from './assets';
|
||||
import { State, CanvasWorkpad, CanvasPage, CanvasElement, ResolvedArgType } from '../../../types';
|
||||
import {
|
||||
State,
|
||||
CanvasWorkpad,
|
||||
CanvasPage,
|
||||
CanvasElement,
|
||||
CanvasVariable,
|
||||
ResolvedArgType,
|
||||
} from '../../../types';
|
||||
import {
|
||||
ExpressionContext,
|
||||
CanvasGroup,
|
||||
|
@ -49,6 +56,23 @@ export function getWorkpadPersisted(state: State) {
|
|||
return getWorkpad(state);
|
||||
}
|
||||
|
||||
export function getWorkpadVariables(state: State) {
|
||||
const workpad = getWorkpad(state);
|
||||
return get(workpad, 'variables', []);
|
||||
}
|
||||
|
||||
export function getWorkpadVariablesAsObject(state: State) {
|
||||
const variables = getWorkpadVariables(state);
|
||||
if (variables.length === 0) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return (variables as CanvasVariable[]).reduce(
|
||||
(vars: Record<string, any>, v: CanvasVariable) => ({ ...vars, [v.name]: v.value }),
|
||||
{}
|
||||
);
|
||||
}
|
||||
|
||||
export function getWorkpadInfo(state: State): WorkpadInfo {
|
||||
return {
|
||||
...getWorkpad(state),
|
||||
|
@ -326,7 +350,9 @@ export function getElements(
|
|||
return elements.map((el) => omit(el, ['ast']));
|
||||
}
|
||||
|
||||
return elements.map(appendAst);
|
||||
const elementAppendAst = (elem: CanvasElement) => appendAst(elem);
|
||||
|
||||
return elements.map(elementAppendAst);
|
||||
}
|
||||
|
||||
const augment = (type: string) => <T extends CanvasElement | CanvasGroup>(n: T): T => ({
|
||||
|
|
|
@ -51,12 +51,19 @@ export const WorkpadAssetSchema = schema.object({
|
|||
value: schema.string(),
|
||||
});
|
||||
|
||||
export const WorkpadVariable = schema.object({
|
||||
name: schema.string(),
|
||||
value: schema.oneOf([schema.string(), schema.number(), schema.boolean()]),
|
||||
type: schema.string(),
|
||||
});
|
||||
|
||||
export const WorkpadSchema = schema.object({
|
||||
'@created': schema.maybe(schema.string()),
|
||||
'@timestamp': schema.maybe(schema.string()),
|
||||
assets: schema.maybe(schema.recordOf(schema.string(), WorkpadAssetSchema)),
|
||||
colors: schema.arrayOf(schema.string()),
|
||||
css: schema.string(),
|
||||
variables: schema.arrayOf(WorkpadVariable),
|
||||
height: schema.number(),
|
||||
id: schema.string(),
|
||||
isWriteable: schema.maybe(schema.boolean()),
|
||||
|
|
|
@ -1644,5 +1644,6 @@ export const pitch: CanvasTemplate = {
|
|||
},
|
||||
css:
|
||||
".canvasPage h1, .canvasPage h2, .canvasPage h3, .canvasPage h4, .canvasPage h5 {\nfont-family: 'Futura';\ncolor: #444444;\n}\n\n.canvasPage h1 {\nfont-size: 112px;\nfont-weight: bold;\ncolor: #FFFFFF;\n}\n\n.canvasPage h2 {\nfont-size: 48px;\nfont-weight: bold;\n}\n\n.canvasPage h3 {\nfont-size: 30px;\nfont-weight: 300;\ntext-transform: uppercase;\ncolor: #FFFFFF;\n}\n\n.canvasPage h5 {\nfont-size: 24px;\nfont-style: italic;\n}",
|
||||
variables: [],
|
||||
},
|
||||
};
|
||||
|
|
|
@ -17,6 +17,7 @@ export const status: CanvasTemplate = {
|
|||
height: 792,
|
||||
css:
|
||||
'.canvasPage h1, .canvasPage h2, .canvasPage h3, .canvasPage h4, .canvasPage h5, .canvasPage h6, .canvasPage li, .canvasPage p, .canvasPage th, .canvasPage td {\nfont-family: "Gill Sans" !important;\ncolor: #333333;\n}\n\n.canvasPage h1, .canvasPage h2 {\nfont-weight: 400;\n}\n\n.canvasPage h2 {\ntext-transform: uppercase;\ncolor: #1785B0;\n}\n\n.canvasMarkdown p,\n.canvasMarkdown li {\nfont-size: 18px;\n}\n\n.canvasMarkdown li {\nmargin-bottom: .75em;\n}\n\n.canvasMarkdown h3:not(:first-child) {\nmargin-top: 2em;\n}\n\n.canvasMarkdown a {\ncolor: #1785B0;\n}\n\n.canvasMarkdown th,\n.canvasMarkdown td {\npadding: .5em 1em;\n}\n\n.canvasMarkdown th {\nbackground-color: #FAFBFD;\n}\n\n.canvasMarkdown table,\n.canvasMarkdown th,\n.canvasMarkdown td {\nborder: 1px solid #e4e9f2;\n}',
|
||||
variables: [],
|
||||
page: 0,
|
||||
pages: [
|
||||
{
|
||||
|
|
|
@ -493,5 +493,6 @@ export const summary: CanvasTemplate = {
|
|||
'@created': '2019-05-31T16:01:45.751Z',
|
||||
assets: {},
|
||||
css: 'h3 {\ncolor: #343741;\nfont-weight: 400;\n}\n\nh5 {\ncolor: #69707D;\n}',
|
||||
variables: [],
|
||||
},
|
||||
};
|
||||
|
|
|
@ -17,6 +17,7 @@ export const dark: CanvasTemplate = {
|
|||
height: 720,
|
||||
page: 0,
|
||||
css: '',
|
||||
variables: [],
|
||||
pages: [
|
||||
{
|
||||
id: 'page-fda26a1f-c096-44e4-a149-cb99e1038a34',
|
||||
|
|
|
@ -14,6 +14,7 @@ export const light: CanvasTemplate = {
|
|||
template: {
|
||||
name: 'Light',
|
||||
css: '',
|
||||
variables: [],
|
||||
width: 1080,
|
||||
height: 720,
|
||||
page: 0,
|
||||
|
|
|
@ -37,12 +37,19 @@ export interface CanvasPage {
|
|||
groups: CanvasGroup[];
|
||||
}
|
||||
|
||||
export interface CanvasVariable {
|
||||
name: string;
|
||||
value: boolean | number | string;
|
||||
type: 'boolean' | 'number' | 'string';
|
||||
}
|
||||
|
||||
export interface CanvasWorkpad {
|
||||
'@created': string;
|
||||
'@timestamp': string;
|
||||
assets: { [id: string]: CanvasAsset };
|
||||
colors: string[];
|
||||
css: string;
|
||||
variables: CanvasVariable[];
|
||||
height: number;
|
||||
id: string;
|
||||
isWriteable: boolean;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue