@perses-dev/plugin-system 0.51.0-beta.1 → 0.51.0-rc.1

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 (83) hide show
  1. package/dist/cjs/components/DatasourceSelect.js +163 -81
  2. package/dist/cjs/components/HTTPSettingsEditor/HTTPSettingsEditor.js +5 -2
  3. package/dist/cjs/components/MultiQueryEditor/MultiQueryEditor.js +1 -1
  4. package/dist/cjs/components/PanelSpecEditor/PanelSpecEditor.js +2 -2
  5. package/dist/cjs/components/TimeRangeControls/TimeRangeControls.js +91 -1
  6. package/dist/cjs/components/Variables/VariableEditorForm/VariableEditorForm.js +11 -7
  7. package/dist/cjs/components/Variables/variable-model.js +4 -2
  8. package/dist/cjs/constants/user-interface-text.js +3 -1
  9. package/dist/cjs/remote/PluginRuntime.js +168 -162
  10. package/dist/cjs/runtime/TimeRangeProvider/TimeRangeSettingsProvider.js +13 -0
  11. package/dist/cjs/runtime/time-series-queries.js +5 -14
  12. package/dist/cjs/runtime/trace-queries.js +46 -16
  13. package/dist/cjs/runtime/utils.js +39 -0
  14. package/dist/cjs/test/render-hook.js +31 -0
  15. package/dist/cjs/test/render.js +4 -21
  16. package/dist/cjs/test/utils.js +49 -0
  17. package/dist/components/DatasourceSelect.d.ts +20 -3
  18. package/dist/components/DatasourceSelect.d.ts.map +1 -1
  19. package/dist/components/DatasourceSelect.js +150 -74
  20. package/dist/components/DatasourceSelect.js.map +1 -1
  21. package/dist/components/HTTPSettingsEditor/HTTPSettingsEditor.d.ts.map +1 -1
  22. package/dist/components/HTTPSettingsEditor/HTTPSettingsEditor.js +5 -2
  23. package/dist/components/HTTPSettingsEditor/HTTPSettingsEditor.js.map +1 -1
  24. package/dist/components/MultiQueryEditor/MultiQueryEditor.js +1 -1
  25. package/dist/components/MultiQueryEditor/MultiQueryEditor.js.map +1 -1
  26. package/dist/components/PanelSpecEditor/PanelSpecEditor.js +2 -2
  27. package/dist/components/PanelSpecEditor/PanelSpecEditor.js.map +1 -1
  28. package/dist/components/PluginRegistry/PluginRegistry.js.map +1 -1
  29. package/dist/components/PluginRegistry/plugin-indexes.d.ts +1 -1
  30. package/dist/components/PluginRegistry/plugin-indexes.d.ts.map +1 -1
  31. package/dist/components/PluginRegistry/plugin-indexes.js.map +1 -1
  32. package/dist/components/TimeRangeControls/TimeRangeControls.d.ts +2 -1
  33. package/dist/components/TimeRangeControls/TimeRangeControls.d.ts.map +1 -1
  34. package/dist/components/TimeRangeControls/TimeRangeControls.js +94 -2
  35. package/dist/components/TimeRangeControls/TimeRangeControls.js.map +1 -1
  36. package/dist/components/Variables/VariableEditorForm/VariableEditorForm.d.ts.map +1 -1
  37. package/dist/components/Variables/VariableEditorForm/VariableEditorForm.js +11 -7
  38. package/dist/components/Variables/VariableEditorForm/VariableEditorForm.js.map +1 -1
  39. package/dist/components/Variables/variable-model.d.ts.map +1 -1
  40. package/dist/components/Variables/variable-model.js +4 -2
  41. package/dist/components/Variables/variable-model.js.map +1 -1
  42. package/dist/constants/user-interface-text.d.ts +2 -0
  43. package/dist/constants/user-interface-text.d.ts.map +1 -1
  44. package/dist/constants/user-interface-text.js +3 -1
  45. package/dist/constants/user-interface-text.js.map +1 -1
  46. package/dist/model/trace-queries.d.ts +13 -1
  47. package/dist/model/trace-queries.d.ts.map +1 -1
  48. package/dist/model/trace-queries.js.map +1 -1
  49. package/dist/remote/PluginRuntime.d.ts +0 -1
  50. package/dist/remote/PluginRuntime.d.ts.map +1 -1
  51. package/dist/remote/PluginRuntime.js +169 -160
  52. package/dist/remote/PluginRuntime.js.map +1 -1
  53. package/dist/runtime/TimeRangeProvider/TimeRangeSettingsProvider.d.ts +7 -0
  54. package/dist/runtime/TimeRangeProvider/TimeRangeSettingsProvider.d.ts.map +1 -1
  55. package/dist/runtime/TimeRangeProvider/TimeRangeSettingsProvider.js +13 -0
  56. package/dist/runtime/TimeRangeProvider/TimeRangeSettingsProvider.js.map +1 -1
  57. package/dist/runtime/plugin-registry.d.ts +2 -2
  58. package/dist/runtime/plugin-registry.d.ts.map +1 -1
  59. package/dist/runtime/plugin-registry.js.map +1 -1
  60. package/dist/runtime/time-series-queries.d.ts +1 -1
  61. package/dist/runtime/time-series-queries.d.ts.map +1 -1
  62. package/dist/runtime/time-series-queries.js +4 -13
  63. package/dist/runtime/time-series-queries.js.map +1 -1
  64. package/dist/runtime/trace-queries.d.ts.map +1 -1
  65. package/dist/runtime/trace-queries.js +47 -17
  66. package/dist/runtime/trace-queries.js.map +1 -1
  67. package/dist/runtime/utils.d.ts +7 -0
  68. package/dist/runtime/utils.d.ts.map +1 -0
  69. package/dist/runtime/utils.js +25 -0
  70. package/dist/runtime/utils.js.map +1 -0
  71. package/dist/test/render-hook.d.ts +8 -0
  72. package/dist/test/render-hook.d.ts.map +1 -0
  73. package/dist/test/render-hook.js +26 -0
  74. package/dist/test/render-hook.js.map +1 -0
  75. package/dist/test/render.d.ts +1 -5
  76. package/dist/test/render.d.ts.map +1 -1
  77. package/dist/test/render.js +4 -21
  78. package/dist/test/render.js.map +1 -1
  79. package/dist/test/utils.d.ts +9 -0
  80. package/dist/test/utils.d.ts.map +1 -0
  81. package/dist/test/utils.js +41 -0
  82. package/dist/test/utils.js.map +1 -0
  83. package/package.json +5 -5
