@perses-dev/dashboards 0.15.0 → 0.17.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.
Files changed (86) hide show
  1. package/dist/cjs/components/DashboardToolbar/DashboardToolbar.js +8 -12
  2. package/dist/cjs/components/DownloadButton/DownloadButton.js +109 -0
  3. package/dist/cjs/{utils/component-ids.js → components/DownloadButton/index.js} +11 -14
  4. package/dist/cjs/components/GridLayout/GridLayout.js +13 -2
  5. package/dist/cjs/components/GridLayout/GridTitle.js +11 -17
  6. package/dist/cjs/components/Panel/Panel.js +4 -2
  7. package/dist/cjs/components/Panel/PanelHeader.js +54 -50
  8. package/dist/cjs/components/PanelDrawer/PanelDrawer.js +1 -3
  9. package/dist/cjs/components/PanelDrawer/PanelDrawer.test.js +23 -0
  10. package/dist/cjs/components/TimeRangeControls/TimeRangeControls.test.js +3 -1
  11. package/dist/cjs/components/Variables/Variable.js +7 -46
  12. package/dist/cjs/components/Variables/VariableEditor.js +157 -126
  13. package/dist/cjs/components/Variables/VariableEditorForm/VariableEditorForm.js +353 -161
  14. package/dist/cjs/components/Variables/VariableList.js +15 -8
  15. package/dist/cjs/components/Variables/variable-model.js +74 -0
  16. package/dist/cjs/components/index.js +1 -0
  17. package/dist/cjs/context/DashboardProvider/panel-editor-slice.js +40 -15
  18. package/dist/cjs/context/DashboardProvider/panel-group-editor-slice.js +5 -9
  19. package/dist/cjs/context/DashboardProvider/panel-group-slice.js +16 -1
  20. package/dist/cjs/views/ViewDashboard/tests/panelGroups.test.js +15 -21
  21. package/dist/components/DashboardToolbar/DashboardToolbar.d.ts.map +1 -1
  22. package/dist/components/DashboardToolbar/DashboardToolbar.js +8 -12
  23. package/dist/components/DashboardToolbar/DashboardToolbar.js.map +1 -1
  24. package/dist/components/DownloadButton/DownloadButton.d.ts +3 -0
  25. package/dist/components/DownloadButton/DownloadButton.d.ts.map +1 -0
  26. package/dist/components/DownloadButton/DownloadButton.js +60 -0
  27. package/dist/components/DownloadButton/DownloadButton.js.map +1 -0
  28. package/dist/components/DownloadButton/index.d.ts +2 -0
  29. package/dist/components/DownloadButton/index.d.ts.map +1 -0
  30. package/dist/{utils/component-ids.js → components/DownloadButton/index.js} +2 -14
  31. package/dist/components/DownloadButton/index.js.map +1 -0
  32. package/dist/components/GridLayout/GridLayout.d.ts.map +1 -1
  33. package/dist/components/GridLayout/GridLayout.js +13 -2
  34. package/dist/components/GridLayout/GridLayout.js.map +1 -1
  35. package/dist/components/GridLayout/GridTitle.d.ts.map +1 -1
  36. package/dist/components/GridLayout/GridTitle.js +11 -17
  37. package/dist/components/GridLayout/GridTitle.js.map +1 -1
  38. package/dist/components/Panel/Panel.d.ts.map +1 -1
  39. package/dist/components/Panel/Panel.js +4 -2
  40. package/dist/components/Panel/Panel.js.map +1 -1
  41. package/dist/components/Panel/PanelHeader.d.ts.map +1 -1
  42. package/dist/components/Panel/PanelHeader.js +54 -50
  43. package/dist/components/Panel/PanelHeader.js.map +1 -1
  44. package/dist/components/PanelDrawer/PanelDrawer.js +1 -3
  45. package/dist/components/PanelDrawer/PanelDrawer.js.map +1 -1
  46. package/dist/components/PanelDrawer/PanelDrawer.test.js +23 -0
  47. package/dist/components/PanelDrawer/PanelDrawer.test.js.map +1 -1
  48. package/dist/components/TimeRangeControls/TimeRangeControls.test.js +4 -2
  49. package/dist/components/TimeRangeControls/TimeRangeControls.test.js.map +1 -1
  50. package/dist/components/Variables/Variable.d.ts.map +1 -1
  51. package/dist/components/Variables/Variable.js +8 -47
  52. package/dist/components/Variables/Variable.js.map +1 -1
  53. package/dist/components/Variables/VariableEditor.d.ts.map +1 -1
  54. package/dist/components/Variables/VariableEditor.js +159 -128
  55. package/dist/components/Variables/VariableEditor.js.map +1 -1
  56. package/dist/components/Variables/VariableEditorForm/VariableEditorForm.d.ts.map +1 -1
  57. package/dist/components/Variables/VariableEditorForm/VariableEditorForm.js +315 -162
  58. package/dist/components/Variables/VariableEditorForm/VariableEditorForm.js.map +1 -1
  59. package/dist/components/Variables/VariableEditorForm/variable-editor-form-model.js.map +1 -1
  60. package/dist/components/Variables/VariableList.d.ts.map +1 -1
  61. package/dist/components/Variables/VariableList.js +15 -8
  62. package/dist/components/Variables/VariableList.js.map +1 -1
  63. package/dist/components/Variables/variable-model.d.ts +8 -0
  64. package/dist/components/Variables/variable-model.d.ts.map +1 -0
  65. package/dist/components/Variables/variable-model.js +64 -0
  66. package/dist/components/Variables/variable-model.js.map +1 -0
  67. package/dist/components/index.d.ts +1 -0
  68. package/dist/components/index.d.ts.map +1 -1
  69. package/dist/components/index.js +1 -0
  70. package/dist/components/index.js.map +1 -1
  71. package/dist/context/DashboardProvider/panel-editor-slice.d.ts.map +1 -1
  72. package/dist/context/DashboardProvider/panel-editor-slice.js +40 -15
  73. package/dist/context/DashboardProvider/panel-editor-slice.js.map +1 -1
  74. package/dist/context/DashboardProvider/panel-group-editor-slice.d.ts.map +1 -1
  75. package/dist/context/DashboardProvider/panel-group-editor-slice.js +5 -9
  76. package/dist/context/DashboardProvider/panel-group-editor-slice.js.map +1 -1
  77. package/dist/context/DashboardProvider/panel-group-slice.d.ts +9 -0
  78. package/dist/context/DashboardProvider/panel-group-slice.d.ts.map +1 -1
  79. package/dist/context/DashboardProvider/panel-group-slice.js +17 -0
  80. package/dist/context/DashboardProvider/panel-group-slice.js.map +1 -1
  81. package/dist/views/ViewDashboard/tests/panelGroups.test.js +15 -21
  82. package/dist/views/ViewDashboard/tests/panelGroups.test.js.map +1 -1
  83. package/package.json +4 -4
  84. package/dist/utils/component-ids.d.ts +0 -8
  85. package/dist/utils/component-ids.d.ts.map +0 -1
  86. 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
