@perses-dev/dashboards 0.22.0 → 0.23.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 (126) hide show
  1. package/dist/cjs/components/DashboardToolbar/DashboardToolbar.js +3 -0
  2. package/dist/cjs/components/DeletePanelDialog/DeletePanelDialog.js +8 -30
  3. package/dist/cjs/components/DeletePanelGroupDialog/DeletePanelGroupDialog.js +1 -0
  4. package/dist/cjs/components/DiscardChangesConfirmationDialog/DiscardChangesConfirmationDialog.js +1 -0
  5. package/dist/cjs/components/GridLayout/GridItemContent.js +2 -1
  6. package/dist/cjs/components/GridLayout/GridLayout.js +4 -7
  7. package/dist/cjs/components/Panel/Panel.js +4 -5
  8. package/dist/cjs/components/Panel/Panel.test.js +10 -2
  9. package/dist/cjs/components/Panel/PanelContent.js +2 -1
  10. package/dist/cjs/components/Panel/PanelHeader.js +17 -0
  11. package/dist/cjs/components/PanelGroupDialog/PanelGroupDialog.js +1 -0
  12. package/dist/cjs/components/TimeRangeControls/TimeRangeControls.test.js +2 -2
  13. package/dist/cjs/components/Variables/EditVariablesButton.js +1 -0
  14. package/dist/cjs/components/Variables/VariableEditor.js +1 -1
  15. package/dist/cjs/components/Variables/VariableList.js +8 -2
  16. package/dist/cjs/{utils/functions.js → constants/grid-layout-config.js} +13 -5
  17. package/dist/cjs/constants/index.js +1 -0
  18. package/dist/cjs/constants/user-interface-text.js +2 -0
  19. package/dist/cjs/context/DashboardProvider/DashboardProvider.js +27 -2
  20. package/dist/cjs/context/DashboardProvider/dashboard-provider-api.js +6 -4
  21. package/dist/cjs/context/DashboardProvider/duplicate-panel-slice.js +62 -0
  22. package/dist/cjs/context/DashboardProvider/panel-editor-slice.js +8 -37
  23. package/dist/cjs/context/useDashboard.js +4 -2
  24. package/dist/cjs/utils/index.js +1 -1
  25. package/dist/cjs/utils/panelUtils.js +168 -0
  26. package/dist/cjs/utils/panelUtils.test.js +195 -0
  27. package/dist/cjs/views/ViewDashboard/DashboardApp.js +4 -1
  28. package/dist/cjs/views/ViewDashboard/ViewDashboard.js +4 -2
  29. package/dist/components/DashboardToolbar/DashboardToolbar.d.ts.map +1 -1
  30. package/dist/components/DashboardToolbar/DashboardToolbar.js +3 -0
  31. package/dist/components/DashboardToolbar/DashboardToolbar.js.map +1 -1
  32. package/dist/components/DeletePanelDialog/DeletePanelDialog.d.ts.map +1 -1
  33. package/dist/components/DeletePanelDialog/DeletePanelDialog.js +7 -24
  34. package/dist/components/DeletePanelDialog/DeletePanelDialog.js.map +1 -1
  35. package/dist/components/DeletePanelGroupDialog/DeletePanelGroupDialog.js +1 -0
  36. package/dist/components/DeletePanelGroupDialog/DeletePanelGroupDialog.js.map +1 -1
  37. package/dist/components/DiscardChangesConfirmationDialog/DiscardChangesConfirmationDialog.js +1 -0
  38. package/dist/components/DiscardChangesConfirmationDialog/DiscardChangesConfirmationDialog.js.map +1 -1
  39. package/dist/components/GridLayout/GridItemContent.d.ts.map +1 -1
  40. package/dist/components/GridLayout/GridItemContent.js +2 -1
  41. package/dist/components/GridLayout/GridItemContent.js.map +1 -1
  42. package/dist/components/GridLayout/GridLayout.d.ts.map +1 -1
  43. package/dist/components/GridLayout/GridLayout.js +4 -7
  44. package/dist/components/GridLayout/GridLayout.js.map +1 -1
  45. package/dist/components/Panel/Panel.d.ts.map +1 -1
  46. package/dist/components/Panel/Panel.js +5 -6
  47. package/dist/components/Panel/Panel.js.map +1 -1
  48. package/dist/components/Panel/Panel.test.js +10 -2
  49. package/dist/components/Panel/Panel.test.js.map +1 -1
  50. package/dist/components/Panel/PanelContent.d.ts.map +1 -1
  51. package/dist/components/Panel/PanelContent.js +2 -1
  52. package/dist/components/Panel/PanelContent.js.map +1 -1
  53. package/dist/components/Panel/PanelHeader.d.ts +1 -0
  54. package/dist/components/Panel/PanelHeader.d.ts.map +1 -1
  55. package/dist/components/Panel/PanelHeader.js +17 -0
  56. package/dist/components/Panel/PanelHeader.js.map +1 -1
  57. package/dist/components/PanelGroupDialog/PanelGroupDialog.js +1 -0
  58. package/dist/components/PanelGroupDialog/PanelGroupDialog.js.map +1 -1
  59. package/dist/components/TimeRangeControls/TimeRangeControls.test.js +2 -2
  60. package/dist/components/TimeRangeControls/TimeRangeControls.test.js.map +1 -1
  61. package/dist/components/Variables/EditVariablesButton.d.ts.map +1 -1
  62. package/dist/components/Variables/EditVariablesButton.js +1 -0
  63. package/dist/components/Variables/EditVariablesButton.js.map +1 -1
  64. package/dist/components/Variables/VariableEditor.js +1 -1
  65. package/dist/components/Variables/VariableEditor.js.map +1 -1
  66. package/dist/components/Variables/VariableList.d.ts.map +1 -1
  67. package/dist/components/Variables/VariableList.js +8 -2
  68. package/dist/components/Variables/VariableList.js.map +1 -1
  69. package/dist/constants/grid-layout-config.d.ts +6 -0
  70. package/dist/constants/grid-layout-config.d.ts.map +1 -0
  71. package/dist/{utils/functions.js → constants/grid-layout-config.js} +5 -3
  72. package/dist/constants/grid-layout-config.js.map +1 -0
  73. package/dist/constants/index.d.ts +1 -0
  74. package/dist/constants/index.d.ts.map +1 -1
  75. package/dist/constants/index.js +1 -0
  76. package/dist/constants/index.js.map +1 -1
  77. package/dist/constants/user-interface-text.d.ts +2 -0
  78. package/dist/constants/user-interface-text.d.ts.map +1 -1
  79. package/dist/constants/user-interface-text.js +2 -0
  80. package/dist/constants/user-interface-text.js.map +1 -1
  81. package/dist/context/DashboardProvider/DashboardProvider.d.ts +6 -4
  82. package/dist/context/DashboardProvider/DashboardProvider.d.ts.map +1 -1
  83. package/dist/context/DashboardProvider/DashboardProvider.js +28 -3
  84. package/dist/context/DashboardProvider/DashboardProvider.js.map +1 -1
  85. package/dist/context/DashboardProvider/dashboard-provider-api.d.ts +1 -0
  86. package/dist/context/DashboardProvider/dashboard-provider-api.d.ts.map +1 -1
  87. package/dist/context/DashboardProvider/dashboard-provider-api.js +6 -4
  88. package/dist/context/DashboardProvider/dashboard-provider-api.js.map +1 -1
  89. package/dist/context/DashboardProvider/duplicate-panel-slice.d.ts +19 -0
  90. package/dist/context/DashboardProvider/duplicate-panel-slice.d.ts.map +1 -0
  91. package/dist/context/DashboardProvider/duplicate-panel-slice.js +58 -0
  92. package/dist/context/DashboardProvider/duplicate-panel-slice.js.map +1 -0
  93. package/dist/context/DashboardProvider/index.d.ts +1 -1
  94. package/dist/context/DashboardProvider/index.d.ts.map +1 -1
  95. package/dist/context/DashboardProvider/index.js.map +1 -1
  96. package/dist/context/DashboardProvider/panel-editor-slice.d.ts +4 -0
  97. package/dist/context/DashboardProvider/panel-editor-slice.d.ts.map +1 -1
  98. package/dist/context/DashboardProvider/panel-editor-slice.js +6 -35
  99. package/dist/context/DashboardProvider/panel-editor-slice.js.map +1 -1
  100. package/dist/context/useDashboard.d.ts.map +1 -1
  101. package/dist/context/useDashboard.js +4 -2
  102. package/dist/context/useDashboard.js.map +1 -1
  103. package/dist/utils/index.d.ts +1 -1
  104. package/dist/utils/index.d.ts.map +1 -1
  105. package/dist/utils/index.js +1 -1
  106. package/dist/utils/index.js.map +1 -1
  107. package/dist/utils/panelUtils.d.ts +27 -0
  108. package/dist/utils/panelUtils.d.ts.map +1 -0
  109. package/dist/utils/panelUtils.js +174 -0
  110. package/dist/utils/panelUtils.js.map +1 -0
  111. package/dist/utils/panelUtils.test.d.ts +2 -0
  112. package/dist/utils/panelUtils.test.d.ts.map +1 -0
  113. package/dist/utils/panelUtils.test.js +193 -0
  114. package/dist/utils/panelUtils.test.js.map +1 -0
  115. package/dist/views/ViewDashboard/DashboardApp.d.ts +1 -0
  116. package/dist/views/ViewDashboard/DashboardApp.d.ts.map +1 -1
  117. package/dist/views/ViewDashboard/DashboardApp.js +4 -1
  118. package/dist/views/ViewDashboard/DashboardApp.js.map +1 -1
  119. package/dist/views/ViewDashboard/ViewDashboard.d.ts +2 -0
  120. package/dist/views/ViewDashboard/ViewDashboard.d.ts.map +1 -1
  121. package/dist/views/ViewDashboard/ViewDashboard.js +4 -2
  122. package/dist/views/ViewDashboard/ViewDashboard.js.map +1 -1
  123. package/package.json +4 -4
  124. package/dist/utils/functions.d.ts +0 -2
  125. package/dist/utils/functions.d.ts.map +0 -1
  126. package/dist/utils/functions.js.map +0 -1