@@ -0,0 +1,49 @@
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, "getTestContextWrapper", {
18
+ enumerable: true,
19
+ get: function() {
20
+ return getTestContextWrapper;
21
+ }
22
+ });
23
+ const _jsxruntime = require("react/jsx-runtime");
24
+ const _reactquery = require("@tanstack/react-query");
25
+ const _components = require("../components");
26
+ const _testplugins = require("./test-plugins");
27
+ function getTestContextWrapper(contextOptions) {
28
+ // Create a new QueryClient for each test to avoid caching issues
29
+ const queryClient = new _reactquery.QueryClient({
30
+ defaultOptions: {
31
+ queries: {
32
+ refetchOnWindowFocus: false,
33
+ retry: false
34
+ }
35
+ }
36
+ });
37
+ return function Wrapper({ children }) {
38
+ return /*#__PURE__*/ (0, _jsxruntime.jsx)(_reactquery.QueryClientProvider, {
39
+ client: queryClient,
40
+ children: /*#__PURE__*/ (0, _jsxruntime.jsx)(_components.PluginRegistry, {
41
+ pluginLoader: _testplugins.testPluginLoader,
42
+ defaultPluginKinds: contextOptions?.defaultPluginKinds ?? {
43
+ TimeSeriesQuery: 'PrometheusTimeSeriesQuery'
44
+ },
45
+ children: children
46
+ })
47
+ });
48
+ };
49
+ }
@@ -1,10 +1,12 @@
1
1
  import { OutlinedSelectProps, BaseSelectProps } from '@mui/material';
2
- import { DatasourceSelector } from '@perses-dev/core';
2
+ import { DatasourceSelector, VariableName } from '@perses-dev/core';
3
3
  import { ReactElement } from 'react';
4
+ import { DatasourceSelectItemGroup, DatasourceSelectItemSelector, VariableStateMap } from '../runtime';
4
5
  type OmittedMuiProps = 'children' | 'value' | 'onChange';
6
+ export type DatasourceSelectValue<T = DatasourceSelector> = T | VariableName;
5
7
  export interface DatasourceSelectProps extends Omit<OutlinedSelectProps & BaseSelectProps<string>, OmittedMuiProps> {
6
- value: DatasourceSelector;
7
- onChange: (next: DatasourceSelector) => void;
8
+ value: DatasourceSelectValue;
9
+ onChange: (next: DatasourceSelectValue) => void;
8
10
  datasourcePluginKind: string;
9
11
  project?: string;
10
12
  }
@@ -18,5 +20,20 @@ export declare function DatasourceName(props: {
18
20
  overridden?: boolean;
19
21
  overriding?: boolean;
20
22
  }): ReactElement;
23
+ /**
24
+ * Given a DatasourceSelectItemSelector,
25
+ * returns a string value like `{kind}_____{group}_____{name}` that can be used as a Select input value.
26
+ * @param selector
27
+ */
28
+ export declare function selectorToOptionValue(selector: DatasourceSelectItemSelector | VariableName): string;
29
+ /**
30
+ * Given an option value name like `{kind}_____{group}_____{name}`,
31
+ * returns a DatasourceSelector to be used by the query data model.
32
+ * @param optionValue
33
+ */
34
+ export declare function optionValueToSelector(optionValue: string): DatasourceSelectValue;
35
+ export declare function isVariableDatasource(value: DatasourceSelectValue | undefined): value is VariableName;
36
+ export declare const datasourceSelectValueToSelector: (value: DatasourceSelectValue | undefined, variables: VariableStateMap, datasourceSelectItemGroups: DatasourceSelectItemGroup[] | undefined) => DatasourceSelector | undefined;
37
+ export declare const useDatasourceSelectValueToSelector: (value: DatasourceSelectValue, datasourcePluginKind: string) => DatasourceSelector;
21
38
  export {};
22
39
  //# sourceMappingURL=DatasourceSelect.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"DatasourceSelect.d.ts","sourceRoot":"","sources":["../../src/components/DatasourceSelect.tsx"],"names":[],"mappings":"AAcA,OAAO,EAUL,mBAAmB,EACnB,eAAe,EAChB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,YAAY,EAAW,MAAM,OAAO,CAAC;AAK9C,KAAK,eAAe,GAAG,UAAU,GAAG,OAAO,GAAG,UAAU,CAAC;AAEzD,MAAM,WAAW,qBAAsB,SAAQ,IAAI,CAAC,mBAAmB,GAAG,eAAe,CAAC,MAAM,CAAC,EAAE,eAAe,CAAC;IACjH,KAAK,EAAE,kBAAkB,CAAC;IAC1B,QAAQ,EAAE,CAAC,IAAI,EAAE,kBAAkB,KAAK,IAAI,CAAC;IAC7C,oBAAoB,EAAE,MAAM,CAAC;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,qBAAqB,GAAG,YAAY,CAuF3E;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,OAAO,CAAC;IAAC,UAAU,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,YAAY,CAahH"}
