@grafana/scenes 6.50.0--canary.1316.20298598344.0 → 6.51.0--canary.1316.20309633325.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 (28) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/esm/packages/scenes/src/index.js +1 -1
  3. package/dist/esm/packages/scenes/src/index.js.map +1 -1
  4. package/dist/esm/packages/scenes/src/locales/en-US/grafana-scenes.json.js +7 -0
  5. package/dist/esm/packages/scenes/src/locales/en-US/grafana-scenes.json.js.map +1 -1
  6. package/dist/esm/packages/scenes/src/variables/DrilldownDependenciesManager.js.map +1 -1
  7. package/dist/esm/packages/scenes/src/variables/adhoc/AdHocFiltersCombobox/AdHocFiltersCombobox.js +3 -0
  8. package/dist/esm/packages/scenes/src/variables/adhoc/AdHocFiltersCombobox/AdHocFiltersCombobox.js.map +1 -1
  9. package/dist/esm/packages/scenes/src/variables/adhoc/AdHocFiltersCombobox/AdHocFiltersComboboxRenderer.js +2 -1
  10. package/dist/esm/packages/scenes/src/variables/adhoc/AdHocFiltersCombobox/AdHocFiltersComboboxRenderer.js.map +1 -1
  11. package/dist/esm/packages/scenes/src/variables/adhoc/AdHocFiltersRecommendations.js +154 -0
  12. package/dist/esm/packages/scenes/src/variables/adhoc/AdHocFiltersRecommendations.js.map +1 -0
  13. package/dist/esm/packages/scenes/src/variables/adhoc/AdHocFiltersVariable.js +33 -9
  14. package/dist/esm/packages/scenes/src/variables/adhoc/AdHocFiltersVariable.js.map +1 -1
  15. package/dist/esm/packages/scenes/src/variables/adhoc/controller/AdHocFiltersVariableController.js +6 -1
  16. package/dist/esm/packages/scenes/src/variables/adhoc/controller/AdHocFiltersVariableController.js.map +1 -1
  17. package/dist/esm/packages/scenes/src/variables/components/DrilldownRecommendations.js +102 -0
  18. package/dist/esm/packages/scenes/src/variables/components/DrilldownRecommendations.js.map +1 -0
  19. package/dist/esm/packages/scenes/src/variables/groupby/GroupByRecommendations.js +169 -0
  20. package/dist/esm/packages/scenes/src/variables/groupby/GroupByRecommendations.js.map +1 -0
  21. package/dist/esm/packages/scenes/src/variables/groupby/GroupByVariable.js +96 -35
  22. package/dist/esm/packages/scenes/src/variables/groupby/GroupByVariable.js.map +1 -1
  23. package/dist/{grafana-scenes-Dml42t8G.js → grafana-scenes-DrGXYYn6.js} +8 -1
  24. package/dist/{grafana-scenes-Dml42t8G.js.map → grafana-scenes-DrGXYYn6.js.map} +1 -1
  25. package/dist/index.d.ts +93 -2
  26. package/dist/index.js +5267 -4788
  27. package/dist/index.js.map +1 -1
  28. package/package.json +2 -2
