Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/puny-needles-wish.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'grafana-infinity-datasource': patch
---

Migrated deprecated `Select` component to `Combobox` across the plugin
5 changes: 5 additions & 0 deletions .changeset/swift-numbers-smoke.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'grafana-infinity-datasource': minor
---

Updated the required grafana version to 11.6 from 10.4
2 changes: 2 additions & 0 deletions cspell.config.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"website/out/**",
"website/public/**",
"renovate.json",
"jest*.js",
"docker-compose-debug.yaml",
"docker-compose.yaml"
],
Expand All @@ -39,6 +40,7 @@
"clsx",
"cmdk",
"Collapsable",
"Combobox",
"compatibilitycheck",
"contentlayer",
"countif",
Expand Down
25 changes: 24 additions & 1 deletion docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,27 @@ services:
- ./provisioning/dashboards-actual/:/dashboards/
- ./conf:/grafana-config
environment:
- GF_PATHS_CONFIG=/grafana-config/grafana.ini
- GF_PATHS_CONFIG=/grafana-config/grafana.ini
depends_on:
- httpbin
networks:
- infinity-test-network

httpbin:
image: mccutchen/go-httpbin:latest
container_name: 'infinity-httpbin'
ports:
- "8888:8080"
networks:
- infinity-test-network
environment:
- MAX_BODY_SIZE=10485760
healthcheck:
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:8080/status/200"]
interval: 5s
timeout: 3s
retries: 3

networks:
infinity-test-network:
driver: bridge
5 changes: 5 additions & 0 deletions jest-setup.js
Original file line number Diff line number Diff line change
@@ -1,2 +1,7 @@
// Jest setup provided by Grafana scaffolding
import './.config/jest-setup';

// Mock canvas context for Combobox component
HTMLCanvasElement.prototype.getContext = jest.fn(() => ({
measureText: jest.fn(() => ({ width: 100 })),
})) as any;
7 changes: 7 additions & 0 deletions playwright.config.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
/// <reference types="node" />
import type { PluginOptions } from '@grafana/plugin-e2e';
import { defineConfig, devices } from '@playwright/test';
import { dirname } from 'node:path';

const pluginE2eAuth = `${dirname(require.resolve('@grafana/plugin-e2e'))}/auth`;

// Test auth server URL for authentication E2E tests
// Health checks are executed by the Grafana backend (inside Docker),
// so the URL must be reachable from within the Docker network.
// In Docker: http://httpbin:8080 (service name + internal port)
export const testAuthServerUrl = process.env.TEST_AUTH_SERVER_URL || 'http://httpbin:8080';

