[Canvas] Move Templates to be stored as Saved Objects (#69438)
* Moves Canvas templates to live server side * Adds Clone from template test * Fix url * Clean up * PR Feedback * i18n
This commit is contained in:
parent
4784686978
commit
a9f72bc5e4
|
@ -24,7 +24,6 @@ import { modelSpecs } from './uis/models';
|
|||
import { initializeViews } from './uis/views';
|
||||
import { initializeArgs } from './uis/arguments';
|
||||
import { tagSpecs } from './uis/tags';
|
||||
import { templateSpecs } from './templates';
|
||||
|
||||
interface SetupDeps {
|
||||
canvas: CanvasSetup;
|
||||
|
@ -59,7 +58,6 @@ export class CanvasSrcPlugin implements Plugin<void, void, SetupDeps, StartDeps>
|
|||
plugins.canvas.addViewUIs(initializeViews(core, plugins));
|
||||
plugins.canvas.addArgumentUIs(initializeArgs(core, plugins));
|
||||
plugins.canvas.addTagUIs(tagSpecs);
|
||||
plugins.canvas.addTemplates(templateSpecs);
|
||||
plugins.canvas.addTransformUIs(transformSpecs);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,21 +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 { applyTemplateStrings } from '../../i18n/templates';
|
||||
|
||||
import darkTemplate from './theme_dark.json';
|
||||
import lightTemplate from './theme_light.json';
|
||||
// import pitchTemplate from './pitch_presentation.json';
|
||||
import statusTemplate from './status_report.json';
|
||||
import summaryTemplate from './summary_report.json';
|
||||
|
||||
// Registry expects a function that returns a spec object
|
||||
export const templateSpecs = applyTemplateStrings([
|
||||
darkTemplate,
|
||||
lightTemplate,
|
||||
// pitchTemplate,
|
||||
statusTemplate,
|
||||
summaryTemplate,
|
||||
]);
|
File diff suppressed because one or more lines are too long
|
@ -1,455 +0,0 @@
|
|||
{
|
||||
"name": "Summary",
|
||||
"id": "workpad-6181471b-147d-4397-a0d3-1c0f1600fa12",
|
||||
"displayName": "Summary",
|
||||
"help": "Infographic-style report with live charts",
|
||||
"tags": ["report"],
|
||||
"width": 1100,
|
||||
"height": 2570,
|
||||
"page": 0,
|
||||
"pages": [
|
||||
{
|
||||
"id": "page-28d2523e-aa4d-4134-8092-b849835b620f",
|
||||
"style": {
|
||||
"background": "#FFF"
|
||||
},
|
||||
"transition": {},
|
||||
"elements": [
|
||||
{
|
||||
"id": "element-7e937714-3a57-4d41-bcc7-859b2d2db497",
|
||||
"position": {
|
||||
"left": -1.375,
|
||||
"top": -2.5,
|
||||
"width": 1101.75,
|
||||
"height": 115,
|
||||
"angle": 0,
|
||||
"parent": null
|
||||
},
|
||||
"expression": "shape \"square\" fill=\"#69707D\" border=\"rgba(255,255,255,0)\" borderWidth=0 maintainAspect=false\n| render css=\".canvasRenderEl {\n\n}\" containerStyle={containerStyle}"
|
||||
},
|
||||
{
|
||||
"id": "element-8cbe96d4-f555-4891-8f23-ef6cd679d9cf",
|
||||
"position": {
|
||||
"left": 31.75,
|
||||
"top": 1186,
|
||||
"width": 1034.5,
|
||||
"height": 421,
|
||||
"angle": 0,
|
||||
"parent": null
|
||||
},
|
||||
"expression": "shape \"square\" fill=\"rgba(255,255,255,0)\" border=\"rgba(255,255,255,0)\" borderWidth=2 maintainAspect=false\n| render css=\".canvasRenderEl {\n\n}\" \n containerStyle={containerStyle borderRadius=\"6px\" border=\"2px solid #D3DAE6\" backgroundColor=\"rgba(255,255,255,0)\"}"
|
||||
},
|
||||
{
|
||||
"id": "element-9c467f5e-3594-41db-8602-ec45e4f3fe8f",
|
||||
"position": {
|
||||
"left": 566.25,
|
||||
"top": 1650,
|
||||
"width": 500,
|
||||
"height": 386,
|
||||
"angle": 0,
|
||||
"parent": null
|
||||
},
|
||||
"expression": "shape \"square\" fill=\"rgba(255,255,255,0)\" border=\"rgba(255,255,255,0)\" borderWidth=2 maintainAspect=false\n| render css=\".canvasRenderEl {\n\n}\" \n containerStyle={containerStyle borderRadius=\"6px\" border=\"2px solid #D3DAE6\" backgroundColor=\"rgba(255,255,255,0)\"}"
|
||||
},
|
||||
{
|
||||
"id": "element-a07f8a00-d3da-470c-aea1-b88407900ba5",
|
||||
"position": {
|
||||
"left": 30.75,
|
||||
"top": 1650,
|
||||
"width": 508.25,
|
||||
"height": 386,
|
||||
"angle": 0,
|
||||
"parent": null
|
||||
},
|
||||
"expression": "shape \"square\" fill=\"rgba(255,255,255,0)\" border=\"rgba(255,255,255,0)\" borderWidth=2 maintainAspect=false\n| render css=\".canvasRenderEl {\n\n}\" \n containerStyle={containerStyle borderRadius=\"6px\" border=\"2px solid #D3DAE6\" backgroundColor=\"rgba(255,255,255,0)\"}"
|
||||
},
|
||||
{
|
||||
"id": "element-80c70a23-12d9-4282-a68e-5d98ceb5a31f",
|
||||
"position": {
|
||||
"left": 31.75,
|
||||
"top": 2084.5,
|
||||
"width": 1034.5,
|
||||
"height": 413,
|
||||
"angle": 0,
|
||||
"parent": null
|
||||
},
|
||||
"expression": "shape \"square\" fill=\"rgba(255,255,255,0)\" border=\"rgba(255,255,255,0)\" borderWidth=2 maintainAspect=false\n| render css=\".canvasRenderEl {\n\n}\" \n containerStyle={containerStyle borderRadius=\"6px\" border=\"2px solid #D3DAE6\" backgroundColor=\"rgba(255,255,255,0)\"}"
|
||||
},
|
||||
{
|
||||
"id": "element-105a0788-e347-4fa0-afff-0a6b80633b80",
|
||||
"position": {
|
||||
"left": 31.75,
|
||||
"top": 707,
|
||||
"width": 1034.5,
|
||||
"height": 437,
|
||||
"angle": 0,
|
||||
"parent": null
|
||||
},
|
||||
"expression": "shape \"square\" fill=\"rgba(255,255,255,0)\" border=\"rgba(255,255,255,0)\" borderWidth=2 maintainAspect=false\n| render css=\".canvasRenderEl {\n\n}\" \n containerStyle={containerStyle borderRadius=\"6px\" border=\"2px solid #D3DAE6\" backgroundColor=\"rgba(255,255,255,0)\"}"
|
||||
},
|
||||
{
|
||||
"id": "element-f1d3d480-8aba-48cb-b5f0-2f6a62e64f3a",
|
||||
"position": {
|
||||
"left": 566.25,
|
||||
"top": 158,
|
||||
"width": 500,
|
||||
"height": 508.5,
|
||||
"angle": 0,
|
||||
"parent": null
|
||||
},
|
||||
"expression": "shape \"square\" fill=\"rgba(255,255,255,0)\" border=\"rgba(255,255,255,0)\" borderWidth=2 maintainAspect=false\n| render css=\".canvasRenderEl {\n\n}\" \n containerStyle={containerStyle borderRadius=\"6px\" border=\"2px solid #D3DAE6\" backgroundColor=\"rgba(255,255,255,0)\"}"
|
||||
},
|
||||
{
|
||||
"id": "element-58634438-d8c7-4368-8e41-640d858374c3",
|
||||
"position": {
|
||||
"left": 31.75,
|
||||
"top": 158,
|
||||
"width": 507.25,
|
||||
"height": 508.5,
|
||||
"angle": 0,
|
||||
"parent": null
|
||||
},
|
||||
"expression": "shape \"square\" fill=\"rgba(255,255,255,0)\" border=\"rgba(255,255,255,0)\" borderWidth=2 maintainAspect=false\n| render css=\".canvasRenderEl {\n\n}\" \n containerStyle={containerStyle borderRadius=\"6px\" border=\"2px solid #D3DAE6\" backgroundColor=\"rgba(255,255,255,0)\"}"
|
||||
},
|
||||
{
|
||||
"id": "element-9f76c74a-28d9-4ceb-bd7d-b1b34999a11e",
|
||||
"position": {
|
||||
"left": 52,
|
||||
"top": 178,
|
||||
"width": 500,
|
||||
"height": 38,
|
||||
"angle": 0,
|
||||
"parent": null
|
||||
},
|
||||
"expression": "filters\n| demodata\n| markdown \"### Total cost by project type\" \n font={font family=\"'Open Sans', Helvetica, Arial, sans-serif\" size=14 align=\"left\" color=\"#000000\" weight=\"normal\" underline=false italic=false}\n| render css=\"\""
|
||||
},
|
||||
{
|
||||
"id": "element-3b6345a5-16ea-4828-beec-425458e758a7",
|
||||
"position": {
|
||||
"left": 591.25,
|
||||
"top": 240,
|
||||
"width": 455,
|
||||
"height": 403,
|
||||
"angle": 0,
|
||||
"parent": null
|
||||
},
|
||||
"expression": "filters\n| demodata\n| pointseries x=\"size(project)\" y=\"project\" color=\"project\"\n| plot defaultStyle={seriesStyle bars=0.75 horizontalBars=true} legend=false seriesStyle={seriesStyle label=\"elasticsearch\" color=\"#882e72\"}\n seriesStyle={seriesStyle label=\"machine-learning\" color=\"#d6c1de\"}\n seriesStyle={seriesStyle label=\"apm\" color=\"#5289c7\"}\n seriesStyle={seriesStyle label=\"kibana\" color=\"#7bafde\"}\n seriesStyle={seriesStyle label=\"beats\" color=\"#b178a6\"}\n seriesStyle={seriesStyle label=\"logstash\" color=\"#1965b0\"}\n seriesStyle={seriesStyle label=\"x-pack\" color=\"#4eb265\"}\n seriesStyle={seriesStyle label=\"swiftype\" color=\"#90c987\"}\n| render \n css=\".flot-y-axis {\n left: 14px !important;\n}\n\n.flot-x-axis>div {\n top: 380px !important;\n}\""
|
||||
},
|
||||
{
|
||||
"id": "element-bdfb3910-5f65-4c24-9bbe-e62feb9e5e11",
|
||||
"position": {
|
||||
"left": 585.75,
|
||||
"top": 178,
|
||||
"width": 378,
|
||||
"height": 38,
|
||||
"angle": 0,
|
||||
"parent": null
|
||||
},
|
||||
"expression": "filters\n| demodata\n| markdown \"### Number of projects by project type\" \n font={font family=\"'Open Sans', Helvetica, Arial, sans-serif\" size=14 align=\"left\" color=\"#000000\" weight=\"normal\" underline=false italic=false}\n| render css=\"\""
|
||||
},
|
||||
{
|
||||
"id": "element-161aafca-ba71-43e1-b2a2-dab96a78d717",
|
||||
"position": {
|
||||
"left": 53,
|
||||
"top": 211,
|
||||
"width": 500,
|
||||
"height": 38,
|
||||
"angle": 0,
|
||||
"parent": null
|
||||
},
|
||||
"expression": "filters\n| demodata\n| markdown \"##### Global cost distribution\" \n font={font family=\"'Open Sans', Helvetica, Arial, sans-serif\" size=14 align=\"left\" color=\"#000000\" weight=\"normal\" underline=false italic=false}\n| render css=\"\""
|
||||
},
|
||||
{
|
||||
"id": "element-d0c43968-cdcd-4a25-980f-83d6f0adf68e",
|
||||
"position": {
|
||||
"left": 586,
|
||||
"top": 211,
|
||||
"width": 500,
|
||||
"height": 38,
|
||||
"angle": 0,
|
||||
"parent": null
|
||||
},
|
||||
"expression": "filters\n| demodata\n| markdown \"##### Project type distribution\n\" \n font={font family=\"'Open Sans', Helvetica, Arial, sans-serif\" size=14 align=\"left\" color=\"#000000\" weight=\"normal\" underline=false italic=false}\n| render css=\"\""
|
||||
},
|
||||
{
|
||||
"id": "element-ea1f3942-066f-4032-a9d0-125072d353d9",
|
||||
"position": {
|
||||
"left": 61.75,
|
||||
"top": 793,
|
||||
"width": 643,
|
||||
"height": 300,
|
||||
"angle": 0,
|
||||
"parent": null
|
||||
},
|
||||
"expression": "filters\n| demodata\n| pointseries x=\"project\" y=\"mean(percent_uptime)\" color=\"project\"\n| plot defaultStyle={seriesStyle bars=0.75} legend=false seriesStyle={seriesStyle label=\"elasticsearch\" color=\"#882e72\"}\n seriesStyle={seriesStyle label=\"machine-learning\" color=\"#d6c1de\"}\n seriesStyle={seriesStyle label=\"apm\" color=\"#5289c7\"}\n seriesStyle={seriesStyle label=\"logstash\" color=\"#1965b0\"}\n seriesStyle={seriesStyle label=\"x-pack\" color=\"#4eb265\"}\n seriesStyle={seriesStyle label=\"kibana\" color=\"#7bafde\"}\n seriesStyle={seriesStyle label=\"swiftype\" color=\"#90c987\"}\n seriesStyle={seriesStyle label=\"beats\" color=\"#b178a6\"}\n| render css=\".flot-x-axis>div {\n top: 258px !important;\n}\""
|
||||
},
|
||||
{
|
||||
"id": "element-5a891ee6-5cb8-4b8a-9c01-302ed42e6a8f",
|
||||
"position": {
|
||||
"left": 53,
|
||||
"top": 726,
|
||||
"width": 500,
|
||||
"height": 38,
|
||||
"angle": 0,
|
||||
"parent": null
|
||||
},
|
||||
"expression": "filters\n| demodata\n| markdown \"### Average uptime\" \n font={font family=\"'Open Sans', Helvetica, Arial, sans-serif\" size=14 align=\"left\" color=\"#000000\" weight=\"normal\" underline=false italic=false}\n| render css=\"\""
|
||||
},
|
||||
{
|
||||
"id": "element-09713339-044e-4084-b4e4-553dbc939d8a",
|
||||
"position": {
|
||||
"left": 729,
|
||||
"top": 757,
|
||||
"width": 301,
|
||||
"height": 38,
|
||||
"angle": 0,
|
||||
"parent": null
|
||||
},
|
||||
"expression": "filters\n| demodata\n| markdown \"##### Global average uptime\n\" \n font={font family=\"'Open Sans', Helvetica, Arial, sans-serif\" size=14 align=\"left\" color=\"#000000\" weight=\"normal\" underline=false italic=false}\n| render css=\"\""
|
||||
},
|
||||
{
|
||||
"id": "element-bd806eff-400b-4816-b728-b28a0390352d",
|
||||
"position": {
|
||||
"left": 764,
|
||||
"top": 833.5,
|
||||
"width": 200,
|
||||
"height": 200,
|
||||
"angle": 0,
|
||||
"parent": null
|
||||
},
|
||||
"expression": "filters\n| demodata\n| math \"mean(percent_uptime)\"\n| progress shape=\"wheel\" label={formatnumber \"0%\"} \n font={font size=24 family=\"'Open Sans', Helvetica, Arial, sans-serif\" color=\"#000000\" align=\"center\"} valueColor=\"#4eb265\"\n| render containerStyle={containerStyle}"
|
||||
},
|
||||
{
|
||||
"id": "element-ccd76ddc-2c03-458d-a0eb-09fcd1e2455f",
|
||||
"position": {
|
||||
"left": 53,
|
||||
"top": 1212,
|
||||
"width": 500,
|
||||
"height": 38,
|
||||
"angle": 0,
|
||||
"parent": null
|
||||
},
|
||||
"expression": "filters\n| demodata\n| markdown \"### Average price by project type\" \n font={font family=\"'Open Sans', Helvetica, Arial, sans-serif\" size=14 align=\"left\" color=\"#000000\" weight=\"normal\" underline=false italic=false}\n| render css=\"\""
|
||||
},
|
||||
{
|
||||
"id": "element-ef88de44-1629-4a66-abc5-3764b03342e5",
|
||||
"position": {
|
||||
"left": 55.5,
|
||||
"top": 2110,
|
||||
"width": 500,
|
||||
"height": 38,
|
||||
"angle": 0,
|
||||
"parent": null
|
||||
},
|
||||
"expression": "filters\n| demodata\n| markdown \"### Raw data\" \n font={font family=\"'Open Sans', Helvetica, Arial, sans-serif\" size=14 align=\"left\" color=\"#000000\" weight=\"normal\" underline=false italic=false}\n| render css=\"\""
|
||||
},
|
||||
{
|
||||
"id": "element-1dbb5050-7b7c-4dd2-ab83-95913d15cc91",
|
||||
"position": {
|
||||
"left": 62.75,
|
||||
"top": 273.75,
|
||||
"width": 434.625,
|
||||
"height": 285,
|
||||
"angle": 0,
|
||||
"parent": null
|
||||
},
|
||||
"expression": "filters\n| demodata\n| pointseries color=\"project\" size=\"sum(cost)\"\n| pie hole=50 labels=false legend=\"ne\"\n| render \n css=\"table {\n right: -16px !important;\n}\n\n\ntr {\n height: 36px;\n}\n\n.legendColorBox div {\n margin-right: 7px;\n}\n\n.legendColorBox div div {\n width: 24px !important;\n height: 24px !important;\nborder-width: 4px !important;\n}\n\ntd {\n vertical-align: middle;\n}\" containerStyle={containerStyle overflow=\"visible\"}"
|
||||
},
|
||||
{
|
||||
"id": "element-8ca58ae7-2091-491f-996f-4256dfd5f4e1",
|
||||
"position": {
|
||||
"left": 51.875,
|
||||
"top": 2162,
|
||||
"width": 994.25,
|
||||
"height": 300,
|
||||
"angle": 0,
|
||||
"parent": null
|
||||
},
|
||||
"expression": "filters\n| demodata\n| table\n| render containerStyle={containerStyle overflow=\"hidden\"}"
|
||||
},
|
||||
{
|
||||
"id": "element-64db6690-dd39-4591-973d-d880e068de74",
|
||||
"position": {
|
||||
"left": 88,
|
||||
"top": 1259.5,
|
||||
"width": 902,
|
||||
"height": 300,
|
||||
"angle": 0,
|
||||
"parent": null
|
||||
},
|
||||
"expression": "filters\n| demodata\n| pointseries x=\"time\" y=\"mean(price)\" color=\"project\"\n| plot defaultStyle={seriesStyle lines=3} \n palette={palette \"#882E72\" \"#B178A6\" \"#D6C1DE\" \"#1965B0\" \"#5289C7\" \"#7BAFDE\" \"#4EB265\" \"#90C987\" \"#CAE0AB\" \"#F7EE55\" \"#F6C141\" \"#F1932D\" \"#E8601C\" \"#DC050C\" gradient=false} legend=\"ne\" seriesStyle={seriesStyle label=\"elasticsearch\" color=\"#882e72\"}\n seriesStyle={seriesStyle color=\"#b178a6\" label=\"beats\"}\n seriesStyle={seriesStyle label=\"machine-learning\" color=\"#d6c1de\"}\n seriesStyle={seriesStyle label=\"logstash\" color=\"#1965b0\"}\n seriesStyle={seriesStyle label=\"apm\" color=\"#5289c7\"}\n seriesStyle={seriesStyle label=\"kibana\" color=\"#7bafde\"}\n seriesStyle={seriesStyle label=\"x-pack\" color=\"#4eb265\"}\n seriesStyle={seriesStyle label=\"swiftype\" color=\"#90c987\"}\n| render containerStyle={containerStyle overflow=\"visible\"} \n css=\".legend table {\n top: 266px !important;\n width: 100%;\n left: 80px;\n}\n\n.legend td {\nvertical-align: middle;\n}\n\ntr {\n padding-left: 14px;\n}\n\n.legendLabel {\n padding-left: 4px;\n}\n\ntbody {\n display: flex;\n}\n\n.flot-x-axis {\n top: 16px !important;\n}\""
|
||||
},
|
||||
{
|
||||
"id": "element-28fdc851-17bf-4a78-84f1-944fbf508d50",
|
||||
"position": {
|
||||
"left": 861.25,
|
||||
"top": 44.75,
|
||||
"width": 205,
|
||||
"height": 36,
|
||||
"angle": 0,
|
||||
"parent": null
|
||||
},
|
||||
"expression": "timefilterControl compact=true column=\"@timestamp\"\n| render css=\".canvasTimePickerPopover__button {\n border: none !important;\n}\"",
|
||||
"filter": "timefilter from=\"now-14d\" to=now column=@timestamp"
|
||||
},
|
||||
{
|
||||
"id": "element-bf025bbc-7109-45a1-b954-bab851bc80df",
|
||||
"position": {
|
||||
"left": 764,
|
||||
"top": 44.75,
|
||||
"width": 89,
|
||||
"height": 25,
|
||||
"angle": 0,
|
||||
"parent": null
|
||||
},
|
||||
"expression": "filters\n| demodata\n| markdown \"#### Time period\" \n font={font family=\"'Open Sans', Helvetica, Arial, sans-serif\" size=14 align=\"left\" color=\"#FFFFFF\" weight=\"normal\" underline=false italic=false}\n| render css=\"h4 {\n font-weight: 400;\n}\""
|
||||
},
|
||||
{
|
||||
"id": "element-120f58cd-3ef0-40b6-99fd-32cc1480b9aa",
|
||||
"position": {
|
||||
"left": 53,
|
||||
"top": 757,
|
||||
"width": 500,
|
||||
"height": 38,
|
||||
"angle": 0,
|
||||
"parent": null
|
||||
},
|
||||
"expression": "filters\n| demodata\n| markdown \"##### Average uptime by project type\" \n font={font family=\"'Open Sans', Helvetica, Arial, sans-serif\" size=14 align=\"left\" color=\"#000000\" weight=\"normal\" underline=false italic=false}\n| render css=\"\""
|
||||
},
|
||||
{
|
||||
"id": "element-c30023e3-5df6-4b54-8286-544811ce7b6a",
|
||||
"position": {
|
||||
"left": 51.875,
|
||||
"top": 1670,
|
||||
"width": 500,
|
||||
"height": 38,
|
||||
"angle": 0,
|
||||
"parent": null
|
||||
},
|
||||
"expression": "filters\n| demodata\n| markdown \"### Total cost by project type\" \n font={font family=\"'Open Sans', Helvetica, Arial, sans-serif\" size=14 align=\"left\" color=\"#000000\" weight=\"normal\" underline=false italic=false}\n| render css=\"\""
|
||||
},
|
||||
{
|
||||
"id": "element-137409de-6f24-4234-9c5a-024054d0632a",
|
||||
"position": {
|
||||
"left": 593.25,
|
||||
"top": 1665.5,
|
||||
"width": 446,
|
||||
"height": 38,
|
||||
"angle": 0,
|
||||
"parent": null
|
||||
},
|
||||
"expression": "filters\n| demodata\n| markdown \"### Average price over time\" \n font={font family=\"'Open Sans', Helvetica, Arial, sans-serif\" size=14 align=\"left\" color=\"#000000\" weight=\"normal\" underline=false italic=false}\n| render css=\"\""
|
||||
},
|
||||
{
|
||||
"id": "element-b90b71f0-139b-419f-b43b-b2057abf777b",
|
||||
"position": {
|
||||
"left": 595.75,
|
||||
"top": 1698.5,
|
||||
"width": 223,
|
||||
"height": 19,
|
||||
"angle": 0,
|
||||
"parent": null
|
||||
},
|
||||
"expression": "filters\n| demodata\n| markdown \"##### Price trend over time\" \n font={font family=\"'Open Sans', Helvetica, Arial, sans-serif\" size=14 align=\"left\" color=\"#000000\" weight=\"normal\" underline=false italic=false}\n| render css=\"\""
|
||||
},
|
||||
{
|
||||
"id": "element-a9b94f64-5336-4e39-ac69-5c9dacfbe129",
|
||||
"position": {
|
||||
"left": 53,
|
||||
"top": 1703.5,
|
||||
"width": 500,
|
||||
"height": 38,
|
||||
"angle": 0,
|
||||
"parent": null
|
||||
},
|
||||
"expression": "filters\n| demodata\n| markdown \"##### State distribution\n\" \n font={font family=\"'Open Sans', Helvetica, Arial, sans-serif\" size=14 align=\"left\" color=\"#000000\" weight=\"normal\" underline=false italic=false}\n| render css=\"\""
|
||||
},
|
||||
{
|
||||
"id": "element-8777dd63-fbe7-446f-a23a-74cf55dc0a7c",
|
||||
"position": {
|
||||
"left": 109.75,
|
||||
"top": 37.75,
|
||||
"width": 500,
|
||||
"height": 39,
|
||||
"angle": 0,
|
||||
"parent": null
|
||||
},
|
||||
"expression": "filters\n| demodata\n| markdown \"## Monitoring Elastic projects\" \"\" \n font={font family=\"'Open Sans', Helvetica, Arial, sans-serif\" size=14 align=\"left\" color=\"#FFFFFF\" weight=\"bold\" underline=false italic=false}\n| render css=\".canvasRenderEl {\n\n}\""
|
||||
},
|
||||
{
|
||||
"id": "element-5e85d913-fb4b-41d5-9caf-ca2de9970cc7",
|
||||
"position": {
|
||||
"left": 13.75,
|
||||
"top": 29.8125,
|
||||
"width": 92,
|
||||
"height": 54.875,
|
||||
"angle": 0,
|
||||
"parent": null
|
||||
},
|
||||
"expression": "image dataurl=null mode=\"contain\"\n| render"
|
||||
},
|
||||
{
|
||||
"id": "element-896f3043-4036-45f4-9e84-8aa6d870f215",
|
||||
"position": {
|
||||
"left": 53,
|
||||
"top": 1729,
|
||||
"width": 417.375,
|
||||
"height": 290,
|
||||
"angle": 0,
|
||||
"parent": null
|
||||
},
|
||||
"expression": "filters\n| demodata\n| pointseries x=\"sum(cost)\" y=\"project\" color=\"state\"\n| plot defaultStyle={seriesStyle bars=0.75 horizontalBars=true} legend=\"ne\"\n| render containerStyle={containerStyle overflow=\"visible\"} \n css=\".legend table {\n top: 100px !important;\n right: -46px !important;\n}\n\n.legendColorBox>div{\nmargin-right: 3px !important;\n}\n\n.legend td {\n\nvertical-align: middle;\n}\n\n.legend tr {\n height: 20px;\n}\n\n.flot-x-axis {\n top: -15px !important;\n}\n\n.flot-y-axis {\n left: 10px !important;\n}\""
|
||||
},
|
||||
{
|
||||
"id": "element-13888369-9dac-4948-90b1-0ae42fa8fa53",
|
||||
"position": {
|
||||
"left": 593.75,
|
||||
"top": 1733,
|
||||
"width": 441,
|
||||
"height": 282,
|
||||
"angle": 0,
|
||||
"parent": null
|
||||
},
|
||||
"expression": "filters\n| demodata\n| pointseries x=\"time\" y=\"mean(price)\"\n| plot defaultStyle={seriesStyle bars=0.75} legend=false \n palette={palette \"#882E72\" \"#B178A6\" \"#D6C1DE\" \"#1965B0\" \"#5289C7\" \"#7BAFDE\" \"#4EB265\" \"#90C987\" \"#CAE0AB\" \"#F7EE55\" \"#F6C141\" \"#F1932D\" \"#E8601C\" \"#DC050C\" gradient=false}\n| render \n css=\".flot-x-axis {\n top: -15px !important;\n}\n\n.flot-y-axis {\n left: 10px !important;\n}\""
|
||||
}
|
||||
],
|
||||
"groups": []
|
||||
}
|
||||
],
|
||||
"colors": [
|
||||
"#37988d",
|
||||
"#c19628",
|
||||
"#b83c6f",
|
||||
"#3f9939",
|
||||
"#1785b0",
|
||||
"#ca5f35",
|
||||
"#45bdb0",
|
||||
"#f2bc33",
|
||||
"#e74b8b",
|
||||
"#4fbf48",
|
||||
"#1ea6dc",
|
||||
"#fd7643",
|
||||
"#72cec3",
|
||||
"#f5cc5d",
|
||||
"#ec77a8",
|
||||
"#7acf74",
|
||||
"#4cbce4",
|
||||
"#fd986f",
|
||||
"#a1ded7",
|
||||
"#f8dd91",
|
||||
"#f2a4c5",
|
||||
"#a6dfa2",
|
||||
"#86d2ed",
|
||||
"#fdba9f",
|
||||
"#000000",
|
||||
"#444444",
|
||||
"#777777",
|
||||
"#BBBBBB",
|
||||
"#FFFFFF",
|
||||
"rgba(255,255,255,0)"
|
||||
],
|
||||
"@timestamp": "2019-05-31T16:02:40.420Z",
|
||||
"@created": "2019-05-31T16:01:45.751Z",
|
||||
"assets": {},
|
||||
"css": "h3 {\ncolor: #343741;\nfont-weight: 400;\n}\n\nh5 {\ncolor: #69707D;\n}"
|
||||
}
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -8,6 +8,7 @@ import { SHAREABLE_RUNTIME_NAME } from '../../shareable_runtime/constants_static
|
|||
|
||||
export const CANVAS_TYPE = 'canvas-workpad';
|
||||
export const CUSTOM_ELEMENT_TYPE = 'canvas-element';
|
||||
export const TEMPLATE_TYPE = `${CANVAS_TYPE}-template`;
|
||||
export const CANVAS_APP = 'canvas';
|
||||
export const APP_ROUTE = '/app/canvas';
|
||||
export const APP_ROUTE_WORKPAD = `${APP_ROUTE}#/workpad`;
|
||||
|
@ -16,6 +17,7 @@ export const API_ROUTE_WORKPAD = `${API_ROUTE}/workpad`;
|
|||
export const API_ROUTE_WORKPAD_ASSETS = `${API_ROUTE}/workpad-assets`;
|
||||
export const API_ROUTE_WORKPAD_STRUCTURES = `${API_ROUTE}/workpad-structures`;
|
||||
export const API_ROUTE_CUSTOM_ELEMENT = `${API_ROUTE}/custom-element`;
|
||||
export const API_ROUTE_TEMPLATES = `${API_ROUTE}/templates`;
|
||||
export const LOCALSTORAGE_PREFIX = `kibana.canvas`;
|
||||
export const LOCALSTORAGE_CLIPBOARD = `${LOCALSTORAGE_PREFIX}.clipboard`;
|
||||
export const SESSIONSTORAGE_LASTPATH = 'lastPath:canvas';
|
||||
|
|
|
@ -1604,5 +1604,12 @@ export const ComponentStrings = {
|
|||
i18n.translate('xpack.canvas.workpadTemplate.searchPlaceholder', {
|
||||
defaultMessage: 'Find template',
|
||||
}),
|
||||
getCreatingTemplateLabel: (templateName: string) =>
|
||||
i18n.translate('xpack.canvas.workpadTemplate.creatingTemplateLabel', {
|
||||
defaultMessage: `Creating from template '{templateName}'`,
|
||||
values: {
|
||||
templateName,
|
||||
},
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
|
|
@ -45,6 +45,6 @@ export const applyTemplateStrings = (templates: CanvasTemplate[]) => {
|
|||
});
|
||||
}
|
||||
|
||||
return () => template;
|
||||
return template;
|
||||
});
|
||||
};
|
||||
|
|
|
@ -5,13 +5,13 @@
|
|||
*/
|
||||
|
||||
import { getTemplateStrings } from './template_strings';
|
||||
import { templateSpecs } from '../../canvas_plugin_src/templates';
|
||||
import { templates } from '../../server/templates'; // eslint-disable-line
|
||||
|
||||
import { TagStrings } from '../tags';
|
||||
|
||||
describe('TemplateStrings', () => {
|
||||
const templateStrings = getTemplateStrings();
|
||||
const templateNames = templateSpecs.map((template) => template().name);
|
||||
const templateNames = templates.map((template) => template.name);
|
||||
const stringKeys = Object.keys(templateStrings);
|
||||
|
||||
test('All template names should exist in the strings definition', () => {
|
||||
|
@ -39,8 +39,8 @@ describe('TemplateStrings', () => {
|
|||
test('All templates should have tags that are defined', () => {
|
||||
const tagNames = Object.keys(TagStrings);
|
||||
|
||||
templateSpecs.forEach((template) => {
|
||||
template().tags.forEach((tagName: string) => expect(tagNames).toContain(tagName));
|
||||
templates.forEach((template) => {
|
||||
template.tags.forEach((tagName: string) => expect(tagNames).toContain(tagName));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -53,9 +53,6 @@ export const getTemplateStrings = (): TemplateStringDict => ({
|
|||
defaultMessage: 'Infographic-style report with live charts',
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
export const getUnusedTemplateStrings = (): TemplateStringDict => ({
|
||||
Pitch: {
|
||||
name: i18n.translate('xpack.canvas.templates.pitchName', {
|
||||
defaultMessage: 'Pitch',
|
||||
|
|
|
@ -1,57 +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 PropTypes from 'prop-types';
|
||||
import { compose, withState, withProps, withHandlers, lifecycle } from 'recompose';
|
||||
import { Paginate as Component } from './paginate';
|
||||
|
||||
export const Paginate = compose(
|
||||
withProps(({ rows, perPage }) => ({
|
||||
perPage: Number(perPage),
|
||||
totalPages: Math.ceil(rows.length / (perPage || 10)),
|
||||
})),
|
||||
withState('currentPage', 'setPage', ({ startPage, totalPages }) => {
|
||||
if (totalPages > 0) {
|
||||
return Math.min(startPage, totalPages - 1);
|
||||
}
|
||||
return 0;
|
||||
}),
|
||||
withProps(({ rows, totalPages, currentPage, perPage }) => {
|
||||
const maxPage = totalPages - 1;
|
||||
const start = currentPage * perPage;
|
||||
const end = currentPage === 0 ? perPage : perPage * (currentPage + 1);
|
||||
return {
|
||||
pageNumber: currentPage,
|
||||
nextPageEnabled: currentPage < maxPage,
|
||||
prevPageEnabled: currentPage > 0,
|
||||
partialRows: rows.slice(start, end),
|
||||
};
|
||||
}),
|
||||
withHandlers({
|
||||
nextPage: ({ currentPage, nextPageEnabled, setPage }) => () =>
|
||||
nextPageEnabled && setPage(currentPage + 1),
|
||||
prevPage: ({ currentPage, prevPageEnabled, setPage }) => () =>
|
||||
prevPageEnabled && setPage(currentPage - 1),
|
||||
}),
|
||||
lifecycle({
|
||||
componentDidUpdate(prevProps) {
|
||||
if (prevProps.perPage !== this.props.perPage) {
|
||||
this.props.setPage(0);
|
||||
}
|
||||
},
|
||||
})
|
||||
)(Component);
|
||||
|
||||
Paginate.propTypes = {
|
||||
rows: PropTypes.array.isRequired,
|
||||
perPage: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
||||
startPage: PropTypes.number,
|
||||
};
|
||||
|
||||
Paginate.defaultProps = {
|
||||
perPage: 10,
|
||||
startPage: 0,
|
||||
};
|
76
x-pack/plugins/canvas/public/components/paginate/index.tsx
Normal file
76
x-pack/plugins/canvas/public/components/paginate/index.tsx
Normal 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, { useState, useEffect, useRef } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Paginate as Component, PaginateProps, PaginateChildProps } from './paginate';
|
||||
|
||||
export { PaginateProps, PaginateChildProps };
|
||||
export interface InPaginateProps {
|
||||
perPage?: number;
|
||||
startPage?: number;
|
||||
rows: any[];
|
||||
children: (props: PaginateChildProps) => React.ReactNode;
|
||||
}
|
||||
|
||||
export const Paginate: React.FunctionComponent<InPaginateProps> = ({
|
||||
perPage = 10,
|
||||
startPage = 0,
|
||||
rows,
|
||||
children,
|
||||
}) => {
|
||||
const totalPages = Math.ceil(rows.length / perPage);
|
||||
const initialCurrentPage = totalPages > 0 ? Math.min(startPage, totalPages - 1) : 0;
|
||||
const [currentPage, setPage] = useState(initialCurrentPage);
|
||||
const hasRenderedRef = useRef<boolean>(false);
|
||||
const maxPage = totalPages - 1;
|
||||
const start = currentPage * perPage;
|
||||
const end = currentPage === 0 ? perPage : perPage * (currentPage + 1);
|
||||
const nextPageEnabled = currentPage < maxPage;
|
||||
const prevPageEnabled = currentPage > 0;
|
||||
const partialRows = rows.slice(start, end);
|
||||
|
||||
const nextPage = () => {
|
||||
if (nextPageEnabled) {
|
||||
setPage(currentPage + 1);
|
||||
}
|
||||
};
|
||||
|
||||
const prevPage = () => {
|
||||
if (prevPageEnabled) {
|
||||
setPage(currentPage - 1);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!hasRenderedRef.current) {
|
||||
hasRenderedRef.current = true;
|
||||
} else {
|
||||
setPage(0);
|
||||
}
|
||||
}, [perPage, hasRenderedRef]);
|
||||
|
||||
return (
|
||||
<Component
|
||||
rows={partialRows}
|
||||
perPage={perPage}
|
||||
pageNumber={currentPage}
|
||||
totalPages={totalPages}
|
||||
setPage={setPage}
|
||||
nextPage={nextPage}
|
||||
prevPage={prevPage}
|
||||
nextPageEnabled={nextPageEnabled}
|
||||
prevPageEnabled={prevPageEnabled}
|
||||
children={children}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
Paginate.propTypes = {
|
||||
rows: PropTypes.array.isRequired,
|
||||
perPage: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
||||
startPage: PropTypes.number,
|
||||
};
|
|
@ -4,25 +4,32 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { InPaginateProps } from './';
|
||||
|
||||
export const Paginate = (props) => {
|
||||
return props.children({
|
||||
rows: props.partialRows,
|
||||
perPage: props.perPage,
|
||||
pageNumber: props.pageNumber,
|
||||
totalPages: props.totalPages,
|
||||
nextPageEnabled: props.nextPageEnabled,
|
||||
prevPageEnabled: props.prevPageEnabled,
|
||||
setPage: (num) => props.setPage(num),
|
||||
nextPage: props.nextPage,
|
||||
prevPage: props.prevPage,
|
||||
});
|
||||
export type PaginateProps = Omit<InPaginateProps, 'startPage'> & {
|
||||
pageNumber: number;
|
||||
totalPages: number;
|
||||
nextPageEnabled: boolean;
|
||||
prevPageEnabled: boolean;
|
||||
setPage: (num: number) => void;
|
||||
nextPage: () => void;
|
||||
prevPage: () => void;
|
||||
};
|
||||
|
||||
export type PaginateChildProps = Omit<PaginateProps, 'children'>;
|
||||
|
||||
export const Paginate: React.FunctionComponent<PaginateProps> = ({
|
||||
children,
|
||||
...childrenProps
|
||||
}) => {
|
||||
return <React.Fragment>{children(childrenProps)}</React.Fragment>;
|
||||
};
|
||||
|
||||
Paginate.propTypes = {
|
||||
children: PropTypes.func.isRequired,
|
||||
partialRows: PropTypes.array.isRequired,
|
||||
rows: PropTypes.array.isRequired,
|
||||
perPage: PropTypes.number.isRequired,
|
||||
pageNumber: PropTypes.number.isRequired,
|
||||
totalPages: PropTypes.number.isRequired,
|
19
x-pack/plugins/canvas/public/components/router/context.ts
Normal file
19
x-pack/plugins/canvas/public/components/router/context.ts
Normal file
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* 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 from 'react';
|
||||
|
||||
// TODO: We should fully build out this interface for our router
|
||||
// or switch to a different router that is already typed
|
||||
interface Router {
|
||||
navigateTo: (
|
||||
name: string,
|
||||
params: Record<string, number | string>,
|
||||
state?: Record<string, string>
|
||||
) => void;
|
||||
}
|
||||
|
||||
export const RouterContext = React.createContext<Router | undefined>(undefined);
|
|
@ -15,6 +15,7 @@ import {
|
|||
// @ts-expect-error untyped local
|
||||
import { Router as Component } from './router';
|
||||
import { State } from '../../../types';
|
||||
export * from './context';
|
||||
|
||||
const mapDispatchToProps = {
|
||||
enableAutoplay,
|
||||
|
|
|
@ -10,6 +10,7 @@ import { routerProvider } from '../../lib/router_provider';
|
|||
import { getAppState } from '../../lib/app_state';
|
||||
import { getTimeInterval } from '../../lib/time_interval';
|
||||
import { CanvasLoading } from './canvas_loading';
|
||||
import { RouterContext } from './';
|
||||
|
||||
export class Router extends React.PureComponent {
|
||||
static propTypes = {
|
||||
|
@ -97,6 +98,10 @@ export class Router extends React.PureComponent {
|
|||
return React.createElement(CanvasLoading, { msg: this.props.loadingMessage });
|
||||
}
|
||||
|
||||
return <this.state.activeComponent />;
|
||||
return (
|
||||
<RouterContext.Provider value={this.state.router}>
|
||||
<this.state.activeComponent />
|
||||
</RouterContext.Provider>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,566 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Storyshots components/WorkpadTemplates default 1`] = `
|
||||
<div
|
||||
style={
|
||||
Object {
|
||||
"width": "500px",
|
||||
}
|
||||
}
|
||||
>
|
||||
<div
|
||||
className="euiFlexGroup euiFlexGroup--gutterMedium euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow euiFlexGroup--responsive euiFlexGroup--wrap"
|
||||
>
|
||||
<div
|
||||
className="euiFlexItem euiSearchBar__searchHolder"
|
||||
>
|
||||
<div
|
||||
className="euiFormControlLayout euiFormControlLayout--fullWidth"
|
||||
>
|
||||
<div
|
||||
className="euiFormControlLayout__childrenWrapper"
|
||||
>
|
||||
<input
|
||||
aria-label="This is a search bar. As you type, the results lower in the page will automatically filter."
|
||||
className="euiFieldSearch euiFieldSearch--fullWidth"
|
||||
defaultValue=""
|
||||
onKeyUp={[Function]}
|
||||
placeholder="Find template"
|
||||
type="search"
|
||||
/>
|
||||
<div
|
||||
className="euiFormControlLayoutIcons"
|
||||
>
|
||||
<span
|
||||
className="euiFormControlLayoutCustomIcon"
|
||||
>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
className="euiFormControlLayoutCustomIcon__icon"
|
||||
data-euiicon-type="search"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="euiFlexItem euiFlexItem--flexGrowZero euiSearchBar__filtersHolder"
|
||||
>
|
||||
<div
|
||||
className="euiFilterGroup"
|
||||
>
|
||||
<div
|
||||
className="euiPopover euiPopover--anchorDownCenter"
|
||||
id="field_value_selection_0"
|
||||
onKeyDown={[Function]}
|
||||
onMouseDown={[Function]}
|
||||
onMouseUp={[Function]}
|
||||
onTouchEnd={[Function]}
|
||||
onTouchStart={[Function]}
|
||||
>
|
||||
<div
|
||||
className="euiPopover__anchor"
|
||||
>
|
||||
<button
|
||||
className="euiButtonEmpty euiButtonEmpty--text euiButtonEmpty--iconRight euiFilterButton euiFilterButton--hasIcon"
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
className="euiButtonEmpty__content"
|
||||
>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
className="euiButtonEmpty__icon"
|
||||
data-euiicon-type="arrowDown"
|
||||
size="m"
|
||||
/>
|
||||
<span
|
||||
className="euiButtonEmpty__text"
|
||||
>
|
||||
<span
|
||||
className="euiFilterButton__textShift"
|
||||
data-text="Tags"
|
||||
title="Tags"
|
||||
>
|
||||
Tags
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="euiSpacer euiSpacer--l"
|
||||
/>
|
||||
<div
|
||||
className="euiBasicTable canvasWorkpad__dropzoneTable canvasWorkpad__dropzoneTable--tags"
|
||||
>
|
||||
<div>
|
||||
<div
|
||||
className="euiTableHeaderMobile"
|
||||
>
|
||||
<div
|
||||
className="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--alignItemsBaseline euiFlexGroup--justifyContentSpaceBetween euiFlexGroup--directionRow"
|
||||
>
|
||||
<div
|
||||
className="euiFlexItem euiFlexItem--flexGrowZero"
|
||||
/>
|
||||
<div
|
||||
className="euiFlexItem euiFlexItem--flexGrowZero"
|
||||
>
|
||||
<div
|
||||
className="euiTableSortMobile"
|
||||
>
|
||||
<div
|
||||
className="euiPopover euiPopover--anchorDownRight"
|
||||
onKeyDown={[Function]}
|
||||
onMouseDown={[Function]}
|
||||
onMouseUp={[Function]}
|
||||
onTouchEnd={[Function]}
|
||||
onTouchStart={[Function]}
|
||||
>
|
||||
<div
|
||||
className="euiPopover__anchor"
|
||||
>
|
||||
<button
|
||||
className="euiButtonEmpty euiButtonEmpty--primary euiButtonEmpty--xSmall euiButtonEmpty--iconRight euiButtonEmpty--flushRight"
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
className="euiButtonEmpty__content"
|
||||
>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
className="euiButtonEmpty__icon"
|
||||
data-euiicon-type="arrowDown"
|
||||
size="m"
|
||||
/>
|
||||
<span
|
||||
className="euiButtonEmpty__text"
|
||||
>
|
||||
Sorting
|
||||
</span>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<table
|
||||
className="euiTable euiTable--compressed euiTable--responsive"
|
||||
>
|
||||
<caption
|
||||
className="euiScreenReaderOnly euiTableCaption"
|
||||
/>
|
||||
<thead>
|
||||
<tr>
|
||||
<th
|
||||
aria-live="polite"
|
||||
aria-sort="ascending"
|
||||
className="euiTableHeaderCell"
|
||||
data-test-subj="tableHeaderCell_name_0"
|
||||
role="columnheader"
|
||||
scope="col"
|
||||
style={
|
||||
Object {
|
||||
"width": "30%",
|
||||
}
|
||||
}
|
||||
>
|
||||
<button
|
||||
className="euiTableHeaderButton euiTableHeaderButton-isSorted"
|
||||
data-test-subj="tableHeaderSortButton"
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
className="euiTableCellContent"
|
||||
>
|
||||
<span
|
||||
className="euiTableCellContent__text"
|
||||
title={
|
||||
<Component
|
||||
ariaSortValue="ascending"
|
||||
/>
|
||||
}
|
||||
>
|
||||
Template name
|
||||
</span>
|
||||
<div
|
||||
aria-label="Sorted in ascending order"
|
||||
className="euiTableSortIcon"
|
||||
data-euiicon-type="sortUp"
|
||||
size="m"
|
||||
/>
|
||||
<span
|
||||
className="euiScreenReaderOnly"
|
||||
>
|
||||
Click to sort in descending order
|
||||
</span>
|
||||
</span>
|
||||
</button>
|
||||
</th>
|
||||
<th
|
||||
className="euiTableHeaderCell"
|
||||
data-test-subj="tableHeaderCell_help_1"
|
||||
role="columnheader"
|
||||
scope="col"
|
||||
style={
|
||||
Object {
|
||||
"width": "30%",
|
||||
}
|
||||
}
|
||||
>
|
||||
<div
|
||||
className="euiTableCellContent"
|
||||
>
|
||||
<span
|
||||
className="euiTableCellContent__text"
|
||||
>
|
||||
Description
|
||||
</span>
|
||||
</div>
|
||||
</th>
|
||||
<th
|
||||
className="euiTableHeaderCell"
|
||||
data-test-subj="tableHeaderCell_tags_2"
|
||||
role="columnheader"
|
||||
scope="col"
|
||||
style={
|
||||
Object {
|
||||
"width": "30%",
|
||||
}
|
||||
}
|
||||
>
|
||||
<div
|
||||
className="euiTableCellContent"
|
||||
>
|
||||
<span
|
||||
className="euiTableCellContent__text"
|
||||
>
|
||||
Tags
|
||||
</span>
|
||||
</div>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr
|
||||
className="euiTableRow"
|
||||
>
|
||||
<td
|
||||
className="euiTableRowCell"
|
||||
style={
|
||||
Object {
|
||||
"width": "30%",
|
||||
}
|
||||
}
|
||||
>
|
||||
<div
|
||||
className="euiTableRowCell__mobileHeader euiTableRowCell--hideForDesktop"
|
||||
>
|
||||
Template name
|
||||
</div>
|
||||
<div
|
||||
className="euiTableCellContent euiTableCellContent--overflowingContent"
|
||||
>
|
||||
<button
|
||||
aria-label="Clone workpad template 'test1'"
|
||||
className="euiButtonEmpty euiButtonEmpty--primary"
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
className="euiButtonEmpty__content"
|
||||
>
|
||||
<span
|
||||
className="euiButtonEmpty__text"
|
||||
>
|
||||
test1
|
||||
</span>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
className="euiTableRowCell"
|
||||
style={
|
||||
Object {
|
||||
"width": "30%",
|
||||
}
|
||||
}
|
||||
>
|
||||
<div
|
||||
className="euiTableRowCell__mobileHeader euiTableRowCell--hideForDesktop"
|
||||
>
|
||||
Description
|
||||
</div>
|
||||
<div
|
||||
className="euiTableCellContent"
|
||||
>
|
||||
<span
|
||||
className="euiTableCellContent__text"
|
||||
>
|
||||
This is a test template
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
className="euiTableRowCell"
|
||||
style={
|
||||
Object {
|
||||
"width": "30%",
|
||||
}
|
||||
}
|
||||
>
|
||||
<div
|
||||
className="euiTableRowCell__mobileHeader euiTableRowCell--hideForDesktop"
|
||||
>
|
||||
Tags
|
||||
</div>
|
||||
<div
|
||||
className="euiTableCellContent euiTableCellContent--overflowingContent"
|
||||
>
|
||||
<div
|
||||
className="euiHealth"
|
||||
>
|
||||
<div
|
||||
className="euiFlexGroup euiFlexGroup--gutterExtraSmall euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow"
|
||||
>
|
||||
<div
|
||||
className="euiFlexItem euiFlexItem--flexGrowZero"
|
||||
>
|
||||
<div
|
||||
color="#666666"
|
||||
data-euiicon-type="dot"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className="euiFlexItem euiFlexItem--flexGrowZero"
|
||||
>
|
||||
tag1
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="euiHealth"
|
||||
>
|
||||
<div
|
||||
className="euiFlexGroup euiFlexGroup--gutterExtraSmall euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow"
|
||||
>
|
||||
<div
|
||||
className="euiFlexItem euiFlexItem--flexGrowZero"
|
||||
>
|
||||
<div
|
||||
color="#666666"
|
||||
data-euiicon-type="dot"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className="euiFlexItem euiFlexItem--flexGrowZero"
|
||||
>
|
||||
tag2
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr
|
||||
className="euiTableRow"
|
||||
>
|
||||
<td
|
||||
className="euiTableRowCell"
|
||||
style={
|
||||
Object {
|
||||
"width": "30%",
|
||||
}
|
||||
}
|
||||
>
|
||||
<div
|
||||
className="euiTableRowCell__mobileHeader euiTableRowCell--hideForDesktop"
|
||||
>
|
||||
Template name
|
||||
</div>
|
||||
<div
|
||||
className="euiTableCellContent euiTableCellContent--overflowingContent"
|
||||
>
|
||||
<button
|
||||
aria-label="Clone workpad template 'test2'"
|
||||
className="euiButtonEmpty euiButtonEmpty--primary"
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
className="euiButtonEmpty__content"
|
||||
>
|
||||
<span
|
||||
className="euiButtonEmpty__text"
|
||||
>
|
||||
test2
|
||||
</span>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
className="euiTableRowCell"
|
||||
style={
|
||||
Object {
|
||||
"width": "30%",
|
||||
}
|
||||
}
|
||||
>
|
||||
<div
|
||||
className="euiTableRowCell__mobileHeader euiTableRowCell--hideForDesktop"
|
||||
>
|
||||
Description
|
||||
</div>
|
||||
<div
|
||||
className="euiTableCellContent"
|
||||
>
|
||||
<span
|
||||
className="euiTableCellContent__text"
|
||||
>
|
||||
This is a second test template
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
className="euiTableRowCell"
|
||||
style={
|
||||
Object {
|
||||
"width": "30%",
|
||||
}
|
||||
}
|
||||
>
|
||||
<div
|
||||
className="euiTableRowCell__mobileHeader euiTableRowCell--hideForDesktop"
|
||||
>
|
||||
Tags
|
||||
</div>
|
||||
<div
|
||||
className="euiTableCellContent euiTableCellContent--overflowingContent"
|
||||
>
|
||||
<div
|
||||
className="euiHealth"
|
||||
>
|
||||
<div
|
||||
className="euiFlexGroup euiFlexGroup--gutterExtraSmall euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow"
|
||||
>
|
||||
<div
|
||||
className="euiFlexItem euiFlexItem--flexGrowZero"
|
||||
>
|
||||
<div
|
||||
color="#666666"
|
||||
data-euiicon-type="dot"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className="euiFlexItem euiFlexItem--flexGrowZero"
|
||||
>
|
||||
tag2
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="euiHealth"
|
||||
>
|
||||
<div
|
||||
className="euiFlexGroup euiFlexGroup--gutterExtraSmall euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow"
|
||||
>
|
||||
<div
|
||||
className="euiFlexItem euiFlexItem--flexGrowZero"
|
||||
>
|
||||
<div
|
||||
color="#666666"
|
||||
data-euiicon-type="dot"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className="euiFlexItem euiFlexItem--flexGrowZero"
|
||||
>
|
||||
tag3
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="euiSpacer euiSpacer--l"
|
||||
/>
|
||||
<div
|
||||
className="euiFlexGroup euiFlexGroup--justifyContentFlexEnd euiFlexGroup--directionRow euiFlexGroup--responsive"
|
||||
>
|
||||
<div
|
||||
className="euiFlexItem euiFlexItem--flexGrowZero"
|
||||
>
|
||||
<div
|
||||
className="euiPagination"
|
||||
role="group"
|
||||
>
|
||||
<button
|
||||
aria-label="Previous page"
|
||||
className="euiButtonIcon euiButtonIcon--text"
|
||||
data-test-subj="pagination-button-previous"
|
||||
disabled={true}
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
className="euiButtonIcon__icon"
|
||||
data-euiicon-type="arrowLeft"
|
||||
size="m"
|
||||
/>
|
||||
</button>
|
||||
<button
|
||||
aria-label="Page 1 of 1"
|
||||
className="euiButtonEmpty euiButtonEmpty--text euiButtonEmpty--xSmall euiPaginationButton euiPaginationButton-isActive euiPaginationButton--hideOnMobile"
|
||||
data-test-subj="pagination-button-0"
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
className="euiButtonEmpty__content"
|
||||
>
|
||||
<span
|
||||
className="euiButtonEmpty__text"
|
||||
>
|
||||
1
|
||||
</span>
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
aria-label="Next page"
|
||||
className="euiButtonIcon euiButtonIcon--text"
|
||||
data-test-subj="pagination-button-next"
|
||||
disabled={true}
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
className="euiButtonIcon__icon"
|
||||
data-euiicon-type="arrowRight"
|
||||
size="m"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* 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 from 'react';
|
||||
import { storiesOf } from '@storybook/react';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import { WorkpadTemplates } from '../workpad_templates';
|
||||
import { CanvasTemplate } from '../../../../types';
|
||||
|
||||
const templates: Record<string, CanvasTemplate> = {
|
||||
test1: {
|
||||
id: 'test1-id',
|
||||
name: 'test1',
|
||||
help: 'This is a test template',
|
||||
tags: ['tag1', 'tag2'],
|
||||
template_key: 'test1-key',
|
||||
},
|
||||
test2: {
|
||||
id: 'test2-id',
|
||||
name: 'test2',
|
||||
help: 'This is a second test template',
|
||||
tags: ['tag2', 'tag3'],
|
||||
template_key: 'test2-key',
|
||||
},
|
||||
};
|
||||
|
||||
storiesOf('components/WorkpadTemplates', module)
|
||||
.addDecorator((story) => <div style={{ width: '500px' }}>{story()}</div>)
|
||||
.add('default', () => {
|
||||
const onCreateFromTemplateAction = action('onCreateFromTemplate');
|
||||
return (
|
||||
<WorkpadTemplates
|
||||
templates={templates}
|
||||
onClose={action('onClose')}
|
||||
onCreateFromTemplate={(template) => {
|
||||
onCreateFromTemplateAction(template);
|
||||
return Promise.resolve();
|
||||
}}
|
||||
/>
|
||||
);
|
||||
});
|
|
@ -1,40 +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 PropTypes from 'prop-types';
|
||||
import { compose, getContext, withHandlers, withProps } from 'recompose';
|
||||
import * as workpadService from '../../lib/workpad_service';
|
||||
import { getId } from '../../lib/get_id';
|
||||
import { templatesRegistry } from '../../lib/templates_registry';
|
||||
import { withKibana } from '../../../../../../src/plugins/kibana_react/public';
|
||||
import { WorkpadTemplates as Component } from './workpad_templates';
|
||||
|
||||
export const WorkpadTemplates = compose(
|
||||
getContext({
|
||||
router: PropTypes.object,
|
||||
}),
|
||||
withProps(() => ({
|
||||
templates: templatesRegistry.toJS(),
|
||||
})),
|
||||
withKibana,
|
||||
withHandlers(({ kibana }) => ({
|
||||
// Clone workpad given an id
|
||||
cloneWorkpad: (props) => (workpad) => {
|
||||
workpad.id = getId('workpad');
|
||||
workpad.name = `My Canvas Workpad - ${workpad.name}`;
|
||||
// Remove unneeded fields
|
||||
workpad.tags = undefined;
|
||||
workpad.displayName = undefined;
|
||||
workpad.help = undefined;
|
||||
return workpadService
|
||||
.create(workpad)
|
||||
.then(() => props.router.navigateTo('loadWorkpad', { id: workpad.id, page: 1 }))
|
||||
.catch((err) =>
|
||||
kibana.services.canvas.notify.error(err, { title: `Couldn't clone workpad template` })
|
||||
);
|
||||
},
|
||||
}))
|
||||
)(Component);
|
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
* 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, { useContext, useState, useEffect, FunctionComponent } from 'react';
|
||||
import { EuiLoadingSpinner } from '@elastic/eui';
|
||||
import { RouterContext } from '../router';
|
||||
import { ComponentStrings } from '../../../i18n/components';
|
||||
// @ts-expect-error
|
||||
import * as workpadService from '../../lib/workpad_service';
|
||||
import { useKibana } from '../../../../../../src/plugins/kibana_react/public';
|
||||
import { WorkpadTemplates as Component } from './workpad_templates';
|
||||
import { CanvasTemplate } from '../../../types';
|
||||
import { UseKibanaProps } from '../../';
|
||||
import { list } from '../../lib/template_service';
|
||||
import { applyTemplateStrings } from '../../../i18n/templates/apply_strings';
|
||||
|
||||
interface WorkpadTemplatesProps {
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
const Creating: FunctionComponent<{ name: string }> = ({ name }) => (
|
||||
<div>
|
||||
<EuiLoadingSpinner size="l" />{' '}
|
||||
{ComponentStrings.WorkpadTemplates.getCreatingTemplateLabel(name)}
|
||||
</div>
|
||||
);
|
||||
export const WorkpadTemplates: FunctionComponent<WorkpadTemplatesProps> = ({ onClose }) => {
|
||||
const router = useContext(RouterContext);
|
||||
const [templates, setTemplates] = useState<CanvasTemplate[] | undefined>(undefined);
|
||||
const [creatingFromTemplateName, setCreatingFromTemplateName] = useState<string | undefined>(
|
||||
undefined
|
||||
);
|
||||
const kibana = useKibana<UseKibanaProps>();
|
||||
|
||||
useEffect(() => {
|
||||
if (!templates) {
|
||||
(async () => {
|
||||
const fetchedTemplates = await list();
|
||||
setTemplates(applyTemplateStrings(fetchedTemplates));
|
||||
})();
|
||||
}
|
||||
}, [templates]);
|
||||
|
||||
let templateProp: Record<string, CanvasTemplate> = {};
|
||||
|
||||
if (templates) {
|
||||
templateProp = templates.reduce<Record<string, any>>((reduction, template) => {
|
||||
reduction[template.name] = template;
|
||||
return reduction;
|
||||
}, {});
|
||||
}
|
||||
|
||||
const createFromTemplate = async (template: CanvasTemplate) => {
|
||||
setCreatingFromTemplateName(template.name);
|
||||
try {
|
||||
const result = await workpadService.createFromTemplate(template.id);
|
||||
if (router) {
|
||||
router.navigateTo('loadWorkpad', { id: result.data.id, page: 1 });
|
||||
}
|
||||
} catch (error) {
|
||||
setCreatingFromTemplateName(undefined);
|
||||
kibana.services.canvas.notify.error(error, {
|
||||
title: `Couldn't create workpad from template`,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
if (creatingFromTemplateName) {
|
||||
return <Creating name={creatingFromTemplateName} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<Component
|
||||
onClose={onClose}
|
||||
templates={templateProp}
|
||||
onCreateFromTemplate={createFromTemplate}
|
||||
/>
|
||||
);
|
||||
};
|
|
@ -14,63 +14,101 @@ import {
|
|||
EuiSpacer,
|
||||
EuiButtonEmpty,
|
||||
EuiSearchBar,
|
||||
EuiTableSortingType,
|
||||
Direction,
|
||||
SortDirection,
|
||||
} from '@elastic/eui';
|
||||
import { sortByOrder } from 'lodash';
|
||||
import { Paginate } from '../paginate';
|
||||
// @ts-ignore untyped local
|
||||
import { EuiBasicTableColumn } from '@elastic/eui';
|
||||
import { Paginate, PaginateChildProps } from '../paginate';
|
||||
import { TagList } from '../tag_list';
|
||||
import { getTagsFilter } from '../../lib/get_tags_filter';
|
||||
// @ts-ignore untyped local
|
||||
import { extractSearch } from '../../lib/extract_search';
|
||||
import { ComponentStrings } from '../../../i18n';
|
||||
import { CanvasTemplate } from '../../../types';
|
||||
|
||||
interface TableChange<T> {
|
||||
page?: {
|
||||
index: number;
|
||||
size: number;
|
||||
};
|
||||
sort?: {
|
||||
field: keyof T;
|
||||
direction: Direction;
|
||||
};
|
||||
}
|
||||
|
||||
const { WorkpadTemplates: strings } = ComponentStrings;
|
||||
|
||||
export class WorkpadTemplates extends React.PureComponent {
|
||||
interface WorkpadTemplatesProps {
|
||||
onCreateFromTemplate: (template: CanvasTemplate) => Promise<void>;
|
||||
onClose: () => void;
|
||||
templates: Record<string, CanvasTemplate>;
|
||||
}
|
||||
|
||||
interface WorkpadTemplatesState {
|
||||
sortField: string;
|
||||
sortDirection: Direction;
|
||||
pageSize: number;
|
||||
searchTerm: string;
|
||||
filterTags: string[];
|
||||
}
|
||||
|
||||
export class WorkpadTemplates extends React.PureComponent<
|
||||
WorkpadTemplatesProps,
|
||||
WorkpadTemplatesState
|
||||
> {
|
||||
static propTypes = {
|
||||
cloneWorkpad: PropTypes.func.isRequired,
|
||||
createFromTemplate: PropTypes.func.isRequired,
|
||||
onClose: PropTypes.func.isRequired,
|
||||
templates: PropTypes.object,
|
||||
uniqueTags: PropTypes.object,
|
||||
};
|
||||
|
||||
state = {
|
||||
sortField: 'name',
|
||||
sortDirection: 'asc',
|
||||
sortDirection: SortDirection.ASC,
|
||||
pageSize: 10,
|
||||
searchTerm: '',
|
||||
filterTags: [],
|
||||
};
|
||||
|
||||
tagType = 'health';
|
||||
tagType: 'health' = 'health';
|
||||
|
||||
onTableChange = ({ sort = {} }) => {
|
||||
const { field: sortField, direction: sortDirection } = sort;
|
||||
onTableChange = (tableChange: TableChange<CanvasTemplate>) => {
|
||||
if (tableChange.sort) {
|
||||
const { field: sortField, direction: sortDirection } = tableChange.sort;
|
||||
this.setState({
|
||||
sortField,
|
||||
sortDirection,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
onSearch = ({ queryText }) => this.setState(extractSearch(queryText));
|
||||
onSearch = ({ queryText = '' }) => this.setState(extractSearch(queryText));
|
||||
|
||||
cloneTemplate = (template) => this.props.cloneWorkpad(template).then(() => this.props.onClose());
|
||||
cloneTemplate = (template: CanvasTemplate) =>
|
||||
this.props.onCreateFromTemplate(template).then(() => this.props.onClose());
|
||||
|
||||
renderWorkpadTable = ({ rows, pageNumber, totalPages, setPage }) => {
|
||||
renderWorkpadTable = ({ rows, pageNumber, totalPages, setPage }: PaginateChildProps) => {
|
||||
const { sortField, sortDirection } = this.state;
|
||||
|
||||
const columns = [
|
||||
const columns: Array<EuiBasicTableColumn<CanvasTemplate>> = [
|
||||
{
|
||||
field: 'name',
|
||||
name: strings.getTableNameColumnTitle(),
|
||||
sortable: true,
|
||||
width: '30%',
|
||||
dataType: 'string',
|
||||
render: (name, template) => {
|
||||
const templateName = name.length ? name : <em>{template.id}</em>;
|
||||
render: (name: string, template) => {
|
||||
const templateName = name.length ? name : 'Unnamed Template';
|
||||
|
||||
return (
|
||||
<EuiButtonEmpty
|
||||
onClick={() => this.cloneTemplate(template)}
|
||||
aria-label={strings.getCloneTemplateLinkAriaLabel(templateName)}
|
||||
type="link"
|
||||
type="button"
|
||||
>
|
||||
{templateName}
|
||||
</EuiButtonEmpty>
|
||||
|
@ -90,11 +128,11 @@ export class WorkpadTemplates extends React.PureComponent {
|
|||
sortable: false,
|
||||
dataType: 'string',
|
||||
width: '30%',
|
||||
render: (tags) => <TagList tags={tags} tagType={this.tagType} />,
|
||||
render: (tags: string[]) => <TagList tags={tags} tagType={this.tagType} />,
|
||||
},
|
||||
];
|
||||
|
||||
const sorting = {
|
||||
const sorting: EuiTableSortingType<any> = {
|
||||
sort: {
|
||||
field: sortField,
|
||||
direction: sortDirection,
|
||||
|
@ -162,7 +200,7 @@ export class WorkpadTemplates extends React.PureComponent {
|
|||
|
||||
return (
|
||||
<Paginate rows={filteredTemplates}>
|
||||
{(pagination) => (
|
||||
{(pagination: PaginateChildProps) => (
|
||||
<Fragment>
|
||||
{this.renderSearch()}
|
||||
<EuiSpacer />
|
|
@ -17,4 +17,8 @@ export interface WithKibanaProps {
|
|||
};
|
||||
}
|
||||
|
||||
export interface UseKibanaProps {
|
||||
canvas: CanvasServices;
|
||||
}
|
||||
|
||||
export const plugin = (initializerContext: PluginInitializerContext) => new CanvasPlugin();
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
import React from 'react';
|
||||
import { sortBy } from 'lodash';
|
||||
import { SearchFilterConfig } from '@elastic/eui';
|
||||
import { Tag } from '../components/tag';
|
||||
import { getId } from './get_id';
|
||||
import { tagsRegistry } from './tags_registry';
|
||||
|
@ -15,11 +16,12 @@ const { WorkpadTemplates: strings } = ComponentStrings;
|
|||
|
||||
// EUI helper function
|
||||
// generates the FieldValueSelectionFilter object for EuiSearchBar for tag filtering
|
||||
export const getTagsFilter = (type: 'health' | 'badge') => {
|
||||
export const getTagsFilter = (type: 'health' | 'badge'): SearchFilterConfig => {
|
||||
const uniqueTags = sortBy(Object.values(tagsRegistry.toJS()), 'name');
|
||||
const filterType = 'field_value_selection';
|
||||
|
||||
return {
|
||||
type: 'field_value_selection',
|
||||
type: filterType,
|
||||
field: 'tag',
|
||||
name: strings.getTableTagsColumnTitle(),
|
||||
multiSelect: true,
|
||||
|
|
25
x-pack/plugins/canvas/public/lib/template_service.ts
Normal file
25
x-pack/plugins/canvas/public/lib/template_service.ts
Normal file
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* 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 { API_ROUTE_TEMPLATES } from '../../common/lib/constants';
|
||||
import { fetch } from '../../common/lib/fetch';
|
||||
import { platformService } from '../services';
|
||||
import { CanvasTemplate } from '../../types';
|
||||
|
||||
const getApiPath = function () {
|
||||
const basePath = platformService.getService().coreStart.http.basePath.get();
|
||||
return `${basePath}${API_ROUTE_TEMPLATES}`;
|
||||
};
|
||||
|
||||
interface ListResponse {
|
||||
templates: CanvasTemplate[];
|
||||
}
|
||||
|
||||
export async function list() {
|
||||
const templateResponse = await fetch.get<ListResponse>(`${getApiPath()}`);
|
||||
|
||||
return templateResponse.data.templates;
|
||||
}
|
|
@ -64,6 +64,12 @@ export function create(workpad) {
|
|||
});
|
||||
}
|
||||
|
||||
export async function createFromTemplate(templateId) {
|
||||
return fetch.post(getApiPath(), {
|
||||
templateId,
|
||||
});
|
||||
}
|
||||
|
||||
export function get(workpadId) {
|
||||
return fetch.get(`${getApiPath()}/${workpadId}`).then(({ data: workpad }) => {
|
||||
// shim old workpads with new properties
|
||||
|
|
|
@ -21,7 +21,6 @@ export interface CanvasApi {
|
|||
addModelUIs: AddToRegistry<any>;
|
||||
addRenderers: AddToRegistry<RendererFactory>;
|
||||
addTagUIs: AddToRegistry<any>;
|
||||
addTemplates: AddToRegistry<any>;
|
||||
addTransformUIs: AddToRegistry<any>;
|
||||
addTransitions: AddToRegistry<any>;
|
||||
addTypes: AddToRegistry<() => AnyExpressionTypeDefinition>;
|
||||
|
@ -35,7 +34,6 @@ export interface SetupRegistries {
|
|||
modelUIs: any[];
|
||||
viewUIs: any[];
|
||||
argumentUIs: any[];
|
||||
templates: any[];
|
||||
tagUIs: any[];
|
||||
transitions: any[];
|
||||
}
|
||||
|
@ -50,7 +48,6 @@ export function getPluginApi(
|
|||
modelUIs: [],
|
||||
viewUIs: [],
|
||||
argumentUIs: [],
|
||||
templates: [],
|
||||
tagUIs: [],
|
||||
transitions: [],
|
||||
};
|
||||
|
@ -80,7 +77,6 @@ export function getPluginApi(
|
|||
addModelUIs: (models) => registries.modelUIs.push(...models),
|
||||
addViewUIs: (views) => registries.viewUIs.push(...views),
|
||||
addArgumentUIs: (args) => registries.argumentUIs.push(...args),
|
||||
addTemplates: (templates) => registries.templates.push(...templates),
|
||||
addTagUIs: (tags) => registries.tagUIs.push(...tags),
|
||||
addTransitions: (transitions) => registries.transitions.push(...transitions),
|
||||
};
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
|
||||
import { first } from 'rxjs/operators';
|
||||
import { CoreSetup, PluginInitializerContext, Plugin, Logger } from 'src/core/server';
|
||||
import { CoreSetup, PluginInitializerContext, Plugin, Logger, CoreStart } from 'src/core/server';
|
||||
import { ExpressionsServerSetup } from 'src/plugins/expressions/server';
|
||||
import { UsageCollectionSetup } from 'src/plugins/usage_collection/server';
|
||||
import { HomeServerPluginSetup } from 'src/plugins/home/server';
|
||||
|
@ -14,7 +14,8 @@ import { initRoutes } from './routes';
|
|||
import { registerCanvasUsageCollector } from './collectors';
|
||||
import { loadSampleData } from './sample_data';
|
||||
import { setupInterpreter } from './setup_interpreter';
|
||||
import { customElementType, workpadType } from './saved_objects';
|
||||
import { customElementType, workpadType, workpadTemplateType } from './saved_objects';
|
||||
import { initializeTemplates } from './templates';
|
||||
|
||||
interface PluginsSetup {
|
||||
expressions: ExpressionsServerSetup;
|
||||
|
@ -32,6 +33,7 @@ export class CanvasPlugin implements Plugin {
|
|||
public async setup(coreSetup: CoreSetup, plugins: PluginsSetup) {
|
||||
coreSetup.savedObjects.registerType(customElementType);
|
||||
coreSetup.savedObjects.registerType(workpadType);
|
||||
coreSetup.savedObjects.registerType(workpadTemplateType);
|
||||
|
||||
plugins.features.registerFeature({
|
||||
id: 'canvas',
|
||||
|
@ -81,7 +83,10 @@ export class CanvasPlugin implements Plugin {
|
|||
setupInterpreter(plugins.expressions);
|
||||
}
|
||||
|
||||
public start() {}
|
||||
public start(coreStart: CoreStart) {
|
||||
const client = coreStart.savedObjects.createInternalRepository();
|
||||
initializeTemplates(client);
|
||||
}
|
||||
|
||||
public stop() {}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import { initCustomElementsRoutes } from './custom_elements';
|
|||
import { initESFieldsRoutes } from './es_fields';
|
||||
import { initShareablesRoutes } from './shareables';
|
||||
import { initWorkpadRoutes } from './workpad';
|
||||
import { initTemplateRoutes } from './templates';
|
||||
|
||||
export interface RouteInitializerDeps {
|
||||
router: IRouter;
|
||||
|
@ -20,4 +21,5 @@ export function initRoutes(deps: RouteInitializerDeps) {
|
|||
initESFieldsRoutes(deps);
|
||||
initShareablesRoutes(deps);
|
||||
initWorkpadRoutes(deps);
|
||||
initTemplateRoutes(deps);
|
||||
}
|
||||
|
|
12
x-pack/plugins/canvas/server/routes/templates/index.ts
Normal file
12
x-pack/plugins/canvas/server/routes/templates/index.ts
Normal file
|
@ -0,0 +1,12 @@
|
|||
/*
|
||||
* 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 { RouteInitializerDeps } from '../';
|
||||
import { initializeListTemplates } from './list';
|
||||
|
||||
export function initTemplateRoutes(deps: RouteInitializerDeps) {
|
||||
initializeListTemplates(deps);
|
||||
}
|
103
x-pack/plugins/canvas/server/routes/templates/list.test.ts
Normal file
103
x-pack/plugins/canvas/server/routes/templates/list.test.ts
Normal file
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
* 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 { badRequest } from 'boom';
|
||||
import { initializeListTemplates } from './list';
|
||||
import {
|
||||
IRouter,
|
||||
kibanaResponseFactory,
|
||||
RequestHandlerContext,
|
||||
RequestHandler,
|
||||
} from 'src/core/server';
|
||||
import {
|
||||
savedObjectsClientMock,
|
||||
httpServiceMock,
|
||||
httpServerMock,
|
||||
loggingSystemMock,
|
||||
} from 'src/core/server/mocks';
|
||||
|
||||
const mockRouteContext = ({
|
||||
core: {
|
||||
savedObjects: {
|
||||
client: savedObjectsClientMock.create(),
|
||||
},
|
||||
},
|
||||
} as unknown) as RequestHandlerContext;
|
||||
|
||||
describe('Find workpad', () => {
|
||||
let routeHandler: RequestHandler<any, any, any>;
|
||||
|
||||
beforeEach(() => {
|
||||
const httpService = httpServiceMock.createSetupContract();
|
||||
const router = httpService.createRouter() as jest.Mocked<IRouter>;
|
||||
initializeListTemplates({
|
||||
router,
|
||||
logger: loggingSystemMock.create().get(),
|
||||
});
|
||||
|
||||
routeHandler = router.get.mock.calls[0][1];
|
||||
});
|
||||
|
||||
it(`returns 200 with the found templates`, async () => {
|
||||
const template1 = { name: 'template1' };
|
||||
const template2 = { name: 'template2' };
|
||||
|
||||
const mockResults = {
|
||||
total: 2,
|
||||
saved_objects: [
|
||||
{ id: 1, attributes: template1 },
|
||||
{ id: 2, attributes: template2 },
|
||||
],
|
||||
};
|
||||
|
||||
const findMock = mockRouteContext.core.savedObjects.client.find as jest.Mock;
|
||||
|
||||
findMock.mockResolvedValueOnce(mockResults);
|
||||
|
||||
const request = httpServerMock.createKibanaRequest({
|
||||
method: 'get',
|
||||
path: `api/canvas/templates/list`,
|
||||
});
|
||||
|
||||
const response = await routeHandler(mockRouteContext, request, kibanaResponseFactory);
|
||||
expect(response.status).toBe(200);
|
||||
|
||||
expect(response.payload).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"templates": Array [
|
||||
Object {
|
||||
"name": "template1",
|
||||
},
|
||||
Object {
|
||||
"name": "template2",
|
||||
},
|
||||
],
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it(`returns appropriate error on error`, async () => {
|
||||
(mockRouteContext.core.savedObjects.client.find as jest.Mock).mockImplementationOnce(() => {
|
||||
throw badRequest('generic error');
|
||||
});
|
||||
|
||||
const request = httpServerMock.createKibanaRequest({
|
||||
method: 'get',
|
||||
path: `api/canvas/templates/list`,
|
||||
});
|
||||
|
||||
const response = await routeHandler(mockRouteContext, request, kibanaResponseFactory);
|
||||
|
||||
expect(response.status).toBe(400);
|
||||
expect(response.payload).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"error": "Bad Request",
|
||||
"message": "generic error",
|
||||
"statusCode": 400,
|
||||
}
|
||||
`);
|
||||
});
|
||||
});
|
43
x-pack/plugins/canvas/server/routes/templates/list.ts
Normal file
43
x-pack/plugins/canvas/server/routes/templates/list.ts
Normal file
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* 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 { schema } from '@kbn/config-schema';
|
||||
import { RouteInitializerDeps } from '../';
|
||||
import { TEMPLATE_TYPE, API_ROUTE_TEMPLATES } from '../../../common/lib/constants';
|
||||
import { catchErrorHandler } from '../catch_error_handler';
|
||||
import { CanvasTemplate } from '../../../types';
|
||||
|
||||
export function initializeListTemplates(deps: RouteInitializerDeps) {
|
||||
const { router } = deps;
|
||||
router.get(
|
||||
{
|
||||
path: `${API_ROUTE_TEMPLATES}`,
|
||||
validate: {
|
||||
params: schema.object({}),
|
||||
},
|
||||
},
|
||||
catchErrorHandler(async (context, request, response) => {
|
||||
const savedObjectsClient = context.core.savedObjects.client;
|
||||
|
||||
const templates = await savedObjectsClient.find<CanvasTemplate>({
|
||||
type: TEMPLATE_TYPE,
|
||||
sortField: 'name.keyword',
|
||||
sortOrder: 'desc',
|
||||
search: '*',
|
||||
searchFields: ['name', 'help'],
|
||||
fields: ['id', 'name', 'help', 'tags'],
|
||||
});
|
||||
|
||||
return response.ok({
|
||||
body: {
|
||||
templates: templates.saved_objects.map((hit) => ({
|
||||
...hit.attributes,
|
||||
})),
|
||||
},
|
||||
});
|
||||
})
|
||||
);
|
||||
}
|
|
@ -15,7 +15,7 @@ import { CANVAS_TYPE } from '../../../common/lib/constants';
|
|||
import { initializeCreateWorkpadRoute } from './create';
|
||||
import { kibanaResponseFactory, RequestHandlerContext, RequestHandler } from 'src/core/server';
|
||||
|
||||
const mockRouteContext = ({
|
||||
let mockRouteContext = ({
|
||||
core: {
|
||||
savedObjects: {
|
||||
client: savedObjectsClientMock.create(),
|
||||
|
@ -34,6 +34,14 @@ describe('POST workpad', () => {
|
|||
let clock: sinon.SinonFakeTimers;
|
||||
|
||||
beforeEach(() => {
|
||||
mockRouteContext = ({
|
||||
core: {
|
||||
savedObjects: {
|
||||
client: savedObjectsClientMock.create(),
|
||||
},
|
||||
},
|
||||
} as unknown) as RequestHandlerContext;
|
||||
|
||||
clock = sinon.useFakeTimers(now);
|
||||
|
||||
const httpService = httpServiceMock.createSetupContract();
|
||||
|
@ -65,7 +73,7 @@ describe('POST workpad', () => {
|
|||
const response = await routeHandler(mockRouteContext, request, kibanaResponseFactory);
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.payload).toEqual({ ok: true });
|
||||
expect(response.payload).toEqual({ ok: true, id: `workpad-${mockedUUID}` });
|
||||
expect(mockRouteContext.core.savedObjects.client.create).toBeCalledWith(
|
||||
CANVAS_TYPE,
|
||||
{
|
||||
|
@ -94,4 +102,45 @@ describe('POST workpad', () => {
|
|||
|
||||
expect(response.status).toBe(400);
|
||||
});
|
||||
|
||||
it(`returns 200 when a template is cloned`, async () => {
|
||||
const cloneFromTemplateBody = {
|
||||
templateId: 'template-id',
|
||||
};
|
||||
|
||||
const mockTemplateResponse = {
|
||||
attributes: {
|
||||
id: 'template-id',
|
||||
template: {
|
||||
pages: [],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
(mockRouteContext.core.savedObjects.client.get as jest.Mock).mockResolvedValue(
|
||||
mockTemplateResponse
|
||||
);
|
||||
|
||||
const request = httpServerMock.createKibanaRequest({
|
||||
method: 'post',
|
||||
path: 'api/canvas/workpad',
|
||||
body: cloneFromTemplateBody,
|
||||
});
|
||||
|
||||
const response = await routeHandler(mockRouteContext, request, kibanaResponseFactory);
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.payload).toEqual({ ok: true, id: `workpad-${mockedUUID}` });
|
||||
expect(mockRouteContext.core.savedObjects.client.create).toBeCalledWith(
|
||||
CANVAS_TYPE,
|
||||
{
|
||||
...mockTemplateResponse.attributes.template,
|
||||
'@timestamp': nowIso,
|
||||
'@created': nowIso,
|
||||
},
|
||||
{
|
||||
id: `workpad-${mockedUUID}`,
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -4,8 +4,9 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { schema } from '@kbn/config-schema';
|
||||
import { RouteInitializerDeps } from '../';
|
||||
import { CANVAS_TYPE, API_ROUTE_WORKPAD } from '../../../common/lib/constants';
|
||||
import { CANVAS_TYPE, API_ROUTE_WORKPAD, TEMPLATE_TYPE } from '../../../common/lib/constants';
|
||||
import { CanvasWorkpad } from '../../../types';
|
||||
import { getId } from '../../../common/lib/get_id';
|
||||
import { WorkpadAttributes } from './workpad_attributes';
|
||||
|
@ -13,13 +14,31 @@ import { WorkpadSchema } from './workpad_schema';
|
|||
import { okResponse } from '../ok_response';
|
||||
import { catchErrorHandler } from '../catch_error_handler';
|
||||
|
||||
interface TemplateAttributes {
|
||||
template: CanvasWorkpad;
|
||||
}
|
||||
|
||||
const WorkpadFromTemplateSchema = schema.object({
|
||||
templateId: schema.string(),
|
||||
});
|
||||
|
||||
const createRequestBodySchema = schema.oneOf([WorkpadSchema, WorkpadFromTemplateSchema]);
|
||||
|
||||
function isCreateFromTemplate(
|
||||
maybeCreateFromTemplate: typeof createRequestBodySchema.type
|
||||
): maybeCreateFromTemplate is typeof WorkpadFromTemplateSchema.type {
|
||||
return (
|
||||
(maybeCreateFromTemplate as typeof WorkpadFromTemplateSchema.type).templateId !== undefined
|
||||
);
|
||||
}
|
||||
|
||||
export function initializeCreateWorkpadRoute(deps: RouteInitializerDeps) {
|
||||
const { router } = deps;
|
||||
router.post(
|
||||
{
|
||||
path: `${API_ROUTE_WORKPAD}`,
|
||||
validate: {
|
||||
body: WorkpadSchema,
|
||||
body: createRequestBodySchema,
|
||||
},
|
||||
options: {
|
||||
body: {
|
||||
|
@ -29,14 +48,20 @@ export function initializeCreateWorkpadRoute(deps: RouteInitializerDeps) {
|
|||
},
|
||||
},
|
||||
catchErrorHandler(async (context, request, response) => {
|
||||
if (!request.body) {
|
||||
return response.badRequest({ body: 'A workpad payload is required' });
|
||||
let workpad = request.body as CanvasWorkpad;
|
||||
|
||||
if (isCreateFromTemplate(request.body)) {
|
||||
const templateSavedObject = await context.core.savedObjects.client.get<TemplateAttributes>(
|
||||
TEMPLATE_TYPE,
|
||||
request.body.templateId
|
||||
);
|
||||
workpad = templateSavedObject.attributes.template;
|
||||
}
|
||||
|
||||
const workpad = request.body as CanvasWorkpad;
|
||||
|
||||
const now = new Date().toISOString();
|
||||
const { id, ...payload } = workpad;
|
||||
const { id: maybeId, ...payload } = workpad;
|
||||
|
||||
const id = maybeId ? maybeId : getId('workpad');
|
||||
|
||||
await context.core.savedObjects.client.create<WorkpadAttributes>(
|
||||
CANVAS_TYPE,
|
||||
|
@ -45,11 +70,11 @@ export function initializeCreateWorkpadRoute(deps: RouteInitializerDeps) {
|
|||
'@timestamp': now,
|
||||
'@created': now,
|
||||
},
|
||||
{ id: id || getId('workpad') }
|
||||
{ id }
|
||||
);
|
||||
|
||||
return response.ok({
|
||||
body: okResponse,
|
||||
body: { ...okResponse, id },
|
||||
});
|
||||
})
|
||||
);
|
||||
|
|
|
@ -6,5 +6,6 @@
|
|||
|
||||
import { workpadType } from './workpad';
|
||||
import { customElementType } from './custom_element';
|
||||
import { workpadTemplateType } from './workpad_template';
|
||||
|
||||
export { customElementType, workpadType };
|
||||
export { customElementType, workpadType, workpadTemplateType };
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* 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 { SavedObjectsType } from 'src/core/server';
|
||||
import { TEMPLATE_TYPE } from '../../common/lib/constants';
|
||||
|
||||
export const workpadTemplateType: SavedObjectsType = {
|
||||
name: TEMPLATE_TYPE,
|
||||
hidden: false,
|
||||
namespaceType: 'agnostic',
|
||||
mappings: {
|
||||
dynamic: false,
|
||||
properties: {
|
||||
name: {
|
||||
type: 'text',
|
||||
fields: {
|
||||
keyword: {
|
||||
type: 'keyword',
|
||||
},
|
||||
},
|
||||
},
|
||||
help: {
|
||||
type: 'text',
|
||||
fields: {
|
||||
keyword: {
|
||||
type: 'keyword',
|
||||
},
|
||||
},
|
||||
},
|
||||
tags: {
|
||||
type: 'text',
|
||||
fields: {
|
||||
keyword: {
|
||||
type: 'keyword',
|
||||
},
|
||||
},
|
||||
},
|
||||
template_key: {
|
||||
type: 'keyword',
|
||||
},
|
||||
},
|
||||
},
|
||||
migrations: {},
|
||||
management: {
|
||||
importableAndExportable: true,
|
||||
icon: 'canvasApp',
|
||||
defaultSearchField: 'name',
|
||||
getTitle(obj) {
|
||||
return obj.attributes.name;
|
||||
},
|
||||
},
|
||||
};
|
32
x-pack/plugins/canvas/server/templates/index.ts
Normal file
32
x-pack/plugins/canvas/server/templates/index.ts
Normal file
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* 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 { SavedObjectsRepository } from 'src/core/server';
|
||||
import { pitch } from './pitch_presentation';
|
||||
import { status } from './status_report';
|
||||
import { summary } from './summary_report';
|
||||
import { dark } from './theme_dark';
|
||||
import { light } from './theme_light';
|
||||
|
||||
import { TEMPLATE_TYPE } from '../../common/lib/constants';
|
||||
|
||||
export const templates = [pitch, status, summary, dark, light];
|
||||
|
||||
export async function initializeTemplates(
|
||||
client: Pick<SavedObjectsRepository, 'bulkCreate' | 'find'>
|
||||
) {
|
||||
const existingTemplates = await client.find({ type: TEMPLATE_TYPE, perPage: 1 });
|
||||
|
||||
if (existingTemplates.total === 0) {
|
||||
const templateObjects = templates.map((template) => ({
|
||||
id: template.id,
|
||||
type: TEMPLATE_TYPE,
|
||||
attributes: template,
|
||||
}));
|
||||
|
||||
client.bulkCreate(templateObjects);
|
||||
}
|
||||
}
|
File diff suppressed because one or more lines are too long
888
x-pack/plugins/canvas/server/templates/status_report.ts
Normal file
888
x-pack/plugins/canvas/server/templates/status_report.ts
Normal file
File diff suppressed because one or more lines are too long
497
x-pack/plugins/canvas/server/templates/summary_report.ts
Normal file
497
x-pack/plugins/canvas/server/templates/summary_report.ts
Normal file
|
@ -0,0 +1,497 @@
|
|||
/*
|
||||
* 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 { CanvasTemplate } from '../../types';
|
||||
|
||||
export const summary: CanvasTemplate = {
|
||||
id: 'workpad-template-6181471b-147d-4397-a0d3-1c0f1600fa12',
|
||||
name: 'Summary',
|
||||
help: 'Infographic-style report with live charts',
|
||||
tags: ['report'],
|
||||
template_key: 'summary-report',
|
||||
template: {
|
||||
name: 'Summary',
|
||||
width: 1100,
|
||||
height: 2570,
|
||||
page: 0,
|
||||
pages: [
|
||||
{
|
||||
id: 'page-28d2523e-aa4d-4134-8092-b849835b620f',
|
||||
style: {
|
||||
background: '#FFF',
|
||||
},
|
||||
transition: {},
|
||||
elements: [
|
||||
{
|
||||
id: 'element-7e937714-3a57-4d41-bcc7-859b2d2db497',
|
||||
position: {
|
||||
left: -1.375,
|
||||
top: -2.5,
|
||||
width: 1101.75,
|
||||
height: 115,
|
||||
angle: 0,
|
||||
parent: null,
|
||||
},
|
||||
expression:
|
||||
'shape "square" fill="#69707D" border="rgba(255,255,255,0)" borderWidth=0 maintainAspect=false\n| render css=".canvasRenderEl {\n\n}" containerStyle={containerStyle}',
|
||||
},
|
||||
{
|
||||
id: 'element-8cbe96d4-f555-4891-8f23-ef6cd679d9cf',
|
||||
position: {
|
||||
left: 31.75,
|
||||
top: 1186,
|
||||
width: 1034.5,
|
||||
height: 421,
|
||||
angle: 0,
|
||||
parent: null,
|
||||
},
|
||||
expression:
|
||||
'shape "square" fill="rgba(255,255,255,0)" border="rgba(255,255,255,0)" borderWidth=2 maintainAspect=false\n| render css=".canvasRenderEl {\n\n}" \n containerStyle={containerStyle borderRadius="6px" border="2px solid #D3DAE6" backgroundColor="rgba(255,255,255,0)"}',
|
||||
},
|
||||
{
|
||||
id: 'element-9c467f5e-3594-41db-8602-ec45e4f3fe8f',
|
||||
position: {
|
||||
left: 566.25,
|
||||
top: 1650,
|
||||
width: 500,
|
||||
height: 386,
|
||||
angle: 0,
|
||||
parent: null,
|
||||
},
|
||||
expression:
|
||||
'shape "square" fill="rgba(255,255,255,0)" border="rgba(255,255,255,0)" borderWidth=2 maintainAspect=false\n| render css=".canvasRenderEl {\n\n}" \n containerStyle={containerStyle borderRadius="6px" border="2px solid #D3DAE6" backgroundColor="rgba(255,255,255,0)"}',
|
||||
},
|
||||
{
|
||||
id: 'element-a07f8a00-d3da-470c-aea1-b88407900ba5',
|
||||
position: {
|
||||
left: 30.75,
|
||||
top: 1650,
|
||||
width: 508.25,
|
||||
height: 386,
|
||||
angle: 0,
|
||||
parent: null,
|
||||
},
|
||||
expression:
|
||||
'shape "square" fill="rgba(255,255,255,0)" border="rgba(255,255,255,0)" borderWidth=2 maintainAspect=false\n| render css=".canvasRenderEl {\n\n}" \n containerStyle={containerStyle borderRadius="6px" border="2px solid #D3DAE6" backgroundColor="rgba(255,255,255,0)"}',
|
||||
},
|
||||
{
|
||||
id: 'element-80c70a23-12d9-4282-a68e-5d98ceb5a31f',
|
||||
position: {
|
||||
left: 31.75,
|
||||
top: 2084.5,
|
||||
width: 1034.5,
|
||||
height: 413,
|
||||
angle: 0,
|
||||
parent: null,
|
||||
},
|
||||
expression:
|
||||
'shape "square" fill="rgba(255,255,255,0)" border="rgba(255,255,255,0)" borderWidth=2 maintainAspect=false\n| render css=".canvasRenderEl {\n\n}" \n containerStyle={containerStyle borderRadius="6px" border="2px solid #D3DAE6" backgroundColor="rgba(255,255,255,0)"}',
|
||||
},
|
||||
{
|
||||
id: 'element-105a0788-e347-4fa0-afff-0a6b80633b80',
|
||||
position: {
|
||||
left: 31.75,
|
||||
top: 707,
|
||||
width: 1034.5,
|
||||
height: 437,
|
||||
angle: 0,
|
||||
parent: null,
|
||||
},
|
||||
expression:
|
||||
'shape "square" fill="rgba(255,255,255,0)" border="rgba(255,255,255,0)" borderWidth=2 maintainAspect=false\n| render css=".canvasRenderEl {\n\n}" \n containerStyle={containerStyle borderRadius="6px" border="2px solid #D3DAE6" backgroundColor="rgba(255,255,255,0)"}',
|
||||
},
|
||||
{
|
||||
id: 'element-f1d3d480-8aba-48cb-b5f0-2f6a62e64f3a',
|
||||
position: {
|
||||
left: 566.25,
|
||||
top: 158,
|
||||
width: 500,
|
||||
height: 508.5,
|
||||
angle: 0,
|
||||
parent: null,
|
||||
},
|
||||
expression:
|
||||
'shape "square" fill="rgba(255,255,255,0)" border="rgba(255,255,255,0)" borderWidth=2 maintainAspect=false\n| render css=".canvasRenderEl {\n\n}" \n containerStyle={containerStyle borderRadius="6px" border="2px solid #D3DAE6" backgroundColor="rgba(255,255,255,0)"}',
|
||||
},
|
||||
{
|
||||
id: 'element-58634438-d8c7-4368-8e41-640d858374c3',
|
||||
position: {
|
||||
left: 31.75,
|
||||
top: 158,
|
||||
width: 507.25,
|
||||
height: 508.5,
|
||||
angle: 0,
|
||||
parent: null,
|
||||
},
|
||||
expression:
|
||||
'shape "square" fill="rgba(255,255,255,0)" border="rgba(255,255,255,0)" borderWidth=2 maintainAspect=false\n| render css=".canvasRenderEl {\n\n}" \n containerStyle={containerStyle borderRadius="6px" border="2px solid #D3DAE6" backgroundColor="rgba(255,255,255,0)"}',
|
||||
},
|
||||
{
|
||||
id: 'element-9f76c74a-28d9-4ceb-bd7d-b1b34999a11e',
|
||||
position: {
|
||||
left: 52,
|
||||
top: 178,
|
||||
width: 500,
|
||||
height: 38,
|
||||
angle: 0,
|
||||
parent: null,
|
||||
},
|
||||
expression:
|
||||
'filters\n| demodata\n| markdown "### Total cost by project type" \n font={font family="\'Open Sans\', Helvetica, Arial, sans-serif" size=14 align="left" color="#000000" weight="normal" underline=false italic=false}\n| render css=""',
|
||||
},
|
||||
{
|
||||
id: 'element-3b6345a5-16ea-4828-beec-425458e758a7',
|
||||
position: {
|
||||
left: 591.25,
|
||||
top: 240,
|
||||
width: 455,
|
||||
height: 403,
|
||||
angle: 0,
|
||||
parent: null,
|
||||
},
|
||||
expression:
|
||||
'filters\n| demodata\n| pointseries x="size(project)" y="project" color="project"\n| plot defaultStyle={seriesStyle bars=0.75 horizontalBars=true} legend=false seriesStyle={seriesStyle label="elasticsearch" color="#882e72"}\n seriesStyle={seriesStyle label="machine-learning" color="#d6c1de"}\n seriesStyle={seriesStyle label="apm" color="#5289c7"}\n seriesStyle={seriesStyle label="kibana" color="#7bafde"}\n seriesStyle={seriesStyle label="beats" color="#b178a6"}\n seriesStyle={seriesStyle label="logstash" color="#1965b0"}\n seriesStyle={seriesStyle label="x-pack" color="#4eb265"}\n seriesStyle={seriesStyle label="swiftype" color="#90c987"}\n| render \n css=".flot-y-axis {\n left: 14px !important;\n}\n\n.flot-x-axis>div {\n top: 380px !important;\n}"',
|
||||
},
|
||||
{
|
||||
id: 'element-bdfb3910-5f65-4c24-9bbe-e62feb9e5e11',
|
||||
position: {
|
||||
left: 585.75,
|
||||
top: 178,
|
||||
width: 378,
|
||||
height: 38,
|
||||
angle: 0,
|
||||
parent: null,
|
||||
},
|
||||
expression:
|
||||
'filters\n| demodata\n| markdown "### Number of projects by project type" \n font={font family="\'Open Sans\', Helvetica, Arial, sans-serif" size=14 align="left" color="#000000" weight="normal" underline=false italic=false}\n| render css=""',
|
||||
},
|
||||
{
|
||||
id: 'element-161aafca-ba71-43e1-b2a2-dab96a78d717',
|
||||
position: {
|
||||
left: 53,
|
||||
top: 211,
|
||||
width: 500,
|
||||
height: 38,
|
||||
angle: 0,
|
||||
parent: null,
|
||||
},
|
||||
expression:
|
||||
'filters\n| demodata\n| markdown "##### Global cost distribution" \n font={font family="\'Open Sans\', Helvetica, Arial, sans-serif" size=14 align="left" color="#000000" weight="normal" underline=false italic=false}\n| render css=""',
|
||||
},
|
||||
{
|
||||
id: 'element-d0c43968-cdcd-4a25-980f-83d6f0adf68e',
|
||||
position: {
|
||||
left: 586,
|
||||
top: 211,
|
||||
width: 500,
|
||||
height: 38,
|
||||
angle: 0,
|
||||
parent: null,
|
||||
},
|
||||
expression:
|
||||
'filters\n| demodata\n| markdown "##### Project type distribution\n" \n font={font family="\'Open Sans\', Helvetica, Arial, sans-serif" size=14 align="left" color="#000000" weight="normal" underline=false italic=false}\n| render css=""',
|
||||
},
|
||||
{
|
||||
id: 'element-ea1f3942-066f-4032-a9d0-125072d353d9',
|
||||
position: {
|
||||
left: 61.75,
|
||||
top: 793,
|
||||
width: 643,
|
||||
height: 300,
|
||||
angle: 0,
|
||||
parent: null,
|
||||
},
|
||||
expression:
|
||||
'filters\n| demodata\n| pointseries x="project" y="mean(percent_uptime)" color="project"\n| plot defaultStyle={seriesStyle bars=0.75} legend=false seriesStyle={seriesStyle label="elasticsearch" color="#882e72"}\n seriesStyle={seriesStyle label="machine-learning" color="#d6c1de"}\n seriesStyle={seriesStyle label="apm" color="#5289c7"}\n seriesStyle={seriesStyle label="logstash" color="#1965b0"}\n seriesStyle={seriesStyle label="x-pack" color="#4eb265"}\n seriesStyle={seriesStyle label="kibana" color="#7bafde"}\n seriesStyle={seriesStyle label="swiftype" color="#90c987"}\n seriesStyle={seriesStyle label="beats" color="#b178a6"}\n| render css=".flot-x-axis>div {\n top: 258px !important;\n}"',
|
||||
},
|
||||
{
|
||||
id: 'element-5a891ee6-5cb8-4b8a-9c01-302ed42e6a8f',
|
||||
position: {
|
||||
left: 53,
|
||||
top: 726,
|
||||
width: 500,
|
||||
height: 38,
|
||||
angle: 0,
|
||||
parent: null,
|
||||
},
|
||||
expression:
|
||||
'filters\n| demodata\n| markdown "### Average uptime" \n font={font family="\'Open Sans\', Helvetica, Arial, sans-serif" size=14 align="left" color="#000000" weight="normal" underline=false italic=false}\n| render css=""',
|
||||
},
|
||||
{
|
||||
id: 'element-09713339-044e-4084-b4e4-553dbc939d8a',
|
||||
position: {
|
||||
left: 729,
|
||||
top: 757,
|
||||
width: 301,
|
||||
height: 38,
|
||||
angle: 0,
|
||||
parent: null,
|
||||
},
|
||||
expression:
|
||||
'filters\n| demodata\n| markdown "##### Global average uptime\n" \n font={font family="\'Open Sans\', Helvetica, Arial, sans-serif" size=14 align="left" color="#000000" weight="normal" underline=false italic=false}\n| render css=""',
|
||||
},
|
||||
{
|
||||
id: 'element-bd806eff-400b-4816-b728-b28a0390352d',
|
||||
position: {
|
||||
left: 764,
|
||||
top: 833.5,
|
||||
width: 200,
|
||||
height: 200,
|
||||
angle: 0,
|
||||
parent: null,
|
||||
},
|
||||
expression:
|
||||
'filters\n| demodata\n| math "mean(percent_uptime)"\n| progress shape="wheel" label={formatnumber "0%"} \n font={font size=24 family="\'Open Sans\', Helvetica, Arial, sans-serif" color="#000000" align="center"} valueColor="#4eb265"\n| render containerStyle={containerStyle}',
|
||||
},
|
||||
{
|
||||
id: 'element-ccd76ddc-2c03-458d-a0eb-09fcd1e2455f',
|
||||
position: {
|
||||
left: 53,
|
||||
top: 1212,
|
||||
width: 500,
|
||||
height: 38,
|
||||
angle: 0,
|
||||
parent: null,
|
||||
},
|
||||
expression:
|
||||
'filters\n| demodata\n| markdown "### Average price by project type" \n font={font family="\'Open Sans\', Helvetica, Arial, sans-serif" size=14 align="left" color="#000000" weight="normal" underline=false italic=false}\n| render css=""',
|
||||
},
|
||||
{
|
||||
id: 'element-ef88de44-1629-4a66-abc5-3764b03342e5',
|
||||
position: {
|
||||
left: 55.5,
|
||||
top: 2110,
|
||||
width: 500,
|
||||
height: 38,
|
||||
angle: 0,
|
||||
parent: null,
|
||||
},
|
||||
expression:
|
||||
'filters\n| demodata\n| markdown "### Raw data" \n font={font family="\'Open Sans\', Helvetica, Arial, sans-serif" size=14 align="left" color="#000000" weight="normal" underline=false italic=false}\n| render css=""',
|
||||
},
|
||||
{
|
||||
id: 'element-1dbb5050-7b7c-4dd2-ab83-95913d15cc91',
|
||||
position: {
|
||||
left: 62.75,
|
||||
top: 273.75,
|
||||
width: 434.625,
|
||||
height: 285,
|
||||
angle: 0,
|
||||
parent: null,
|
||||
},
|
||||
expression:
|
||||
'filters\n| demodata\n| pointseries color="project" size="sum(cost)"\n| pie hole=50 labels=false legend="ne"\n| render \n css="table {\n right: -16px !important;\n}\n\n\ntr {\n height: 36px;\n}\n\n.legendColorBox div {\n margin-right: 7px;\n}\n\n.legendColorBox div div {\n width: 24px !important;\n height: 24px !important;\nborder-width: 4px !important;\n}\n\ntd {\n vertical-align: middle;\n}" containerStyle={containerStyle overflow="visible"}',
|
||||
},
|
||||
{
|
||||
id: 'element-8ca58ae7-2091-491f-996f-4256dfd5f4e1',
|
||||
position: {
|
||||
left: 51.875,
|
||||
top: 2162,
|
||||
width: 994.25,
|
||||
height: 300,
|
||||
angle: 0,
|
||||
parent: null,
|
||||
},
|
||||
expression:
|
||||
'filters\n| demodata\n| table\n| render containerStyle={containerStyle overflow="hidden"}',
|
||||
},
|
||||
{
|
||||
id: 'element-64db6690-dd39-4591-973d-d880e068de74',
|
||||
position: {
|
||||
left: 88,
|
||||
top: 1259.5,
|
||||
width: 902,
|
||||
height: 300,
|
||||
angle: 0,
|
||||
parent: null,
|
||||
},
|
||||
expression:
|
||||
'filters\n| demodata\n| pointseries x="time" y="mean(price)" color="project"\n| plot defaultStyle={seriesStyle lines=3} \n palette={palette "#882E72" "#B178A6" "#D6C1DE" "#1965B0" "#5289C7" "#7BAFDE" "#4EB265" "#90C987" "#CAE0AB" "#F7EE55" "#F6C141" "#F1932D" "#E8601C" "#DC050C" gradient=false} legend="ne" seriesStyle={seriesStyle label="elasticsearch" color="#882e72"}\n seriesStyle={seriesStyle color="#b178a6" label="beats"}\n seriesStyle={seriesStyle label="machine-learning" color="#d6c1de"}\n seriesStyle={seriesStyle label="logstash" color="#1965b0"}\n seriesStyle={seriesStyle label="apm" color="#5289c7"}\n seriesStyle={seriesStyle label="kibana" color="#7bafde"}\n seriesStyle={seriesStyle label="x-pack" color="#4eb265"}\n seriesStyle={seriesStyle label="swiftype" color="#90c987"}\n| render containerStyle={containerStyle overflow="visible"} \n css=".legend table {\n top: 266px !important;\n width: 100%;\n left: 80px;\n}\n\n.legend td {\nvertical-align: middle;\n}\n\ntr {\n padding-left: 14px;\n}\n\n.legendLabel {\n padding-left: 4px;\n}\n\ntbody {\n display: flex;\n}\n\n.flot-x-axis {\n top: 16px !important;\n}"',
|
||||
},
|
||||
{
|
||||
id: 'element-28fdc851-17bf-4a78-84f1-944fbf508d50',
|
||||
position: {
|
||||
left: 861.25,
|
||||
top: 44.75,
|
||||
width: 205,
|
||||
height: 36,
|
||||
angle: 0,
|
||||
parent: null,
|
||||
},
|
||||
expression:
|
||||
'timefilterControl compact=true column="@timestamp"\n| render css=".canvasTimePickerPopover__button {\n border: none !important;\n}"',
|
||||
filter: 'timefilter from="now-14d" to=now column=@timestamp',
|
||||
},
|
||||
{
|
||||
id: 'element-bf025bbc-7109-45a1-b954-bab851bc80df',
|
||||
position: {
|
||||
left: 764,
|
||||
top: 44.75,
|
||||
width: 89,
|
||||
height: 25,
|
||||
angle: 0,
|
||||
parent: null,
|
||||
},
|
||||
expression:
|
||||
'filters\n| demodata\n| markdown "#### Time period" \n font={font family="\'Open Sans\', Helvetica, Arial, sans-serif" size=14 align="left" color="#FFFFFF" weight="normal" underline=false italic=false}\n| render css="h4 {\n font-weight: 400;\n}"',
|
||||
},
|
||||
{
|
||||
id: 'element-120f58cd-3ef0-40b6-99fd-32cc1480b9aa',
|
||||
position: {
|
||||
left: 53,
|
||||
top: 757,
|
||||
width: 500,
|
||||
height: 38,
|
||||
angle: 0,
|
||||
parent: null,
|
||||
},
|
||||
expression:
|
||||
'filters\n| demodata\n| markdown "##### Average uptime by project type" \n font={font family="\'Open Sans\', Helvetica, Arial, sans-serif" size=14 align="left" color="#000000" weight="normal" underline=false italic=false}\n| render css=""',
|
||||
},
|
||||
{
|
||||
id: 'element-c30023e3-5df6-4b54-8286-544811ce7b6a',
|
||||
position: {
|
||||
left: 51.875,
|
||||
top: 1670,
|
||||
width: 500,
|
||||
height: 38,
|
||||
angle: 0,
|
||||
parent: null,
|
||||
},
|
||||
expression:
|
||||
'filters\n| demodata\n| markdown "### Total cost by project type" \n font={font family="\'Open Sans\', Helvetica, Arial, sans-serif" size=14 align="left" color="#000000" weight="normal" underline=false italic=false}\n| render css=""',
|
||||
},
|
||||
{
|
||||
id: 'element-137409de-6f24-4234-9c5a-024054d0632a',
|
||||
position: {
|
||||
left: 593.25,
|
||||
top: 1665.5,
|
||||
width: 446,
|
||||
height: 38,
|
||||
angle: 0,
|
||||
parent: null,
|
||||
},
|
||||
expression:
|
||||
'filters\n| demodata\n| markdown "### Average price over time" \n font={font family="\'Open Sans\', Helvetica, Arial, sans-serif" size=14 align="left" color="#000000" weight="normal" underline=false italic=false}\n| render css=""',
|
||||
},
|
||||
{
|
||||
id: 'element-b90b71f0-139b-419f-b43b-b2057abf777b',
|
||||
position: {
|
||||
left: 595.75,
|
||||
top: 1698.5,
|
||||
width: 223,
|
||||
height: 19,
|
||||
angle: 0,
|
||||
parent: null,
|
||||
},
|
||||
expression:
|
||||
'filters\n| demodata\n| markdown "##### Price trend over time" \n font={font family="\'Open Sans\', Helvetica, Arial, sans-serif" size=14 align="left" color="#000000" weight="normal" underline=false italic=false}\n| render css=""',
|
||||
},
|
||||
{
|
||||
id: 'element-a9b94f64-5336-4e39-ac69-5c9dacfbe129',
|
||||
position: {
|
||||
left: 53,
|
||||
top: 1703.5,
|
||||
width: 500,
|
||||
height: 38,
|
||||
angle: 0,
|
||||
parent: null,
|
||||
},
|
||||
expression:
|
||||
'filters\n| demodata\n| markdown "##### State distribution\n" \n font={font family="\'Open Sans\', Helvetica, Arial, sans-serif" size=14 align="left" color="#000000" weight="normal" underline=false italic=false}\n| render css=""',
|
||||
},
|
||||
{
|
||||
id: 'element-8777dd63-fbe7-446f-a23a-74cf55dc0a7c',
|
||||
position: {
|
||||
left: 109.75,
|
||||
top: 37.75,
|
||||
width: 500,
|
||||
height: 39,
|
||||
angle: 0,
|
||||
parent: null,
|
||||
},
|
||||
expression:
|
||||
'filters\n| demodata\n| markdown "## Monitoring Elastic projects" "" \n font={font family="\'Open Sans\', Helvetica, Arial, sans-serif" size=14 align="left" color="#FFFFFF" weight="bold" underline=false italic=false}\n| render css=".canvasRenderEl {\n\n}"',
|
||||
},
|
||||
{
|
||||
id: 'element-5e85d913-fb4b-41d5-9caf-ca2de9970cc7',
|
||||
position: {
|
||||
left: 13.75,
|
||||
top: 29.8125,
|
||||
width: 92,
|
||||
height: 54.875,
|
||||
angle: 0,
|
||||
parent: null,
|
||||
},
|
||||
expression: 'image dataurl=null mode="contain"\n| render',
|
||||
},
|
||||
{
|
||||
id: 'element-896f3043-4036-45f4-9e84-8aa6d870f215',
|
||||
position: {
|
||||
left: 53,
|
||||
top: 1729,
|
||||
width: 417.375,
|
||||
height: 290,
|
||||
angle: 0,
|
||||
parent: null,
|
||||
},
|
||||
expression:
|
||||
'filters\n| demodata\n| pointseries x="sum(cost)" y="project" color="state"\n| plot defaultStyle={seriesStyle bars=0.75 horizontalBars=true} legend="ne"\n| render containerStyle={containerStyle overflow="visible"} \n css=".legend table {\n top: 100px !important;\n right: -46px !important;\n}\n\n.legendColorBox>div{\nmargin-right: 3px !important;\n}\n\n.legend td {\n\nvertical-align: middle;\n}\n\n.legend tr {\n height: 20px;\n}\n\n.flot-x-axis {\n top: -15px !important;\n}\n\n.flot-y-axis {\n left: 10px !important;\n}"',
|
||||
},
|
||||
{
|
||||
id: 'element-13888369-9dac-4948-90b1-0ae42fa8fa53',
|
||||
position: {
|
||||
left: 593.75,
|
||||
top: 1733,
|
||||
width: 441,
|
||||
height: 282,
|
||||
angle: 0,
|
||||
parent: null,
|
||||
},
|
||||
expression:
|
||||
'filters\n| demodata\n| pointseries x="time" y="mean(price)"\n| plot defaultStyle={seriesStyle bars=0.75} legend=false \n palette={palette "#882E72" "#B178A6" "#D6C1DE" "#1965B0" "#5289C7" "#7BAFDE" "#4EB265" "#90C987" "#CAE0AB" "#F7EE55" "#F6C141" "#F1932D" "#E8601C" "#DC050C" gradient=false}\n| render \n css=".flot-x-axis {\n top: -15px !important;\n}\n\n.flot-y-axis {\n left: 10px !important;\n}"',
|
||||
},
|
||||
],
|
||||
groups: [],
|
||||
},
|
||||
],
|
||||
colors: [
|
||||
'#37988d',
|
||||
'#c19628',
|
||||
'#b83c6f',
|
||||
'#3f9939',
|
||||
'#1785b0',
|
||||
'#ca5f35',
|
||||
'#45bdb0',
|
||||
'#f2bc33',
|
||||
'#e74b8b',
|
||||
'#4fbf48',
|
||||
'#1ea6dc',
|
||||
'#fd7643',
|
||||
'#72cec3',
|
||||
'#f5cc5d',
|
||||
'#ec77a8',
|
||||
'#7acf74',
|
||||
'#4cbce4',
|
||||
'#fd986f',
|
||||
'#a1ded7',
|
||||
'#f8dd91',
|
||||
'#f2a4c5',
|
||||
'#a6dfa2',
|
||||
'#86d2ed',
|
||||
'#fdba9f',
|
||||
'#000000',
|
||||
'#444444',
|
||||
'#777777',
|
||||
'#BBBBBB',
|
||||
'#FFFFFF',
|
||||
'rgba(255,255,255,0)',
|
||||
],
|
||||
'@timestamp': '2019-05-31T16:02:40.420Z',
|
||||
'@created': '2019-05-31T16:01:45.751Z',
|
||||
assets: {},
|
||||
css: 'h3 {\ncolor: #343741;\nfont-weight: 400;\n}\n\nh5 {\ncolor: #69707D;\n}',
|
||||
},
|
||||
};
|
397
x-pack/plugins/canvas/server/templates/theme_dark.ts
Normal file
397
x-pack/plugins/canvas/server/templates/theme_dark.ts
Normal file
File diff suppressed because one or more lines are too long
405
x-pack/plugins/canvas/server/templates/theme_light.ts
Normal file
405
x-pack/plugins/canvas/server/templates/theme_light.ts
Normal file
File diff suppressed because one or more lines are too long
|
@ -52,10 +52,16 @@ export interface CanvasWorkpad {
|
|||
width: number;
|
||||
}
|
||||
|
||||
export type CanvasTemplate = CanvasWorkpad & {
|
||||
type CanvasTemplateElement = Omit<CanvasElement, 'filter' | 'type'>;
|
||||
type CanvasTemplatePage = Omit<CanvasPage, 'elements'> & { elements: CanvasTemplateElement[] };
|
||||
export interface CanvasTemplate {
|
||||
id: string;
|
||||
name: string;
|
||||
help: string;
|
||||
tags: string[];
|
||||
};
|
||||
template_key: string;
|
||||
template?: Omit<CanvasWorkpad, 'id' | 'isWriteable' | 'pages'> & { pages: CanvasTemplatePage[] };
|
||||
}
|
||||
|
||||
export interface CanvasWorkpadBoundingBox {
|
||||
left: number;
|
||||
|
|
Loading…
Reference in a new issue