1
+ {"version":3,"file":"DatasourceSelect.d.ts","sourceRoot":"","sources":["../../src/components/DatasourceSelect.tsx"],"names":[],"mappings":"AAcA,OAAO,EAML,mBAAmB,EACnB,eAAe,EAGhB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACpE,OAAO,EAAE,YAAY,EAAW,MAAM,OAAO,CAAC;AAC9C,OAAO,EAEL,yBAAyB,EACzB,4BAA4B,EAG5B,gBAAgB,EACjB,MAAM,YAAY,CAAC;AAOpB,KAAK,eAAe,GAAG,UAAU,GAAG,OAAO,GAAG,UAAU,CAAC;AAWzD,MAAM,MAAM,qBAAqB,CAAC,CAAC,GAAG,kBAAkB,IAAI,CAAC,GAAG,YAAY,CAAC;AAE7E,MAAM,WAAW,qBAAsB,SAAQ,IAAI,CAAC,mBAAmB,GAAG,eAAe,CAAC,MAAM,CAAC,EAAE,eAAe,CAAC;IACjH,KAAK,EAAE,qBAAqB,CAAC;IAC7B,QAAQ,EAAE,CAAC,IAAI,EAAE,qBAAqB,KAAK,IAAI,CAAC;IAChD,oBAAoB,EAAE,MAAM,CAAC;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,qBAAqB,GAAG,YAAY,CAiH3E;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,OAAO,CAAC;IAAC,UAAU,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,YAAY,CAahH;AAKD;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,4BAA4B,GAAG,YAAY,GAAG,MAAM,CAKnG;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,MAAM,GAAG,qBAAqB,CAehF;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,qBAAqB,GAAG,SAAS,GAAG,KAAK,IAAI,YAAY,CAEpG;AAED,eAAO,MAAM,+BAA+B,UACnC,qBAAqB,GAAG,SAAS,aAC7B,gBAAgB,8BACC,yBAAyB,EAAE,GAAG,SAAS,KAClE,kBAAkB,GAAG,SA4BvB,CAAC;AAEF,eAAO,MAAM,kCAAkC,UACtC,qBAAqB,wBACN,MAAM,KAC3B,kBAQF,CAAC"}
@@ -11,18 +11,29 @@
11
11
  // See the License for the specific language governing permissions and
12
12
  // limitations under the License.
13
13
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
14
+ import { createElement as _createElement } from "react";
14
15
  import OpenInNewIcon from 'mdi-material-ui/OpenInNew';
15
- import { Select, MenuItem, Stack, Divider, ListItemText, Chip, IconButton, Box } from '@mui/material';
16
+ import { Stack, ListItemText, Chip, IconButton, Box, Autocomplete, TextField } from '@mui/material';
16
17
  import { useMemo } from 'react';