@@ -60,9 +61,7 @@ const DashboardToolbar = (props)=>{
60
61
  /*#__PURE__*/ (0, _jsxRuntime.jsxs)(_material.Stack, {
61
62
  direction: "row",
62
63
  spacing: 1,
63
- sx: {
64
- marginLeft: 'auto'
65
- },
64
+ marginLeft: "auto",
66
65
  children: [
67
66
  /*#__PURE__*/ (0, _jsxRuntime.jsx)(_material.Button, {
68
67
  variant: "contained",
@@ -94,11 +93,9 @@ const DashboardToolbar = (props)=>{
94
93
  })
95
94
  }),
96
95
  /*#__PURE__*/ (0, _jsxRuntime.jsxs)(_material.Stack, {
97
- direction: 'row',
96
+ direction: "row",
98
97
  spacing: 1,
99
- sx: {
100
- marginLeft: 'auto'
101
- },
98
+ marginLeft: "auto",
102
99
  children: [
103
100
  /*#__PURE__*/ (0, _jsxRuntime.jsx)(_material.Button, {
104
101
  startIcon: /*#__PURE__*/ (0, _jsxRuntime.jsx)(_plusBoxOutline.default, {}),
@@ -117,7 +114,7 @@ const DashboardToolbar = (props)=>{
117
114
  })
118
115
  ]
119
116
  }) : /*#__PURE__*/ (0, _jsxRuntime.jsxs)(_material.Stack, {
120
- spacing: 2,
117
+ spacing: 1,
121
118
  padding: 2,
122
119
  children: [
123
120
  /*#__PURE__*/ (0, _jsxRuntime.jsxs)(_material.Box, {
@@ -129,12 +126,11 @@ const DashboardToolbar = (props)=>{
129
126
  dashboardTitle,
130
127
  /*#__PURE__*/ (0, _jsxRuntime.jsxs)(_material.Stack, {
131
128
  direction: "row",
132
- spacing: 2,
133
- sx: {
134
- marginLeft: 'auto'
135
- },
129
+ spacing: 1,
130
+ marginLeft: "auto",
136
131
  children: [
137
132
  /*#__PURE__*/ (0, _jsxRuntime.jsx)(_timeRangeControls.TimeRangeControls, {}),
133
+ /*#__PURE__*/ (0, _jsxRuntime.jsx)(_downloadButton.DownloadButton, {}),
138
134
  isLaptopSize && /*#__PURE__*/ (0, _jsxRuntime.jsx)(_material.Button, {
139
135
  variant: "outlined",
140
136
  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
- Object.defineProperty(exports, "useId", {
18
- enumerable: true,
19
- get: ()=>useId
20
- });
21
- const _react = require("react");
22
- function useId(prefix) {
23
- if (globalThis.useIdValue === undefined) {
24
- globalThis.useIdValue = 0;
25
- }
26
- const id = (0, _react.useRef)(undefined);
27
- if (id.current === undefined) {
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
  }
@@ -28,6 +28,7 @@ const _gridTitle = require("./GridTitle");
28
28
  const _gridItemContent = require("./GridItemContent");
29
29
  const _gridContainer = require("./GridContainer");
30
30
  const ResponsiveGridLayout = (0, _reactGridLayout.WidthProvider)(_reactGridLayout.Responsive);
31
+ const SMALL_LAYOUT_BREAKPOINT = 'sm';
31
32
  function GridLayout(props) {
32
33
  const { panelGroupId /*...others */ } = props;
33
34
  const theme = (0, _material.useTheme)();
@@ -36,6 +37,16 @@ function GridLayout(props) {
36
37
  var ref;
37
38
  const [isOpen, setIsOpen] = (0, _react.useState)((ref = !groupDefinition.isCollapsed) !== null && ref !== void 0 ? ref : true);
38
39
  const { isEditMode } = (0, _context.useEditMode)();
40
+ const handleLayoutChange = (currentLayout, allLayouts)=>{
41
+ // Using the value from `allLayouts` instead of `currentLayout` because of
42
+ // a bug in react-layout-grid where `currentLayout` does not adjust properly
43
+ // when going to a smaller breakpoint and then back to a larger breakpoint.
44
+ // https://github.com/react-grid-layout/react-grid-layout/issues/1663
45
+ const smallLayout = allLayouts[SMALL_LAYOUT_BREAKPOINT];
46
+ if (smallLayout) {
47
+ updatePanelGroupLayouts(smallLayout);
48
+ }
49
+ };
39
50
  return /*#__PURE__*/ (0, _jsxRuntime.jsxs)(_gridContainer.GridContainer, {
40
51
  children: [
41
52
  groupDefinition.title !== undefined && /*#__PURE__*/ (0, _jsxRuntime.jsx)(_gridTitle.GridTitle, {
@@ -72,9 +83,9 @@ function GridLayout(props) {
72
83
  10
73
84
  ],
74
85
  layouts: {
75
- sm: groupDefinition.itemLayouts
86
+ [SMALL_LAYOUT_BREAKPOINT]: groupDefinition.itemLayouts
76
87
  },
77
- onLayoutChange: updatePanelGroupLayouts,
88
+ onLayoutChange: handleLayoutChange,
78
89
  children: groupDefinition.itemLayouts.map(({ i })=>/*#__PURE__*/ (0, _jsxRuntime.jsx)("div", {
79
90
  children: /*#__PURE__*/ (0, _jsxRuntime.jsx)(_components.ErrorBoundary, {
80
91
  FallbackComponent: _components.ErrorAlert,
@@ -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
- const _chevronUp = /*#__PURE__*/ _interopRequireDefault(require("mdi-material-ui/ChevronUp"));
25
23
  const _chevronDown = /*#__PURE__*/ _interopRequireDefault(require("mdi-material-ui/ChevronDown"));
26
- const _plus = /*#__PURE__*/ _interopRequireDefault(require("mdi-material-ui/Plus"));
24
+ const _chevronRight = /*#__PURE__*/ _interopRequireDefault(require("mdi-material-ui/ChevronRight"));
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,44 +53,40 @@ 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, {
63
59
  onClick: collapse.onToggleOpen,
64
- children: collapse.isOpen ? /*#__PURE__*/ (0, _jsxRuntime.jsx)(_chevronUp.default, {}) : /*#__PURE__*/ (0, _jsxRuntime.jsx)(_chevronDown.default, {})
60
+ children: collapse.isOpen ? /*#__PURE__*/ (0, _jsxRuntime.jsx)(_chevronDown.default, {}) : /*#__PURE__*/ (0, _jsxRuntime.jsx)(_chevronRight.default, {})
65
61
  }),
66
62
  text,
67
- isEditMode && isHovered && /*#__PURE__*/ (0, _jsxRuntime.jsxs)(_material.Stack, {
63
+ isEditMode && /*#__PURE__*/ (0, _jsxRuntime.jsxs)(_material.Stack, {
68
64
  direction: "row",
69
- sx: {
70
- marginLeft: 'auto'
71
- },
65
+ marginLeft: "auto",
72
66
  children: [
73
67
  /*#__PURE__*/ (0, _jsxRuntime.jsx)(_material.IconButton, {
74
- "aria-label": "add panel to group",
68
+ "aria-label": `add panel to group ${title}`,
75
69
  onClick: openAddPanel,
76
- children: /*#__PURE__*/ (0, _jsxRuntime.jsx)(_plus.default, {})
70
+ children: /*#__PURE__*/ (0, _jsxRuntime.jsx)(_chartBoxPlusOutline.default, {})
77
71
  }),
78
72
  /*#__PURE__*/ (0, _jsxRuntime.jsx)(_material.IconButton, {
79
- "aria-label": "edit group",
73
+ "aria-label": `edit group ${title}`,
80
74
  onClick: openEditPanelGroup,
81
75
  children: /*#__PURE__*/ (0, _jsxRuntime.jsx)(_pencilOutline.default, {})
82
76
  }),
83
77
  /*#__PURE__*/ (0, _jsxRuntime.jsx)(_material.IconButton, {
84
- "aria-label": "delete group",
78
+ "aria-label": `delete group ${title}`,
85
79
  onClick: ()=>openDeletePanelGroupDialog(panelGroupId),
86
80
  children: /*#__PURE__*/ (0, _jsxRuntime.jsx)(_deleteOutline.default, {})
87
81
  }),
88
82
  /*#__PURE__*/ (0, _jsxRuntime.jsx)(_material.IconButton, {
89
- "aria-label": "move group down",
83
+ "aria-label": `move group ${title} down`,
90
84
  disabled: moveDown === undefined,
91
85
  onClick: moveDown,
92
86
  children: /*#__PURE__*/ (0, _jsxRuntime.jsx)(_arrowDown.default, {})
93
87
  }),
94
88
  /*#__PURE__*/ (0, _jsxRuntime.jsx)(_material.IconButton, {
95
- "aria-label": "move group up",
89
+ "aria-label": `move group ${title} up`,
96
90
  disabled: moveUp === undefined,
97
91
  onClick: moveUp,
98
92
  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, _componentIds.useId)('Panel');
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 (isHovered) {
39
- if (editHandlers !== undefined) {
40
- // If there are edit handlers, always just show the edit buttons
41
- action = /*#__PURE__*/ (0, _jsxRuntime.jsxs)(_material.Stack, {
42
- direction: "row",
43
- alignItems: "center",
44
- spacing: 0.5,
45
- children: [
46
- /*#__PURE__*/ (0, _jsxRuntime.jsx)(HeaderIconButton, {
47
- "aria-label": "edit panel",
48
- size: "small",
49
- onClick: editHandlers.onEditPanelClick,
50
- children: /*#__PURE__*/ (0, _jsxRuntime.jsx)(_pencilOutline.default, {})
51
- }),
52
- /*#__PURE__*/ (0, _jsxRuntime.jsx)(HeaderIconButton, {
53
- "aria-label": "delete panel",
54
- size: "small",
55
- onClick: editHandlers.onDeletePanelClick,
56
- children: /*#__PURE__*/ (0, _jsxRuntime.jsx)(_deleteOutline.default, {})
57
- }),
58
- /*#__PURE__*/ (0, _jsxRuntime.jsx)(HeaderIconButton, {
59
- "aria-label": "drag handle",
60
- size: "small",
61
- children: /*#__PURE__*/ (0, _jsxRuntime.jsx)(_dragVertical.default, {
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
+ spacing: 0.5,
42
+ alignItems: "center",
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
- color: (theme)=>theme.palette.grey[700]
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
- whiteSpace: 'nowrap',
106
- overflow: 'hidden',
107
- textOverflow: 'ellipsis'
111
+ overflow: 'hidden'
108
112
  }
109
113
  }), sx),
110
114
  ...rest
@@ -70,9 +70,7 @@ const PanelDrawer = ()=>{
70
70
  /*#__PURE__*/ (0, _jsxRuntime.jsxs)(_material.Stack, {
71
71
  direction: "row",
72
72
  spacing: 1,
73
- sx: {
74
- marginLeft: 'auto'
75
- },
73
+ marginLeft: "auto",
76
74
  children: [
77
75
  /*#__PURE__*/ (0, _jsxRuntime.jsx)(_material.Button, {
78
76
  type: "submit",
@@ -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
- history.back();
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.
@@ -22,8 +22,8 @@ const _jsxRuntime = require("react/jsx-runtime");
22
22
  const _react = require("react");
23
23
  const _material = require("@mui/material");
24
24
  const _pluginSystem = require("@perses-dev/plugin-system");
25
- const _reactQuery = require("@tanstack/react-query");
26
25
  const _context = require("../../context");
26
+ const _variableModel = require("./variable-model");
27
27
  function TemplateVariable({ name }) {
28
28
  var ref;
29
29
  const ctx = (0, _context.useTemplateVariable)(name);
@@ -45,60 +45,16 @@ function TemplateVariable({ name }) {
45
45
  ]
46
46
  });
47
47
  }
48
- /**
49
- * Returns a serialized string of the current state of variable values.
50
- */ function getVariableValuesKey(v) {
51
- return Object.values(v).map((v)=>JSON.stringify(v.value)).join(',');
52
- }
53
48
  function ListVariable({ name }) {
54
49
  var ref, ref1, ref2, ref3;
55
50
  const ctx = (0, _context.useTemplateVariable)(name);
56
51
  const definition = ctx.definition;
57
- const { data: variablePlugin } = (0, _pluginSystem.usePlugin)('Variable', definition.spec.plugin.kind);
52
+ const variablesOptionsQuery = (0, _variableModel.useListVariablePluginValues)(definition);
58
53
  const { setVariableValue , setVariableLoading , setVariableOptions } = (0, _context.useTemplateVariableActions)();
59
- const datasourceStore = (0, _pluginSystem.useDatasourceStore)();
60
- const allVariables = (0, _pluginSystem.useTemplateVariableValues)();
61
- const { timeRange } = (0, _pluginSystem.useTimeRange)();
62
- const variablePluginCtx = {
63
- timeRange,
64
- datasourceStore,
65
- variables: allVariables
66
- };
67
- const spec = definition.spec.plugin.spec;
68
- let dependsOnVariables;
69
- if (variablePlugin === null || variablePlugin === void 0 ? void 0 : variablePlugin.dependsOn) {
70
- const dependencies = variablePlugin.dependsOn(spec, variablePluginCtx);
71
- dependsOnVariables = dependencies.variables;
72
- }
73
- const variables = (0, _pluginSystem.useTemplateVariableValues)(dependsOnVariables);
74
54
  const allowMultiple = (definition === null || definition === void 0 ? void 0 : definition.spec.allow_multiple) === true;
75
55
  const allowAllValue = (definition === null || definition === void 0 ? void 0 : definition.spec.allow_all_value) === true;
76
56
  var ref4;
77
57
  const title = (ref4 = (ref = definition === null || definition === void 0 ? void 0 : definition.spec.display) === null || ref === void 0 ? void 0 : ref.name) !== null && ref4 !== void 0 ? ref4 : name;
78
- let waitToLoad = false;
79
- if (dependsOnVariables) {
80
- waitToLoad = dependsOnVariables.some((v)=>{
81
- var ref;
82
- return (ref = variables[v]) === null || ref === void 0 ? void 0 : ref.loading;
83
- });
84
- }
85
- const variablesValueKey = getVariableValuesKey(variables);
86
- const variablesOptionsQuery = (0, _reactQuery.useQuery)([
87
- name,
88
- definition,
89
- variablesValueKey,
90
- timeRange
91
- ], async ()=>{
92
- const resp = await (variablePlugin === null || variablePlugin === void 0 ? void 0 : variablePlugin.getVariableOptions(spec, {
93
- datasourceStore,
94
- variables,
95
- timeRange
96
- }));
97
- var ref;
98
- return (ref = resp === null || resp === void 0 ? void 0 : resp.data) !== null && ref !== void 0 ? ref : [];
99
- }, {
100
- enabled: !!variablePlugin || waitToLoad
101
- });
102
58
  (0, _react.useEffect)(()=>{
103
59
  setVariableLoading(name, variablesOptionsQuery.isFetching);
104
60
  if (variablesOptionsQuery.data) {
@@ -199,6 +155,11 @@ function ListVariable({ name }) {
199
155
  disabled: true,
200
156
  children: "Loading"
201
157
  }),
158
+ finalOptions.length === 0 && /*#__PURE__*/ (0, _jsxRuntime.jsx)(_material.MenuItem, {
159
+ value: "empty",
160
+ disabled: true,
161
+ children: "No options"
162
+ }),
202
163
  finalOptions.map((option)=>/*#__PURE__*/ (0, _jsxRuntime.jsx)(_material.MenuItem, {
203
164
  value: option.value,
204
165
  children: option.label