Skip to content

Commit 8df391d

Browse files
authored
feature: show title in selectors with spreadsheet ID in description (#377)
1 parent 1a690b3 commit 8df391d

File tree

6 files changed

+93
-13
lines changed

6 files changed

+93
-13
lines changed

.changeset/silver-vans-like.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'grafana-google-sheets-datasource': minor
3+
---
4+
5+
show title in selectors with spreadsheet ID in description

src/DataSource.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,11 @@ export class DataSource extends DataSourceWithBackend<SheetsQuery, DataSourceOpt
6464
async getSpreadSheets(): Promise<Array<SelectableValue<string>>> {
6565
return this.getResource('spreadsheets').then(({ spreadsheets }) =>
6666
spreadsheets
67-
? Object.entries(spreadsheets).map(([value, label]) => ({ label, value }) as SelectableValue<string>)
67+
? Object.entries(spreadsheets).map(([value, label]) => ({
68+
label,
69+
value,
70+
description: value,
71+
}) as SelectableValue<string>)
6872
: []
6973
);
7074
}

src/components/ConfigEditor.test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ jest.mock('@grafana/runtime', () => ({
1515
Promise.resolve({
1616
getSpreadSheets: () =>
1717
Promise.resolve([
18-
{ label: 'label1', value: 'value1' },
18+
{ label: 'label1', value: 'value1', description: 'value1' },
1919
]),
2020
}),
2121
}),

src/components/ConfigEditor.tsx

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,25 @@
11
import {
22
DataSourcePluginOptionsEditorProps,
33
onUpdateDatasourceSecureJsonDataOption,
4+
SelectableValue,
45
} from '@grafana/data';
5-
import { AuthConfig, DataSourceOptions } from '@grafana/google-sdk';
6+
import { AuthConfig } from '@grafana/google-sdk';
67
import { DataSourceDescription } from '@grafana/plugin-ui';
78
import { Field, SecretInput, SegmentAsync, Divider } from '@grafana/ui';
8-
import React from 'react';
9-
import { GoogleSheetsSecureJSONData, googleSheetsAuthTypes, GoogleSheetsAuth } from '../types';
9+
import React, { useState, useEffect } from 'react';
10+
import { GoogleSheetsSecureJSONData, googleSheetsAuthTypes, GoogleSheetsAuth, GoogleSheetsDataSourceOptions } from '../types';
1011
import { getBackwardCompatibleOptions } from '../utils';
1112
import { ConfigurationHelp } from './ConfigurationHelp';
1213
import { getDataSourceSrv } from '@grafana/runtime';
1314
import { DataSource } from '../DataSource';
1415

15-
export type Props = DataSourcePluginOptionsEditorProps<DataSourceOptions, GoogleSheetsSecureJSONData>;
16+
export type Props = DataSourcePluginOptionsEditorProps<GoogleSheetsDataSourceOptions, GoogleSheetsSecureJSONData>;
1617

1718
export function ConfigEditor(props: Props) {
1819
const options = getBackwardCompatibleOptions(props.options);
20+
const [selectedSheetOption, setSelectedSheetOption] = useState<SelectableValue<string> | string | undefined>(
21+
options.jsonData.defaultSheetID
22+
);
1923

2024
const apiKeyProps = {
2125
isConfigured: Boolean(options.secureJsonFields.apiKey),
@@ -43,6 +47,25 @@ export function ConfigEditor(props: Props) {
4347
return [];
4448
}
4549
};
50+
51+
useEffect(() => {
52+
const currentValue = options.jsonData.defaultSheetID;
53+
if (!currentValue || !options.uid) {
54+
setSelectedSheetOption(currentValue);
55+
return;
56+
}
57+
const updateSelectedOption = async () => {
58+
try {
59+
const ds = (await getDataSourceSrv().get(options.uid!)) as DataSource;
60+
const sheetOptions = await ds.getSpreadSheets();
61+
const matchingOption = sheetOptions.find((opt) => opt.value === currentValue);
62+
setSelectedSheetOption(matchingOption || currentValue);
63+
} catch {
64+
setSelectedSheetOption(currentValue);
65+
}
66+
};
67+
updateSelectedOption();
68+
}, [options.jsonData.defaultSheetID, options.uid]);
4669
return (
4770
<>
4871
<DataSourceDescription
@@ -85,15 +108,17 @@ export function ConfigEditor(props: Props) {
85108
<SegmentAsync
86109
loadOptions={loadSheetIDs}
87110
placeholder="Select Spreadsheet ID"
88-
value={(options.jsonData as any).defaultSheetID}
111+
value={selectedSheetOption}
89112
allowCustomValue={true}
90113
onChange={(value) => {
114+
const sheetId = typeof value === 'string' ? value : value?.value;
115+
setSelectedSheetOption(value);
91116
props.onOptionsChange({
92117
...options,
93118
jsonData: {
94119
...options.jsonData,
95-
defaultSheetID: value?.value || value,
96-
} as any,
120+
defaultSheetID: sheetId,
121+
},
97122
});
98123
}}
99124
/>

src/components/QueryEditor.tsx

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { QueryEditorProps } from '@grafana/data';
1+
import { QueryEditorProps, SelectableValue } from '@grafana/data';
22
import { DataSourceOptions } from '@grafana/google-sdk';
33
import { InlineFieldRow, InlineFormLabel, InlineSwitch, Input, LinkButton, Segment, SegmentAsync } from '@grafana/ui';
44
import React, { ChangeEvent, PureComponent } from 'react';
@@ -51,15 +51,45 @@ export const formatCacheTimeLabel = (s: number = defaultCacheDuration) => {
5151
};
5252

5353
export class QueryEditor extends PureComponent<Props> {
54+
state = {
55+
selectedSheetOption: undefined as SelectableValue<string> | string | undefined,
56+
};
57+
5458
componentDidMount() {
5559
if (!this.props.query.hasOwnProperty('cacheDurationSeconds')) {
5660
this.props.onChange({
5761
...this.props.query,
5862
cacheDurationSeconds: defaultCacheDuration, // um :(
5963
});
6064
}
65+
this.updateSelectedSheetOption();
66+
}
67+
68+
componentDidUpdate(prevProps: Props) {
69+
if (prevProps.query.spreadsheet !== this.props.query.spreadsheet) {
70+
this.updateSelectedSheetOption();
71+
}
6172
}
6273

74+
updateSelectedSheetOption = async () => {
75+
const { query, datasource } = this.props;
76+
if (!query.spreadsheet) {
77+
this.setState({ selectedSheetOption: undefined });
78+
return;
79+
}
80+
try {
81+
const sheetOptions = await datasource.getSpreadSheets();
82+
const matchingOption = sheetOptions.find((opt) => opt.value === query.spreadsheet);
83+
if (matchingOption) {
84+
this.setState({ selectedSheetOption: matchingOption });
85+
} else {
86+
this.setState({ selectedSheetOption: query.spreadsheet });
87+
}
88+
} catch {
89+
this.setState({ selectedSheetOption: query.spreadsheet });
90+
}
91+
};
92+
6393
onRangeChange = (event: ChangeEvent<HTMLInputElement>) => {
6494
this.props.onChange({
6595
...this.props.query,
@@ -71,10 +101,12 @@ export class QueryEditor extends PureComponent<Props> {
71101
const { query, onRunQuery, onChange } = this.props;
72102

73103
if (!item.value) {
104+
this.setState({ selectedSheetOption: undefined });
74105
return; // ignore delete?
75106
}
76107

77108
const v = item.value;
109+
this.setState({ selectedSheetOption: item });
78110
// Check for pasted full URLs
79111
if (/(.*)\/spreadsheets\/d\/(.*)/.test(v)) {
80112
onChange({ ...query, ...getGoogleSheetRangeInfoFromURL(v) });
@@ -118,9 +150,19 @@ export class QueryEditor extends PureComponent<Props> {
118150
Spreadsheet ID
119151
</InlineFormLabel>
120152
<SegmentAsync
121-
loadOptions={() => datasource.getSpreadSheets()}
153+
loadOptions={async () => {
154+
const options = await datasource.getSpreadSheets();
155+
const { query } = this.props;
156+
if (query.spreadsheet) {
157+
const matchingOption = options.find((opt) => opt.value === query.spreadsheet);
158+
if (matchingOption && this.state.selectedSheetOption !== matchingOption) {
159+
this.setState({ selectedSheetOption: matchingOption });
160+
}
161+
}
162+
return options;
163+
}}
122164
placeholder="Enter SpreadsheetID"
123-
value={query.spreadsheet}
165+
value={this.state.selectedSheetOption ?? query.spreadsheet}
124166
allowCustomValue={true}
125167
onChange={this.onSpreadsheetIDChange}
126168
/>

src/types.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { DataQuery } from '@grafana/schema';
2-
import { GoogleAuthType, GOOGLE_AUTH_TYPE_OPTIONS, DataSourceSecureJsonData } from '@grafana/google-sdk';
2+
import { GoogleAuthType, GOOGLE_AUTH_TYPE_OPTIONS, DataSourceSecureJsonData, DataSourceOptions } from '@grafana/google-sdk';
33

44
export const GoogleSheetsAuth = {
55
...GoogleAuthType,
@@ -12,6 +12,10 @@ export interface GoogleSheetsSecureJSONData extends DataSourceSecureJsonData {
1212
apiKey?: string;
1313
}
1414

15+
export interface GoogleSheetsDataSourceOptions extends DataSourceOptions {
16+
defaultSheetID?: string;
17+
}
18+
1519
export interface CacheInfo {
1620
hit: boolean;
1721
count: number;

0 commit comments

Comments
 (0)