@@ -0,0 +1,102 @@
1
+ import { useStyles2, ClickOutsideWrapper, Stack, Text, IconButton, Popover } from '@grafana/ui';
2
+ import React, { useState, useRef } from 'react';
3
+ import { css, cx } from '@emotion/css';
4
+ import { Trans, t } from '@grafana/i18n';
5
+
6
+ function DrilldownRecommendations({ recentDrilldowns, recommendedDrilldowns }) {
7
+ const styles = useStyles2(getStyles);
8
+ const [isPopoverVisible, setPopoverVisible] = useState(false);
9
+ const ref = useRef(null);
10
+ const openPopover = () => {
11
+ setPopoverVisible(true);
12
+ };
13
+ const onClickAction = (action) => {
14
+ action();
15
+ setPopoverVisible(false);
16
+ };
17
+ const content = /* @__PURE__ */ React.createElement(ClickOutsideWrapper, { onClick: () => setPopoverVisible(false), useCapture: true }, /* @__PURE__ */ React.createElement("div", { className: styles.menuContainer, onClick: (ev) => ev.stopPropagation() }, /* @__PURE__ */ React.createElement(Stack, { direction: "column" }, /* @__PURE__ */ React.createElement(Text, { weight: "bold", variant: "bodySmall", color: "secondary" }, /* @__PURE__ */ React.createElement(Trans, { i18nKey: "grafana-scenes.components.drilldown-recommendations.recent" }, "Recent")), recentDrilldowns && recentDrilldowns.length > 0 ? recentDrilldowns.map((drilldown) => /* @__PURE__ */ React.createElement(
18
+ "div",
19
+ {
20
+ key: drilldown.label,
21
+ className: cx(styles.combinedFilterPill),
22
+ onClick: () => onClickAction(drilldown.onClick)
23
+ },
24
+ drilldown.label
25
+ )) : /* @__PURE__ */ React.createElement("div", { className: styles.emptyMessage }, /* @__PURE__ */ React.createElement(Trans, { i18nKey: "grafana-scenes.components.drilldown-recommendations.recent-empty" }, "No recent values")), /* @__PURE__ */ React.createElement(Text, { weight: "bold", variant: "bodySmall", color: "secondary" }, /* @__PURE__ */ React.createElement(Trans, { i18nKey: "grafana-scenes.components.drilldown-recommendations.recommended" }, "Recommended")), recommendedDrilldowns && recommendedDrilldowns.length > 0 ? recommendedDrilldowns.map((drilldown) => /* @__PURE__ */ React.createElement(
26
+ "div",
27
+ {
28
+ key: drilldown.label,
29
+ className: cx(styles.combinedFilterPill),
30
+ onClick: () => onClickAction(drilldown.onClick)
31
+ },
32
+ drilldown.label
33
+ )) : /* @__PURE__ */ React.createElement("div", { className: styles.emptyMessage }, /* @__PURE__ */ React.createElement(Trans, { i18nKey: "grafana-scenes.components.drilldown-recommendations.recommended-empty" }, "No recommended values")))));
34
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
35
+ IconButton,
36
+ {
37
+ name: "plus",
38
+ tooltip: t("grafana-scenes.components.drilldown-recommendations.tooltip", "Show recommendations"),
39
+ ref,
40
+ className: cx(isPopoverVisible && styles.iconActive),
41
+ onClick: (ev) => {
42
+ openPopover();
43
+ ev.stopPropagation();
44
+ }
45
+ }
46
+ ), isPopoverVisible && ref.current && /* @__PURE__ */ React.createElement(
47
+ Popover,
48
+ {
49
+ content,
50
+ onKeyDown: (event) => {
51
+ if (event.key === " ") {
52
+ event.stopPropagation();
53
+ }
54
+ },
55
+ placement: "bottom-start",
56
+ referenceElement: ref.current,
57
+ show: true
58
+ }
59
+ ));
60
+ }
61
+ const getStyles = (theme) => ({
62
+ menuContainer: css({
63
+ display: "flex",
64
+ flexDirection: "column",
65
+ background: theme.colors.background.elevated,
66
+ border: `1px solid ${theme.colors.border.weak}`,
67
+ borderRadius: theme.shape.radius.default,
68
+ boxShadow: theme.shadows.z3,
69
+ padding: theme.spacing(2)
70
+ }),
71
+ combinedFilterPill: css({
72
+ alignItems: "center",
73
+ background: theme.colors.action.selected,
74
+ borderRadius: theme.shape.radius.default,
75
+ border: `1px solid ${theme.colors.border.weak}`,
76
+ padding: theme.spacing(0.2, 1),
77
+ color: theme.colors.text.primary,
78
+ overflow: "hidden",
79
+ whiteSpace: "nowrap",
80
+ minHeight: theme.spacing(2.75),
81
+ ...theme.typography.bodySmall,
82
+ fontWeight: theme.typography.fontWeightBold,
83
+ cursor: "pointer",
84
+ "&:hover": {
85
+ background: theme.colors.action.hover
86
+ }
87
+ }),
88
+ iconActive: css({
89
+ "&:before": {
90
+ backgroundColor: theme.colors.action.hover,
91
+ opacity: 1
92
+ }
93
+ }),
94
+ emptyMessage: css({
95
+ padding: theme.spacing(0.5, 0),
96
+ color: theme.colors.text.secondary,
97
+ ...theme.typography.bodySmall
98
+ })
99
+ });
100
+
101
+ export { DrilldownRecommendations };
102
+ //# sourceMappingURL=DrilldownRecommendations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DrilldownRecommendations.js","sources":["../../../../../../../src/variables/components/DrilldownRecommendations.tsx"],"sourcesContent":["import { ClickOutsideWrapper, IconButton, Popover, Stack, Text, useStyles2 } from '@grafana/ui';\nimport React, { useRef, useState } from 'react';\nimport { GrafanaTheme2 } from '@grafana/data';\nimport { css, cx } from '@emotion/css';\nimport { Trans, t } from '@grafana/i18n';\n\nexport interface DrilldownPill {\n label: string;\n onClick: () => void;\n}\n\ninterface Props {\n recentDrilldowns?: DrilldownPill[];\n recommendedDrilldowns?: DrilldownPill[];\n}\n\nexport function DrilldownRecommendations({ recentDrilldowns, recommendedDrilldowns }: Props) {\n const styles = useStyles2(getStyles);\n\n const [isPopoverVisible, setPopoverVisible] = useState<boolean>(false);\n const ref = useRef<HTMLButtonElement>(null);\n\n const openPopover = () => {\n setPopoverVisible(true);\n };\n\n const onClickAction = (action: () => void) => {\n action();\n setPopoverVisible(false);\n };\n\n const content = (\n <ClickOutsideWrapper onClick={() => setPopoverVisible(false)} useCapture={true}>\n <div className={styles.menuContainer} onClick={(ev) => ev.stopPropagation()}>\n <Stack direction=\"column\">\n <Text weight=\"bold\" variant=\"bodySmall\" color=\"secondary\">\n <Trans i18nKey=\"grafana-scenes.components.drilldown-recommendations.recent\">Recent</Trans>\n </Text>\n {recentDrilldowns && recentDrilldowns.length > 0 ? (\n recentDrilldowns.map((drilldown) => (\n <div\n key={drilldown.label}\n className={cx(styles.combinedFilterPill)}\n onClick={() => onClickAction(drilldown.onClick)}\n >\n {drilldown.label}\n </div>\n ))\n ) : (\n <div className={styles.emptyMessage}>\n <Trans i18nKey=\"grafana-scenes.components.drilldown-recommendations.recent-empty\">No recent values</Trans>\n </div>\n )}\n <Text weight=\"bold\" variant=\"bodySmall\" color=\"secondary\">\n <Trans i18nKey=\"grafana-scenes.components.drilldown-recommendations.recommended\">Recommended</Trans>\n </Text>\n {recommendedDrilldowns && recommendedDrilldowns.length > 0 ? (\n recommendedDrilldowns.map((drilldown) => (\n <div\n key={drilldown.label}\n className={cx(styles.combinedFilterPill)}\n onClick={() => onClickAction(drilldown.onClick)}\n >\n {drilldown.label}\n </div>\n ))\n ) : (\n <div className={styles.emptyMessage}>\n <Trans i18nKey=\"grafana-scenes.components.drilldown-recommendations.recommended-empty\">\n No recommended values\n </Trans>\n </div>\n )}\n </Stack>\n </div>\n </ClickOutsideWrapper>\n );\n\n return (\n <>\n <IconButton\n name=\"plus\"\n tooltip={t('grafana-scenes.components.drilldown-recommendations.tooltip', 'Show recommendations')}\n ref={ref}\n className={cx(isPopoverVisible && styles.iconActive)}\n onClick={(ev) => {\n openPopover();\n ev.stopPropagation();\n }}\n />\n\n {isPopoverVisible && ref.current && (\n <Popover\n content={content}\n onKeyDown={(event) => {\n if (event.key === ' ') {\n event.stopPropagation();\n }\n }}\n placement=\"bottom-start\"\n referenceElement={ref.current}\n show\n />\n )}\n </>\n );\n}\n\nconst getStyles = (theme: GrafanaTheme2) => ({\n menuContainer: css({\n display: 'flex',\n flexDirection: 'column',\n background: theme.colors.background.elevated,\n border: `1px solid ${theme.colors.border.weak}`,\n borderRadius: theme.shape.radius.default,\n boxShadow: theme.shadows.z3,\n padding: theme.spacing(2),\n }),\n combinedFilterPill: css({\n alignItems: 'center',\n background: theme.colors.action.selected,\n borderRadius: theme.shape.radius.default,\n border: `1px solid ${theme.colors.border.weak}`,\n padding: theme.spacing(0.2, 1),\n color: theme.colors.text.primary,\n overflow: 'hidden',\n whiteSpace: 'nowrap',\n minHeight: theme.spacing(2.75),\n ...theme.typography.bodySmall,\n fontWeight: theme.typography.fontWeightBold,\n cursor: 'pointer',\n\n '&:hover': {\n background: theme.colors.action.hover,\n },\n }),\n iconActive: css({\n '&:before': {\n backgroundColor: theme.colors.action.hover,\n opacity: 1,\n },\n }),\n emptyMessage: css({\n padding: theme.spacing(0.5, 0),\n color: theme.colors.text.secondary,\n ...theme.typography.bodySmall,\n }),\n});\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,169 @@
1
+ import React from 'react';
2
+ import { store } from '@grafana/data';
3
+ import { sceneGraph } from '../../core/sceneGraph/index.js';
4
+ import { getEnrichedDataRequest } from '../../querying/getEnrichedDataRequest.js';
5
+ import { getQueriesForVariables } from '../utils.js';
6
+ import { getDataSource } from '../../utils/getDataSource.js';
7
+ import { DrilldownRecommendations } from '../components/DrilldownRecommendations.js';
8
+ import { ScopesVariable } from '../variants/ScopesVariable.js';
9
+ import { SCOPES_VARIABLE_NAME } from '../constants.js';
10
+ import { GroupByVariable } from './GroupByVariable.js';
11
+ import '../adhoc/AdHocFiltersRecommendations.js';
12
+ import { isArray } from 'lodash';
13
+ import { SceneObjectBase } from '../../core/SceneObjectBase.js';
14
+ import { wrapInSafeSerializableSceneObject } from '../../utils/wrapInSafeSerializableSceneObject.js';
15
+
16
+ const getRecentGroupingKey = (datasourceUid) => `grafana.grouping.recent.${datasourceUid != null ? datasourceUid : "default"}`;
17
+ class GroupByRecommendations extends SceneObjectBase {
18
+ constructor(state = {}) {
19
+ super(state);
20
+ this._activationHandler = () => {
21
+ const json = store.get(this._getStorageKey());
22
+ const storedGroupings = json ? JSON.parse(json) : [];
23
+ if (storedGroupings.length > 0) {
24
+ this._verifyRecentGroupingsApplicability(storedGroupings);
25
+ } else {
26
+ this.setState({ recentGrouping: [] });
27
+ }
28
+ this._fetchRecommendedDrilldowns();
29
+ const scopesVariable = sceneGraph.lookupVariable(SCOPES_VARIABLE_NAME, this._groupBy);
30
+ let scopesSubscription;
31
+ if (scopesVariable instanceof ScopesVariable) {
32
+ this._subs.add(
33
+ scopesSubscription = scopesVariable.subscribeToState((newState, prevState) => {
34
+ if (newState.scopes !== prevState.scopes) {
35
+ const json2 = store.get(this._getStorageKey());
36
+ const storedGroupings2 = json2 ? JSON.parse(json2) : [];
37
+ if (storedGroupings2.length > 0) {
38
+ this._verifyRecentGroupingsApplicability(storedGroupings2);
39
+ }
40
+ this._fetchRecommendedDrilldowns();
41
+ }
42
+ })
43
+ );
44
+ }
45
+ return () => {
46
+ scopesSubscription == null ? void 0 : scopesSubscription.unsubscribe();
47
+ };
48
+ };
49
+ this.addActivationHandler(this._activationHandler);
50
+ }
51
+ get _groupBy() {
52
+ if (!(this.parent instanceof GroupByVariable)) {
53
+ throw new Error("GroupByRecommendations must be a child of GroupByVariable");
54
+ }
55
+ return this.parent;
56
+ }
57
+ get _scopedVars() {
58
+ return { __sceneObject: wrapInSafeSerializableSceneObject(this._groupBy) };
59
+ }
60
+ _getStorageKey() {
61
+ var _a;
62
+ return getRecentGroupingKey((_a = this._groupBy.state.datasource) == null ? void 0 : _a.uid);
63
+ }
64
+ async _fetchRecommendedDrilldowns() {
65
+ const ds = await getDataSource(this._groupBy.state.datasource, this._scopedVars);
66
+ if (!ds || !ds.getRecommendedDrilldowns) {
67
+ return;
68
+ }
69
+ const queries = getQueriesForVariables(this._groupBy);
70
+ const timeRange = sceneGraph.getTimeRange(this._groupBy).state.value;
71
+ const scopes = sceneGraph.getScopes(this._groupBy);
72
+ const groupByKeys = Array.isArray(this._groupBy.state.value) ? this._groupBy.state.value.map((v) => String(v)) : this._groupBy.state.value ? [String(this._groupBy.state.value)] : [];
73
+ const enrichedRequest = getEnrichedDataRequest(this._groupBy);
74
+ const dashboardUid = enrichedRequest == null ? void 0 : enrichedRequest.dashboardUID;
75
+ try {
76
+ const recommendedDrilldowns = await ds.getRecommendedDrilldowns({
77
+ timeRange,
78
+ dashboardUid,
79
+ queries,
80
+ groupByKeys,
81
+ scopes
82
+ });
83
+ if (recommendedDrilldowns == null ? void 0 : recommendedDrilldowns.groupByKeys) {
84
+ this.setState({
85
+ recommendedGrouping: recommendedDrilldowns.groupByKeys.map((key) => ({ value: key, text: key }))
86
+ });
87
+ }
88
+ } catch (error) {
89
+ console.error("Failed to fetch recommended drilldowns:", error);
90
+ }
91
+ }
92
+ async _verifyRecentGroupingsApplicability(storedGroupings) {
93
+ const queries = getQueriesForVariables(this._groupBy);
94
+ const keys = storedGroupings.map((g) => String(g.value));
95
+ const response = await this._groupBy.getGroupByApplicabilityForQueries(keys, queries);
96
+ if (!response) {
97
+ this.setState({ recentGrouping: storedGroupings.slice(-3) });
98
+ return;
99
+ }
100
+ const applicabilityMap = /* @__PURE__ */ new Map();
101
+ response.forEach((item) => {
102
+ applicabilityMap.set(item.key, item.applicable !== false);
103
+ });
104
+ const applicableGroupings = storedGroupings.filter((g) => {
105
+ const isApplicable = applicabilityMap.get(String(g.value));
106
+ return isApplicable === void 0 || isApplicable === true;
107
+ }).slice(-3);
108
+ this.setState({ recentGrouping: applicableGroupings });
109
+ }
110
+ /**
111
+ * Stores recent groupings in localStorage and updates state.
112
+ * Should be called by the parent variable when a grouping is added/updated.
113
+ */
114
+ storeRecentGrouping(applicableValues) {
115
+ if (applicableValues.length === 0) {
116
+ return;
117
+ }
118
+ const key = this._getStorageKey();
119
+ const storedGroupings = store.get(key);
120
+ const allRecentGroupings = storedGroupings ? JSON.parse(storedGroupings) : [];
121
+ const existingWithoutApplicableValues = allRecentGroupings.filter(
122
+ (grouping) => !applicableValues.includes(String(grouping.value))
123
+ );
124
+ const updatedStoredGroupings = [
125
+ ...existingWithoutApplicableValues,
126
+ ...applicableValues.map((value) => ({ value, text: value }))
127
+ ];
128
+ const limitedStoredGroupings = updatedStoredGroupings.slice(-10);
129
+ store.set(key, JSON.stringify(limitedStoredGroupings));
130
+ this.setState({ recentGrouping: limitedStoredGroupings.slice(-3) });
131
+ }
132
+ /**
133
+ * Add a grouping value to the parent variable
134
+ */
135
+ addValueToParent(newValue, newText) {
136
+ const value = isArray(this._groupBy.state.value) ? this._groupBy.state.value : [this._groupBy.state.value];
137
+ const text = isArray(this._groupBy.state.text) ? this._groupBy.state.text.map(String) : [String(this._groupBy.state.text)];
138
+ if (value.includes(newValue)) {
139
+ return;
140
+ }
141
+ this._groupBy.changeValueTo(
142
+ [...value.filter((v) => v !== ""), newValue],
143
+ [...text.filter((t) => t !== ""), newText != null ? newText : String(newValue)],
144
+ true
145
+ );
146
+ }
147
+ }
148
+ GroupByRecommendations.Component = GroupByRecommendationsRenderer;
149
+ function GroupByRecommendationsRenderer({ model }) {
150
+ const { recentGrouping, recommendedGrouping } = model.useState();
151
+ const recentDrilldowns = recentGrouping == null ? void 0 : recentGrouping.map((groupBy) => ({
152
+ label: `${groupBy.value}`,
153
+ onClick: () => {
154
+ var _a;
155
+ model.addValueToParent(groupBy.value, (_a = groupBy.text) != null ? _a : String(groupBy.value));
156
+ }
157
+ }));
158
+ const recommendedDrilldowns = recommendedGrouping == null ? void 0 : recommendedGrouping.map((groupBy) => ({
159
+ label: `${groupBy.value}`,
160
+ onClick: () => {
161
+ var _a;
162
+ model.addValueToParent(groupBy.value, (_a = groupBy.text) != null ? _a : String(groupBy.value));
163
+ }
164
+ }));
165
+ return /* @__PURE__ */ React.createElement(DrilldownRecommendations, { recentDrilldowns, recommendedDrilldowns });
166
+ }
167
+
168
+ export { GroupByRecommendations, getRecentGroupingKey };
169
+ //# sourceMappingURL=GroupByRecommendations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GroupByRecommendations.js","sources":["../../../../../../../src/variables/groupby/GroupByRecommendations.tsx"],"sourcesContent":["import React from 'react';\nimport {\n // @ts-expect-error (temporary till we update grafana/data)\n DrilldownsApplicability,\n SelectableValue,\n store,\n} from '@grafana/data';\nimport { sceneGraph } from '../../core/sceneGraph';\nimport { getEnrichedDataRequest } from '../../querying/getEnrichedDataRequest';\nimport { getQueriesForVariables } from '../utils';\nimport { getDataSource } from '../../utils/getDataSource';\nimport { DrilldownRecommendations, DrilldownPill } from '../components/DrilldownRecommendations';\nimport { ScopesVariable } from '../variants/ScopesVariable';\nimport { SCOPES_VARIABLE_NAME } from '../constants';\nimport { GroupByVariable } from './GroupByVariable';\nimport { MAX_RECENT_DRILLDOWNS, MAX_STORED_RECENT_DRILLDOWNS } from '../adhoc/AdHocFiltersRecommendations';\nimport { VariableValueSingle } from '../types';\nimport { isArray } from 'lodash';\nimport { SceneObjectBase } from '../../core/SceneObjectBase';\nimport { SceneComponentProps, SceneObjectState } from '../../core/types';\nimport { wrapInSafeSerializableSceneObject } from '../../utils/wrapInSafeSerializableSceneObject';\nimport { Unsubscribable } from 'rxjs';\n\nexport const getRecentGroupingKey = (datasourceUid: string | undefined) =>\n `grafana.grouping.recent.${datasourceUid ?? 'default'}`;\n\nexport interface GroupByRecommendationsState extends SceneObjectState {\n recentGrouping?: Array<SelectableValue<VariableValueSingle>>;\n recommendedGrouping?: Array<SelectableValue<VariableValueSingle>>;\n}\n\nexport class GroupByRecommendations extends SceneObjectBase<GroupByRecommendationsState> {\n static Component = GroupByRecommendationsRenderer;\n\n public constructor(state: Partial<GroupByRecommendationsState> = {}) {\n super(state);\n\n this.addActivationHandler(this._activationHandler);\n }\n\n private get _groupBy(): GroupByVariable {\n if (!(this.parent instanceof GroupByVariable)) {\n throw new Error('GroupByRecommendations must be a child of GroupByVariable');\n }\n\n return this.parent;\n }\n\n private get _scopedVars() {\n return { __sceneObject: wrapInSafeSerializableSceneObject(this._groupBy) };\n }\n\n private _activationHandler = () => {\n const json = store.get(this._getStorageKey());\n const storedGroupings = json ? JSON.parse(json) : [];\n\n if (storedGroupings.length > 0) {\n this._verifyRecentGroupingsApplicability(storedGroupings);\n } else {\n this.setState({ recentGrouping: [] });\n }\n\n this._fetchRecommendedDrilldowns();\n\n // Subscribe to scopes variable changes\n const scopesVariable = sceneGraph.lookupVariable(SCOPES_VARIABLE_NAME, this._groupBy);\n let scopesSubscription: Unsubscribable | undefined;\n\n if (scopesVariable instanceof ScopesVariable) {\n this._subs.add(\n (scopesSubscription = scopesVariable.subscribeToState((newState, prevState) => {\n if (newState.scopes !== prevState.scopes) {\n const json = store.get(this._getStorageKey());\n const storedGroupings = json ? JSON.parse(json) : [];\n\n if (storedGroupings.length > 0) {\n this._verifyRecentGroupingsApplicability(storedGroupings);\n }\n\n this._fetchRecommendedDrilldowns();\n }\n }))\n );\n }\n\n return () => {\n scopesSubscription?.unsubscribe();\n };\n };\n\n private _getStorageKey(): string {\n return getRecentGroupingKey(this._groupBy.state.datasource?.uid);\n }\n\n private async _fetchRecommendedDrilldowns() {\n const ds = await getDataSource(this._groupBy.state.datasource, this._scopedVars);\n\n // @ts-expect-error (temporary till we update grafana/data)\n if (!ds || !ds.getRecommendedDrilldowns) {\n return;\n }\n\n const queries = getQueriesForVariables(this._groupBy);\n const timeRange = sceneGraph.getTimeRange(this._groupBy).state.value;\n const scopes = sceneGraph.getScopes(this._groupBy);\n const groupByKeys = Array.isArray(this._groupBy.state.value)\n ? this._groupBy.state.value.map((v) => String(v))\n : this._groupBy.state.value\n ? [String(this._groupBy.state.value)]\n : [];\n\n const enrichedRequest = getEnrichedDataRequest(this._groupBy);\n const dashboardUid = enrichedRequest?.dashboardUID;\n\n try {\n // @ts-expect-error (temporary till we update grafana/data)\n const recommendedDrilldowns = await ds.getRecommendedDrilldowns({\n timeRange,\n dashboardUid,\n queries,\n groupByKeys,\n scopes,\n });\n\n if (recommendedDrilldowns?.groupByKeys) {\n this.setState({\n recommendedGrouping: recommendedDrilldowns.groupByKeys.map((key: string) => ({ value: key, text: key })),\n });\n }\n } catch (error) {\n console.error('Failed to fetch recommended drilldowns:', error);\n }\n }\n\n private async _verifyRecentGroupingsApplicability(storedGroupings: Array<SelectableValue<VariableValueSingle>>) {\n const queries = getQueriesForVariables(this._groupBy);\n const keys = storedGroupings.map((g) => String(g.value));\n const response = await this._groupBy.getGroupByApplicabilityForQueries(keys, queries);\n\n if (!response) {\n this.setState({ recentGrouping: storedGroupings.slice(-MAX_RECENT_DRILLDOWNS) });\n return;\n }\n\n const applicabilityMap = new Map<string, boolean>();\n response.forEach((item: DrilldownsApplicability) => {\n applicabilityMap.set(item.key, item.applicable !== false);\n });\n\n const applicableGroupings = storedGroupings\n .filter((g) => {\n const isApplicable = applicabilityMap.get(String(g.value));\n return isApplicable === undefined || isApplicable === true;\n })\n .slice(-MAX_RECENT_DRILLDOWNS);\n\n this.setState({ recentGrouping: applicableGroupings });\n }\n\n /**\n * Stores recent groupings in localStorage and updates state.\n * Should be called by the parent variable when a grouping is added/updated.\n */\n public storeRecentGrouping(applicableValues: string[]) {\n if (applicableValues.length === 0) {\n return;\n }\n\n const key = this._getStorageKey();\n const storedGroupings = store.get(key);\n const allRecentGroupings: Array<SelectableValue<VariableValueSingle>> = storedGroupings\n ? JSON.parse(storedGroupings)\n : [];\n\n const existingWithoutApplicableValues = allRecentGroupings.filter(\n (grouping) => !applicableValues.includes(String(grouping.value))\n );\n const updatedStoredGroupings = [\n ...existingWithoutApplicableValues,\n ...applicableValues.map((value) => ({ value, text: value })),\n ];\n\n const limitedStoredGroupings = updatedStoredGroupings.slice(-MAX_STORED_RECENT_DRILLDOWNS);\n\n store.set(key, JSON.stringify(limitedStoredGroupings));\n\n this.setState({ recentGrouping: limitedStoredGroupings.slice(-MAX_RECENT_DRILLDOWNS) });\n }\n\n /**\n * Add a grouping value to the parent variable\n */\n public addValueToParent(newValue: VariableValueSingle, newText?: string) {\n const value = isArray(this._groupBy.state.value) ? this._groupBy.state.value : [this._groupBy.state.value];\n const text = isArray(this._groupBy.state.text)\n ? this._groupBy.state.text.map(String)\n : [String(this._groupBy.state.text)];\n\n // Check if value already exists\n if (value.includes(newValue)) {\n return;\n }\n\n this._groupBy.changeValueTo(\n [...value.filter((v) => v !== ''), newValue],\n [...text.filter((t) => t !== ''), newText ?? String(newValue)],\n true\n );\n }\n}\n\nfunction GroupByRecommendationsRenderer({ model }: SceneComponentProps<GroupByRecommendations>) {\n const { recentGrouping, recommendedGrouping } = model.useState();\n\n const recentDrilldowns: DrilldownPill[] | undefined = recentGrouping?.map((groupBy) => ({\n label: `${groupBy.value}`,\n onClick: () => {\n model.addValueToParent(groupBy.value!, groupBy.text ?? String(groupBy.value));\n },\n }));\n\n const recommendedDrilldowns: DrilldownPill[] | undefined = recommendedGrouping?.map((groupBy) => ({\n label: `${groupBy.value}`,\n onClick: () => {\n model.addValueToParent(groupBy.value!, groupBy.text ?? String(groupBy.value));\n },\n }));\n\n return <DrilldownRecommendations recentDrilldowns={recentDrilldowns} recommendedDrilldowns={recommendedDrilldowns} />;\n}\n"],"names":["json","storedGroupings"],"mappings":"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"}
@@ -1,15 +1,14 @@
1
- import { css } from '@emotion/css';
2
1
  import { t } from '@grafana/i18n';
