From 1ce333a572f8e04bb50f1c522f6ab2d4453530b8 Mon Sep 17 00:00:00 2001 From: Alyssa Joyner <58453566+alyssajoyner@users.noreply.github.com> Date: Tue, 12 Aug 2025 14:23:51 -0600 Subject: [PATCH] [InfluxDB] Config design improvements (#108562) --- .../DataSourceSettings/CertificationKey.tsx | 5 +- .../AdvancedDBConnectionSettings.tsx | 43 ++++--- .../editor/config-v2/AdvancedHttpSettings.tsx | 11 +- .../editor/config-v2/AuthSettings.test.tsx | 17 +++ .../editor/config-v2/AuthSettings.tsx | 117 ++++++++++++------ .../editor/config-v2/ConfigEditor.tsx | 35 ++++-- .../config-v2/DatabaseConnectionSection.tsx | 19 +-- .../config-v2/InfluxFluxDBConnection.tsx | 7 +- .../config-v2/InfluxInfluxQLDBConnection.tsx | 7 +- .../config-v2/InfluxSQLDBConnection.tsx | 5 +- .../editor/config-v2/LeftSideBar.tsx | 2 +- .../config-v2/UrlAndAuthenticationSection.tsx | 15 ++- .../components/editor/config-v2/constants.ts | 5 +- .../app/plugins/datasource/influxdb/types.ts | 1 + 14 files changed, 191 insertions(+), 98 deletions(-) diff --git a/packages/grafana-ui/src/components/DataSourceSettings/CertificationKey.tsx b/packages/grafana-ui/src/components/DataSourceSettings/CertificationKey.tsx index 86f00684e1b..d8382cd2301 100644 --- a/packages/grafana-ui/src/components/DataSourceSettings/CertificationKey.tsx +++ b/packages/grafana-ui/src/components/DataSourceSettings/CertificationKey.tsx @@ -12,15 +12,16 @@ interface Props { label: string; hasCert: boolean; placeholder: string; + useGrow?: boolean; onChange: (event: ChangeEvent) => void; onClick: (event: MouseEvent) => void; } -export const CertificationKey = ({ hasCert, label, onChange, onClick, placeholder }: Props) => { +export const CertificationKey = ({ hasCert, label, onChange, onClick, placeholder, useGrow }: Props) => { return ( - + {hasCert ? ( ) : ( diff --git a/public/app/plugins/datasource/influxdb/components/editor/config-v2/AdvancedDBConnectionSettings.tsx b/public/app/plugins/datasource/influxdb/components/editor/config-v2/AdvancedDBConnectionSettings.tsx index d3254abbdcb..fa7f7089ee7 100644 --- a/public/app/plugins/datasource/influxdb/components/editor/config-v2/AdvancedDBConnectionSettings.tsx +++ b/public/app/plugins/datasource/influxdb/components/editor/config-v2/AdvancedDBConnectionSettings.tsx @@ -11,7 +11,7 @@ import { InlineFieldRow, InlineField, Combobox, InlineSwitch, Input, Space, useS import { InfluxVersion } from '../../../types'; -import { getInlineLabelStyles, HTTP_MODES } from './constants'; +import { DB_SETTINGS_LABEL_WIDTH, getInlineLabelStyles, HTTP_MODES } from './constants'; import { trackInfluxDBConfigV2AdvancedDbConnectionSettingsAutocompleteClicked, trackInfluxDBConfigV2AdvancedDbConnectionSettingsHTTPMethodClicked, @@ -55,13 +55,13 @@ export const AdvancedDbConnectionSettings = (props: Props) => { httpMode.value === options.jsonData.httpMode)} options={HTTP_MODES} onChange={onUpdateDatasourceJsonDataOptionSelect(props, 'httpMode')} @@ -72,28 +72,15 @@ export const AdvancedDbConnectionSettings = (props: Props) => { )} - {options.jsonData.version === InfluxVersion.SQL && ( - - - - - - )} - {(options.jsonData.version === InfluxVersion.InfluxQL || options.jsonData.version === InfluxVersion.Flux) && ( { { { /> + {options.jsonData.version === InfluxVersion.SQL && ( + + + + + + )} )} diff --git a/public/app/plugins/datasource/influxdb/components/editor/config-v2/AdvancedHttpSettings.tsx b/public/app/plugins/datasource/influxdb/components/editor/config-v2/AdvancedHttpSettings.tsx index 2fe868514d7..8d66374eef1 100644 --- a/public/app/plugins/datasource/influxdb/components/editor/config-v2/AdvancedHttpSettings.tsx +++ b/public/app/plugins/datasource/influxdb/components/editor/config-v2/AdvancedHttpSettings.tsx @@ -26,9 +26,14 @@ export type Props = DataSourcePluginOptionsEditorProps; export const AdvancedHttpSettings = ({ options, onOptionsChange }: Props) => { const styles = useStyles2(getInlineLabelStyles); - const [advancedHttpSettingsIsOpen, setAdvancedHttpSettingsIsOpen] = useState( - () => 'keepCookies' in options.jsonData || 'timeout' in options.jsonData - ); + const [advancedHttpSettingsIsOpen, setAdvancedHttpSettingsIsOpen] = useState(() => { + const keys = Object.keys(options.jsonData); + return ( + 'keepCookies' in options.jsonData || + 'timeout' in options.jsonData || + keys.some((key) => key.includes('httpHeaderName')) + ); + }); return ( <> diff --git a/public/app/plugins/datasource/influxdb/components/editor/config-v2/AuthSettings.test.tsx b/public/app/plugins/datasource/influxdb/components/editor/config-v2/AuthSettings.test.tsx index 4a51bb94728..340942d1896 100644 --- a/public/app/plugins/datasource/influxdb/components/editor/config-v2/AuthSettings.test.tsx +++ b/public/app/plugins/datasource/influxdb/components/editor/config-v2/AuthSettings.test.tsx @@ -127,5 +127,22 @@ describe('AuthSettings', () => { expect(skipSwitch).not.toBeChecked(); }); }); + + describe('With Credentials toggle', () => { + it('toggles checked state of the switch', () => { + const withCredentialsSwitch = screen.getByTestId('influxdb-v2-config-auth-settings-with-credentials'); + + // Default unchecked + expect(withCredentialsSwitch).not.toBeChecked(); + + // Enable + fireEvent.click(withCredentialsSwitch); + expect(withCredentialsSwitch).toBeChecked(); + + // Disable + fireEvent.click(withCredentialsSwitch); + expect(withCredentialsSwitch).not.toBeChecked(); + }); + }); }); }); diff --git a/public/app/plugins/datasource/influxdb/components/editor/config-v2/AuthSettings.tsx b/public/app/plugins/datasource/influxdb/components/editor/config-v2/AuthSettings.tsx index 1f359afb594..dbcf14c3560 100644 --- a/public/app/plugins/datasource/influxdb/components/editor/config-v2/AuthSettings.tsx +++ b/public/app/plugins/datasource/influxdb/components/editor/config-v2/AuthSettings.tsx @@ -1,5 +1,6 @@ import { cx } from '@emotion/css'; import { useCallback, useMemo, useState } from 'react'; +import { useWindowSize } from 'react-use'; import { onUpdateDatasourceOption, @@ -7,6 +8,7 @@ import { updateDatasourcePluginResetOption, } from '@grafana/data'; import { AuthMethod, convertLegacyAuthProps } from '@grafana/plugin-ui'; +import { config } from '@grafana/runtime'; import { Box, CertificationKey, @@ -20,9 +22,15 @@ import { useStyles2, Text, Stack, + InlineLabel, } from '@grafana/ui'; -import { AUTH_RADIO_BUTTON_OPTIONS, getInlineLabelStyles, RADIO_BUTTON_OPTIONS } from './constants'; +import { + AUTH_RADIO_BUTTON_OPTIONS, + DB_SETTINGS_LABEL_WIDTH, + getInlineLabelStyles, + RADIO_BUTTON_OPTIONS, +} from './constants'; import { trackInfluxDBConfigV2AuthSettingsAuthMethodSelected, trackInfluxDBConfigV2AuthSettingsToggleClicked, @@ -30,6 +38,7 @@ import { import { Props } from './types'; type AuthOptionState = { + noAuth: boolean; basicAuth: boolean; tlsClientAuth: boolean; caCert: boolean; @@ -41,10 +50,8 @@ type AuthOptionState = { export const AuthSettings = (props: Props) => { const { options, onOptionsChange } = props; const styles = useStyles2(getInlineLabelStyles); + const { width } = useWindowSize(); - /** - * Derived props from legacy helpers - */ const authProps = useMemo( () => convertLegacyAuthProps({ @@ -54,36 +61,38 @@ export const AuthSettings = (props: Props) => { [options, onOptionsChange] ); - /** - * Selected authentication method. Fallback to the most common if the selected one is missing. - */ const isAuthMethod = (v: unknown): v is AuthMethod => v === AuthMethod.NoAuth || v === AuthMethod.BasicAuth || v === AuthMethod.OAuthForward; - const selectedMethod = useMemo(() => { - if (isAuthMethod(authProps.selectedMethod)) { - return authProps.selectedMethod; - } - return isAuthMethod(authProps.mostCommonMethod) ? authProps.mostCommonMethod : undefined; - }, [authProps.selectedMethod, authProps.mostCommonMethod]); - - /** - * Local UI state - */ const [authOptions, setAuthOptions] = useState({ - basicAuth: selectedMethod === AuthMethod.BasicAuth, + noAuth: (!options.basicAuth && !options.jsonData.oauthPassThru) ?? false, + basicAuth: options.basicAuth ?? false, tlsClientAuth: authProps.TLS?.TLSClientAuth.enabled ?? false, caCert: authProps.TLS?.selfSignedCertificate.enabled ?? false, skipTLS: authProps.TLS?.skipTLSVerification.enabled ?? false, - oAuthForward: selectedMethod === AuthMethod.OAuthForward, + oAuthForward: options.jsonData.oauthPassThru ?? false, withCredentials: options.withCredentials ?? false, }); - /** - * Expand/collapse top–level section - */ - const [authenticationSettingsIsOpen, setAuthenticationSettingsIsOpen] = useState( - Object.values(authOptions).some(Boolean) + const selectedMethod = useMemo(() => { + if (isAuthMethod(authProps.selectedMethod) && authProps.selectedMethod !== AuthMethod.CrossSiteCredentials) { + return authProps.selectedMethod; + } + + switch (!!authOptions) { + case authOptions.basicAuth: + return AuthMethod.BasicAuth; + case authOptions.oAuthForward: + return AuthMethod.OAuthForward; + case authOptions.noAuth: + return AuthMethod.NoAuth; + default: + return undefined; + } + }, [authProps.selectedMethod, authOptions]); + + const [authenticationSettingsIsOpen, setAuthenticationSettingsIsOpen] = useState(() => + Object.entries(authOptions).some(([key, value]) => key !== 'noAuth' && Boolean(value)) ); const toggleOpen = useCallback(() => { @@ -98,8 +107,10 @@ export const AuthSettings = (props: Props) => { authProps.onAuthMethodSelect(option); setAuthOptions((prev) => ({ ...prev, + noAuth: option === AuthMethod.NoAuth, basicAuth: option === AuthMethod.BasicAuth, oAuthForward: option === AuthMethod.OAuthForward, + withCredentials: prev.withCredentials, })); trackInfluxDBConfigV2AuthSettingsAuthMethodSelected({ authMethod: option }); }, @@ -121,7 +132,6 @@ export const AuthSettings = (props: Props) => { return ( - {/* Header toggle */} Auth and TLS/SSL Settings} labelWidth={35}> { - {/* Collapsible settings body */} {authenticationSettingsIsOpen && ( - {/* Authentication Method */} - + Authentication Method} noMargin> - - {/* Basic Auth settings */} - {authOptions.basicAuth && ( + {authOptions.basicAuth && ( + <> - - + + - + { - )} + + )} + + + With Credentials + + { + authProps.onAuthMethodSelect(selectedMethod!); + onOptionsChange({ + ...options, + withCredentials: e.currentTarget.checked, + jsonData: { + ...options.jsonData, + oauthPassThru: selectedMethod === AuthMethod.OAuthForward, + }, + }); + setAuthOptions({ + ...authOptions, + noAuth: selectedMethod === AuthMethod.NoAuth, + basicAuth: selectedMethod === AuthMethod.BasicAuth, + oAuthForward: selectedMethod === AuthMethod.OAuthForward, + withCredentials: e.currentTarget.checked, + }); + }} + /> - - {/* TLS Client Auth */} <> @@ -209,6 +245,7 @@ export const AuthSettings = (props: Props) => { onChange={(e) => authProps.TLS?.TLSClientAuth.onClientCertificateChange(e.currentTarget.value)} hasCert={!!authProps.TLS?.TLSClientAuth.clientCertificateConfigured} onClick={() => authProps.TLS?.TLSClientAuth.onClientCertificateReset()} + useGrow={config.featureToggles.newInfluxDSConfigPageDesign} /> { onChange={(e) => authProps.TLS?.TLSClientAuth.onClientKeyChange(e.currentTarget.value)} hasCert={!!authProps.TLS?.TLSClientAuth.clientKeyConfigured} onClick={() => authProps.TLS?.TLSClientAuth.onClientKeyReset()} + useGrow={config.featureToggles.newInfluxDSConfigPageDesign} /> )} @@ -223,7 +261,6 @@ export const AuthSettings = (props: Props) => { - {/* CA Cert */} <> @@ -244,6 +281,7 @@ export const AuthSettings = (props: Props) => { onChange={(e) => authProps.TLS?.selfSignedCertificate.onCertificateChange(e.currentTarget.value)} hasCert={!!authProps.TLS?.selfSignedCertificate.certificateConfigured} onClick={() => authProps.TLS?.selfSignedCertificate.onCertificateReset()} + useGrow={config.featureToggles.newInfluxDSConfigPageDesign} /> )} @@ -251,9 +289,8 @@ export const AuthSettings = (props: Props) => { - {/* Skip TLS verify */} - + Skip TLS Verify = ({ onOptionsChange, options }: Props) => { + const styles = useStyles2(getStyles); return ( - - - - +
+ + + +
+ <> - If something isn't working correctly, you can revert to the original configuration page design by - disabling the newInfluxDSConfigPageDesign feature flag.{' '} - Submit feedback. - + Share your thoughts + {' '} + to help us make it even better. @@ -39,3 +44,15 @@ export const ConfigEditor: React.FC = ({ onOptionsChange, options }: Prop ); }; + +const getStyles = (theme: GrafanaTheme2) => { + return { + hideOnSmallScreen: css({ + width: '250px', + flex: '0 0 250px', + [theme.breakpoints.down('sm')]: { + display: 'none', + }, + }), + }; +}; diff --git a/public/app/plugins/datasource/influxdb/components/editor/config-v2/DatabaseConnectionSection.tsx b/public/app/plugins/datasource/influxdb/components/editor/config-v2/DatabaseConnectionSection.tsx index 4635258b8cc..e06fa0baf32 100644 --- a/public/app/plugins/datasource/influxdb/components/editor/config-v2/DatabaseConnectionSection.tsx +++ b/public/app/plugins/datasource/influxdb/components/editor/config-v2/DatabaseConnectionSection.tsx @@ -6,12 +6,19 @@ import { AdvancedDbConnectionSettings } from './AdvancedDBConnectionSettings'; import { InfluxFluxDBConnection } from './InfluxFluxDBConnection'; import { InfluxInfluxQLDBConnection } from './InfluxInfluxQLDBConnection'; import { InfluxSQLDBConnection } from './InfluxSQLDBConnection'; -import { CONFIG_SECTION_HEADERS } from './constants'; +import { CONFIG_SECTION_HEADERS, CONTAINER_MIN_WIDTH } from './constants'; import { Props } from './types'; export const DatabaseConnectionSection = ({ options, onOptionsChange }: Props) => ( <> - + 2. {CONFIG_SECTION_HEADERS[1].label}} isOpen={CONFIG_SECTION_HEADERS[1].isOpen} @@ -26,12 +33,8 @@ export const DatabaseConnectionSection = ({ options, onOptionsChange }: Props) =

Setting the database for this datasource does not deny access to other databases. The InfluxDB query - syntax allows switching the database in the query. For example: - SHOW MEASUREMENTS ON _internal or - SELECT * FROM "_internal".."database" LIMIT 10 -
-
- To support data isolation and security, make sure appropriate permissions are configured in InfluxDB. + syntax allows switching the database in the query. To support data isolation and security, make sure + appropriate permissions are configured in InfluxDB.

diff --git a/public/app/plugins/datasource/influxdb/components/editor/config-v2/InfluxFluxDBConnection.tsx b/public/app/plugins/datasource/influxdb/components/editor/config-v2/InfluxFluxDBConnection.tsx index 0436f970742..affb5a0e95b 100644 --- a/public/app/plugins/datasource/influxdb/components/editor/config-v2/InfluxFluxDBConnection.tsx +++ b/public/app/plugins/datasource/influxdb/components/editor/config-v2/InfluxFluxDBConnection.tsx @@ -5,6 +5,7 @@ import { } from '@grafana/data'; import { InlineFieldRow, InlineField, Input, SecretInput } from '@grafana/ui'; +import { DB_SETTINGS_LABEL_WIDTH } from './constants'; import { trackInfluxDBConfigV2FluxDBDetailsDefaultBucketInputField, trackInfluxDBConfigV2FluxDBDetailsOrgInputField, @@ -20,7 +21,7 @@ export const InfluxFluxDBConnection = (props: Props) => { return ( <> - + { - + { - + { return ( <> - + { - + { - + { return ( <> - + { - + { const headers = pdcInjected ? CONFIG_SECTION_HEADERS_WITH_PDC : CONFIG_SECTION_HEADERS; return ( - + InfluxDB {headers.map((header, index) => ( diff --git a/public/app/plugins/datasource/influxdb/components/editor/config-v2/UrlAndAuthenticationSection.tsx b/public/app/plugins/datasource/influxdb/components/editor/config-v2/UrlAndAuthenticationSection.tsx index 49e4a2074aa..be726a65f54 100644 --- a/public/app/plugins/datasource/influxdb/components/editor/config-v2/UrlAndAuthenticationSection.tsx +++ b/public/app/plugins/datasource/influxdb/components/editor/config-v2/UrlAndAuthenticationSection.tsx @@ -17,7 +17,7 @@ import { InfluxVersion } from '../../../types'; import { AdvancedHttpSettings } from './AdvancedHttpSettings'; import { AuthSettings } from './AuthSettings'; -import { CONFIG_SECTION_HEADERS } from './constants'; +import { CONFIG_SECTION_HEADERS, CONTAINER_MIN_WIDTH } from './constants'; import { trackInfluxDBConfigV2ProductSelected, trackInfluxDBConfigV2QueryLanguageSelected, @@ -66,7 +66,14 @@ export const UrlAndAuthenticationSection = (props: Props) => { const onUrlChange = (event: React.ChangeEvent) => onUpdateDatasourceOption(props, 'url')(event); return ( - + 1. {CONFIG_SECTION_HEADERS[0].label}} isOpen={CONFIG_SECTION_HEADERS[0].isOpen} @@ -76,7 +83,7 @@ export const UrlAndAuthenticationSection = (props: Props) => { available settings and authentication methods in the next steps. If you are unsure what product you are using, view the{' '} - InfluxDB Docs. + InfluxDB Docs . @@ -85,7 +92,7 @@ export const UrlAndAuthenticationSection = (props: Props) => { URL} noMargin>