@@ -58,9 +58,11 @@ const DashboardToolbar = (props)=>{
58
58
  setEditMode(false);
59
59
  }
60
60
  };
61
+ const testId = 'dashboard-toolbar';
61
62
  return /*#__PURE__*/ (0, _jsxRuntime.jsx)(_jsxRuntime.Fragment, {
62
63
  children: isEditMode ? /*#__PURE__*/ (0, _jsxRuntime.jsxs)(_material.Stack, {
63
64
  spacing: 1,
65
+ "data-testid": testId,
64
66
  children: [
65
67
  /*#__PURE__*/ (0, _jsxRuntime.jsxs)(_material.Box, {
66
68
  p: 2,
@@ -167,6 +169,7 @@ const DashboardToolbar = (props)=>{
167
169
  }) : /*#__PURE__*/ (0, _jsxRuntime.jsxs)(_material.Stack, {
168
170
  spacing: 1,
169
171
  padding: 2,
172
+ "data-testid": testId,
170
173
  children: [
171
174
  /*#__PURE__*/ (0, _jsxRuntime.jsxs)(_material.Box, {
172
175
  sx: {
@@ -19,32 +19,17 @@ Object.defineProperty(exports, "DeletePanelDialog", {
19
19
  get: ()=>DeletePanelDialog
20
20
  });
21
21
  const _jsxRuntime = require("react/jsx-runtime");
22
- const _material = require("@mui/material");
23
- const _close = /*#__PURE__*/ _interopRequireDefault(require("mdi-material-ui/Close"));
22
+ const _components = require("@perses-dev/components");
24
23
  const _context = require("../../context");
25
- function _interopRequireDefault(obj) {
26
- return obj && obj.__esModule ? obj : {
27
- default: obj
28
- };
29
- }
30
24
  const DeletePanelDialog = ()=>{
31
25
  const { deletePanelDialog , closeDeletePanelDialog } = (0, _context.useDeletePanelDialog)();
32
- return /*#__PURE__*/ (0, _jsxRuntime.jsxs)(_material.Dialog, {
26
+ return /*#__PURE__*/ (0, _jsxRuntime.jsxs)(_components.Dialog, {
33
27
  open: deletePanelDialog !== undefined,
34
28
  children: [
35
- /*#__PURE__*/ (0, _jsxRuntime.jsx)(_material.DialogTitle, {
29
+ /*#__PURE__*/ (0, _jsxRuntime.jsx)(_components.Dialog.Header, {
30
+ onClose: ()=>closeDeletePanelDialog(),
36
31
  children: "Delete Panel"
37
32
  }),
38
- /*#__PURE__*/ (0, _jsxRuntime.jsx)(_material.IconButton, {
39
- "aria-label": "Close",
40
- onClick: ()=>closeDeletePanelDialog(),
41
- sx: (theme)=>({
42
- position: 'absolute',
43
- top: theme.spacing(0.5),
44
- right: theme.spacing(0.5)
45
- }),
46
- children: /*#__PURE__*/ (0, _jsxRuntime.jsx)(_close.default, {})
47
- }),
48
33
  deletePanelDialog && /*#__PURE__*/ (0, _jsxRuntime.jsx)(DeletePanelForm, {
49
34
  deletePanelDialog: deletePanelDialog
50
35
  })
@@ -62,11 +47,7 @@ const DeletePanelForm = ({ deletePanelDialog })=>{
62
47
  return /*#__PURE__*/ (0, _jsxRuntime.jsxs)("form", {
63
48
  onSubmit: handleDelete,
64
49
  children: [
65
- /*#__PURE__*/ (0, _jsxRuntime.jsxs)(_material.DialogContent, {
66
- dividers: true,
67
- sx: {
68
- width: '500px'
69
- },
50
+ /*#__PURE__*/ (0, _jsxRuntime.jsxs)(_components.Dialog.Content, {
70
51
  children: [
71
52
  "Are you sure you want to delete ",
72
53
  deletePanelDialog.panelName,
@@ -75,15 +56,12 @@ const DeletePanelForm = ({ deletePanelDialog })=>{
75
56
  "? This action cannot be undone."
76
57
  ]
77
58
  }),
78
- /*#__PURE__*/ (0, _jsxRuntime.jsxs)(_material.DialogActions, {
59
+ /*#__PURE__*/ (0, _jsxRuntime.jsxs)(_components.Dialog.Actions, {
79
60
  children: [
80
- /*#__PURE__*/ (0, _jsxRuntime.jsx)(_material.Button, {
81
- variant: "contained",
82
- type: "submit",
61
+ /*#__PURE__*/ (0, _jsxRuntime.jsx)(_components.Dialog.PrimaryButton, {
83
62
  children: "Delete"
84
63
  }),
85
- /*#__PURE__*/ (0, _jsxRuntime.jsx)(_material.Button, {
86
- variant: "outlined",
64
+ /*#__PURE__*/ (0, _jsxRuntime.jsx)(_components.Dialog.SecondaryButton, {
87
65
  onClick: ()=>closeDeletePanelDialog(),
88
66
  children: "Cancel"
89
67
  })
@@ -78,6 +78,7 @@ const DeletePanelGroupDialog = ()=>{
78
78
  }),
79
79
  /*#__PURE__*/ (0, _jsxRuntime.jsx)(_material.Button, {
80
80
  variant: "outlined",
81
+ color: "secondary",
81
82
  onClick: ()=>closeDeletePanelGroupDialog(),
82
83
  children: "Cancel"
83
84
  })
@@ -63,6 +63,7 @@ const DiscardChangesConfirmationDialog = ()=>{
63
63
  }),
64
64
  /*#__PURE__*/ (0, _jsxRuntime.jsx)(_material.Button, {
65
65
  variant: "outlined",
66
+ color: "secondary",
66
67
  onClick: dialog.onCancel,
67
68
  children: "Cancel"
68
69
  })
@@ -25,12 +25,13 @@ function GridItemContent(props) {
25
25
  const { panelGroupItemId } = props;
26
26
  const panelDefinition = (0, _context.usePanel)(panelGroupItemId);
27
27
  const { isEditMode } = (0, _context.useEditMode)();
28
- const { openEditPanel , openDeletePanelDialog } = (0, _context.usePanelActions)(panelGroupItemId);
28
+ const { openEditPanel , openDeletePanelDialog , duplicatePanel } = (0, _context.usePanelActions)(panelGroupItemId);
29
29
  // Provide actions to the panel when in edit mode
30
30
  let editHandlers = undefined;
31
31
  if (isEditMode) {
32
32
  editHandlers = {
33
33
  onEditPanelClick: openEditPanel,
34
+ onDuplicatePanelClick: duplicatePanel,
34
35
  onDeletePanelClick: openDeletePanelDialog
35
36
  };
36
37
  }
@@ -24,11 +24,11 @@ const _reactGridLayout = require("react-grid-layout");
24
24
  const _material = require("@mui/material");
25
25
  const _components = require("@perses-dev/components");
26
26
  const _context = require("../../context");
27
+ const _constants = require("../../constants");
27
28
  const _gridTitle = require("./GridTitle");
28
29
  const _gridItemContent = require("./GridItemContent");
29
30
  const _gridContainer = require("./GridContainer");
30
31
  const ResponsiveGridLayout = (0, _reactGridLayout.WidthProvider)(_reactGridLayout.Responsive);
31
- const SMALL_LAYOUT_BREAKPOINT = 'sm';
32
32
  function GridLayout(props) {
33
33
  const { panelGroupId /*...others */ } = props;
34
34
  const theme = (0, _material.useTheme)();
@@ -42,7 +42,7 @@ function GridLayout(props) {
42
42
  // a bug in react-layout-grid where `currentLayout` does not adjust properly
43
43
  // when going to a smaller breakpoint and then back to a larger breakpoint.
44
44
  // https://github.com/react-grid-layout/react-grid-layout/issues/1663
45
- const smallLayout = allLayouts[SMALL_LAYOUT_BREAKPOINT];
45
+ const smallLayout = allLayouts[_constants.GRID_LAYOUT_SMALL_BREAKPOINT];
46
46
  if (smallLayout) {
47
47
  updatePanelGroupLayouts(smallLayout);
48
48
  }
@@ -68,10 +68,7 @@ function GridLayout(props) {
68
68
  sm: theme.breakpoints.values.sm,
69
69
  xxs: 0
70
70
  },
71
- cols: {
72
- sm: 24,
73
- xxs: 2
74
- },
71
+ cols: _constants.GRID_LAYOUT_COLS,
75
72
  rowHeight: 30,
76
73
  draggableHandle: '.drag-handle',
77
74
  resizeHandles: [
@@ -84,7 +81,7 @@ function GridLayout(props) {
84
81
  10
85
82
  ],
86
83
  layouts: {
87
- [SMALL_LAYOUT_BREAKPOINT]: groupDefinition.itemLayouts
84
+ [_constants.GRID_LAYOUT_SMALL_BREAKPOINT]: groupDefinition.itemLayouts
88
85
  },
89
86
  onLayoutChange: handleLayoutChange,
90
87
  children: groupDefinition.itemLayouts.map(({ i })=>/*#__PURE__*/ (0, _jsxRuntime.jsx)("div", {
@@ -56,8 +56,7 @@ function Panel(props) {
56
56
  initialInView: false,
57
57
  triggerOnce: true
58
58
  });
59
- // TODO: adjust padding for small panels, consistent way to determine isLargePanel here and in StatChart
60
- const panelPadding = 1.5;
59
+ const chartsTheme = (0, _components.useChartsTheme)();
61
60
  const handleMouseEnter = (e)=>{
62
61
  setIsHovered(true);
63
62
  onMouseEnter === null || onMouseEnter === void 0 ? void 0 : onMouseEnter(e);
@@ -90,7 +89,7 @@ function Panel(props) {
90
89
  editHandlers: editHandlers,
91
90
  isHovered: isHovered,
92
91
  sx: {
93
- paddingX: (theme)=>theme.spacing(panelPadding)
92
+ paddingX: `${chartsTheme.container.padding.default}px`
94
93
  }
95
94
  }),
96
95
  /*#__PURE__*/ (0, _jsxRuntime.jsx)(_material.CardContent, {
@@ -100,10 +99,10 @@ function Panel(props) {
100
99
  overflow: 'hidden',
101
100
  flexGrow: 1,
102
101
  margin: 0,
103
- padding: (theme)=>theme.spacing(panelPadding),
102
+ padding: 0,
104
103
  // Override MUI default style for last-child
105
104
  ':last-child': {
106
- padding: (theme)=>theme.spacing(panelPadding)
105
+ padding: 0
107
106
  }
108
107
  },
109
108
  ref: setContentElement,
@@ -97,7 +97,8 @@ describe('Panel', ()=>{
97
97
  it('does not show description in edit mode', ()=>{
98
98
  renderPanel(undefined, {
99
99
  onEditPanelClick: jest.fn(),
100
- onDeletePanelClick: jest.fn()
100
+ onDeletePanelClick: jest.fn(),
101
+ onDuplicatePanelClick: jest.fn()
101
102
  });
102
103
  const panel = getPanel();
103
104
  _userEvent.default.hover(panel);
@@ -109,9 +110,11 @@ describe('Panel', ()=>{
109
110
  it('can trigger panel actions in edit mode', ()=>{
110
111
  const onEditPanelClick = jest.fn();
111
112
  const onDeletePanelClick = jest.fn();
113
+ const onDuplicatePanelClick = jest.fn();
112
114
  renderPanel(undefined, {
113
115
  onEditPanelClick,
114
- onDeletePanelClick
116
+ onDeletePanelClick,
117
+ onDuplicatePanelClick
115
118
  });
116
119
  const panel = getPanel();
117
120
  _userEvent.default.hover(panel);
@@ -123,7 +126,12 @@ describe('Panel', ()=>{
123
126
  name: /delete/i
124
127
  });
125
128
  _userEvent.default.click(deleteButton);
129
+ const duplicateButton = _react.screen.getByRole('button', {
130
+ name: /duplicate/i
131
+ });
132
+ _userEvent.default.click(duplicateButton);
126
133
  expect(onEditPanelClick).toHaveBeenCalledTimes(1);
127
134
  expect(onDeletePanelClick).toHaveBeenCalledTimes(1);
135
+ expect(onDuplicatePanelClick).toHaveBeenCalledTimes(1);
128
136
  });
129
137
  });
@@ -31,7 +31,8 @@ function PanelContent(props) {
31
31
  return /*#__PURE__*/ (0, _jsxRuntime.jsx)(_material.Skeleton, {
32
32
  variant: "rectangular",
33
33
  width: contentDimensions === null || contentDimensions === void 0 ? void 0 : contentDimensions.width,
34
- height: contentDimensions === null || contentDimensions === void 0 ? void 0 : contentDimensions.height
34
+ height: contentDimensions === null || contentDimensions === void 0 ? void 0 : contentDimensions.height,
35
+ "aria-label": "Loading..."
35
36
  });
36
37
  }
37
38
  if (PanelComponent === undefined) {
@@ -25,6 +25,7 @@ const _informationOutline = /*#__PURE__*/ _interopRequireDefault(require("mdi-ma
25
25
  const _pencilOutline = /*#__PURE__*/ _interopRequireDefault(require("mdi-material-ui/PencilOutline"));
26
26
  const _deleteOutline = /*#__PURE__*/ _interopRequireDefault(require("mdi-material-ui/DeleteOutline"));
27
27
  const _dragVertical = /*#__PURE__*/ _interopRequireDefault(require("mdi-material-ui/DragVertical"));
28
+ const _contentCopy = /*#__PURE__*/ _interopRequireDefault(require("mdi-material-ui/ContentCopy"));
28
29
  const _constants = require("../../constants");
29
30
  function _interopRequireDefault(obj) {
30
31
  return obj && obj.__esModule ? obj : {
@@ -50,6 +51,22 @@ function PanelHeader({ id , title , description , editHandlers , isHovered , sx
50
51
  })
51
52
  })
52
53
  }),
54
+ /*#__PURE__*/ (0, _jsxRuntime.jsx)(_components.InfoTooltip, {
55
+ description: _constants.TOOLTIP_TEXT.duplicatePanel,
56
+ children: /*#__PURE__*/ (0, _jsxRuntime.jsx)(HeaderIconButton, {
57
+ "aria-label": _constants.ARIA_LABEL_TEXT.duplicatePanel(title),
58
+ size: "small",
59
+ onClick: editHandlers.onDuplicatePanelClick,
60
+ children: /*#__PURE__*/ (0, _jsxRuntime.jsx)(_contentCopy.default, {
61
+ fontSize: "inherit",
62
+ sx: {
63
+ // Shrink this icon a little bit to look more consistent
64
+ // with the other icons in the header.
65
+ transform: 'scale(0.925)'
66
+ }
67
+ })
68
+ })
69
+ }),
53
70
  /*#__PURE__*/ (0, _jsxRuntime.jsx)(_components.InfoTooltip, {
54
71
  description: _constants.TOOLTIP_TEXT.deletePanel,
55
72
  children: /*#__PURE__*/ (0, _jsxRuntime.jsx)(HeaderIconButton, {
@@ -92,6 +92,7 @@ function PanelGroupDialog() {
92
92
  }),
93
93
  /*#__PURE__*/ (0, _jsxRuntime.jsx)(_material.Button, {
94
94
  variant: "outlined",
95
+ color: "secondary",
95
96
  onClick: panelGroupEditor.close,
96
97
  children: "Cancel"
97
98
  })
@@ -61,7 +61,7 @@ describe('TimeRangeControls', ()=>{
61
61
  renderTimeRangeControls(false);
62
62
  expect(_react.screen.getByText('Last 30 minutes')).toBeInTheDocument();
63
63
  const dateButton = _react.screen.getByRole('button', {
64
- name: /last/i
64
+ name: /time range/i
65
65
  });
66
66
  _userEvent.default.click(dateButton);
67
67
  const firstSelected = _react.screen.getByRole('option', {
@@ -73,7 +73,7 @@ describe('TimeRangeControls', ()=>{
73
73
  it('should update URL params with correct time range values', ()=>{
74
74
  renderTimeRangeControls(true);
75
75
  const dateButton = _react.screen.getByRole('button', {
76
- name: /last/i
76
+ name: /time range/i
77
77
  });
78
78
  _userEvent.default.click(dateButton);
79
79
  const firstSelected = _react.screen.getByRole('option', {
@@ -60,6 +60,7 @@ function EditVariablesButton() {
60
60
  width: '50%'
61
61
  }
62
62
  },
63
+ "data-testid": "variable-editor",
63
64
  children: /*#__PURE__*/ (0, _jsxRuntime.jsx)(_variableEditor.VariableEditor, {
64
65
  variableDefinitions: variableDefinitions,
65
66
  onCancel: closeVariableEditor,
@@ -195,7 +195,7 @@ function VariableEditor(props) {
195
195
  sx: {
196
196
  minWidth: 650
197
197
  },
198
- "aria-label": "simple table",
198
+ "aria-label": "table of variables",
199
199
  children: [
200
200
  /*#__PURE__*/ (0, _jsxRuntime.jsx)(_material.TableHead, {
201
201
  children: /*#__PURE__*/ (0, _jsxRuntime.jsxs)(_material.TableRow, {
@@ -39,12 +39,17 @@ function TemplateVariableList(props) {
39
39
  disableHysteresis: true
40
40
  });
41
41
  const isSticky = scrollTrigger && props.initialVariableIsSticky && isPin;
42
- return /*#__PURE__*/ (0, _jsxRuntime.jsx)(_material.Box, {
42
+ return(// marginBottom={-1} counteracts the marginBottom={1} on every variable input.
43
+ // The margin on the inputs is for spacing between inputs, but is not meant to add space to bottom of the container.
44
+ /*#__PURE__*/ (0, _jsxRuntime.jsx)(_material.Box, {
45
+ marginBottom: -1,
46
+ "data-testid": "variable-list",
43
47
  children: /*#__PURE__*/ (0, _jsxRuntime.jsx)(_material.AppBar, {
44
48
  color: "inherit",
45
49
  position: isSticky ? 'fixed' : 'static',
46
50
  elevation: isSticky ? 4 : 0,
47
51
  sx: {
52
+ backgroundColor: 'inherit',
48
53
  ...props.sx
49
54
  },
50
55
  children: /*#__PURE__*/ (0, _jsxRuntime.jsxs)(_material.Box, {
@@ -62,6 +67,7 @@ function TemplateVariableList(props) {
62
67
  maxWidth: VARIABLE_INPUT_MAX_WIDTH,
63
68
  marginBottom: 1,
64
69
  marginRight: 1,
70
+ "data-testid": "template-variable",
65
71
  children: /*#__PURE__*/ (0, _jsxRuntime.jsx)(_variable.TemplateVariable, {
66
72
  name: v.spec.name
67
73
  }, v.spec.name)
@@ -74,5 +80,5 @@ function TemplateVariableList(props) {
74
80
  ]
75
81
  })
76
82
  })
77
- });
83
+ }));
78
84
  }
@@ -14,10 +14,18 @@
14
14
  Object.defineProperty(exports, "__esModule", {
15
15
  value: true
16
16
  });
17
- Object.defineProperty(exports, "removeWhiteSpacesAndSpecialCharacters", {
18
- enumerable: true,
19
- get: ()=>removeWhiteSpacesAndSpecialCharacters
17
+ function _export(target, all) {
18
+ for(var name in all)Object.defineProperty(target, name, {
19
+ enumerable: true,
20
+ get: all[name]
21
+ });
22
+ }
23
+ _export(exports, {
24
+ GRID_LAYOUT_COLS: ()=>GRID_LAYOUT_COLS,
25
+ GRID_LAYOUT_SMALL_BREAKPOINT: ()=>GRID_LAYOUT_SMALL_BREAKPOINT
20
26
  });
21
- const removeWhiteSpacesAndSpecialCharacters = (str)=>{
22
- return str.replace(/\s+/g, '');
27
+ const GRID_LAYOUT_COLS = {
28
+ sm: 24,
29
+ xxs: 2
23
30
  };
31
+ const GRID_LAYOUT_SMALL_BREAKPOINT = 'sm';
@@ -14,6 +14,7 @@
14
14
  Object.defineProperty(exports, "__esModule", {
15
15
  value: true
16
16
  });
17
+ _exportStar(require("./grid-layout-config"), exports);
17
18
  _exportStar(require("./user-interface-text"), exports);
18
19
  function _exportStar(from, to) {
19
20
  Object.keys(from).forEach(function(k) {
@@ -39,6 +39,7 @@ const TOOLTIP_TEXT = {
39
39
  moveGroupUp: 'Move group up',
40
40
  // Panel buttons
41
41
  editPanel: 'Edit',
42
+ duplicatePanel: 'Duplicate',
42
43
  deletePanel: 'Delete',
43
44
  movePanel: 'Move',
44
45
  // Variable editor buttons
@@ -54,6 +55,7 @@ const ARIA_LABEL_TEXT = {
54
55
  moveGroupUp: (groupName)=>`move group ${groupName} up`,
55
56
  // Panel buttons
56
57
  editPanel: (panelName)=>`edit panel ${panelName}`,
58
+ duplicatePanel: (panelName)=>`duplicate panel ${panelName}`,
57
59
  deletePanel: (panelName)=>`delete panel ${panelName}`,
58
60
  movePanel: (panelName)=>`move panel ${panelName}`
59
61
  };
@@ -31,6 +31,7 @@ const _middleware = require("zustand/middleware");
31
31
  const _immer = require("zustand/middleware/immer");
32
32
  const _shallow = /*#__PURE__*/ _interopRequireDefault(require("zustand/shallow"));
33
33
  const _react = require("react");
34
+ const _pluginSystem = require("@perses-dev/plugin-system");
34
35
  const _panelGroupEditorSlice = require("./panel-group-editor-slice");
35
36
  const _panelGroupSlice = require("./panel-group-slice");
36
37
  const _panelEditorSlice = require("./panel-editor-slice");
@@ -38,6 +39,7 @@ const _panelSlice = require("./panel-slice");
38
39
  const _deletePanelGroupSlice = require("./delete-panel-group-slice");
39
40
  const _deletePanelSlice = require("./delete-panel-slice");
40
41
  const _discardChangesDialogSlice = require("./discard-changes-dialog-slice");
42
+ const _duplicatePanelSlice = require("./duplicate-panel-slice");
41
43
  function _interopRequireDefault(obj) {
42
44
  return obj && obj.__esModule ? obj : {
43
45
  default: obj
@@ -55,7 +57,27 @@ function DashboardProvider(props) {
55
57
  const createDashboardStore = (0, _react.useCallback)(initStore, [
56
58
  props
57
59
  ]);
60
+ // load plugin to retrieve initial spec if default panel kind is defined
61
+ const { defaultPluginKinds } = (0, _pluginSystem.usePluginRegistry)();
62
+ var ref;
63
+ const defaultPanelKind = (ref = defaultPluginKinds === null || defaultPluginKinds === void 0 ? void 0 : defaultPluginKinds['Panel']) !== null && ref !== void 0 ? ref : '';
64
+ const { data: plugin } = (0, _pluginSystem.usePlugin)('Panel', defaultPanelKind);
58
65
  const [store] = (0, _react.useState)(createDashboardStore(props)); // prevent calling createDashboardStore every time it rerenders
66
+ (0, _react.useEffect)(()=>{
67
+ if (plugin === undefined) return;
68
+ const spec = plugin.createInitialOptions();
69
+ // set default panel kind and spec for add panel editor
70
+ store.setState({
71
+ initialValues: {
72
+ kind: defaultPanelKind,
73
+ spec
74
+ }
75
+ });
76
+ }, [
77
+ plugin,
78
+ store,
79
+ defaultPanelKind
80
+ ]);
59
81
  return /*#__PURE__*/ (0, _jsxRuntime.jsx)(DashboardContext.Provider, {
60
82
  value: store,
61
83
  children: props.children
@@ -63,7 +85,7 @@ function DashboardProvider(props) {
63
85
  }
64
86
  function initStore(props) {
65
87
  const { initialState: { dashboardResource , isEditMode } , } = props;
66
- const { spec: { layouts , panels , duration } , metadata , } = dashboardResource;
88
+ const { spec: { display , layouts , panels , duration } , metadata , } = dashboardResource;
67
89
  const store = (0, _zustand.createStore)()((0, _immer.immer)((0, _middleware.devtools)((...args)=>{
68
90
  const [set] = args;
69
91
  return {
@@ -74,7 +96,9 @@ function initStore(props) {
74
96
  ...(0, _panelEditorSlice.createPanelEditorSlice)()(...args),
75
97
  ...(0, _deletePanelSlice.createDeletePanelSlice)()(...args),
76
98
  ...(0, _discardChangesDialogSlice.createDiscardChangesDialogSlice)(...args),
99
+ ...(0, _duplicatePanelSlice.createDuplicatePanelSlice)()(...args),
77
100
  metadata,
101
+ display,
78
102
  defaultTimeRange: {
79
103
  pastDuration: duration
80
104
  },
@@ -82,9 +106,10 @@ function initStore(props) {
82
106
  setEditMode: (isEditMode)=>set({
83
107
  isEditMode
84
108
  }),
85
- setDashboard: ({ metadata , spec: { panels , layouts } })=>{
109
+ setDashboard: ({ metadata , spec: { display , panels , layouts } })=>{
86
110
  set((state)=>{
87
111
  state.metadata = metadata;
112
+ state.display = display;
88
113
  const { panelGroups , panelGroupOrder } = (0, _panelGroupSlice.convertLayoutsToPanelGroups)(layouts);
89
114
  state.panels = panels;
90
115
  state.panelGroups = panelGroups;
@@ -162,15 +162,17 @@ function usePanel(panelGroupItemId) {
162
162
  }
163
163
  return panel;
164
164
  }
165
- const selectPanelActions = ({ openEditPanel , openDeletePanelDialog })=>({
165
+ const selectPanelActions = ({ openEditPanel , openDeletePanelDialog , duplicatePanel })=>({
166
166
  openEditPanel,
167
- openDeletePanelDialog
167
+ openDeletePanelDialog,
168
+ duplicatePanel
168
169
  });
169
170
  function usePanelActions(panelGroupItemId) {
170
- const { openEditPanel , openDeletePanelDialog } = (0, _dashboardProvider.useDashboardStore)(selectPanelActions);
171
+ const { openEditPanel , openDeletePanelDialog , duplicatePanel } = (0, _dashboardProvider.useDashboardStore)(selectPanelActions);
171
172
  return {
172
173
  openEditPanel: ()=>openEditPanel(panelGroupItemId),
173
- openDeletePanelDialog: ()=>openDeletePanelDialog(panelGroupItemId)
174
+ openDeletePanelDialog: ()=>openDeletePanelDialog(panelGroupItemId),
175
+ duplicatePanel: ()=>duplicatePanel(panelGroupItemId)
174
176
  };
175
177
  }
176
178
  const selectPanelEditor = (state)=>state.panelEditor;
@@ -0,0 +1,62 @@
1
+ // Copyright 2023 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, "createDuplicatePanelSlice", {
18
+ enumerable: true,
19
+ get: ()=>createDuplicatePanelSlice
20
+ });
21
+ const _panelUtils = require("../../utils/panelUtils");
22
+ const _common = require("./common");
23
+ function createDuplicatePanelSlice() {
24
+ return (set)=>({
25
+ duplicatePanel (panelGroupItemId) {
26
+ set((state)=>{
27
+ const panels = state.panels;
28
+ // Figure out the panel key at that location
29
+ const { panelGroupId , panelGroupItemLayoutId: panelGroupLayoutId } = panelGroupItemId;
30
+ const group = state.panelGroups[panelGroupId];
31
+ if (group === undefined) {
32
+ throw new Error(`Missing panel group ${panelGroupId}`);
33
+ }
34
+ const panelKey = group.itemPanelKeys[panelGroupLayoutId];
35
+ if (panelKey === undefined) {
36
+ throw new Error(`Could not find Panel Group item ${panelGroupItemId}`);
37
+ }
38
+ // Find the panel to edit
39
+ const panelToDupe = panels[panelKey];
40
+ if (panelToDupe === undefined) {
41
+ throw new Error(`Cannot find Panel with key '${panelKey}'`);
42
+ }
43
+ // Find the layout for the item being duped
44
+ const matchingLayout = group.itemLayouts.find((itemLayout)=>{
45
+ return itemLayout.i === panelGroupLayoutId;
46
+ });
47
+ if (matchingLayout === undefined) {
48
+ throw new Error(`Cannot find layout for Panel with key '${panelKey}'`);
49
+ }
50
+ const dupePanelKey = (0, _panelUtils.getValidPanelKey)(panelKey, panels);
51
+ state.panels[dupePanelKey] = panelToDupe;
52
+ const duplicateLayout = {
53
+ i: (0, _common.generateId)().toString(),
54
+ w: matchingLayout.w,
55
+ h: matchingLayout.h
56
+ };
57
+ group.itemLayouts = (0, _panelUtils.insertPanelInLayout)(duplicateLayout, matchingLayout, group.itemLayouts);
58
+ group.itemPanelKeys[duplicateLayout.i] = dupePanelKey;
59
+ });
60
+ }
61
+ });
62
+ }