3
2
  import React, { useMemo, useState, useEffect } from 'react';
4
3
  import { allActiveGroupByVariables } from './findActiveGroupByVariablesByUid.js';
5
4
  import { sceneGraph } from '../../core/sceneGraph/index.js';
6
5
  import { SceneVariableValueChangedEvent } from '../types.js';
7
6
  import { MultiValueVariable } from '../variants/MultiValueVariable.js';
8
- import { lastValueFrom, map, of, from, mergeMap, tap, take } from 'rxjs';
7
+ import { map, of, from, mergeMap, tap, take, lastValueFrom } from 'rxjs';
9
8
  import { getDataSource } from '../../utils/getDataSource.js';
10
9
  import { useStyles2, MultiSelect, Select } from '@grafana/ui';
11
10
  import { isArray, isEqual } from 'lodash';
12
- import { handleOptionGroups, dataFromResponse, getQueriesForVariables, responseHasError } from '../utils.js';
11
+ import { dataFromResponse, getQueriesForVariables, responseHasError, handleOptionGroups } from '../utils.js';
13
12
  import { OptionWithCheckbox } from '../components/VariableValueSelect.js';
14
13
  import { GroupByVariableUrlSyncHandler } from './GroupByVariableUrlSyncHandler.js';
15
14
  import { getOptionSearcher } from '../components/getOptionSearcher.js';
