Feat: Workpad Templates (#23966)

* Added workpad manager which contains workpad_loader and workpad_templates

* Fixed term filter in workpad_templates

* design changes

* Removed console logs

Closes workpad manager modal after cloning template

Fixed filtering workpad templates

Removed console log

Added sample templates

Added more templates to test with

Removed cloneDeep

* case insensitive template search

* Case insensitive tag order in popover

* added descriptions and tags to sample data workpads

* refine list of initial templates

* remove sample data templates, make buttons bigger

* Added template and tag registries

* Fixed workpad loader resizing issue on home page

* Moved tags to ui folder

* Fixed template class

* Fixed properties in templates to match workpad
This commit is contained in:
Catherine Liu 2018-12-11 09:56:52 -07:00 committed by GitHub
parent f06ec83458
commit 350ce1805d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
29 changed files with 1244 additions and 86 deletions

View file

@ -30,4 +30,6 @@ export const pluginPaths = {
modelUIs: ['uis', 'models'],
viewUIs: ['uis', 'views'],
argumentUIs: ['uis', 'arguments'],
templates: ['templates'],
tagUIs: ['uis', 'tags'],
};

View file

@ -0,0 +1,11 @@
/*
* 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.
*/
const themeDark = require('./theme_dark.json');
const themeLight = require('./theme_light.json');
// Registry expects a function that returns a spec object
export const templateSpecs = [themeDark, themeLight].map(template => () => template);

View file

@ -0,0 +1,10 @@
/*
* 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 'babel-polyfill';
import { templateSpecs } from './index';
templateSpecs.forEach(canvas.register);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,10 @@
/*
* 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 { presentation } from './presentation';
// Registry expects a function that returns a spec object
export const tagSpecs = [presentation];

View file

@ -0,0 +1,7 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
export const presentation = () => ({ name: 'presentation', color: '#1EA593' });

View file

@ -0,0 +1,10 @@
/*
* 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 'babel-polyfill';
import { tagSpecs } from './index';
tagSpecs.forEach(canvas.register);

View file

@ -6,16 +6,20 @@
import React from 'react';
import { EuiPage, EuiPageBody, EuiPageContent } from '@elastic/eui';
import { WorkpadLoader } from '../../components/workpad_loader';
import { WorkpadManager } from '../../components/workpad_manager';
import { setDocTitle } from '../../lib/doc_title';
export const HomeApp = () => {
setDocTitle('Canvas');
return (
<EuiPage restrictWidth style={{ width: '100%' }}>
<EuiPage className="canvasHomeApp" restrictWidth>
<EuiPageBody>
<EuiPageContent panelPaddingSize="none" horizontalPosition="center">
<WorkpadLoader onClose={() => {}} />
<EuiPageContent
className="canvasHomeApp__content"
panelPaddingSize="none"
horizontalPosition="center"
>
<WorkpadManager onClose={() => {}} />
</EuiPageContent>
</EuiPageBody>
</EuiPage>

View file

@ -1,3 +1,10 @@
.canvasHomeApp__modalHeader {
padding-right: 24px;
.canvasHomeApp {
width: 100%;
.canvasHomeApp__content {
width: 100%;
}
.canvasHomeApp__modalHeader {
padding-right: 24px;
}
}

View file

@ -11,6 +11,8 @@ import { getAppReady, getBasePath } from '../../state/selectors/app';
import { appReady, appError } from '../../state/actions/app';
import { loadPrivateBrowserFunctions } from '../../lib/load_private_browser_functions';
import { elementsRegistry } from '../../lib/elements_registry';
import { templatesRegistry } from '../../lib/templates_registry';
import { tagsRegistry } from '../../lib/tags_registry';
import { renderFunctionsRegistry } from '../../lib/render_functions_registry';
import {
argTypeRegistry,
@ -40,6 +42,8 @@ const types = {
modelUIs: modelRegistry,
viewUIs: viewRegistry,
argumentUIs: argTypeRegistry,
templates: templatesRegistry,
tags: tagsRegistry,
};
const mapDispatchToProps = dispatch => ({

View file

@ -41,5 +41,5 @@ export const Toolbar = compose(
},
}),
withState('tray', 'setTray', props => props.tray),
withState('showWorkpadLoader', 'setShowWorkpadLoader', false)
withState('showWorkpadManager', 'setShowWorkpadManager', false)
)(Component);

View file

@ -17,7 +17,7 @@ import {
EuiButton,
} from '@elastic/eui';
import { Navbar } from '../navbar';
import { WorkpadLoader } from '../workpad_loader';
import { WorkpadManager } from '../workpad_manager';
import { PageManager } from '../page_manager';
import { Expression } from '../expression';
import { Tray } from './tray';
@ -32,8 +32,8 @@ export const Toolbar = props => {
selectedPageNumber,
workpadName,
totalPages,
showWorkpadLoader,
setShowWorkpadLoader,
showWorkpadManager,
setShowWorkpadManager,
} = props;
const elementIsSelected = Boolean(selectedElement);
@ -45,15 +45,15 @@ export const Toolbar = props => {
setTray(exp);
};
const closeWorkpadLoader = () => setShowWorkpadLoader(false);
const openWorkpadLoader = () => setShowWorkpadLoader(true);
const closeWorkpadManager = () => setShowWorkpadManager(false);
const openWorkpadManager = () => setShowWorkpadManager(true);
const workpadLoader = (
const workpadManager = (
<EuiOverlayMask>
<EuiModal onClose={closeWorkpadLoader} className="canvasModal--fixedSize" maxWidth="1000px">
<WorkpadLoader onClose={closeWorkpadLoader} />
<EuiModal onClose={closeWorkpadManager} className="canvasModal--fixedSize" maxWidth="1000px">
<WorkpadManager onClose={closeWorkpadManager} />
<EuiModalFooter>
<EuiButton size="s" onClick={closeWorkpadLoader}>
<EuiButton size="s" onClick={closeWorkpadManager}>
Dismiss
</EuiButton>
</EuiModalFooter>
@ -72,7 +72,7 @@ export const Toolbar = props => {
<Navbar>
<EuiFlexGroup alignItems="center" gutterSize="none" className="canvasToolbar__controls">
<EuiFlexItem grow={false}>
<EuiButtonEmpty color="text" iconType="grid" onClick={() => openWorkpadLoader()}>
<EuiButtonEmpty color="text" iconType="grid" onClick={() => openWorkpadManager()}>
{workpadName}
</EuiButtonEmpty>
</EuiFlexItem>
@ -116,7 +116,7 @@ export const Toolbar = props => {
</EuiFlexGroup>
</Navbar>
{showWorkpadLoader && workpadLoader}
{showWorkpadManager && workpadManager}
</div>
);
};
@ -130,6 +130,6 @@ Toolbar.propTypes = {
selectedPageNumber: PropTypes.number.isRequired,
totalPages: PropTypes.number.isRequired,
selectedElement: PropTypes.object,
showWorkpadLoader: PropTypes.bool.isRequired,
setShowWorkpadLoader: PropTypes.func.isRequired,
showWorkpadManager: PropTypes.bool.isRequired,
setShowWorkpadManager: PropTypes.func.isRequired,
};

View file

@ -9,14 +9,7 @@ import PropTypes from 'prop-types';
import { EuiButton } from '@elastic/eui';
export const WorkpadCreate = ({ createPending, onCreate, ...rest }) => (
<EuiButton
{...rest}
iconType="plusInCircle"
size="s"
fill
onClick={onCreate}
isLoading={createPending}
>
<EuiButton {...rest} iconType="plusInCircle" fill onClick={onCreate} isLoading={createPending}>
Create workpad
</EuiButton>
);

View file

@ -10,3 +10,14 @@
.canvasWorkpad__dropzoneTable .euiTable {
background-color: transparent;
}
.canvasWorkpad__dropzoneTable--tags {
.euiTableCellContent {
flex-wrap: wrap;
}
.euiHealth {
width: 100%;
}
}

View file

@ -10,21 +10,15 @@ import {
EuiFlexGroup,
EuiFlexItem,
EuiBasicTable,
EuiBetaBadge,
EuiButtonIcon,
EuiLink,
EuiPagination,
EuiSpacer,
EuiButton,
EuiToolTip,
EuiModalHeader,
EuiModalHeaderTitle,
EuiModalBody,
EuiEmptyPrompt,
} from '@elastic/eui';
import { sortByOrder } from 'lodash';
import moment from 'moment';
import { documentationLinks } from '../../lib/documentation_links';
import { ConfirmModal } from '../confirm_modal';
import { Link } from '../link';
import { Paginate } from '../paginate';
@ -282,7 +276,6 @@ export class WorkpadLoader extends React.PureComponent {
let deleteButton = (
<EuiButton
size="s"
color="danger"
iconType="trash"
onClick={this.openRemoveConfirm}
@ -293,7 +286,7 @@ export class WorkpadLoader extends React.PureComponent {
);
const downloadButton = (
<EuiButton size="s" color="secondary" onClick={this.downloadWorkpads} iconType="sortDown">
<EuiButton color="secondary" onClick={this.downloadWorkpads} iconType="sortDown">
{`Download (${selectedWorkpads.length})`}
</EuiButton>
);
@ -347,62 +340,43 @@ export class WorkpadLoader extends React.PureComponent {
<Paginate rows={sortedWorkpads}>
{pagination => (
<Fragment>
<EuiModalHeader className="canvasHomeApp__modalHeader">
<div style={{ width: '100%' }}>
<EuiFlexGroup alignItems="center" gutterSize="m">
<EuiFlexItem grow={false}>
<EuiModalHeaderTitle>Canvas workpads</EuiModalHeaderTitle>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiBetaBadge
label="Beta"
tooltipContent="Canvas is still in beta. Please help us improve by reporting issues or bugs in the Kibana repo."
<EuiFlexGroup justifyContent="spaceBetween">
<EuiFlexItem grow={2}>
<EuiFlexGroup gutterSize="s">
{selectedWorkpads.length > 0 && (
<Fragment>
<EuiFlexItem grow={false}>{downloadButton}</EuiFlexItem>
<EuiFlexItem grow={false}>{deleteButton}</EuiFlexItem>
</Fragment>
)}
<EuiFlexItem grow={1}>
<WorkpadSearch
onChange={text => {
pagination.setPage(0);
this.props.findWorkpads(text);
}}
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiLink href={documentationLinks.canvas} target="_blank">
Docs
</EuiLink>
</EuiFlexItem>
</EuiFlexGroup>
<EuiSpacer size="l" />
<EuiFlexGroup justifyContent="spaceBetween">
<EuiFlexItem grow={2}>
<EuiFlexGroup gutterSize="s">
{selectedWorkpads.length > 0 && (
<Fragment>
<EuiFlexItem grow={false}>{downloadButton}</EuiFlexItem>
<EuiFlexItem grow={false}>{deleteButton}</EuiFlexItem>
</Fragment>
)}
<EuiFlexItem grow={1}>
<WorkpadSearch
onChange={text => {
pagination.setPage(0);
this.props.findWorkpads(text);
}}
/>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
<EuiFlexItem grow={2}>
<EuiFlexGroup gutterSize="s" justifyContent="flexEnd" wrap>
<EuiFlexItem grow={false}>{uploadButton}</EuiFlexItem>
<EuiFlexItem grow={false}>{createButton}</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
</EuiFlexItem>
<EuiFlexItem grow={2}>
<EuiFlexGroup gutterSize="s" justifyContent="flexEnd" wrap>
<EuiFlexItem grow={false}>{uploadButton}</EuiFlexItem>
<EuiFlexItem grow={false}>{createButton}</EuiFlexItem>
</EuiFlexGroup>
</div>
</EuiModalHeader>
<EuiModalBody>
{createPending && <div>Creating Workpad...</div>}
</EuiFlexItem>
</EuiFlexGroup>
{!createPending && isLoading && <div>Fetching Workpads...</div>}
<EuiSpacer />
{!createPending && !isLoading && this.renderWorkpadTable(pagination)}
{createPending && <div style={{ width: '100%' }}>Creating Workpad...</div>}
{confirmModal}
</EuiModalBody>
{!createPending &&
isLoading && <div style={{ width: '100%' }}>Fetching Workpads...</div>}
{!createPending && !isLoading && this.renderWorkpadTable(pagination)}
{confirmModal}
</Fragment>
)}
</Paginate>

View file

@ -0,0 +1,14 @@
.canvasWorkpad__upload--compressed {
&.euiFilePicker--compressed.euiFilePicker {
.euiFilePicker__prompt {
height: $euiSizeXXL;
padding: $euiSizeM;
padding-left: $euiSizeXXL;
}
.euiFilePicker__icon {
top: $euiSizeM;
}
}
}

View file

@ -30,7 +30,6 @@ export class WorkpadSearch extends React.PureComponent {
render() {
return (
<EuiFieldSearch
compressed
placeholder="Find workpad"
value={this.state.searchText}
onChange={this.setSearchText}

View file

@ -13,6 +13,7 @@ export const WorkpadUpload = ({ onUpload, ...rest }) => (
<EuiFilePicker
{...rest}
compressed
className="canvasWorkpad__upload--compressed"
initialPromptText="Import workpad JSON file"
onChange={([file]) => uploadWorkpad(file, onUpload)}
/>

View file

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

View file

@ -0,0 +1,76 @@
/*
* 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, { Fragment } from 'react';
import PropTypes from 'prop-types';
import {
EuiTabbedContent,
EuiModalHeader,
EuiModalHeaderTitle,
EuiModalBody,
EuiSpacer,
EuiFlexGroup,
EuiFlexItem,
EuiBetaBadge,
EuiLink,
} from '@elastic/eui';
import { WorkpadLoader } from '../workpad_loader';
import { WorkpadTemplates } from '../workpad_templates';
import { documentationLinks } from '../../lib/documentation_links';
export const WorkpadManager = ({ onClose }) => {
const tabs = [
{
id: 'workpadLoader',
name: 'My Workpads',
content: (
<Fragment>
<EuiSpacer />
<WorkpadLoader onClose={onClose} />
</Fragment>
),
},
{
id: 'workpadTemplates',
name: 'Templates',
content: (
<Fragment>
<EuiSpacer />
<WorkpadTemplates onClose={onClose} />
</Fragment>
),
},
];
return (
<Fragment>
<EuiModalHeader className="canvasHomeApp__modalHeader">
<EuiFlexGroup alignItems="center" gutterSize="s">
<EuiFlexItem grow={false}>
<EuiModalHeaderTitle>Canvas workpads</EuiModalHeaderTitle>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiBetaBadge
label="Beta"
tooltipContent="Canvas is still in beta. Please help us improve by reporting issues or bugs in the Kibana repo."
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiLink href={documentationLinks.canvas} target="_blank">
Docs
</EuiLink>
</EuiFlexItem>
</EuiFlexGroup>
</EuiModalHeader>
<EuiModalBody>
<EuiTabbedContent tabs={tabs} initialSelectedTab={tabs[0]} />
</EuiModalBody>
</Fragment>
);
};
WorkpadManager.propTypes = {
onClose: PropTypes.func,
};

View file

@ -0,0 +1,34 @@
/*
* 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 PropTypes from 'prop-types';
import { compose, getContext, withHandlers, withProps } from 'recompose';
import * as workpadService from '../../lib/workpad_service';
import { notify } from '../../lib/notify';
import { getId } from '../../lib/get_id';
import { templatesRegistry } from '../../lib/templates_registry';
import { tagsRegistry } from '../../lib/tags_registry';
import { WorkpadTemplates as Component } from './workpad_templates';
export const WorkpadTemplates = compose(
getContext({
router: PropTypes.object,
}),
withProps(() => ({
templates: templatesRegistry.toJS(),
uniqueTags: tagsRegistry.toJS(),
})),
withHandlers({
// Clone workpad given an id
cloneWorkpad: props => workpad => {
workpad.id = getId('workpad');
return workpadService
.create(workpad)
.then(() => props.router.navigateTo('loadWorkpad', { id: workpad.id, page: 1 }))
.catch(err => notify.error(err, { title: `Couldn't clone workpad template` }));
},
})
)(Component);

View file

@ -0,0 +1,210 @@
/*
* 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, { Fragment } from 'react';
import PropTypes from 'prop-types';
import {
EuiFlexGroup,
EuiFlexItem,
EuiBasicTable,
EuiPagination,
EuiSpacer,
EuiHealth,
EuiButtonEmpty,
EuiSearchBar,
} from '@elastic/eui';
import { get, sortByOrder } from 'lodash';
import { getId } from '../../lib/get_id';
import { Paginate } from '../paginate';
export class WorkpadTemplates extends React.PureComponent {
static propTypes = {
cloneWorkpad: PropTypes.func.isRequired,
templates: PropTypes.object,
uniqueTags: PropTypes.object,
};
state = {
sortField: 'name',
sortDirection: 'asc',
pageSize: 10,
searchTerm: '',
filterTags: [],
};
onTableChange = ({ sort = {} }) => {
const { field: sortField, direction: sortDirection } = sort;
this.setState({
sortField,
sortDirection,
});
};
onSearch = ({ query }) => {
const clauses = get(query, 'ast._clauses', []);
const filterTags = [];
const searchTerms = [];
clauses.forEach(clause => {
const { type, field, value } = clause;
// extract terms from the query AST
if (type === 'term') searchTerms.push(value);
// extracts tags from the query AST
else if (field === 'tags') filterTags.push(value);
});
this.setState({ searchTerm: searchTerms.join(' '), filterTags });
};
cloneTemplate = template => this.props.cloneWorkpad(template).then(() => this.props.onClose());
renderWorkpadTable = ({ rows, pageNumber, totalPages, setPage }) => {
const { uniqueTags } = this.props;
const { sortField, sortDirection } = this.state;
const columns = [
{
field: 'name',
name: 'Template Name',
sortable: true,
width: '30%',
dataType: 'string',
render: (name, template) => {
const templateName = template.name.length ? template.name : <em>{template.id}</em>;
return (
<EuiButtonEmpty
onClick={() => this.cloneTemplate(template)}
aria-label={`Clone workpad template "${templateName}"`}
type="link"
>
{templateName}
</EuiButtonEmpty>
);
},
},
{
field: 'help',
name: 'Description',
sortable: false,
dataType: 'string',
width: '30%',
},
{
field: 'tags',
name: 'Tags',
sortable: false,
dataType: 'string',
width: '30%',
render: tags => {
if (!tags) return 'No tags';
return tags.map(tag => (
<EuiHealth key={getId('tag')} color={get(uniqueTags, `${tag}.color`, '#666666')}>
{tag}
</EuiHealth>
));
},
},
];
const sorting = {
sort: {
field: sortField,
direction: sortDirection,
},
};
return (
<Fragment>
<EuiBasicTable
compressed
items={rows}
itemId="id"
columns={columns}
sorting={sorting}
onChange={this.onTableChange}
className="canvasWorkpad__dropzoneTable canvasWorkpad__dropzoneTable--tags"
/>
<EuiSpacer />
<EuiFlexGroup gutterSize="none" justifyContent="flexEnd">
<EuiFlexItem grow={false}>
<EuiPagination activePage={pageNumber} onPageClick={setPage} pageCount={totalPages} />
</EuiFlexItem>
</EuiFlexGroup>
</Fragment>
);
};
renderSearch = () => {
let { uniqueTags } = this.props;
const { searchTerm } = this.state;
uniqueTags = Object.values(uniqueTags);
const filters = [
{
type: 'field_value_selection',
field: 'tags',
name: 'Tags',
multiSelect: true,
options: uniqueTags.map(({ name, color }) => ({
value: name,
name: name,
view: (
<EuiHealth key={getId('tag')} color={color}>
{name}
</EuiHealth>
),
})),
},
];
return (
<EuiSearchBar
defaultQuery={searchTerm}
box={{
placeholder: 'Find template',
incremental: true,
}}
filters={filters}
onChange={this.onSearch}
/>
);
};
render() {
const { templates } = this.props;
const { sortField, sortDirection, searchTerm, filterTags } = this.state;
const sortedTemplates = sortByOrder(templates, [sortField, 'name'], [sortDirection, 'asc']);
const filteredTemplates = sortedTemplates.filter(({ name = '', help = '', tags = [] }) => {
const tagMatch = filterTags.length
? filterTags.every(filterTag => tags.indexOf(filterTag) > -1)
: true;
const lowercaseSearch = searchTerm.toLowerCase();
const textMatch = lowercaseSearch
? name.toLowerCase().indexOf(lowercaseSearch) > -1 ||
help.toLowerCase().indexOf(lowercaseSearch) > -1
: true;
return tagMatch && textMatch;
});
return (
<Paginate rows={filteredTemplates}>
{pagination => (
<Fragment>
{this.renderSearch()}
<EuiSpacer />
{this.renderWorkpadTable(pagination)}
</Fragment>
)}
</Paginate>
);
}
}

View file

@ -0,0 +1,13 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
export function Tag(config) {
// The name of the tag
this.name = config.name;
// color of the tag to display in a list
this.color = config.color;
}

View file

@ -0,0 +1,16 @@
/*
* 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 { Registry } from '@kbn/interpreter/common';
import { Tag } from './tag';
class TagRegistry extends Registry {
wrapper(obj) {
return new Tag(obj);
}
}
export const tagsRegistry = new TagRegistry();

View file

@ -0,0 +1,49 @@
/*
* 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 { tagsRegistry } from '../lib/tags_registry';
import { getDefaultWorkpad } from '../state/defaults';
import { getId } from './get_id';
const defaultWorkpad = getDefaultWorkpad();
export function Template(config) {
// The name of the template
this.name = config.name;
// Use this to set a more friendly name
this.displayName = config.displayName || this.name;
this.id = config.id || getId('workpad');
// A sentence or few about what this template contains
this.help = config.help || '';
// Tags for categorizing the template
this.tags = config.tags || [];
this.tags.forEach(tag => {
if (!tagsRegistry.get(tag)) tagsRegistry.register(() => ({ name: tag, color: '#666666' }));
});
this.width = config.width || defaultWorkpad.width;
this.height = config.height || defaultWorkpad.height;
this.page = config.page || defaultWorkpad.page;
this.pages = config.pages || defaultWorkpad.pages;
this.colors = config.colors || defaultWorkpad.colors;
this['@timestamp'] = config['@timestamp'] || defaultWorkpad['@timestamp'];
this['@created'] = config['@created'] || defaultWorkpad['@created'];
this.assets = config.assets || {};
this.css = config.css || defaultWorkpad.css;
}

View file

@ -0,0 +1,16 @@
/*
* 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 { Registry } from '@kbn/interpreter/common';
import { Template } from './template';
class TemplateRegistry extends Registry {
wrapper(obj) {
return new Template(obj);
}
}
export const templatesRegistry = new TemplateRegistry();

View file

@ -52,5 +52,6 @@
@import '../components/toolbar/toolbar';
@import '../components/toolbar/tray/tray';
@import '../components/workpad/workpad';
@import '../components/workpad_loader/workpad_loader';
@import '../components/workpad_loader/workpad_dropzone/workpad_dropzone';
@import '../components/workpad_page/workpad_page';

View file

@ -26,6 +26,8 @@ export function getWebpackConfig({ devtool, watch } = {}) {
'uis/arguments/all': path.join(sourceDir, 'uis/arguments/register.js'),
'functions/browser/all': path.join(sourceDir, 'functions/browser/register.js'),
'functions/common/all': path.join(sourceDir, 'functions/common/register.js'),
'templates/all': path.join(sourceDir, 'templates/register.js'),
'tags/all': path.join(sourceDir, 'uis/tags/register.js'),
},
// there were problems with the node and web targets since this code is actually