/**
* Read environment variables from file.
* https://github.com/motdotla/dotenv
Expand Down
4 changes: 2 additions & 2 deletions src/app/parsers/filter.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { FilterOperator } from '@/constants';
import type { GrafanaTableRow, InfinityColumn, InfinityFilter } from '@/types';
import type { SelectableValue } from '@grafana/data';
import type { ComboboxOption } from '@grafana/ui';

export const filterOperators: Array<SelectableValue<FilterOperator>> = [
export const filterOperators: Array<ComboboxOption<FilterOperator>> = [
{ label: 'Equals', value: FilterOperator.Equals },
{ label: 'Not Equals', value: FilterOperator.NotEquals },
{ label: 'Contains', value: FilterOperator.Contains },
Expand Down
6 changes: 3 additions & 3 deletions src/components/FormatSelector.test.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { render, within } from '@testing-library/react';
import { render } from '@testing-library/react';
import React from 'react';
import { DefaultInfinityQuery } from '@/constants';
import { Components } from '@/selectors';
Expand All @@ -17,7 +17,7 @@ describe('FormatSelector', () => {
expect(wrapper).not.toBeNull();
expect(wrapper.getByText(LabelText)).toBeInTheDocument();
expect(wrapper.getByTitle(DropdownPlaceholderTitle)).toBeInTheDocument();
expect(within(wrapper.getByTitle(DropdownPlaceholderTitle)).getByText('Table')).toBeInTheDocument();
expect(wrapper.getByDisplayValue('Table')).toBeInTheDocument();
});
it('renders timeseries format', () => {
const onChange = jest.fn();
Expand All @@ -27,6 +27,6 @@ describe('FormatSelector', () => {
expect(wrapper).not.toBeNull();
expect(wrapper.getByText(LabelText)).toBeInTheDocument();
expect(wrapper.getByTitle(DropdownPlaceholderTitle)).toBeInTheDocument();
expect(within(wrapper.getByTitle(DropdownPlaceholderTitle)).getByText('Time Series')).toBeInTheDocument();
expect(wrapper.getByDisplayValue('Time Series')).toBeInTheDocument();
});
});
4 changes: 2 additions & 2 deletions src/components/FormatSelector.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Select } from '@grafana/ui';
import { Combobox } from '@grafana/ui';
import React from 'react';
import { isDataQuery } from '@/app/utils';
import { EditorField } from '@/components/extended/EditorField';
Expand Down Expand Up @@ -31,7 +31,7 @@ export const FormatSelector = (props: FormatSelectorProps) => {
return (
<EditorField label={Components.QueryEditor.Format.Label.Text} horizontal={true}>
<div title={Components.QueryEditor.Format.Dropdown.PlaceHolder.Title} data-testid="infinity-query-format-selector">
<Select className="min-width-12 width-12" value={query.format} options={getFormats()} onChange={(e) => onFormatChange(e.value as InfinityQueryFormat)} menuShouldPortal={true} />
<Combobox width={24} value={query.format} options={getFormats()} onChange={(e) => onFormatChange(e.value as InfinityQueryFormat)} />
</div>
</EditorField>
);
Expand Down
11 changes: 4 additions & 7 deletions src/components/GlobalQuerySelector.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
import React from 'react';
import { Select } from '@grafana/ui';
import { Combobox, type ComboboxOption } from '@grafana/ui';
import { EditorField } from '@/components/extended/EditorField';
import type { GlobalInfinityQuery, InfinityQuery } from '@/types';
import type { SelectableValue } from '@grafana/data';

export const GlobalQuerySelector = (props: { query: InfinityQuery; instanceSettings: any; onChange: (e: InfinityQuery) => void; onRunQuery: () => void }) => {
const { query, onChange, onRunQuery, instanceSettings } = props;
if (query.type !== 'global') {
return <></>;
}
const global_queries: SelectableValue[] = (instanceSettings?.jsonData?.global_queries || []).map((q: GlobalInfinityQuery) => {
const global_queries: Array<ComboboxOption<string>> = (instanceSettings?.jsonData?.global_queries || []).map((q: GlobalInfinityQuery) => {
return {
label: q.name,
value: q.id,
Expand All @@ -21,11 +20,9 @@ export const GlobalQuerySelector = (props: { query: InfinityQuery; instanceSetti
};
return (
<>
<EditorField label="Source">
<EditorField label="Source" horizontal={true}>
{global_queries.length > 0 ? (
<div style={{ marginRight: '5px' }}>
<Select options={global_queries} value={query.global_query_id} onChange={(e) => onGlobalQueryIDChange(e.value as string)} menuShouldPortal={true}></Select>
</div>
<Combobox options={global_queries} value={query.global_query_id} onChange={(e) => onGlobalQueryIDChange(e.value)}></Combobox>
) : (
<label className="gf-form-label width-8">No Queries found</label>
)}
Expand Down
8 changes: 4 additions & 4 deletions src/components/QueryColumnItem.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { useState } from 'react';
import { isDataQuery, isBackendQuery } from '@/app/utils';
import { INFINITY_COLUMN_FORMATS } from '@/constants';
import { Select, InlineFormLabel, Input } from '@grafana/ui';
import { Combobox, InlineFormLabel, Input } from '@grafana/ui';
import type { InfinityColumn, InfinityColumnFormat, InfinityQuery } from '@/types';

interface QueryColumnItemProps {
Expand Down Expand Up @@ -45,19 +45,19 @@ export const QueryColumnItem = (props: QueryColumnItemProps) => {
<InlineFormLabel width={2}>as</InlineFormLabel>
<Input value={text} width={20} placeholder="Title" onChange={(e) => setText(e.currentTarget.value)} onBlur={onTextChange}></Input>
<InlineFormLabel width={5}>format as</InlineFormLabel>
<Select width={24} value={column.type} options={INFINITY_COLUMN_FORMATS} onChange={(e) => onFormatChange(e.value as InfinityColumnFormat)} menuShouldPortal={true}></Select>
<Combobox width={24} value={column.type} options={INFINITY_COLUMN_FORMATS} onChange={(e) => onFormatChange(e.value as InfinityColumnFormat)}></Combobox>
{(isBackendQuery(query) || query.type === 'google-sheets') && column.type === 'timestamp' && (
<>
<div style={{ marginLeft: '5px' }}>
<InlineFormLabel width={11} tooltip={'Timestamp format in golang layout. Example: 2006-01-02T15:04:05Z07:00'}>
Time Format (optional)
</InlineFormLabel>
</div>
<Select
<Combobox
width={30}
value={column.timestampFormat}
placeholder="Auto"
allowCustomValue={true}
createCustomValue={true}
options={[
{ value: '', label: 'Auto' },
{ value: '2006-01-02T15:04:05Z07:00', label: 'Default ISO' },
Expand Down
7 changes: 3 additions & 4 deletions src/components/SourceSelector.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';
import { INFINITY_SOURCES } from '@/constants';
import { Select } from '@grafana/ui';
import { Combobox } from '@grafana/ui';
import { EditorField } from '@/components/extended/EditorField';
import type { InfinityQuery, InfinityQuerySources } from '@/types';

Expand All @@ -16,14 +16,13 @@ export const SourceSelector = (props: { query: InfinityQuery; onChange: (e: Infi
};
return (
<EditorField label={query.type === 'series' ? 'Scenario' : 'Source'} horizontal={true}>
<Select
<Combobox
width={18}
data-testid="infinity-query-source-selector"
options={supportedSources}
value={query.source || 'url'}
onChange={(e) => onSourceChange(e.value as InfinityQuerySources)}
menuShouldPortal={true}
></Select>
></Combobox>
</EditorField>
);
};
7 changes: 3 additions & 4 deletions src/components/TypeSelector.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import React from 'react';
import { Select } from '@grafana/ui';
import { Combobox, type ComboboxOption } from '@grafana/ui';
import { SCRAP_QUERY_TYPES } from '@/constants';
import { EditorField } from '@/components/extended/EditorField';
import type { EditorMode, InfinityQuery, InfinityQueryType } from '@/types';
import type { SelectableValue } from '@grafana/data';

export const TypeSelector = (props: { query: InfinityQuery; onChange: (e: InfinityQuery) => void; onRunQuery: () => void; mode: EditorMode }) => {
const { query, mode, onChange, onRunQuery } = props;
const getTypes = (): Array<SelectableValue<InfinityQueryType>> => {
const getTypes = (): Array<ComboboxOption<InfinityQueryType>> => {
switch (mode) {
case 'standard':
return SCRAP_QUERY_TYPES;
Expand All @@ -25,7 +24,7 @@ export const TypeSelector = (props: { query: InfinityQuery; onChange: (e: Infini
};
return (
<EditorField label="Type" tag={query.type === 'google-sheets' ? 'beta' : ''} horizontal={true}>
<Select width={21} options={getTypes()} onChange={(e) => onTypeChange(e.value as InfinityQueryType)} value={query.type || 'json'} menuShouldPortal={true}></Select>
<Combobox width={21} options={getTypes()} onChange={(e) => onTypeChange(e.value as InfinityQueryType)} value={query.type || 'json'}></Combobox>
</EditorField>
);
};
2 changes: 1 addition & 1 deletion src/components/config/SecureFieldsEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ const SecureFieldEditor = ({
marginBottom: '4px',
};
return (
<div style={layoutStyle}>
<div style={layoutStyle} data-testid={`infinity-config-secure-field-${label}`}>
<FormField
label={label || 'Key'}
labelWidth={labelWidth || 8}
Expand Down
14 changes: 7 additions & 7 deletions src/constants.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { SelectableValue } from '@grafana/data';
import type { ComboboxOption } from '@grafana/ui';
import type { InfinityQuery, InfinityQueryType, InfinityQueryFormat, InfinityColumnFormat, ScrapQuerySources, VariableQueryType, AzureBlobCloudType } from '@/types';

export const DefaultInfinityQuery: InfinityQuery = {
Expand All @@ -13,7 +13,7 @@ export const DefaultInfinityQuery: InfinityQuery = {
filters: [],
};

export const SCRAP_QUERY_TYPES: Array<SelectableValue<InfinityQueryType>> = [
export const SCRAP_QUERY_TYPES: Array<ComboboxOption<InfinityQueryType>> = [
{ label: 'JSON', value: 'json' },
{ label: 'CSV', value: 'csv' },
{ label: 'TSV', value: 'tsv' },
Expand All @@ -27,7 +27,7 @@ export const SCRAP_QUERY_TYPES: Array<SelectableValue<InfinityQueryType>> = [
{ label: 'Global Query', value: 'global' },
{ label: 'Transformations', value: 'transformations' },
];
export const INFINITY_RESULT_FORMATS: Array<SelectableValue<InfinityQueryFormat>> = [
export const INFINITY_RESULT_FORMATS: Array<ComboboxOption<InfinityQueryFormat>> = [
{ label: 'Data Frame', value: 'dataframe' },
{ label: 'Table', value: 'table' },
{ label: 'Logs', value: 'logs' },
Expand All @@ -45,7 +45,7 @@ export const INFINITY_SOURCES: ScrapQuerySources[] = [
{ label: 'Random Walk', value: 'random-walk', supported_types: ['series'] },
{ label: 'Expression', value: 'expression', supported_types: ['series'] },
];
export const INFINITY_COLUMN_FORMATS: Array<SelectableValue<InfinityColumnFormat>> = [
export const INFINITY_COLUMN_FORMATS: Array<ComboboxOption<InfinityColumnFormat>> = [
{ label: 'String', value: 'string' },
{ label: 'Number', value: 'number' },
{ label: 'Time', value: 'timestamp' },
Expand All @@ -54,7 +54,7 @@ export const INFINITY_COLUMN_FORMATS: Array<SelectableValue<InfinityColumnFormat
{ label: 'Boolean', value: 'boolean' },
];

export const variableQueryTypes: Array<SelectableValue<VariableQueryType>> = [
export const variableQueryTypes: Array<ComboboxOption<VariableQueryType>> = [
{
label: 'Infinity',
value: 'infinity',
Expand Down Expand Up @@ -95,7 +95,7 @@ export enum FilterOperator {
NumberGreaterThanOrEqualTo = '>=',
}

export const AWSRegions: Array<SelectableValue<string>> = [
export const AWSRegions: Array<ComboboxOption<string>> = [
'af-south-1',
'ap-east-1',
'ap-northeast-1',
Expand Down Expand Up @@ -127,7 +127,7 @@ export const AWSRegions: Array<SelectableValue<string>> = [

export const AzureBlobCloudTypeDefault: AzureBlobCloudType = 'AzureCloud';

export const AzureBlobRegions: Array<SelectableValue<AzureBlobCloudType>> = [
export const AzureBlobRegions: Array<ComboboxOption<AzureBlobCloudType>> = [
{ value: 'AzureCloud', label: 'Azure' },
{ value: 'AzureUSGovernment', label: 'Azure US Government' },
{ value: 'AzureChinaCloud', label: 'Azure China' },
Expand Down
4 changes: 2 additions & 2 deletions src/editors/config/Auth.AzureBlob.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react';
import { Stack, InlineLabel, Input, SecretInput, Select } from '@grafana/ui';
import { Stack, InlineLabel, Input, SecretInput, Combobox } from '@grafana/ui';
import { onUpdateDatasourceSecureJsonDataOption, DataSourcePluginOptionsEditorProps } from '@grafana/data';
import { Components } from '@/selectors';
import { AzureBlobRegions, AzureBlobCloudTypeDefault } from '@/constants';
Expand Down Expand Up @@ -28,7 +28,7 @@ export const AzureBlobAuthEditor = (
<InlineLabel width={24} tooltip={RegionSelector.tooltip}>
{RegionSelector.label}
</InlineLabel>
<Select
<Combobox
width={24}
aria-label={RegionSelector.ariaLabel}
options={AzureBlobRegions}
Expand Down
4 changes: 2 additions & 2 deletions src/editors/config/Auth.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { css } from '@emotion/css';
import { onUpdateDatasourceSecureJsonDataOption, DataSourcePluginOptionsEditorProps, SelectableValue } from '@grafana/data';
import { Icon, InlineFormLabel, LegacyForms, RadioButtonGroup, Select, Grid, Link, useTheme2 } from '@grafana/ui';
import { Icon, InlineFormLabel, LegacyForms, RadioButtonGroup, Combobox, Grid, Link, useTheme2 } from '@grafana/ui';
import React, { useState } from 'react';
import { AllowedHostsEditor } from '@/editors/config/AllowedHosts';
import { AzureBlobAuthEditor } from '@/editors/config/Auth.AzureBlob';
Expand Down Expand Up @@ -230,7 +230,7 @@ export const AuthEditor = (props: DataSourcePluginOptionsEditorProps<InfinityOpt
<>
<div className="gf-form">
<InlineFormLabel>Region</InlineFormLabel>
<Select width={24} options={AWSRegions} placeholder="us-east-2" onChange={(e) => onAwsRegionChange(e.value!)} value={props.options.jsonData?.aws?.region || ''} />
<Combobox width={24} options={AWSRegions} placeholder="us-east-2" onChange={(e) => onAwsRegionChange(e.value!)} value={props.options.jsonData?.aws?.region || ''} />
</div>
<div className="gf-form">
<FormField
Expand Down
3 changes: 2 additions & 1 deletion src/editors/config/CustomHealthCheckEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ export const CustomHealthCheckEditor = (props: DataSourcePluginOptionsEditorProp
const { jsonData = {} } = options;
return (
<>
<div className="gf-form">
<div className="gf-form" data-testid="enable_custom_healthcheck_wrapper">
<InlineLabel width={36}>Enable custom health check</InlineLabel>
<InlineSwitch
data-testid="enable_custom_healthcheck_switch"
value={jsonData.customHealthCheckEnabled || false}
onChange={(e) => onOptionsChange({ ...options, jsonData: { ...jsonData, customHealthCheckEnabled: e.currentTarget.checked } })}
/>
Expand Down
8 changes: 4 additions & 4 deletions src/editors/config/OtherAuthProviders.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { InlineFormLabel, Modal, Select } from '@grafana/ui';
import { InlineFormLabel, Modal, Combobox, type ComboboxOption } from '@grafana/ui';
import React, { useState } from 'react';
import { GuidedBasicAuthEditor } from '@/editors/config/guided-config/GuidedBasicAuthEditor';
import { GoogleJWTEditor } from '@/editors/config/guided-config/GoogleJWT';
import type { InfinityOptions } from '@/types';
import type { DataSourceSettings, SelectableValue } from '@grafana/data';
import type { DataSourceSettings } from '@grafana/data';

export const OthersAuthentication = (props: {
options: DataSourceSettings<InfinityOptions, {}>;
Expand All @@ -12,7 +12,7 @@ export const OthersAuthentication = (props: {
onClose: () => void;
}) => {
const [provider, setProvider] = useState('Other');
const providers: Array<SelectableValue<string>> = [
const providers: Array<ComboboxOption<string>> = [
{ label: 'Github', value: 'github' },
{ label: 'Google JWT', value: 'google-jwt' },
];
Expand All @@ -26,7 +26,7 @@ export const OthersAuthentication = (props: {
<Modal title="Other Authentication" isOpen={isOpen} onDismiss={onClose}>
<div className="gf-form">
<InlineFormLabel width={12}>Provider</InlineFormLabel>
<Select value={provider} options={providers} onChange={(e) => setProvider(e?.value!)} isClearable={true} menuShouldPortal={true}></Select>
<Combobox value={provider} options={providers} onChange={(e) => setProvider(e?.value!)} isClearable={true}></Combobox>
</div>
{provider === 'github' && (
<GuidedBasicAuthEditor
Expand Down
2 changes: 1 addition & 1 deletion src/editors/config/guided-config/GoogleJWT.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import type { InfinityOptions } from '@/types';
type GoogleJWTEditorProps = {
options: DataSourceSettings<InfinityOptions, {}>;
onChange: (options: DataSourceSettings<InfinityOptions, {}>) => void;
children?: React.ReactChild;
children?: React.ReactNode;
moreLink?: string;
};

Expand Down
Loading
Loading