@@ -19,10 +18,18 @@ import { DefaultGroupByCustomIndicatorContainer } from './DefaultGroupByCustomIn
19
18
  import { GroupByValueContainer } from './GroupByValueContainer.js';
20
19
  import { getInteractionTracker } from '../../core/sceneGraph/getInteractionTracker.js';
21
20
  import { GROUPBY_DIMENSIONS_INTERACTION } from '../../performance/interactionConstants.js';
21
+ import { css, cx } from '@emotion/css';
22
+ import { GroupByRecommendations } from './GroupByRecommendations.js';
22
23
  import { c as components } from '../../../../../node_modules/react-select/dist/index-641ee5b8.esm.js';
23
24
 
24
25
  class GroupByVariable extends MultiValueVariable {
25
26
  constructor(initialState) {
27
+ var _a;
28
+ const behaviors = (_a = initialState.$behaviors) != null ? _a : [];
29
+ const recommendations = initialState.drilldownRecommendationsEnabled ? new GroupByRecommendations() : void 0;
30
+ if (recommendations) {
31
+ behaviors.push(recommendations);
32
+ }
26
33
  super({
27
34
  isMulti: true,
28
35
  name: "",
@@ -35,10 +42,12 @@ class GroupByVariable extends MultiValueVariable {
35
42
  layout: "horizontal",
36
43
  type: "groupby",
37
44
  ...initialState,
38
- noValueOnClear: true
45
+ noValueOnClear: true,
46
+ $behaviors: behaviors.length > 0 ? behaviors : void 0
39
47
  });
40
48
  this.isLazy = true;
41
49
  this._urlSync = new GroupByVariableUrlSyncHandler(this);
50
+ this._scopedVars = { __sceneObject: wrapInSafeSerializableSceneObject(this) };
42
51
  this._activationHandler = () => {
43
52
  this._verifyApplicability();
44
53
  if (this.state.defaultValue) {
@@ -91,6 +100,7 @@ class GroupByVariable extends MultiValueVariable {
91
100
  }
92
101
  return keys;
93
102
  };
103
+ this._recommendations = recommendations;
94
104
  if (this.state.defaultValue) {
95
105
  this.changeValueTo(this.state.defaultValue.value, this.state.defaultValue.text, false);
96
106
  }
@@ -131,11 +141,7 @@ class GroupByVariable extends MultiValueVariable {
131
141
  );
132
142
  }
133
143
  this.setState({ loading: true, error: null });
134
- return from(
135
- getDataSource(this.state.datasource, {
136
- __sceneObject: wrapInSafeSerializableSceneObject(this)
137
- })
138
- ).pipe(
144
+ return from(getDataSource(this.state.datasource, this._scopedVars)).pipe(
139
145
  mergeMap((ds) => {
140
146
  return from(this._getKeys(ds)).pipe(
141
147
  tap((response) => {
@@ -159,9 +165,15 @@ class GroupByVariable extends MultiValueVariable {
159
165
  })
160
166
  );
161
167
  }
168
+ /**
169
+ * Gets the GroupByRecommendations behavior if it exists in $behaviors
170
+ */
171
+ getRecommendations() {
172
+ return this._recommendations;
173
+ }
162
174
  getApplicableKeys() {
163
175
  const { value, keysApplicability } = this.state;
164
- const valueArray = isArray(value) ? value : value ? [value] : [];
176
+ const valueArray = isArray(value) ? value.map(String) : value ? [String(value)] : [];
165
177
  if (!keysApplicability || keysApplicability.length === 0) {
166
178
  return valueArray;
167
179
  }
@@ -172,9 +184,7 @@ class GroupByVariable extends MultiValueVariable {
172
184
  return applicableValues;
173
185
  }
174
186
  async getGroupByApplicabilityForQueries(value, queries) {
175
- const ds = await getDataSource(this.state.datasource, {
176
- __sceneObject: wrapInSafeSerializableSceneObject(this)
177
- });
187
+ const ds = await getDataSource(this.state.datasource, this._scopedVars);
178
188
  if (!ds.getDrilldownsApplicability) {
179
189
  return;
180
190
  }
@@ -220,6 +230,17 @@ class GroupByVariable extends MultiValueVariable {
220
230
  }
221
231
  this.changeValueTo(this.state.defaultValue.value, this.state.defaultValue.text, true);
222
232
  }
233
+ async _verifyApplicabilityAndStoreRecentGrouping() {
234
+ await this._verifyApplicability();
235
+ if (!this._recommendations) {
236
+ return;
237
+ }
238
+ const applicableValues = this.getApplicableKeys();
239
+ if (applicableValues.length === 0) {
240
+ return;
241
+ }
242
+ this._recommendations.storeRecentGrouping(applicableValues);
243
+ }
223
244
  /**
224
245
  * Allows clearing the value of the variable to an empty value. Overrides default behavior of a MultiValueVariable
225
246
  */
@@ -229,6 +250,7 @@ class GroupByVariable extends MultiValueVariable {
229
250
  }
230
251
  GroupByVariable.Component = GroupByVariableRenderer;
231
252
  function GroupByVariableRenderer({ model }) {
253
+ var _a, _b;
232
254
  const {
233
255
  value,
234
256
  text,
@@ -240,17 +262,19 @@ function GroupByVariableRenderer({ model }) {
240
262
  includeAll,
241
263
  allowCustomValue = true,
242
264
  defaultValue,
243
- keysApplicability
265
+ keysApplicability,
266
+ drilldownRecommendationsEnabled
244
267
  } = model.useState();
268
+ const recommendations = model.getRecommendations();
245
269
  const styles = useStyles2(getStyles);
246
270
  const values = useMemo(() => {
247
271
  const arrayValue = isArray(value) ? value : [value];
248
272
  const arrayText = isArray(text) ? text : [text];
249
273
  return arrayValue.map((value2, idx) => {
250
- var _a;
274
+ var _a2;
251
275
  return {
252
276
  value: value2,
253
- label: String((_a = arrayText[idx]) != null ? _a : value2)
277
+ label: String((_a2 = arrayText[idx]) != null ? _a2 : value2)
254
278
  };
255
279
  });
256
280
  }, [value, text]);
@@ -281,7 +305,8 @@ function GroupByVariableRenderer({ model }) {
281
305
  () => handleOptionGroups(optionSearcher(inputValue).map(toSelectableValue)),
282
306
  [optionSearcher, inputValue]
283
307
  );
284
- return isMulti ? /* @__PURE__ */ React.createElement("div", { className: styles.selectWrapper }, /* @__PURE__ */ React.createElement(
308
+ const WideInputWrapper = (children) => /* @__PURE__ */ React.createElement("div", { className: styles.selectWrapper }, children);
309
+ const select = isMulti ? /* @__PURE__ */ React.createElement(ConditionalWrapper, { condition: (_a = model.state.wideInput) != null ? _a : false, wrapper: WideInputWrapper }, /* @__PURE__ */ React.createElement(
285
310
  MultiSelect,
286
311
  {
287
312
  "aria-label": t(
@@ -290,11 +315,12 @@ function GroupByVariableRenderer({ model }) {
290
315
  ),
291
316
  "data-testid": `GroupBySelect-${key}`,
292
317
  id: key,
293
- className: styles.fullWidthMultiSelect,
294
318
  placeholder: t(
295
319
  "grafana-scenes.variables.group-by-variable-renderer.placeholder-group-by-label",
296
320
  "Group by label"
297
321
  ),
322
+ width: "auto",
323
+ className: cx(drilldownRecommendationsEnabled && styles.selectStylesInWrapper),
298
324
  allowCustomValue,
299
325
  inputValue,
300
326
  value: uncommittedValue,
@@ -311,6 +337,7 @@ function GroupByVariableRenderer({ model }) {
311
337
  isLoading: isFetchingOptions,
312
338
  components: {
313
339
  Option: OptionWithCheckbox,
340
+ Menu: WideMenu,
314
341
  ...hasDefaultValue ? {
315
342
  IndicatorsContainer: () => /* @__PURE__ */ React.createElement(DefaultGroupByCustomIndicatorContainer, { model })
316
343
  } : {},
@@ -327,7 +354,7 @@ function GroupByVariableRenderer({ model }) {
327
354
  if (restorable !== model.state.restorable) {
328
355
  model.setState({ restorable });
329
356
  }
330
- model._verifyApplicability();
357
+ model._verifyApplicabilityAndStoreRecentGrouping();
331
358
  },
332
359
  onChange: (newValue, action) => {
333
360
  if (action.action === "clear" && noValueOnClear) {
@@ -349,7 +376,7 @@ function GroupByVariableRenderer({ model }) {
349
376
  setIsOptionsOpen(false);
350
377
  }
351
378
  }
352
- )) : /* @__PURE__ */ React.createElement("div", { className: styles.selectWrapper }, /* @__PURE__ */ React.createElement(
379
+ )) : /* @__PURE__ */ React.createElement(ConditionalWrapper, { condition: (_b = model.state.wideInput) != null ? _b : false, wrapper: WideInputWrapper }, /* @__PURE__ */ React.createElement(
353
380
  Select,
354
381
  {
355
382
  "aria-label": t(
@@ -407,6 +434,32 @@ function GroupByVariableRenderer({ model }) {
407
434
  }
408
435
  }
409
436
  ));
437
+ if (!recommendations) {
438
+ return select;
439
+ }
440
+ return /* @__PURE__ */ React.createElement("div", { className: styles.wrapper }, /* @__PURE__ */ React.createElement("div", { className: styles.recommendations }, /* @__PURE__ */ React.createElement(recommendations.Component, { model: recommendations })), select);
441
+ }
442
+ const ConditionalWrapper = ({
443
+ condition,
444
+ wrapper,
445
+ children
446
+ }) => {
447
+ return condition ? wrapper(children) : /* @__PURE__ */ React.createElement(React.Fragment, null, children);
448
+ };
449
+ const filterNoOp = () => true;
450
+ function WideMenu(props) {
451
+ return /* @__PURE__ */ React.createElement(components.Menu, { ...props }, /* @__PURE__ */ React.createElement("div", { style: { minWidth: "220px" } }, props.children));
452
+ }
453
+ function toSelectableValue(input) {
454
+ const { label, value, group } = input;
455
+ const result = {
456
+ label,
457
+ value
458
+ };
459
+ if (group) {
460
+ result.group = group;
461
+ }
462
+ return result;
410
463
  }
411
464
  const getStyles = (theme) => ({
412
465
  selectWrapper: css({
@@ -424,23 +477,31 @@ const getStyles = (theme) => ({
424
477
  gridAutoColumns: "max-content",
425
478
  justifyItems: "start"
426
479
  }
480
+ }),
481
+ wrapper: css({
482
+ display: "flex"
483
+ }),
484
+ selectStylesInWrapper: css({
485
+ borderTopLeftRadius: 0,
486
+ borderBottomLeftRadius: 0,
487
+ border: `1px solid ${theme.colors.border.strong}`,
488
+ borderLeft: "none"
489
+ }),
490
+ recommendations: css({
491
+ display: "flex",
492
+ alignItems: "center",
493
+ paddingInline: theme.spacing(0.5),
494
+ borderTop: `1px solid ${theme.colors.border.strong}`,
495
+ borderBottom: `1px solid ${theme.colors.border.strong}`,
496
+ backgroundColor: theme.components.input.background,
497
+ "& button": {
498
+ borderRadius: 0,
499
+ height: "100%",
500
+ margin: 0,
501
+ paddingInline: theme.spacing(0.5)
502
+ }
427
503
  })
428
504
  });
429
- const filterNoOp = () => true;
430
- function WideMenu(props) {
431
- return /* @__PURE__ */ React.createElement(components.Menu, { ...props }, /* @__PURE__ */ React.createElement("div", { style: { minWidth: "220px" } }, props.children));
432
- }
433
- function toSelectableValue(input) {
434
- const { label, value, group } = input;
435
- const result = {
436
- label,
437
- value
438
- };
439
- if (group) {
440
- result.group = group;
441
- }
442
- return result;
443
- }
444
505
 
445
506
  export { GroupByVariable, GroupByVariableRenderer };
446
507
  //# sourceMappingURL=GroupByVariable.js.map