17
- import { useListDatasourceSelectItems } from '../runtime';
18
+ import { useListDatasourceSelectItems, useVariableValues } from '../runtime';
19
+ import { parseVariables } from '../utils';
20
+ const DATASOURCE_VARIABLE_VALUE_PREFIX = '__DATASOURCE_VARIABLE_VALUE__';
21
+ const VARIABLE_IDENTIFIER = '$';
22
+ const emptyDatasourceOption = {
23
+ name: '',
24
+ value: ''
25
+ };
18
26
  /**
19
27
  * Displays a MUI input for selecting a Datasource of a particular kind. Note: The 'value' and `onChange` handler for
20
28
  * the input deal with a `DatasourceSelector`.
21
29
  */ export function DatasourceSelect(props) {
22
30
  const { datasourcePluginKind, value, project, onChange, ...others } = props;
23
31
  const { data, isLoading } = useListDatasourceSelectItems(datasourcePluginKind, project);
24
- // Rebuild the group of the value if not provided
32
+ const variables = useVariableValues();
25
33
  const defaultValue = useMemo(()=>{
34
+ if (isVariableDatasource(value)) {
35
+ return value;
36
+ }
26
37
  const group = (data ?? []).flatMap((itemGroup)=>itemGroup.items).find((item)=>{
27
38
  return value.kind === item.selector.kind && value.name === item.selector.name && !item.overridden;
28
39
  })?.selector.group;
@@ -34,87 +45,112 @@ import { useListDatasourceSelectItems } from '../runtime';
34
45
  value,
35
46
  data
36
47
  ]);
37
- // Convert the datasource list into menu items with name/group?/value strings that the Select input can work with
38
- const menuItems = useMemo(()=>{
39
- return (data ?? []).map((itemGroup)=>({
40
- group: itemGroup.group,
41
- editLink: itemGroup.editLink,
42
- items: itemGroup.items.map((item)=>({
43
- name: item.name,
44
- overriding: item.overriding,
45
- overridden: item.overridden,
46
- saved: item.saved ?? true,
47
- group: item.selector.group,
48
- value: selectorToOptionValue(item.selector)
49
- }))
50
- }));
48
+ const options = useMemo(()=>{
49
+ const datasourceOptions = (data || []).flatMap((itemGroup)=>itemGroup.items.map((item)=>({
50
+ groupLabel: itemGroup.group,
51
+ groupEditLink: itemGroup.editLink,
52
+ name: item.name,
53
+ overriding: item.overriding,
54
+ overridden: item.overridden,
55
+ saved: item.saved ?? true,
56
+ group: item.selector.group,
57
+ value: selectorToOptionValue(item.selector)
58
+ })));
59
+ const datasourceOptionsMap = new Map(datasourceOptions.map((option)=>[
60
+ option.name,
61
+ option
62
+ ]));
63
+ const variableOptions = Object.entries(variables).flatMap(([name, variable])=>{
64
+ if (Array.isArray(variable.value)) return [];
65
+ const associatedDatasource = datasourceOptionsMap.get(variable.value ?? '');
66
+ if (!associatedDatasource) return [];
67
+ return {
68
+ groupLabel: 'Variables',
69
+ name: `${VARIABLE_IDENTIFIER}${name}`,
70
+ saved: true,
71
+ value: `${DATASOURCE_VARIABLE_VALUE_PREFIX}${VARIABLE_IDENTIFIER}${name}`
72
+ };
73
+ });
74
+ return [
75
+ ...datasourceOptions,
76
+ ...variableOptions
77
+ ];
51
78
  }, [
52
- data
79
+ data,
80
+ variables
53
81
  ]);
54
- // While loading available values, just use an empty string so MUI select doesn't warn about values out of range
55
- const optionValue = isLoading ? '' : selectorToOptionValue(defaultValue);
82
+ // While loading available values, just use an empty datasource option so MUI select doesn't warn about values out of range
83
+ const optionValue = isLoading ? emptyDatasourceOption : options.find((option)=>option.value === selectorToOptionValue(defaultValue));
56
84
  // When the user makes a selection, convert the string option value back to a DatasourceSelector
57
- const handleChange = (e)=>{
58
- const next = optionValueToSelector(e.target.value);
59
- onChange(next);
85
+ const handleChange = (selectedOption)=>{
86
+ if (selectedOption) {
87
+ const next = optionValueToSelector(selectedOption?.value || '');
88
+ onChange(next);
89
+ } else {
90
+ onChange({
91
+ kind: datasourcePluginKind
92
+ });
93
+ }
60
94
  };
61
95
  // We use a fake action event when we click on the action of the chip (hijack the "delete" feature).
62
96
  // This is because the href link action is on the `deleteIcon` property already, but the `onDelete` property
63
97
  // controls its visibility.
64
98
  // eslint-disable-next-line @typescript-eslint/no-empty-function
65
99
  const fakeActionEvent = ()=>{};
66
- // TODO:
67
- // - Does this need a loading indicator of some kind?
68
- // - The group's edit link is not clickable once selected.
69
- // - The group's edit link is disabled if datasource is overridden.
70
- // Ref: https://github.com/mui/material-ui/issues/36572
71
- return /*#__PURE__*/ _jsx(Select, {
72
- ...others,
100
+ return /*#__PURE__*/ _jsx(Autocomplete, {
101
+ options: options,
102
+ renderInput: (params)=>/*#__PURE__*/ _jsx(TextField, {
103
+ ...params,
104
+ label: others.label,
105
+ placeholder: ""
106
+ }),
107
+ groupBy: (option)=>option.groupLabel || 'No group',
108
+ getOptionLabel: (option)=>{
109
+ return option.name;
110
+ },
111
+ onChange: (_, v)=>handleChange(v),
73
112
  value: optionValue,
74
- onChange: handleChange,
75
- children: menuItems.map((menuItemGroup)=>[
76
- /*#__PURE__*/ _jsx(Divider, {}, `${menuItemGroup.group}-divider`),
77
- ...menuItemGroup.items.map((menuItem)=>/*#__PURE__*/ _jsx(MenuItem, {
78
- value: menuItem.value,
79
- disabled: menuItem.overridden || !menuItem.saved,
80
- children: /*#__PURE__*/ _jsxs(Stack, {
81
- direction: "row",
82
- alignItems: "center",
83
- justifyContent: "space-between",
84
- width: "100%",
85
- children: [
86
- /*#__PURE__*/ _jsx(ListItemText, {
87
- children: /*#__PURE__*/ _jsx(DatasourceName, {
88
- name: menuItem.name,
89
- overridden: menuItem.overridden,
90
- overriding: menuItem.overriding
91
- })
92
- }),
93
- !menuItem.saved && /*#__PURE__*/ _jsx(ListItemText, {
94
- children: "Save the dashboard to enable this datasource"
95
- }),
96
- /*#__PURE__*/ _jsx(ListItemText, {
97
- style: {
98
- textAlign: 'right'
99
- },
100
- children: menuItemGroup.group && menuItemGroup.group.length > 0 && /*#__PURE__*/ _jsx(Chip, {
101
- disabled: false,
102
- label: menuItemGroup.group,
103
- size: "small",
104
- onDelete: menuItemGroup.editLink ? fakeActionEvent : undefined,
105
- deleteIcon: menuItemGroup.editLink ? /*#__PURE__*/ _jsx(IconButton, {
106
- href: menuItemGroup.editLink,
107
- target: "_blank",
108
- children: /*#__PURE__*/ _jsx(OpenInNewIcon, {
109
- fontSize: "small"
110
- })
111
- }) : undefined
112
- })
113
+ renderOption: (props, option)=>{
114
+ return /*#__PURE__*/ _createElement("li", {
115
+ ...props,
116
+ key: option.value
117
+ }, /*#__PURE__*/ _jsxs(Stack, {
118
+ direction: "row",
119
+ alignItems: "center",
120
+ justifyContent: "space-between",
121
+ width: "100%",
122
+ children: [
123
+ /*#__PURE__*/ _jsx(ListItemText, {
124
+ children: /*#__PURE__*/ _jsx(DatasourceName, {
125
+ name: option.name,
126
+ overridden: option.overridden,
127
+ overriding: option.overriding
128
+ })
129
+ }),
130
+ !option.saved && /*#__PURE__*/ _jsx(ListItemText, {
131
+ children: "Save the dashboard to enable this datasource"
132
+ }),
133
+ /*#__PURE__*/ _jsx(ListItemText, {
134
+ style: {
135
+ textAlign: 'right'
136
+ },
137
+ children: option.groupLabel && option.groupLabel.length > 0 && /*#__PURE__*/ _jsx(Chip, {
138
+ disabled: false,
139
+ label: option.groupLabel,
140
+ size: "small",
141
+ onDelete: option.groupEditLink ? fakeActionEvent : undefined,
142
+ deleteIcon: option.groupEditLink ? /*#__PURE__*/ _jsx(IconButton, {
143
+ href: option.groupEditLink,
144
+ target: "_blank",
145
+ children: /*#__PURE__*/ _jsx(OpenInNewIcon, {
146
+ fontSize: "small"
113
147
  })
114
- ]
148
+ }) : undefined
115
149
  })
116
- }, menuItem.value))
117
- ])
150
+ })
151
+ ]
152
+ }));
153
+ }
118
154
  });
119
155
  }
120
156
  export function DatasourceName(props) {
@@ -138,7 +174,10 @@ const OPTION_VALUE_DELIMITER = '_____';
138
174
  * Given a DatasourceSelectItemSelector,
139
175
  * returns a string value like `{kind}_____{group}_____{name}` that can be used as a Select input value.
140
176
  * @param selector
141
- */ function selectorToOptionValue(selector) {
177
+ */ export function selectorToOptionValue(selector) {
178
+ if (isVariableDatasource(selector)) {
179
+ return `${DATASOURCE_VARIABLE_VALUE_PREFIX}${selector}`;
180
+ }
142
181
  return [
143
182
  selector.kind,
144
183
  selector.group ?? '',
@@ -149,7 +188,10 @@ const OPTION_VALUE_DELIMITER = '_____';
149
188
  * Given an option value name like `{kind}_____{group}_____{name}`,
150
189
  * returns a DatasourceSelector to be used by the query data model.
151
190
  * @param optionValue
152
- */ function optionValueToSelector(optionValue) {
191
+ */ export function optionValueToSelector(optionValue) {
192
+ if (optionValue.startsWith(DATASOURCE_VARIABLE_VALUE_PREFIX)) {
193
+ return optionValue.split(DATASOURCE_VARIABLE_VALUE_PREFIX)[1];
194
+ }
153
195
  const words = optionValue.split(OPTION_VALUE_DELIMITER);
154
196
  const kind = words[0];
155
197
  const name = words[2];
@@ -161,5 +203,39 @@ const OPTION_VALUE_DELIMITER = '_____';
161
203
  name: name === '' ? undefined : name
162
204
  };
163
205
  }
206
+ export function isVariableDatasource(value) {
207
+ return typeof value === 'string' && value.startsWith(VARIABLE_IDENTIFIER);
208
+ }
209
+ export const datasourceSelectValueToSelector = (value, variables, datasourceSelectItemGroups)=>{
210
+ if (!isVariableDatasource(value)) {
211
+ return value;
212
+ }
213
+ const [variableName] = parseVariables(value);
214
+ const variable = variables[variableName ?? ''];
215
+ // If the variable is not defined or if its value is an array, we cannot determine a selector and return undefined
216
+ if (!variable || Array.isArray(variable.value)) {
217
+ return undefined;
218
+ }
219
+ const associatedDatasource = (datasourceSelectItemGroups || []).flatMap((itemGroup)=>itemGroup.items).find((datasource)=>datasource.name === variable.value);
220
+ // If the variable value is not a datasource, we cannot determine a selector and return undefined
221
+ if (associatedDatasource === undefined) {
222
+ return undefined;
223
+ }
224
+ const datasourceSelector = {
225
+ kind: associatedDatasource.selector.kind,
226
+ name: associatedDatasource.selector.name
227
+ };
228
+ return datasourceSelector;
229
+ };
230
+ export const useDatasourceSelectValueToSelector = (value, datasourcePluginKind)=>{
231
+ const { data } = useListDatasourceSelectItems(datasourcePluginKind);
232
+ const variables = useVariableValues();
233
+ if (!isVariableDatasource(value)) {
234
+ return value;
235
+ }
236
+ return datasourceSelectValueToSelector(value, variables, data) ?? {
237
+ kind: datasourcePluginKind
238
+ };
239
+ };
164
240
 
165
241
  //# sourceMappingURL=DatasourceSelect.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/DatasourceSelect.tsx"],"sourcesContent":["// Copyright 2023 The Perses Authors\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport OpenInNewIcon from 'mdi-material-ui/OpenInNew';\nimport {\n Select,\n SelectProps,\n MenuItem,\n Stack,\n Divider,\n ListItemText,\n Chip,\n IconButton,\n Box,\n OutlinedSelectProps,\n BaseSelectProps,\n} from '@mui/material';\nimport { DatasourceSelector } from '@perses-dev/core';\nimport { ReactElement, useMemo } from 'react';\nimport { DatasourceSelectItemSelector, useListDatasourceSelectItems } from '../runtime';\n\n// Props on MUI Select that we don't want people to pass because we're either redefining them or providing them in\n// this component\ntype OmittedMuiProps = 'children' | 'value' | 'onChange';\n\nexport interface DatasourceSelectProps extends Omit<OutlinedSelectProps & BaseSelectProps<string>, OmittedMuiProps> {\n value: DatasourceSelector;\n onChange: (next: DatasourceSelector) => void;\n datasourcePluginKind: string;\n project?: string;\n}\n\n/**\n * Displays a MUI input for selecting a Datasource of a particular kind. Note: The 'value' and `onChange` handler for\n * the input deal with a `DatasourceSelector`.\n */\nexport function DatasourceSelect(props: DatasourceSelectProps): ReactElement {\n const { datasourcePluginKind, value, project, onChange, ...others } = props;\n const { data, isLoading } = useListDatasourceSelectItems(datasourcePluginKind, project);\n // Rebuild the group of the value if not provided\n const defaultValue = useMemo(() => {\n const group = (data ?? [])\n .flatMap((itemGroup) => itemGroup.items)\n .find((item) => {\n return value.kind === item.selector.kind && value.name === item.selector.name && !item.overridden;\n })?.selector.group;\n return { ...value, group };\n }, [value, data]);\n\n // Convert the datasource list into menu items with name/group?/value strings that the Select input can work with\n const menuItems = useMemo(() => {\n return (data ?? []).map((itemGroup) => ({\n group: itemGroup.group,\n editLink: itemGroup.editLink,\n items: itemGroup.items.map((item) => ({\n name: item.name,\n overriding: item.overriding,\n overridden: item.overridden,\n saved: item.saved ?? true,\n group: item.selector.group,\n value: selectorToOptionValue(item.selector),\n })),\n }));\n }, [data]);\n\n // While loading available values, just use an empty string so MUI select doesn't warn about values out of range\n const optionValue = isLoading ? '' : selectorToOptionValue(defaultValue);\n\n // When the user makes a selection, convert the string option value back to a DatasourceSelector\n const handleChange: SelectProps<string>['onChange'] = (e) => {\n const next = optionValueToSelector(e.target.value);\n onChange(next);\n };\n\n // We use a fake action event when we click on the action of the chip (hijack the \"delete\" feature).\n // This is because the href link action is on the `deleteIcon` property already, but the `onDelete` property\n // controls its visibility.\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n const fakeActionEvent = (): void => {};\n\n // TODO:\n // - Does this need a loading indicator of some kind?\n // - The group's edit link is not clickable once selected.\n // - The group's edit link is disabled if datasource is overridden.\n // Ref: https://github.com/mui/material-ui/issues/36572\n return (\n <Select {...others} value={optionValue} onChange={handleChange}>\n {menuItems.map((menuItemGroup) => [\n <Divider key={`${menuItemGroup.group}-divider`} />,\n ...menuItemGroup.items.map((menuItem) => (\n <MenuItem key={menuItem.value} value={menuItem.value} disabled={menuItem.overridden || !menuItem.saved}>\n <Stack direction=\"row\" alignItems=\"center\" justifyContent=\"space-between\" width=\"100%\">\n <ListItemText>\n <DatasourceName\n name={menuItem.name}\n overridden={menuItem.overridden}\n overriding={menuItem.overriding}\n />\n </ListItemText>\n {!menuItem.saved && <ListItemText>Save the dashboard to enable this datasource</ListItemText>}\n <ListItemText style={{ textAlign: 'right' }}>\n {menuItemGroup.group && menuItemGroup.group.length > 0 && (\n <Chip\n disabled={false}\n label={menuItemGroup.group}\n size=\"small\"\n onDelete={menuItemGroup.editLink ? fakeActionEvent : undefined}\n deleteIcon={\n menuItemGroup.editLink ? (\n <IconButton href={menuItemGroup.editLink} target=\"_blank\">\n <OpenInNewIcon fontSize=\"small\" />\n </IconButton>\n ) : undefined\n }\n />\n )}\n </ListItemText>\n </Stack>\n </MenuItem>\n )),\n ])}\n </Select>\n );\n}\n\nexport function DatasourceName(props: { name: string; overridden?: boolean; overriding?: boolean }): ReactElement {\n const { name, overridden, overriding } = props;\n return (\n <>\n {`${name} `}\n {!overridden && overriding && (\n <Box display=\"inline\" fontWeight=\"normal\" color={(theme) => theme.palette.primary.main}>\n (overriding)\n </Box>\n )}\n {overridden && '(overridden)'}\n </>\n );\n}\n\n// Delimiter used to stringify/parse option values\nconst OPTION_VALUE_DELIMITER = '_____';\n\n/**\n * Given a DatasourceSelectItemSelector,\n * returns a string value like `{kind}_____{group}_____{name}` that can be used as a Select input value.\n * @param selector\n */\nfunction selectorToOptionValue(selector: DatasourceSelectItemSelector): string {\n return [selector.kind, selector.group ?? '', selector.name ?? ''].join(OPTION_VALUE_DELIMITER);\n}\n\n/**\n * Given an option value name like `{kind}_____{group}_____{name}`,\n * returns a DatasourceSelector to be used by the query data model.\n * @param optionValue\n */\nfunction optionValueToSelector(optionValue: string): DatasourceSelector {\n const words = optionValue.split(OPTION_VALUE_DELIMITER);\n const kind = words[0];\n const name = words[2];\n if (kind === undefined || name === undefined) {\n throw new Error('Invalid optionValue string');\n }\n return {\n kind,\n name: name === '' ? undefined : name,\n };\n}\n"],"names":["OpenInNewIcon","Select","MenuItem","Stack","Divider","ListItemText","Chip","IconButton","Box","useMemo","useListDatasourceSelectItems","DatasourceSelect","props","datasourcePluginKind","value","project","onChange","others","data","isLoading","defaultValue","group","flatMap","itemGroup","items","find","item","kind","selector","name","overridden","menuItems","map","editLink","overriding","saved","selectorToOptionValue","optionValue","handleChange","e","next","optionValueToSelector","target","fakeActionEvent","menuItemGroup","menuItem","disabled","direction","alignItems","justifyContent","width","DatasourceName","style","textAlign","length","label","size","onDelete","undefined","deleteIcon","href","fontSize","display","fontWeight","color","theme","palette","primary","main","OPTION_VALUE_DELIMITER","join","words","split","Error"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/components/DatasourceSelect.tsx"],"sourcesContent":["// Copyright 2023 The Perses Authors\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport OpenInNewIcon from 'mdi-material-ui/OpenInNew';\nimport {\n Stack,\n ListItemText,\n Chip,\n IconButton,\n Box,\n OutlinedSelectProps,\n BaseSelectProps,\n Autocomplete,\n TextField,\n} from '@mui/material';\nimport { DatasourceSelector, VariableName } from '@perses-dev/core';\nimport { ReactElement, useMemo } from 'react';\nimport {\n DatasourceSelectItem,\n DatasourceSelectItemGroup,\n DatasourceSelectItemSelector,\n useListDatasourceSelectItems,\n useVariableValues,\n VariableStateMap,\n} from '../runtime';\nimport { parseVariables } from '../utils';\n\nconst DATASOURCE_VARIABLE_VALUE_PREFIX = '__DATASOURCE_VARIABLE_VALUE__';\nconst VARIABLE_IDENTIFIER = '$';\n// Props on MUI Select that we don't want people to pass because we're either redefining them or providing them in\n// this component\ntype OmittedMuiProps = 'children' | 'value' | 'onChange';\n\ntype DataSourceOption = {\n groupEditLink?: string;\n groupLabel?: string;\n value: string;\n} & Omit<DatasourceSelectItem, 'selector'> &\n Omit<DatasourceSelectItem['selector'], 'kind'>;\n\nconst emptyDatasourceOption: DataSourceOption = { name: '', value: '' };\n\nexport type DatasourceSelectValue<T = DatasourceSelector> = T | VariableName;\n\nexport interface DatasourceSelectProps extends Omit<OutlinedSelectProps & BaseSelectProps<string>, OmittedMuiProps> {\n value: DatasourceSelectValue;\n onChange: (next: DatasourceSelectValue) => void;\n datasourcePluginKind: string;\n project?: string;\n}\n\n/**\n * Displays a MUI input for selecting a Datasource of a particular kind. Note: The 'value' and `onChange` handler for\n * the input deal with a `DatasourceSelector`.\n */\nexport function DatasourceSelect(props: DatasourceSelectProps): ReactElement {\n const { datasourcePluginKind, value, project, onChange, ...others } = props;\n const { data, isLoading } = useListDatasourceSelectItems(datasourcePluginKind, project);\n const variables = useVariableValues();\n\n const defaultValue = useMemo<VariableName | DatasourceSelectItemSelector>(() => {\n if (isVariableDatasource(value)) {\n return value;\n }\n\n const group = (data ?? [])\n .flatMap((itemGroup) => itemGroup.items)\n .find((item) => {\n return value.kind === item.selector.kind && value.name === item.selector.name && !item.overridden;\n })?.selector.group;\n return { ...value, group };\n }, [value, data]);\n\n const options = useMemo<DataSourceOption[]>(() => {\n const datasourceOptions = (data || []).flatMap<DataSourceOption>((itemGroup) =>\n itemGroup.items.map<DataSourceOption>((item) => ({\n groupLabel: itemGroup.group,\n groupEditLink: itemGroup.editLink,\n name: item.name,\n overriding: item.overriding,\n overridden: item.overridden,\n saved: item.saved ?? true,\n group: item.selector.group,\n value: selectorToOptionValue(item.selector),\n }))\n );\n\n const datasourceOptionsMap = new Map(datasourceOptions.map((option) => [option.name, option]));\n\n const variableOptions = Object.entries(variables).flatMap<DataSourceOption>(([name, variable]) => {\n if (Array.isArray(variable.value)) return [];\n\n const associatedDatasource = datasourceOptionsMap.get(variable.value ?? '');\n if (!associatedDatasource) return [];\n\n return {\n groupLabel: 'Variables',\n name: `${VARIABLE_IDENTIFIER}${name}`,\n saved: true,\n value: `${DATASOURCE_VARIABLE_VALUE_PREFIX}${VARIABLE_IDENTIFIER}${name}`,\n };\n });\n\n return [...datasourceOptions, ...variableOptions];\n }, [data, variables]);\n\n // While loading available values, just use an empty datasource option so MUI select doesn't warn about values out of range\n const optionValue = isLoading\n ? emptyDatasourceOption\n : options.find((option) => option.value === selectorToOptionValue(defaultValue));\n\n // When the user makes a selection, convert the string option value back to a DatasourceSelector\n const handleChange = (selectedOption: DataSourceOption | null): void => {\n if (selectedOption) {\n const next = optionValueToSelector(selectedOption?.value || '');\n onChange(next);\n } else {\n onChange({ kind: datasourcePluginKind });\n }\n };\n\n // We use a fake action event when we click on the action of the chip (hijack the \"delete\" feature).\n // This is because the href link action is on the `deleteIcon` property already, but the `onDelete` property\n // controls its visibility.\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n const fakeActionEvent = (): void => {};\n\n return (\n <Autocomplete<DataSourceOption>\n options={options}\n renderInput={(params) => <TextField {...params} label={others.label} placeholder=\"\" />}\n groupBy={(option) => option.groupLabel || 'No group'}\n getOptionLabel={(option) => {\n return option.name;\n }}\n onChange={(_, v) => handleChange(v)}\n value={optionValue}\n renderOption={(props, option) => {\n return (\n <li {...props} key={option.value}>\n <Stack direction=\"row\" alignItems=\"center\" justifyContent=\"space-between\" width=\"100%\">\n <ListItemText>\n <DatasourceName name={option.name} overridden={option.overridden} overriding={option.overriding} />\n </ListItemText>\n {!option.saved && <ListItemText>Save the dashboard to enable this datasource</ListItemText>}\n <ListItemText style={{ textAlign: 'right' }}>\n {option.groupLabel && option.groupLabel.length > 0 && (\n <Chip\n disabled={false}\n label={option.groupLabel}\n size=\"small\"\n onDelete={option.groupEditLink ? fakeActionEvent : undefined}\n deleteIcon={\n option.groupEditLink ? (\n <IconButton href={option.groupEditLink} target=\"_blank\">\n <OpenInNewIcon fontSize=\"small\" />\n </IconButton>\n ) : undefined\n }\n />\n )}\n </ListItemText>\n </Stack>\n </li>\n );\n }}\n />\n );\n}\n\nexport function DatasourceName(props: { name: string; overridden?: boolean; overriding?: boolean }): ReactElement {\n const { name, overridden, overriding } = props;\n return (\n <>\n {`${name} `}\n {!overridden && overriding && (\n <Box display=\"inline\" fontWeight=\"normal\" color={(theme) => theme.palette.primary.main}>\n (overriding)\n </Box>\n )}\n {overridden && '(overridden)'}\n </>\n );\n}\n\n// Delimiter used to stringify/parse option values\nconst OPTION_VALUE_DELIMITER = '_____';\n\n/**\n * Given a DatasourceSelectItemSelector,\n * returns a string value like `{kind}_____{group}_____{name}` that can be used as a Select input value.\n * @param selector\n */\nexport function selectorToOptionValue(selector: DatasourceSelectItemSelector | VariableName): string {\n if (isVariableDatasource(selector)) {\n return `${DATASOURCE_VARIABLE_VALUE_PREFIX}${selector}`;\n }\n return [selector.kind, selector.group ?? '', selector.name ?? ''].join(OPTION_VALUE_DELIMITER);\n}\n\n/**\n * Given an option value name like `{kind}_____{group}_____{name}`,\n * returns a DatasourceSelector to be used by the query data model.\n * @param optionValue\n */\nexport function optionValueToSelector(optionValue: string): DatasourceSelectValue {\n if (optionValue.startsWith(DATASOURCE_VARIABLE_VALUE_PREFIX)) {\n return optionValue.split(DATASOURCE_VARIABLE_VALUE_PREFIX)[1]!;\n }\n\n const words = optionValue.split(OPTION_VALUE_DELIMITER);\n const kind = words[0];\n const name = words[2];\n if (kind === undefined || name === undefined) {\n throw new Error('Invalid optionValue string');\n }\n return {\n kind,\n name: name === '' ? undefined : name,\n };\n}\n\nexport function isVariableDatasource(value: DatasourceSelectValue | undefined): value is VariableName {\n return typeof value === 'string' && value.startsWith(VARIABLE_IDENTIFIER);\n}\n\nexport const datasourceSelectValueToSelector = (\n value: DatasourceSelectValue | undefined,\n variables: VariableStateMap,\n datasourceSelectItemGroups: DatasourceSelectItemGroup[] | undefined\n): DatasourceSelector | undefined => {\n if (!isVariableDatasource(value)) {\n return value;\n }\n\n const [variableName] = parseVariables(value);\n const variable = variables[variableName ?? ''];\n\n // If the variable is not defined or if its value is an array, we cannot determine a selector and return undefined\n if (!variable || Array.isArray(variable.value)) {\n return undefined;\n }\n\n const associatedDatasource = (datasourceSelectItemGroups || [])\n .flatMap((itemGroup) => itemGroup.items)\n .find((datasource) => datasource.name === variable.value);\n\n // If the variable value is not a datasource, we cannot determine a selector and return undefined\n if (associatedDatasource === undefined) {\n return undefined;\n }\n\n const datasourceSelector: DatasourceSelector = {\n kind: associatedDatasource.selector.kind,\n name: associatedDatasource.selector.name,\n };\n\n return datasourceSelector;\n};\n\nexport const useDatasourceSelectValueToSelector = (\n value: DatasourceSelectValue,\n datasourcePluginKind: string\n): DatasourceSelector => {\n const { data } = useListDatasourceSelectItems(datasourcePluginKind);\n const variables = useVariableValues();\n if (!isVariableDatasource(value)) {\n return value;\n }\n\n return datasourceSelectValueToSelector(value, variables, data) ?? { kind: datasourcePluginKind };\n};\n"],"names":["OpenInNewIcon","Stack","ListItemText","Chip","IconButton","Box","Autocomplete","TextField","useMemo","useListDatasourceSelectItems","useVariableValues","parseVariables","DATASOURCE_VARIABLE_VALUE_PREFIX","VARIABLE_IDENTIFIER","emptyDatasourceOption","name","value","DatasourceSelect","props","datasourcePluginKind","project","onChange","others","data","isLoading","variables","defaultValue","isVariableDatasource","group","flatMap","itemGroup","items","find","item","kind","selector","overridden","options","datasourceOptions","map","groupLabel","groupEditLink","editLink","overriding","saved","selectorToOptionValue","datasourceOptionsMap","Map","option","variableOptions","Object","entries","variable","Array","isArray","associatedDatasource","get","optionValue","handleChange","selectedOption","next","optionValueToSelector","fakeActionEvent","renderInput","params","label","placeholder","groupBy","getOptionLabel","_","v","renderOption","li","key","direction","alignItems","justifyContent","width","DatasourceName","style","textAlign","length","disabled","size","onDelete","undefined","deleteIcon","href","target","fontSize","display","fontWeight","color","theme","palette","primary","main","OPTION_VALUE_DELIMITER","join","startsWith","split","words","Error","datasourceSelectValueToSelector","datasourceSelectItemGroups","variableName","datasource","datasourceSelector","useDatasourceSelectValueToSelector"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"HTTPSettingsEditor.d.ts","sourceRoot":"","sources":["../../../src/components/HTTPSettingsEditor/HTTPSettingsEditor.tsx"],"names":[],"mappings":"AAaA,OAAO,EAAkB,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAEtE,OAAc,EAAY,YAAY,EAAY,MAAM,OAAO,CAAC;AAOhE,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,kBAAkB,CAAC;IAC1B,QAAQ,EAAE,CAAC,IAAI,EAAE,kBAAkB,KAAK,IAAI,CAAC;IAC7C,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,iBAAiB,EAAE,kBAAkB,CAAC;IACtC,gBAAgB,EAAE,kBAAkB,CAAC;CACtC;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,kBAAkB,GAAG,YAAY,CAwb1E"}
1
+ {"version":3,"file":"HTTPSettingsEditor.d.ts","sourceRoot":"","sources":["../../../src/components/HTTPSettingsEditor/HTTPSettingsEditor.tsx"],"names":[],"mappings":"AAaA,OAAO,EAAkB,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAEtE,OAAc,EAAY,YAAY,EAAY,MAAM,OAAO,CAAC;AAOhE,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,kBAAkB,CAAC;IAC1B,QAAQ,EAAE,CAAC,IAAI,EAAE,kBAAkB,KAAK,IAAI,CAAC;IAC7C,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,iBAAiB,EAAE,kBAAkB,CAAC;IACtC,gBAAgB,EAAE,kBAAkB,CAAC;CACtC;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,kBAAkB,GAAG,YAAY,CA4b1E"}
@@ -22,6 +22,10 @@ export function HTTPSettingsEditor(props) {
22
22
  const { value, onChange, isReadonly, initialSpecDirect, initialSpecProxy } = props;
23
23
  const strDirect = 'Direct access';
24
24
  const strProxy = 'Proxy';
25
+ // Initialize Proxy mode by default, if neither direct nor proxy mode is selected.
26
+ if (!value.directUrl && !value.proxy) {
27
+ Object.assign(value, initialSpecProxy);
28
+ }
25
29
  // utilitary function used for headers when renaming a property
26
30
  // -> TODO it would be cleaner to manipulate headers as an intermediary list instead, to avoid doing this.
27
31
  const buildNewHeaders = (oldHeaders, oldName, newName)=>{
@@ -431,8 +435,7 @@ export function HTTPSettingsEditor(props) {
431
435
  // bug in case the tabs get eventually swapped in the future.
432
436
  const directModeId = tabs.findIndex((tab)=>tab.label === strDirect);
433
437
  const proxyModeId = tabs.findIndex((tab)=>tab.label === strProxy);
434
- // In "update datasource" case, set defaultTab to the mode that this datasource is currently relying on.
435
- // Otherwise (create datasource), set defaultTab to Direct access.
438
+ // Set defaultTab to the mode that this datasource is currently relying on.
436
439
  const defaultTab = value.proxy ? proxyModeId : directModeId;
437
440
  // For better user experience, save previous states in mind for both mode.
438
441
  // This avoids losing everything when the user changes their mind back.