diff --git a/.changeset/silver-vans-like.md b/.changeset/silver-vans-like.md new file mode 100644 index 00000000..29097102 --- /dev/null +++ b/.changeset/silver-vans-like.md @@ -0,0 +1,5 @@ +--- +'grafana-google-sheets-datasource': minor +--- + +show title in selectors with spreadsheet ID in description diff --git a/src/DataSource.ts b/src/DataSource.ts index 1dfc470c..d7f58cad 100644 --- a/src/DataSource.ts +++ b/src/DataSource.ts @@ -64,7 +64,11 @@ export class DataSource extends DataSourceWithBackend>> { return this.getResource('spreadsheets').then(({ spreadsheets }) => spreadsheets - ? Object.entries(spreadsheets).map(([value, label]) => ({ label, value }) as SelectableValue) + ? Object.entries(spreadsheets).map(([value, label]) => ({ + label, + value, + description: value, + }) as SelectableValue) : [] ); } diff --git a/src/components/ConfigEditor.test.tsx b/src/components/ConfigEditor.test.tsx index 08b4bd81..1a5d14e8 100644 --- a/src/components/ConfigEditor.test.tsx +++ b/src/components/ConfigEditor.test.tsx @@ -15,7 +15,7 @@ jest.mock('@grafana/runtime', () => ({ Promise.resolve({ getSpreadSheets: () => Promise.resolve([ - { label: 'label1', value: 'value1' }, + { label: 'label1', value: 'value1', description: 'value1' }, ]), }), }), diff --git a/src/components/ConfigEditor.tsx b/src/components/ConfigEditor.tsx index 24940211..a6cf0828 100644 --- a/src/components/ConfigEditor.tsx +++ b/src/components/ConfigEditor.tsx @@ -1,21 +1,25 @@ import { DataSourcePluginOptionsEditorProps, onUpdateDatasourceSecureJsonDataOption, + SelectableValue, } from '@grafana/data'; -import { AuthConfig, DataSourceOptions } from '@grafana/google-sdk'; +import { AuthConfig } from '@grafana/google-sdk'; import { DataSourceDescription } from '@grafana/plugin-ui'; import { Field, SecretInput, SegmentAsync, Divider } from '@grafana/ui'; -import React from 'react'; -import { GoogleSheetsSecureJSONData, googleSheetsAuthTypes, GoogleSheetsAuth } from '../types'; +import React, { useState, useEffect } from 'react'; +import { GoogleSheetsSecureJSONData, googleSheetsAuthTypes, GoogleSheetsAuth, GoogleSheetsDataSourceOptions } from '../types'; import { getBackwardCompatibleOptions } from '../utils'; import { ConfigurationHelp } from './ConfigurationHelp'; import { getDataSourceSrv } from '@grafana/runtime'; import { DataSource } from '../DataSource'; -export type Props = DataSourcePluginOptionsEditorProps; +export type Props = DataSourcePluginOptionsEditorProps; export function ConfigEditor(props: Props) { const options = getBackwardCompatibleOptions(props.options); + const [selectedSheetOption, setSelectedSheetOption] = useState | string | undefined>( + options.jsonData.defaultSheetID + ); const apiKeyProps = { isConfigured: Boolean(options.secureJsonFields.apiKey), @@ -43,6 +47,25 @@ export function ConfigEditor(props: Props) { return []; } }; + + useEffect(() => { + const currentValue = options.jsonData.defaultSheetID; + if (!currentValue || !options.uid) { + setSelectedSheetOption(currentValue); + return; + } + const updateSelectedOption = async () => { + try { + const ds = (await getDataSourceSrv().get(options.uid!)) as DataSource; + const sheetOptions = await ds.getSpreadSheets(); + const matchingOption = sheetOptions.find((opt) => opt.value === currentValue); + setSelectedSheetOption(matchingOption || currentValue); + } catch { + setSelectedSheetOption(currentValue); + } + }; + updateSelectedOption(); + }, [options.jsonData.defaultSheetID, options.uid]); return ( <> { + const sheetId = typeof value === 'string' ? value : value?.value; + setSelectedSheetOption(value); props.onOptionsChange({ ...options, jsonData: { ...options.jsonData, - defaultSheetID: value?.value || value, - } as any, + defaultSheetID: sheetId, + }, }); }} /> diff --git a/src/components/QueryEditor.tsx b/src/components/QueryEditor.tsx index ce57675e..e5884632 100644 --- a/src/components/QueryEditor.tsx +++ b/src/components/QueryEditor.tsx @@ -1,4 +1,4 @@ -import { QueryEditorProps } from '@grafana/data'; +import { QueryEditorProps, SelectableValue } from '@grafana/data'; import { DataSourceOptions } from '@grafana/google-sdk'; import { InlineFieldRow, InlineFormLabel, InlineSwitch, Input, LinkButton, Segment, SegmentAsync } from '@grafana/ui'; import React, { ChangeEvent, PureComponent } from 'react'; @@ -51,6 +51,10 @@ export const formatCacheTimeLabel = (s: number = defaultCacheDuration) => { }; export class QueryEditor extends PureComponent { + state = { + selectedSheetOption: undefined as SelectableValue | string | undefined, + }; + componentDidMount() { if (!this.props.query.hasOwnProperty('cacheDurationSeconds')) { this.props.onChange({ @@ -58,8 +62,34 @@ export class QueryEditor extends PureComponent { cacheDurationSeconds: defaultCacheDuration, // um :( }); } + this.updateSelectedSheetOption(); + } + + componentDidUpdate(prevProps: Props) { + if (prevProps.query.spreadsheet !== this.props.query.spreadsheet) { + this.updateSelectedSheetOption(); + } } + updateSelectedSheetOption = async () => { + const { query, datasource } = this.props; + if (!query.spreadsheet) { + this.setState({ selectedSheetOption: undefined }); + return; + } + try { + const sheetOptions = await datasource.getSpreadSheets(); + const matchingOption = sheetOptions.find((opt) => opt.value === query.spreadsheet); + if (matchingOption) { + this.setState({ selectedSheetOption: matchingOption }); + } else { + this.setState({ selectedSheetOption: query.spreadsheet }); + } + } catch { + this.setState({ selectedSheetOption: query.spreadsheet }); + } + }; + onRangeChange = (event: ChangeEvent) => { this.props.onChange({ ...this.props.query, @@ -71,10 +101,12 @@ export class QueryEditor extends PureComponent { const { query, onRunQuery, onChange } = this.props; if (!item.value) { + this.setState({ selectedSheetOption: undefined }); return; // ignore delete? } const v = item.value; + this.setState({ selectedSheetOption: item }); // Check for pasted full URLs if (/(.*)\/spreadsheets\/d\/(.*)/.test(v)) { onChange({ ...query, ...getGoogleSheetRangeInfoFromURL(v) }); @@ -118,9 +150,19 @@ export class QueryEditor extends PureComponent { Spreadsheet ID datasource.getSpreadSheets()} + loadOptions={async () => { + const options = await datasource.getSpreadSheets(); + const { query } = this.props; + if (query.spreadsheet) { + const matchingOption = options.find((opt) => opt.value === query.spreadsheet); + if (matchingOption && this.state.selectedSheetOption !== matchingOption) { + this.setState({ selectedSheetOption: matchingOption }); + } + } + return options; + }} placeholder="Enter SpreadsheetID" - value={query.spreadsheet} + value={this.state.selectedSheetOption ?? query.spreadsheet} allowCustomValue={true} onChange={this.onSpreadsheetIDChange} /> diff --git a/src/types.ts b/src/types.ts index 4d98d66d..c85a0690 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,5 +1,5 @@ import { DataQuery } from '@grafana/schema'; -import { GoogleAuthType, GOOGLE_AUTH_TYPE_OPTIONS, DataSourceSecureJsonData } from '@grafana/google-sdk'; +import { GoogleAuthType, GOOGLE_AUTH_TYPE_OPTIONS, DataSourceSecureJsonData, DataSourceOptions } from '@grafana/google-sdk'; export const GoogleSheetsAuth = { ...GoogleAuthType, @@ -12,6 +12,10 @@ export interface GoogleSheetsSecureJSONData extends DataSourceSecureJsonData { apiKey?: string; } +export interface GoogleSheetsDataSourceOptions extends DataSourceOptions { + defaultSheetID?: string; +} + export interface CacheInfo { hit: boolean; count: number;