@perses-dev/dashboards 0.15.0 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/components/DashboardToolbar/DashboardToolbar.js +2 -0
- package/dist/cjs/components/DownloadButton/DownloadButton.js +109 -0
- package/dist/cjs/{utils/component-ids.js → components/DownloadButton/index.js} +11 -14
- package/dist/cjs/components/GridLayout/GridTitle.js +8 -12
- package/dist/cjs/components/Panel/Panel.js +4 -2
- package/dist/cjs/components/Panel/PanelHeader.js +54 -50
- package/dist/cjs/components/PanelDrawer/PanelDrawer.test.js +23 -0
- package/dist/cjs/components/TimeRangeControls/TimeRangeControls.test.js +3 -1
- package/dist/cjs/components/Variables/Variable.js +5 -0
- package/dist/cjs/components/Variables/VariableEditor.js +20 -1
- package/dist/cjs/components/Variables/VariableEditorForm/VariableEditorForm.js +61 -5
- package/dist/cjs/components/Variables/VariableList.js +5 -0
- package/dist/cjs/components/index.js +1 -0
- package/dist/cjs/context/DashboardProvider/panel-editor-slice.js +40 -15
- package/dist/cjs/context/DashboardProvider/panel-group-editor-slice.js +5 -9
- package/dist/cjs/context/DashboardProvider/panel-group-slice.js +16 -1
- package/dist/cjs/views/ViewDashboard/tests/panelGroups.test.js +15 -21
- package/dist/components/DashboardToolbar/DashboardToolbar.d.ts.map +1 -1
- package/dist/components/DashboardToolbar/DashboardToolbar.js +2 -0
- package/dist/components/DashboardToolbar/DashboardToolbar.js.map +1 -1
- package/dist/components/DownloadButton/DownloadButton.d.ts +3 -0
- package/dist/components/DownloadButton/DownloadButton.d.ts.map +1 -0
- package/dist/components/DownloadButton/DownloadButton.js +60 -0
- package/dist/components/DownloadButton/DownloadButton.js.map +1 -0
- package/dist/components/DownloadButton/index.d.ts +2 -0
- package/dist/components/DownloadButton/index.d.ts.map +1 -0
- package/dist/{utils/component-ids.js → components/DownloadButton/index.js} +2 -14
- package/dist/components/DownloadButton/index.js.map +1 -0
- package/dist/components/GridLayout/GridTitle.d.ts.map +1 -1
- package/dist/components/GridLayout/GridTitle.js +8 -12
- package/dist/components/GridLayout/GridTitle.js.map +1 -1
- package/dist/components/Panel/Panel.d.ts.map +1 -1
- package/dist/components/Panel/Panel.js +4 -2
- package/dist/components/Panel/Panel.js.map +1 -1
- package/dist/components/Panel/PanelHeader.d.ts.map +1 -1
- package/dist/components/Panel/PanelHeader.js +54 -50
- package/dist/components/Panel/PanelHeader.js.map +1 -1
- package/dist/components/PanelDrawer/PanelDrawer.test.js +23 -0
- package/dist/components/PanelDrawer/PanelDrawer.test.js.map +1 -1
- package/dist/components/TimeRangeControls/TimeRangeControls.test.js +4 -2
- package/dist/components/TimeRangeControls/TimeRangeControls.test.js.map +1 -1
- package/dist/components/Variables/Variable.js +5 -0
- package/dist/components/Variables/Variable.js.map +1 -1
- package/dist/components/Variables/VariableEditor.d.ts.map +1 -1
- package/dist/components/Variables/VariableEditor.js +22 -3
- package/dist/components/Variables/VariableEditor.js.map +1 -1
- package/dist/components/Variables/VariableEditorForm/VariableEditorForm.d.ts.map +1 -1
- package/dist/components/Variables/VariableEditorForm/VariableEditorForm.js +23 -1
- package/dist/components/Variables/VariableEditorForm/VariableEditorForm.js.map +1 -1
- package/dist/components/Variables/VariableList.js +5 -0
- package/dist/components/Variables/VariableList.js.map +1 -1
- package/dist/components/index.d.ts +1 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +1 -0
- package/dist/components/index.js.map +1 -1
- package/dist/context/DashboardProvider/panel-editor-slice.d.ts.map +1 -1
- package/dist/context/DashboardProvider/panel-editor-slice.js +40 -15
- package/dist/context/DashboardProvider/panel-editor-slice.js.map +1 -1
- package/dist/context/DashboardProvider/panel-group-editor-slice.d.ts.map +1 -1
- package/dist/context/DashboardProvider/panel-group-editor-slice.js +5 -9
- package/dist/context/DashboardProvider/panel-group-editor-slice.js.map +1 -1
- package/dist/context/DashboardProvider/panel-group-slice.d.ts +9 -0
- package/dist/context/DashboardProvider/panel-group-slice.d.ts.map +1 -1
- package/dist/context/DashboardProvider/panel-group-slice.js +17 -0
- package/dist/context/DashboardProvider/panel-group-slice.js.map +1 -1
- package/dist/views/ViewDashboard/tests/panelGroups.test.js +15 -21
- package/dist/views/ViewDashboard/tests/panelGroups.test.js.map +1 -1
- package/package.json +4 -4
- package/dist/utils/component-ids.d.ts +0 -8
- package/dist/utils/component-ids.d.ts.map +0 -1
- package/dist/utils/component-ids.js.map +0 -1
|
@@ -27,6 +27,7 @@ const _components = require("@perses-dev/components");
|
|
|
27
27
|
const _context = require("../../context");
|
|
28
28
|
const _variables = require("../Variables");
|
|
29
29
|
const _timeRangeControls = require("../TimeRangeControls");
|
|
30
|
+
const _downloadButton = require("../DownloadButton");
|
|
30
31
|
function _interopRequireDefault(obj) {
|
|
31
32
|
return obj && obj.__esModule ? obj : {
|
|
32
33
|
default: obj
|
|
@@ -135,6 +136,7 @@ const DashboardToolbar = (props)=>{
|
|
|
135
136
|
},
|
|
136
137
|
children: [
|
|
137
138
|
/*#__PURE__*/ (0, _jsxRuntime.jsx)(_timeRangeControls.TimeRangeControls, {}),
|
|
139
|
+
/*#__PURE__*/ (0, _jsxRuntime.jsx)(_downloadButton.DownloadButton, {}),
|
|
138
140
|
isLaptopSize && /*#__PURE__*/ (0, _jsxRuntime.jsx)(_material.Button, {
|
|
139
141
|
variant: "outlined",
|
|
140
142
|
startIcon: /*#__PURE__*/ (0, _jsxRuntime.jsx)(_pencilOutline.default, {}),
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
// Copyright 2022 The Perses Authors
|
|
2
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
3
|
+
// you may not use this file except in compliance with the License.
|
|
4
|
+
// You may obtain a copy of the License at
|
|
5
|
+
//
|
|
6
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
7
|
+
//
|
|
8
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
9
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
10
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
11
|
+
// See the License for the specific language governing permissions and
|
|
12
|
+
// limitations under the License.
|
|
13
|
+
"use strict";
|
|
14
|
+
Object.defineProperty(exports, "__esModule", {
|
|
15
|
+
value: true
|
|
16
|
+
});
|
|
17
|
+
Object.defineProperty(exports, "DownloadButton", {
|
|
18
|
+
enumerable: true,
|
|
19
|
+
get: ()=>DownloadButton
|
|
20
|
+
});
|
|
21
|
+
const _jsxRuntime = require("react/jsx-runtime");
|
|
22
|
+
const _react = /*#__PURE__*/ _interopRequireWildcard(require("react"));
|
|
23
|
+
const _downloadOutline = /*#__PURE__*/ _interopRequireDefault(require("mdi-material-ui/DownloadOutline"));
|
|
24
|
+
const _material = require("@mui/material");
|
|
25
|
+
const _context = require("../../context");
|
|
26
|
+
function _interopRequireDefault(obj) {
|
|
27
|
+
return obj && obj.__esModule ? obj : {
|
|
28
|
+
default: obj
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
function _getRequireWildcardCache(nodeInterop) {
|
|
32
|
+
if (typeof WeakMap !== "function") return null;
|
|
33
|
+
var cacheBabelInterop = new WeakMap();
|
|
34
|
+
var cacheNodeInterop = new WeakMap();
|
|
35
|
+
return (_getRequireWildcardCache = function(nodeInterop) {
|
|
36
|
+
return nodeInterop ? cacheNodeInterop : cacheBabelInterop;
|
|
37
|
+
})(nodeInterop);
|
|
38
|
+
}
|
|
39
|
+
function _interopRequireWildcard(obj, nodeInterop) {
|
|
40
|
+
if (!nodeInterop && obj && obj.__esModule) {
|
|
41
|
+
return obj;
|
|
42
|
+
}
|
|
43
|
+
if (obj === null || typeof obj !== "object" && typeof obj !== "function") {
|
|
44
|
+
return {
|
|
45
|
+
default: obj
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
var cache = _getRequireWildcardCache(nodeInterop);
|
|
49
|
+
if (cache && cache.has(obj)) {
|
|
50
|
+
return cache.get(obj);
|
|
51
|
+
}
|
|
52
|
+
var newObj = {};
|
|
53
|
+
var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;
|
|
54
|
+
for(var key in obj){
|
|
55
|
+
if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
56
|
+
var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;
|
|
57
|
+
if (desc && (desc.get || desc.set)) {
|
|
58
|
+
Object.defineProperty(newObj, key, desc);
|
|
59
|
+
} else {
|
|
60
|
+
newObj[key] = obj[key];
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
newObj.default = obj;
|
|
65
|
+
if (cache) {
|
|
66
|
+
cache.set(obj, newObj);
|
|
67
|
+
}
|
|
68
|
+
return newObj;
|
|
69
|
+
}
|
|
70
|
+
function DownloadButton() {
|
|
71
|
+
const { dashboard } = (0, _context.useDashboard)();
|
|
72
|
+
const hiddenLinkRef = (0, _react.useRef)(null);
|
|
73
|
+
const onDownloadButtonClick = ()=>{
|
|
74
|
+
if (!hiddenLinkRef || !hiddenLinkRef.current) return;
|
|
75
|
+
// Create blob URL
|
|
76
|
+
const hiddenLinkUrl = URL.createObjectURL(new Blob([
|
|
77
|
+
JSON.stringify(dashboard)
|
|
78
|
+
], {
|
|
79
|
+
type: 'application/json'
|
|
80
|
+
}));
|
|
81
|
+
// Simulate click
|
|
82
|
+
hiddenLinkRef.current.href = hiddenLinkUrl;
|
|
83
|
+
hiddenLinkRef.current.click();
|
|
84
|
+
// Remove blob URL (for memory management)
|
|
85
|
+
URL.revokeObjectURL(hiddenLinkUrl);
|
|
86
|
+
};
|
|
87
|
+
return /*#__PURE__*/ (0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
|
|
88
|
+
children: [
|
|
89
|
+
/*#__PURE__*/ (0, _jsxRuntime.jsx)(DownloadIconButton, {
|
|
90
|
+
title: "Download JSON",
|
|
91
|
+
onClick: onDownloadButtonClick,
|
|
92
|
+
children: /*#__PURE__*/ (0, _jsxRuntime.jsx)(_downloadOutline.default, {})
|
|
93
|
+
}),
|
|
94
|
+
/*#__PURE__*/ (0, _jsxRuntime.jsx)("a", {
|
|
95
|
+
ref: hiddenLinkRef,
|
|
96
|
+
style: {
|
|
97
|
+
display: 'none'
|
|
98
|
+
},
|
|
99
|
+
download: `${dashboard.metadata.name}.json`
|
|
100
|
+
})
|
|
101
|
+
]
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
const DownloadIconButton = (0, _material.styled)(_material.IconButton)(({ theme })=>({
|
|
105
|
+
border: `1px solid ${theme.palette.grey[300]}`,
|
|
106
|
+
borderRadius: theme.shape.borderRadius,
|
|
107
|
+
padding: '4px',
|
|
108
|
+
color: theme.palette.grey[900]
|
|
109
|
+
}));
|
|
@@ -14,18 +14,15 @@
|
|
|
14
14
|
Object.defineProperty(exports, "__esModule", {
|
|
15
15
|
value: true
|
|
16
16
|
});
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
function
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
id.current = `${prefix}-${globalThis.useIdValue++}`;
|
|
29
|
-
}
|
|
30
|
-
return id.current;
|
|
17
|
+
_exportStar(require("./DownloadButton"), exports);
|
|
18
|
+
function _exportStar(from, to) {
|
|
19
|
+
Object.keys(from).forEach(function(k) {
|
|
20
|
+
if (k !== "default" && !Object.prototype.hasOwnProperty.call(to, k)) Object.defineProperty(to, k, {
|
|
21
|
+
enumerable: true,
|
|
22
|
+
get: function() {
|
|
23
|
+
return from[k];
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
return from;
|
|
31
28
|
}
|
|
@@ -19,11 +19,10 @@ Object.defineProperty(exports, "GridTitle", {
|
|
|
19
19
|
get: ()=>GridTitle
|
|
20
20
|
});
|
|
21
21
|
const _jsxRuntime = require("react/jsx-runtime");
|
|
22
|
-
const _react = require("react");
|
|
23
22
|
const _material = require("@mui/material");
|
|
24
23
|
const _chevronUp = /*#__PURE__*/ _interopRequireDefault(require("mdi-material-ui/ChevronUp"));
|
|
25
24
|
const _chevronDown = /*#__PURE__*/ _interopRequireDefault(require("mdi-material-ui/ChevronDown"));
|
|
26
|
-
const
|
|
25
|
+
const _chartBoxPlusOutline = /*#__PURE__*/ _interopRequireDefault(require("mdi-material-ui/ChartBoxPlusOutline"));
|
|
27
26
|
const _pencilOutline = /*#__PURE__*/ _interopRequireDefault(require("mdi-material-ui/PencilOutline"));
|
|
28
27
|
const _arrowUp = /*#__PURE__*/ _interopRequireDefault(require("mdi-material-ui/ArrowUp"));
|
|
29
28
|
const _arrowDown = /*#__PURE__*/ _interopRequireDefault(require("mdi-material-ui/ArrowDown"));
|
|
@@ -36,7 +35,6 @@ function _interopRequireDefault(obj) {
|
|
|
36
35
|
}
|
|
37
36
|
function GridTitle(props) {
|
|
38
37
|
const { panelGroupId , title , collapse } = props;
|
|
39
|
-
const [isHovered, setIsHovered] = (0, _react.useState)(false);
|
|
40
38
|
const { openAddPanel , openEditPanelGroup , moveUp , moveDown } = (0, _context.usePanelGroupActions)(panelGroupId);
|
|
41
39
|
const { openDeletePanelGroupDialog } = (0, _context.useDeletePanelGroupDialog)();
|
|
42
40
|
const { isEditMode } = (0, _context.useEditMode)();
|
|
@@ -55,8 +53,6 @@ function GridTitle(props) {
|
|
|
55
53
|
padding: (theme)=>theme.spacing(1),
|
|
56
54
|
backgroundColor: (theme)=>theme.palette.background.default
|
|
57
55
|
},
|
|
58
|
-
onMouseEnter: ()=>setIsHovered(true),
|
|
59
|
-
onMouseLeave: ()=>setIsHovered(false),
|
|
60
56
|
children: collapse ? /*#__PURE__*/ (0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
|
|
61
57
|
children: [
|
|
62
58
|
/*#__PURE__*/ (0, _jsxRuntime.jsx)(_material.IconButton, {
|
|
@@ -64,35 +60,35 @@ function GridTitle(props) {
|
|
|
64
60
|
children: collapse.isOpen ? /*#__PURE__*/ (0, _jsxRuntime.jsx)(_chevronUp.default, {}) : /*#__PURE__*/ (0, _jsxRuntime.jsx)(_chevronDown.default, {})
|
|
65
61
|
}),
|
|
66
62
|
text,
|
|
67
|
-
isEditMode &&
|
|
63
|
+
isEditMode && /*#__PURE__*/ (0, _jsxRuntime.jsxs)(_material.Stack, {
|
|
68
64
|
direction: "row",
|
|
69
65
|
sx: {
|
|
70
66
|
marginLeft: 'auto'
|
|
71
67
|
},
|
|
72
68
|
children: [
|
|
73
69
|
/*#__PURE__*/ (0, _jsxRuntime.jsx)(_material.IconButton, {
|
|
74
|
-
"aria-label":
|
|
70
|
+
"aria-label": `add panel to group ${title}`,
|
|
75
71
|
onClick: openAddPanel,
|
|
76
|
-
children: /*#__PURE__*/ (0, _jsxRuntime.jsx)(
|
|
72
|
+
children: /*#__PURE__*/ (0, _jsxRuntime.jsx)(_chartBoxPlusOutline.default, {})
|
|
77
73
|
}),
|
|
78
74
|
/*#__PURE__*/ (0, _jsxRuntime.jsx)(_material.IconButton, {
|
|
79
|
-
"aria-label":
|
|
75
|
+
"aria-label": `edit group ${title}`,
|
|
80
76
|
onClick: openEditPanelGroup,
|
|
81
77
|
children: /*#__PURE__*/ (0, _jsxRuntime.jsx)(_pencilOutline.default, {})
|
|
82
78
|
}),
|
|
83
79
|
/*#__PURE__*/ (0, _jsxRuntime.jsx)(_material.IconButton, {
|
|
84
|
-
"aria-label":
|
|
80
|
+
"aria-label": `delete group ${title}`,
|
|
85
81
|
onClick: ()=>openDeletePanelGroupDialog(panelGroupId),
|
|
86
82
|
children: /*#__PURE__*/ (0, _jsxRuntime.jsx)(_deleteOutline.default, {})
|
|
87
83
|
}),
|
|
88
84
|
/*#__PURE__*/ (0, _jsxRuntime.jsx)(_material.IconButton, {
|
|
89
|
-
"aria-label":
|
|
85
|
+
"aria-label": `move group ${title} down`,
|
|
90
86
|
disabled: moveDown === undefined,
|
|
91
87
|
onClick: moveDown,
|
|
92
88
|
children: /*#__PURE__*/ (0, _jsxRuntime.jsx)(_arrowDown.default, {})
|
|
93
89
|
}),
|
|
94
90
|
/*#__PURE__*/ (0, _jsxRuntime.jsx)(_material.IconButton, {
|
|
95
|
-
"aria-label":
|
|
91
|
+
"aria-label": `move group ${title} up`,
|
|
96
92
|
disabled: moveUp === undefined,
|
|
97
93
|
onClick: moveUp,
|
|
98
94
|
children: /*#__PURE__*/ (0, _jsxRuntime.jsx)(_arrowUp.default, {})
|
|
@@ -24,7 +24,6 @@ const _useResizeObserver = /*#__PURE__*/ _interopRequireDefault(require("use-res
|
|
|
24
24
|
const _reactIntersectionObserver = require("react-intersection-observer");
|
|
25
25
|
const _components = require("@perses-dev/components");
|
|
26
26
|
const _material = require("@mui/material");
|
|
27
|
-
const _componentIds = require("../../utils/component-ids");
|
|
28
27
|
const _panelHeader = require("./PanelHeader");
|
|
29
28
|
const _panelContent = require("./PanelContent");
|
|
30
29
|
function _interopRequireDefault(obj) {
|
|
@@ -35,7 +34,7 @@ function _interopRequireDefault(obj) {
|
|
|
35
34
|
function Panel(props) {
|
|
36
35
|
const { definition , editHandlers , onMouseEnter , onMouseLeave , sx , ...others } = props;
|
|
37
36
|
// Make sure we have an ID we can use for aria attributes
|
|
38
|
-
const generatedPanelId = (0,
|
|
37
|
+
const generatedPanelId = (0, _components.useId)('Panel');
|
|
39
38
|
const headerId = `${generatedPanelId}-header`;
|
|
40
39
|
const [contentElement, setContentElement] = (0, _react.useState)(null);
|
|
41
40
|
const [isHovered, setIsHovered] = (0, _react.useState)(false);
|
|
@@ -109,6 +108,9 @@ function Panel(props) {
|
|
|
109
108
|
ref: setContentElement,
|
|
110
109
|
children: /*#__PURE__*/ (0, _jsxRuntime.jsx)(_components.ErrorBoundary, {
|
|
111
110
|
FallbackComponent: _components.ErrorAlert,
|
|
111
|
+
resetKeys: [
|
|
112
|
+
definition.spec.plugin.spec
|
|
113
|
+
],
|
|
112
114
|
children: inView === true && /*#__PURE__*/ (0, _jsxRuntime.jsx)(_panelContent.PanelContent, {
|
|
113
115
|
panelPluginKind: definition.spec.plugin.kind,
|
|
114
116
|
spec: definition.spec.plugin.spec,
|
|
@@ -33,58 +33,55 @@ function _interopRequireDefault(obj) {
|
|
|
33
33
|
function PanelHeader({ id , title , description , editHandlers , isHovered , sx , ...rest }) {
|
|
34
34
|
const titleElementId = `${id}-title`;
|
|
35
35
|
const descriptionTooltipId = `${id}-description`;
|
|
36
|
-
// Don't show any actions unless panel is hovered
|
|
37
36
|
let action = undefined;
|
|
38
|
-
if (
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
className: "drag-handle",
|
|
63
|
-
sx: {
|
|
64
|
-
cursor: 'grab'
|
|
65
|
-
}
|
|
66
|
-
})
|
|
67
|
-
})
|
|
68
|
-
]
|
|
69
|
-
});
|
|
70
|
-
} else if (description !== undefined) {
|
|
71
|
-
// If there aren't edit handlers and we have a description, show a button with a tooltip for the panel description
|
|
72
|
-
action = /*#__PURE__*/ (0, _jsxRuntime.jsx)(_components.InfoTooltip, {
|
|
73
|
-
id: descriptionTooltipId,
|
|
74
|
-
description: description,
|
|
75
|
-
placement: _components.TooltipPlacement.Bottom,
|
|
76
|
-
children: /*#__PURE__*/ (0, _jsxRuntime.jsx)(HeaderIconButton, {
|
|
77
|
-
"aria-label": "Panel Description",
|
|
78
|
-
children: /*#__PURE__*/ (0, _jsxRuntime.jsx)(_informationOutline.default, {
|
|
79
|
-
"aria-describedby": "info-tooltip",
|
|
80
|
-
"aria-hidden": false,
|
|
37
|
+
if (editHandlers !== undefined) {
|
|
38
|
+
// If there are edit handlers, always just show the edit buttons
|
|
39
|
+
action = /*#__PURE__*/ (0, _jsxRuntime.jsxs)(_material.Stack, {
|
|
40
|
+
direction: "row",
|
|
41
|
+
alignItems: "center",
|
|
42
|
+
spacing: 0.5,
|
|
43
|
+
children: [
|
|
44
|
+
/*#__PURE__*/ (0, _jsxRuntime.jsx)(HeaderIconButton, {
|
|
45
|
+
"aria-label": `edit panel ${title}`,
|
|
46
|
+
size: "small",
|
|
47
|
+
onClick: editHandlers.onEditPanelClick,
|
|
48
|
+
children: /*#__PURE__*/ (0, _jsxRuntime.jsx)(_pencilOutline.default, {})
|
|
49
|
+
}),
|
|
50
|
+
/*#__PURE__*/ (0, _jsxRuntime.jsx)(HeaderIconButton, {
|
|
51
|
+
"aria-label": `delete panel ${title}`,
|
|
52
|
+
size: "small",
|
|
53
|
+
onClick: editHandlers.onDeletePanelClick,
|
|
54
|
+
children: /*#__PURE__*/ (0, _jsxRuntime.jsx)(_deleteOutline.default, {})
|
|
55
|
+
}),
|
|
56
|
+
/*#__PURE__*/ (0, _jsxRuntime.jsx)(HeaderIconButton, {
|
|
57
|
+
"aria-label": `move panel ${title}`,
|
|
58
|
+
size: "small",
|
|
59
|
+
children: /*#__PURE__*/ (0, _jsxRuntime.jsx)(_dragVertical.default, {
|
|
60
|
+
className: "drag-handle",
|
|
81
61
|
sx: {
|
|
82
|
-
|
|
62
|
+
cursor: 'grab'
|
|
83
63
|
}
|
|
84
64
|
})
|
|
85
65
|
})
|
|
86
|
-
|
|
87
|
-
}
|
|
66
|
+
]
|
|
67
|
+
});
|
|
68
|
+
} else if (description !== undefined && isHovered) {
|
|
69
|
+
// If there aren't edit handlers and we have a description, show a button with a tooltip for the panel description
|
|
70
|
+
action = /*#__PURE__*/ (0, _jsxRuntime.jsx)(_components.InfoTooltip, {
|
|
71
|
+
id: descriptionTooltipId,
|
|
72
|
+
description: description,
|
|
73
|
+
placement: _components.TooltipPlacement.Bottom,
|
|
74
|
+
children: /*#__PURE__*/ (0, _jsxRuntime.jsx)(HeaderIconButton, {
|
|
75
|
+
"aria-label": "Panel Description",
|
|
76
|
+
children: /*#__PURE__*/ (0, _jsxRuntime.jsx)(_informationOutline.default, {
|
|
77
|
+
"aria-describedby": "info-tooltip",
|
|
78
|
+
"aria-hidden": false,
|
|
79
|
+
sx: {
|
|
80
|
+
color: (theme)=>theme.palette.grey[700]
|
|
81
|
+
}
|
|
82
|
+
})
|
|
83
|
+
})
|
|
84
|
+
});
|
|
88
85
|
}
|
|
89
86
|
return /*#__PURE__*/ (0, _jsxRuntime.jsx)(_material.CardHeader, {
|
|
90
87
|
id: id,
|
|
@@ -95,6 +92,15 @@ function PanelHeader({ id , title , description , editHandlers , isHovered , sx
|
|
|
95
92
|
title: /*#__PURE__*/ (0, _jsxRuntime.jsx)(_material.Typography, {
|
|
96
93
|
id: titleElementId,
|
|
97
94
|
variant: "subtitle1",
|
|
95
|
+
sx: {
|
|
96
|
+
// `minHeight` guarantees that the header has the correct height
|
|
97
|
+
// when there is no title (i.e. in the preview)
|
|
98
|
+
lineHeight: '24px',
|
|
99
|
+
minHeight: '24px',
|
|
100
|
+
whiteSpace: 'nowrap',
|
|
101
|
+
overflow: 'hidden',
|
|
102
|
+
textOverflow: 'ellipsis'
|
|
103
|
+
},
|
|
98
104
|
children: title
|
|
99
105
|
}),
|
|
100
106
|
action: action,
|
|
@@ -102,9 +108,7 @@ function PanelHeader({ id , title , description , editHandlers , isHovered , sx
|
|
|
102
108
|
padding: theme.spacing(1),
|
|
103
109
|
borderBottom: `solid 1px ${theme.palette.divider}`,
|
|
104
110
|
'.MuiCardHeader-content': {
|
|
105
|
-
|
|
106
|
-
overflow: 'hidden',
|
|
107
|
-
textOverflow: 'ellipsis'
|
|
111
|
+
overflow: 'hidden'
|
|
108
112
|
}
|
|
109
113
|
}), sx),
|
|
110
114
|
...rest
|
|
@@ -70,6 +70,29 @@ describe('Panel Drawer', ()=>{
|
|
|
70
70
|
}
|
|
71
71
|
});
|
|
72
72
|
});
|
|
73
|
+
it('should add panel with duplicate panel name', async ()=>{
|
|
74
|
+
const storeApi = renderPanelDrawer();
|
|
75
|
+
(0, _testUtils.act)(()=>storeApi.getState().openAddPanel());
|
|
76
|
+
const nameInput = await _react.screen.findByLabelText(/Name/);
|
|
77
|
+
_userEvent.default.type(nameInput, 'cpu');
|
|
78
|
+
_userEvent.default.click(_react.screen.getByText('Add'));
|
|
79
|
+
const panels = storeApi.getState().panels;
|
|
80
|
+
expect(panels).toMatchObject({
|
|
81
|
+
// make sure we don't have duplicate panel key by appending "-1"
|
|
82
|
+
'cpu-1': {
|
|
83
|
+
kind: 'Panel',
|
|
84
|
+
spec: {
|
|
85
|
+
display: {
|
|
86
|
+
name: 'cpu'
|
|
87
|
+
},
|
|
88
|
+
plugin: {
|
|
89
|
+
kind: '',
|
|
90
|
+
spec: {}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
});
|
|
73
96
|
it('should edit an existing panel', async ()=>{
|
|
74
97
|
const storeApi = renderPanelDrawer();
|
|
75
98
|
// Open the drawer for an existing panel
|
|
@@ -80,7 +80,9 @@ describe('TimeRangeControls', ()=>{
|
|
|
80
80
|
_userEvent.default.click(secondSelected);
|
|
81
81
|
expect(history.location.search).toEqual('?start=12h');
|
|
82
82
|
// back button should return to first option selected
|
|
83
|
-
|
|
83
|
+
(0, _react.act)(()=>{
|
|
84
|
+
history.back();
|
|
85
|
+
});
|
|
84
86
|
expect(history.location.search).toEqual('?start=5m');
|
|
85
87
|
});
|
|
86
88
|
// TODO: add additional tests for absolute time selection, other inputs, form validation, etc.
|
|
@@ -199,6 +199,11 @@ function ListVariable({ name }) {
|
|
|
199
199
|
disabled: true,
|
|
200
200
|
children: "Loading"
|
|
201
201
|
}),
|
|
202
|
+
finalOptions.length === 0 && /*#__PURE__*/ (0, _jsxRuntime.jsx)(_material.MenuItem, {
|
|
203
|
+
value: "empty",
|
|
204
|
+
disabled: true,
|
|
205
|
+
children: "No options"
|
|
206
|
+
}),
|
|
202
207
|
finalOptions.map((option)=>/*#__PURE__*/ (0, _jsxRuntime.jsx)(_material.MenuItem, {
|
|
203
208
|
value: option.value,
|
|
204
209
|
children: option.label
|
|
@@ -32,9 +32,24 @@ function _interopRequireDefault(obj) {
|
|
|
32
32
|
default: obj
|
|
33
33
|
};
|
|
34
34
|
}
|
|
35
|
+
function getValidation(variableDefinitions) {
|
|
36
|
+
const errors = [];
|
|
37
|
+
/** Variable names must be unique */ const variableNames = variableDefinitions.map((variableDefinition)=>variableDefinition.spec.name);
|
|
38
|
+
const uniqueVariableNames = new Set(variableNames);
|
|
39
|
+
if (variableNames.length !== uniqueVariableNames.size) {
|
|
40
|
+
errors.push('Variable names must be unique');
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
errors: errors,
|
|
44
|
+
isValid: errors.length === 0
|
|
45
|
+
};
|
|
46
|
+
}
|
|
35
47
|
function VariableEditor(props) {
|
|
36
48
|
const [variableDefinitions, setVariableDefinitions] = (0, _useImmer.useImmer)(props.variableDefinitions);
|
|
37
49
|
const [variableEditIdx, setVariableEditIdx] = (0, _react.useState)(null);
|
|
50
|
+
const validation = (0, _react.useMemo)(()=>getValidation(variableDefinitions), [
|
|
51
|
+
variableDefinitions
|
|
52
|
+
]);
|
|
38
53
|
const currentEditingVariableDefinition = typeof variableEditIdx === 'number' && variableDefinitions[variableEditIdx];
|
|
39
54
|
const removeVariable = (index)=>{
|
|
40
55
|
setVariableDefinitions((draft)=>{
|
|
@@ -118,7 +133,7 @@ function VariableEditor(props) {
|
|
|
118
133
|
justifyContent: "end",
|
|
119
134
|
children: [
|
|
120
135
|
/*#__PURE__*/ (0, _jsxRuntime.jsx)(_material.Button, {
|
|
121
|
-
disabled: props.variableDefinitions === variableDefinitions,
|
|
136
|
+
disabled: props.variableDefinitions === variableDefinitions || !validation.isValid,
|
|
122
137
|
variant: "contained",
|
|
123
138
|
onClick: ()=>{
|
|
124
139
|
props.onChange(variableDefinitions);
|
|
@@ -142,6 +157,10 @@ function VariableEditor(props) {
|
|
|
142
157
|
/*#__PURE__*/ (0, _jsxRuntime.jsxs)(_material.Stack, {
|
|
143
158
|
spacing: 2,
|
|
144
159
|
children: [
|
|
160
|
+
!validation.isValid && validation.errors.map((error)=>/*#__PURE__*/ (0, _jsxRuntime.jsx)(_material.Alert, {
|
|
161
|
+
severity: "error",
|
|
162
|
+
children: error
|
|
163
|
+
}, error)),
|
|
145
164
|
/*#__PURE__*/ (0, _jsxRuntime.jsx)(_material.TableContainer, {
|
|
146
165
|
component: _material.Paper,
|
|
147
166
|
children: /*#__PURE__*/ (0, _jsxRuntime.jsxs)(_material.Table, {
|
|
@@ -19,20 +19,69 @@ Object.defineProperty(exports, "VariableEditForm", {
|
|
|
19
19
|
get: ()=>VariableEditForm
|
|
20
20
|
});
|
|
21
21
|
const _jsxRuntime = require("react/jsx-runtime");
|
|
22
|
-
const _react = /*#__PURE__*/
|
|
22
|
+
const _react = /*#__PURE__*/ _interopRequireWildcard(require("react"));
|
|
23
23
|
const _material = require("@mui/material");
|
|
24
24
|
const _useImmer = require("use-immer");
|
|
25
25
|
const _pluginSystem = require("@perses-dev/plugin-system");
|
|
26
26
|
const _variableEditorFormModel = require("./variable-editor-form-model");
|
|
27
|
-
function
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
27
|
+
function _getRequireWildcardCache(nodeInterop) {
|
|
28
|
+
if (typeof WeakMap !== "function") return null;
|
|
29
|
+
var cacheBabelInterop = new WeakMap();
|
|
30
|
+
var cacheNodeInterop = new WeakMap();
|
|
31
|
+
return (_getRequireWildcardCache = function(nodeInterop) {
|
|
32
|
+
return nodeInterop ? cacheNodeInterop : cacheBabelInterop;
|
|
33
|
+
})(nodeInterop);
|
|
34
|
+
}
|
|
35
|
+
function _interopRequireWildcard(obj, nodeInterop) {
|
|
36
|
+
if (!nodeInterop && obj && obj.__esModule) {
|
|
37
|
+
return obj;
|
|
38
|
+
}
|
|
39
|
+
if (obj === null || typeof obj !== "object" && typeof obj !== "function") {
|
|
40
|
+
return {
|
|
41
|
+
default: obj
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
var cache = _getRequireWildcardCache(nodeInterop);
|
|
45
|
+
if (cache && cache.has(obj)) {
|
|
46
|
+
return cache.get(obj);
|
|
47
|
+
}
|
|
48
|
+
var newObj = {};
|
|
49
|
+
var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;
|
|
50
|
+
for(var key in obj){
|
|
51
|
+
if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
52
|
+
var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;
|
|
53
|
+
if (desc && (desc.get || desc.set)) {
|
|
54
|
+
Object.defineProperty(newObj, key, desc);
|
|
55
|
+
} else {
|
|
56
|
+
newObj[key] = obj[key];
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
newObj.default = obj;
|
|
61
|
+
if (cache) {
|
|
62
|
+
cache.set(obj, newObj);
|
|
63
|
+
}
|
|
64
|
+
return newObj;
|
|
31
65
|
}
|
|
32
66
|
const VARIABLE_TYPES = [
|
|
33
67
|
'ListVariable',
|
|
34
68
|
'TextVariable'
|
|
35
69
|
];
|
|
70
|
+
// TODO: Replace with proper validation library
|
|
71
|
+
function getValidation(state) {
|
|
72
|
+
/** Name validation */ let name = null;
|
|
73
|
+
if (!state.name) {
|
|
74
|
+
name = 'Name is required';
|
|
75
|
+
}
|
|
76
|
+
// name can only contain alphanumeric characters and underscores and no spaces
|
|
77
|
+
if (state.name && !/^[a-zA-Z0-9_-]+$/.test(state.name)) {
|
|
78
|
+
name = 'Name can only contain alphanumeric characters, underscores, and dashes';
|
|
79
|
+
}
|
|
80
|
+
return {
|
|
81
|
+
name,
|
|
82
|
+
isValid: !name
|
|
83
|
+
};
|
|
84
|
+
}
|
|
36
85
|
const SectionHeader = ({ children })=>/*#__PURE__*/ (0, _jsxRuntime.jsx)(_material.Typography, {
|
|
37
86
|
pb: 2,
|
|
38
87
|
variant: "subtitle1",
|
|
@@ -40,6 +89,9 @@ const SectionHeader = ({ children })=>/*#__PURE__*/ (0, _jsxRuntime.jsx)(_mater
|
|
|
40
89
|
});
|
|
41
90
|
function VariableEditForm({ initialVariableDefinition , onChange , onCancel }) {
|
|
42
91
|
const [state, setState] = (0, _useImmer.useImmer)((0, _variableEditorFormModel.getInitialState)(initialVariableDefinition));
|
|
92
|
+
const validation = (0, _react.useMemo)(()=>getValidation(state), [
|
|
93
|
+
state
|
|
94
|
+
]);
|
|
43
95
|
return /*#__PURE__*/ (0, _jsxRuntime.jsxs)(_material.Box, {
|
|
44
96
|
children: [
|
|
45
97
|
/*#__PURE__*/ (0, _jsxRuntime.jsx)(SectionHeader, {
|
|
@@ -54,9 +106,12 @@ function VariableEditForm({ initialVariableDefinition , onChange , onCancel })
|
|
|
54
106
|
item: true,
|
|
55
107
|
xs: 6,
|
|
56
108
|
children: /*#__PURE__*/ (0, _jsxRuntime.jsx)(_material.TextField, {
|
|
109
|
+
required: true,
|
|
110
|
+
error: !!validation.name,
|
|
57
111
|
fullWidth: true,
|
|
58
112
|
label: "Name",
|
|
59
113
|
value: state.name,
|
|
114
|
+
helperText: validation.name,
|
|
60
115
|
onChange: (v)=>{
|
|
61
116
|
setState((draft)=>{
|
|
62
117
|
draft.name = v.target.value;
|
|
@@ -220,6 +275,7 @@ function VariableEditForm({ initialVariableDefinition , onChange , onCancel })
|
|
|
220
275
|
justifyContent: "end",
|
|
221
276
|
children: [
|
|
222
277
|
/*#__PURE__*/ (0, _jsxRuntime.jsx)(_material.Button, {
|
|
278
|
+
disabled: !validation.isValid,
|
|
223
279
|
variant: "contained",
|
|
224
280
|
onClick: ()=>{
|
|
225
281
|
onChange((0, _variableEditorFormModel.getVariableDefinitionFromState)(state));
|
|
@@ -89,6 +89,11 @@ function TemplateVariableList(props) {
|
|
|
89
89
|
/*#__PURE__*/ (0, _jsxRuntime.jsx)(_material.Drawer, {
|
|
90
90
|
anchor: 'right',
|
|
91
91
|
open: isEditing,
|
|
92
|
+
PaperProps: {
|
|
93
|
+
sx: {
|
|
94
|
+
width: '50%'
|
|
95
|
+
}
|
|
96
|
+
},
|
|
92
97
|
children: /*#__PURE__*/ (0, _jsxRuntime.jsx)(_variableEditor.VariableEditor, {
|
|
93
98
|
onCancel: ()=>{
|
|
94
99
|
setIsEditing(false);
|
|
@@ -18,6 +18,7 @@ _exportStar(require("./Dashboard"), exports);
|
|
|
18
18
|
_exportStar(require("./DashboardToolbar"), exports);
|
|
19
19
|
_exportStar(require("./DeletePanelDialog"), exports);
|
|
20
20
|
_exportStar(require("./DeletePanelGroupDialog"), exports);
|
|
21
|
+
_exportStar(require("./DownloadButton"), exports);
|
|
21
22
|
_exportStar(require("./GridLayout"), exports);
|
|
22
23
|
_exportStar(require("./Panel"), exports);
|
|
23
24
|
_exportStar(require("./PanelDrawer"), exports);
|