[InfluxDB] Config design improvements (#108562)

This commit is contained in:
Alyssa Joyner 2025-08-12 14:23:51 -06:00 committed by GitHub
parent 08501677e3
commit 1ce333a572
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 191 additions and 98 deletions

View File

@ -12,15 +12,16 @@ interface Props {
label: string; label: string;
hasCert: boolean; hasCert: boolean;
placeholder: string; placeholder: string;
useGrow?: boolean;
onChange: (event: ChangeEvent<HTMLTextAreaElement>) => void; onChange: (event: ChangeEvent<HTMLTextAreaElement>) => void;
onClick: (event: MouseEvent<HTMLButtonElement>) => void; onClick: (event: MouseEvent<HTMLButtonElement>) => void;
} }
export const CertificationKey = ({ hasCert, label, onChange, onClick, placeholder }: Props) => { export const CertificationKey = ({ hasCert, label, onChange, onClick, placeholder, useGrow }: Props) => {
return ( return (
<InlineFieldRow> <InlineFieldRow>
<InlineField label={label} labelWidth={14} disabled={hasCert}> <InlineField label={label} labelWidth={14} disabled={hasCert} grow={useGrow}>
{hasCert ? ( {hasCert ? (
<Input type="text" value="configured" width={24} /> <Input type="text" value="configured" width={24} />
) : ( ) : (

View File

@ -11,7 +11,7 @@ import { InlineFieldRow, InlineField, Combobox, InlineSwitch, Input, Space, useS
import { InfluxVersion } from '../../../types'; import { InfluxVersion } from '../../../types';
import { getInlineLabelStyles, HTTP_MODES } from './constants'; import { DB_SETTINGS_LABEL_WIDTH, getInlineLabelStyles, HTTP_MODES } from './constants';
import { import {
trackInfluxDBConfigV2AdvancedDbConnectionSettingsAutocompleteClicked, trackInfluxDBConfigV2AdvancedDbConnectionSettingsAutocompleteClicked,
trackInfluxDBConfigV2AdvancedDbConnectionSettingsHTTPMethodClicked, trackInfluxDBConfigV2AdvancedDbConnectionSettingsHTTPMethodClicked,
@ -55,13 +55,13 @@ export const AdvancedDbConnectionSettings = (props: Props) => {
<InlineFieldRow> <InlineFieldRow>
<InlineField <InlineField
label="HTTP Method" label="HTTP Method"
labelWidth={30} labelWidth={DB_SETTINGS_LABEL_WIDTH}
tooltip="You can use either GET or POST HTTP method to query your InfluxDB database. The POST tooltip="You can use either GET or POST HTTP method to query your InfluxDB database. The POST
method allows you to perform heavy requests (with a lots of WHERE clause) while the GET method method allows you to perform heavy requests (with a lots of WHERE clause) while the GET method
will restrict you and return an error if the query is too large." will restrict you and return an error if the query is too large."
grow
> >
<Combobox <Combobox
width={30}
value={HTTP_MODES.find((httpMode) => httpMode.value === options.jsonData.httpMode)} value={HTTP_MODES.find((httpMode) => httpMode.value === options.jsonData.httpMode)}
options={HTTP_MODES} options={HTTP_MODES}
onChange={onUpdateDatasourceJsonDataOptionSelect(props, 'httpMode')} onChange={onUpdateDatasourceJsonDataOptionSelect(props, 'httpMode')}
@ -72,28 +72,15 @@ export const AdvancedDbConnectionSettings = (props: Props) => {
</InlineFieldRow> </InlineFieldRow>
)} )}
{options.jsonData.version === InfluxVersion.SQL && (
<InlineFieldRow>
<InlineField label="Insecure Connection" labelWidth={30}>
<InlineSwitch
data-testid="influxdb-v2-config-insecure-switch"
value={options.jsonData.insecureGrpc ?? false}
onChange={onUpdateDatasourceJsonDataOptionChecked(props, 'insecureGrpc')}
onBlur={trackInfluxDBConfigV2AdvancedDbConnectionSettingsInsecureConnectClicked}
/>
</InlineField>
</InlineFieldRow>
)}
{(options.jsonData.version === InfluxVersion.InfluxQL || options.jsonData.version === InfluxVersion.Flux) && ( {(options.jsonData.version === InfluxVersion.InfluxQL || options.jsonData.version === InfluxVersion.Flux) && (
<InlineFieldRow> <InlineFieldRow>
<InlineField <InlineField
label="Min time interval" label="Min time interval"
labelWidth={30} labelWidth={DB_SETTINGS_LABEL_WIDTH}
tooltip="A lower limit for the auto group by time interval. Recommended to be set to write frequency, for example 1m if your data is written every minute." tooltip="A lower limit for the auto group by time interval. Recommended to be set to write frequency, for example 1m if your data is written every minute."
grow
> >
<Input <Input
className="width-15"
data-testid="influxdb-v2-config-time-interval" data-testid="influxdb-v2-config-time-interval"
onBlur={trackInfluxDBConfigV2AdvancedDbConnectionSettingsMinTimeClicked} onBlur={trackInfluxDBConfigV2AdvancedDbConnectionSettingsMinTimeClicked}
onChange={onUpdateDatasourceJsonDataOption(props, 'timeInterval')} onChange={onUpdateDatasourceJsonDataOption(props, 'timeInterval')}
@ -108,11 +95,11 @@ export const AdvancedDbConnectionSettings = (props: Props) => {
<InlineFieldRow> <InlineFieldRow>
<InlineField <InlineField
label="Autocomplete Range" label="Autocomplete Range"
labelWidth={30} labelWidth={DB_SETTINGS_LABEL_WIDTH}
tooltip="This time range is used in the query editor's autocomplete to reduce the execution time of tag filter queries." tooltip="This time range is used in the query editor's autocomplete to reduce the execution time of tag filter queries."
grow
> >
<Input <Input
className="width-15"
data-testid="influxdb-v2-config-autocomplete-range" data-testid="influxdb-v2-config-autocomplete-range"
onBlur={trackInfluxDBConfigV2AdvancedDbConnectionSettingsAutocompleteClicked} onBlur={trackInfluxDBConfigV2AdvancedDbConnectionSettingsAutocompleteClicked}
onChange={onUpdateDatasourceJsonDataOption(props, 'showTagTime')} onChange={onUpdateDatasourceJsonDataOption(props, 'showTagTime')}
@ -126,11 +113,11 @@ export const AdvancedDbConnectionSettings = (props: Props) => {
<InlineFieldRow> <InlineFieldRow>
<InlineField <InlineField
label="Max series" label="Max series"
labelWidth={30} labelWidth={DB_SETTINGS_LABEL_WIDTH}
tooltip="Limit the number of series/tables that Grafana will process. Lower this number to prevent abuse, and increase it if you have lots of small time series and not all are shown. Defaults to 1000." tooltip="Limit the number of series/tables that Grafana will process. Lower this number to prevent abuse, and increase it if you have lots of small time series and not all are shown. Defaults to 1000."
grow
> >
<Input <Input
className="width-15"
data-testid="influxdb-v2-config-max-series" data-testid="influxdb-v2-config-max-series"
onBlur={trackInfluxDBConfigV2AdvancedDbConnectionSettingsMaxSeriesClicked} onBlur={trackInfluxDBConfigV2AdvancedDbConnectionSettingsMaxSeriesClicked}
onChange={onMaxSeriesChange} onChange={onMaxSeriesChange}
@ -140,6 +127,18 @@ export const AdvancedDbConnectionSettings = (props: Props) => {
/> />
</InlineField> </InlineField>
</InlineFieldRow> </InlineFieldRow>
{options.jsonData.version === InfluxVersion.SQL && (
<InlineFieldRow>
<InlineField label="Insecure Connection" labelWidth={DB_SETTINGS_LABEL_WIDTH} grow>
<InlineSwitch
data-testid="influxdb-v2-config-insecure-switch"
value={options.jsonData.insecureGrpc ?? false}
onChange={onUpdateDatasourceJsonDataOptionChecked(props, 'insecureGrpc')}
onBlur={trackInfluxDBConfigV2AdvancedDbConnectionSettingsInsecureConnectClicked}
/>
</InlineField>
</InlineFieldRow>
)}
</> </>
)} )}
</> </>

View File

@ -26,9 +26,14 @@ export type Props = DataSourcePluginOptionsEditorProps<InfluxOptions>;
export const AdvancedHttpSettings = ({ options, onOptionsChange }: Props) => { export const AdvancedHttpSettings = ({ options, onOptionsChange }: Props) => {
const styles = useStyles2(getInlineLabelStyles); const styles = useStyles2(getInlineLabelStyles);
const [advancedHttpSettingsIsOpen, setAdvancedHttpSettingsIsOpen] = useState( const [advancedHttpSettingsIsOpen, setAdvancedHttpSettingsIsOpen] = useState(() => {
() => 'keepCookies' in options.jsonData || 'timeout' in options.jsonData const keys = Object.keys(options.jsonData);
return (
'keepCookies' in options.jsonData ||
'timeout' in options.jsonData ||
keys.some((key) => key.includes('httpHeaderName'))
); );
});
return ( return (
<> <>

View File

@ -127,5 +127,22 @@ describe('AuthSettings', () => {
expect(skipSwitch).not.toBeChecked(); 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();
});
});
}); });
}); });

View File

@ -1,5 +1,6 @@
import { cx } from '@emotion/css'; import { cx } from '@emotion/css';
import { useCallback, useMemo, useState } from 'react'; import { useCallback, useMemo, useState } from 'react';
import { useWindowSize } from 'react-use';
import { import {
onUpdateDatasourceOption, onUpdateDatasourceOption,
@ -7,6 +8,7 @@ import {
updateDatasourcePluginResetOption, updateDatasourcePluginResetOption,
} from '@grafana/data'; } from '@grafana/data';
import { AuthMethod, convertLegacyAuthProps } from '@grafana/plugin-ui'; import { AuthMethod, convertLegacyAuthProps } from '@grafana/plugin-ui';
import { config } from '@grafana/runtime';
import { import {
Box, Box,
CertificationKey, CertificationKey,
@ -20,9 +22,15 @@ import {
useStyles2, useStyles2,
Text, Text,
Stack, Stack,
InlineLabel,
} from '@grafana/ui'; } 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 { import {
trackInfluxDBConfigV2AuthSettingsAuthMethodSelected, trackInfluxDBConfigV2AuthSettingsAuthMethodSelected,
trackInfluxDBConfigV2AuthSettingsToggleClicked, trackInfluxDBConfigV2AuthSettingsToggleClicked,
@ -30,6 +38,7 @@ import {
import { Props } from './types'; import { Props } from './types';
type AuthOptionState = { type AuthOptionState = {
noAuth: boolean;
basicAuth: boolean; basicAuth: boolean;
tlsClientAuth: boolean; tlsClientAuth: boolean;
caCert: boolean; caCert: boolean;
@ -41,10 +50,8 @@ type AuthOptionState = {
export const AuthSettings = (props: Props) => { export const AuthSettings = (props: Props) => {
const { options, onOptionsChange } = props; const { options, onOptionsChange } = props;
const styles = useStyles2(getInlineLabelStyles); const styles = useStyles2(getInlineLabelStyles);
const { width } = useWindowSize();
/**
* Derived props from legacy helpers
*/
const authProps = useMemo( const authProps = useMemo(
() => () =>
convertLegacyAuthProps({ convertLegacyAuthProps({
@ -54,36 +61,38 @@ export const AuthSettings = (props: Props) => {
[options, onOptionsChange] [options, onOptionsChange]
); );
/**
* Selected authentication method. Fallback to the most common if the selected one is missing.
*/
const isAuthMethod = (v: unknown): v is AuthMethod => const isAuthMethod = (v: unknown): v is AuthMethod =>
v === AuthMethod.NoAuth || v === AuthMethod.BasicAuth || v === AuthMethod.OAuthForward; v === AuthMethod.NoAuth || v === AuthMethod.BasicAuth || v === AuthMethod.OAuthForward;
const selectedMethod = useMemo<AuthMethod | undefined>(() => {
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<AuthOptionState>({ const [authOptions, setAuthOptions] = useState<AuthOptionState>({
basicAuth: selectedMethod === AuthMethod.BasicAuth, noAuth: (!options.basicAuth && !options.jsonData.oauthPassThru) ?? false,
basicAuth: options.basicAuth ?? false,
tlsClientAuth: authProps.TLS?.TLSClientAuth.enabled ?? false, tlsClientAuth: authProps.TLS?.TLSClientAuth.enabled ?? false,
caCert: authProps.TLS?.selfSignedCertificate.enabled ?? false, caCert: authProps.TLS?.selfSignedCertificate.enabled ?? false,
skipTLS: authProps.TLS?.skipTLSVerification.enabled ?? false, skipTLS: authProps.TLS?.skipTLSVerification.enabled ?? false,
oAuthForward: selectedMethod === AuthMethod.OAuthForward, oAuthForward: options.jsonData.oauthPassThru ?? false,
withCredentials: options.withCredentials ?? false, withCredentials: options.withCredentials ?? false,
}); });
/** const selectedMethod = useMemo<AuthMethod | undefined>(() => {
* Expand/collapse toplevel section if (isAuthMethod(authProps.selectedMethod) && authProps.selectedMethod !== AuthMethod.CrossSiteCredentials) {
*/ return authProps.selectedMethod;
const [authenticationSettingsIsOpen, setAuthenticationSettingsIsOpen] = useState( }
Object.values(authOptions).some(Boolean)
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(() => { const toggleOpen = useCallback(() => {
@ -98,8 +107,10 @@ export const AuthSettings = (props: Props) => {
authProps.onAuthMethodSelect(option); authProps.onAuthMethodSelect(option);
setAuthOptions((prev) => ({ setAuthOptions((prev) => ({
...prev, ...prev,
noAuth: option === AuthMethod.NoAuth,
basicAuth: option === AuthMethod.BasicAuth, basicAuth: option === AuthMethod.BasicAuth,
oAuthForward: option === AuthMethod.OAuthForward, oAuthForward: option === AuthMethod.OAuthForward,
withCredentials: prev.withCredentials,
})); }));
trackInfluxDBConfigV2AuthSettingsAuthMethodSelected({ authMethod: option }); trackInfluxDBConfigV2AuthSettingsAuthMethodSelected({ authMethod: option });
}, },
@ -121,7 +132,6 @@ export const AuthSettings = (props: Props) => {
return ( return (
<Stack direction="column"> <Stack direction="column">
{/* Header toggle */}
<Box alignItems="center"> <Box alignItems="center">
<InlineField label={<div className={cx(styles.label)}>Auth and TLS/SSL Settings</div>} labelWidth={35}> <InlineField label={<div className={cx(styles.label)}>Auth and TLS/SSL Settings</div>} labelWidth={35}>
<InlineSwitch <InlineSwitch
@ -132,35 +142,33 @@ export const AuthSettings = (props: Props) => {
</InlineField> </InlineField>
</Box> </Box>
{/* Collapsible settings body */}
{authenticationSettingsIsOpen && ( {authenticationSettingsIsOpen && (
<Box paddingLeft={1}> <Box paddingLeft={1}>
{/* Authentication Method */} <Box marginBottom={1}>
<Box marginBottom={2}>
<Field label={<Text element="h5">Authentication Method</Text>} noMargin> <Field label={<Text element="h5">Authentication Method</Text>} noMargin>
<Box width="50%" marginY={2}> <Box width="50%" marginY={2}>
<RadioButtonGroup <RadioButtonGroup
options={AUTH_RADIO_BUTTON_OPTIONS} options={AUTH_RADIO_BUTTON_OPTIONS}
value={selectedMethod} value={selectedMethod}
onChange={handleAuthMethodChange} onChange={handleAuthMethodChange}
size={width < 1100 ? 'sm' : 'md'}
/> />
</Box> </Box>
</Field> </Field>
</Box> </Box>
<Box marginBottom={2}>
{/* Basic Auth settings */}
{authOptions.basicAuth && ( {authOptions.basicAuth && (
<Box marginBottom={2}>
<> <>
<Box display="flex" direction="column" width="60%" marginBottom={2}> <Box display="flex" direction="column" marginBottom={2}>
<InlineField label="User" labelWidth={14} grow> <InlineField label="User" labelWidth={DB_SETTINGS_LABEL_WIDTH} grow>
<Input <Input
placeholder="User" placeholder="User"
onChange={onUpdateDatasourceOption(props, 'basicAuthUser')} onChange={onUpdateDatasourceOption(props, 'basicAuthUser')}
value={options.basicAuthUser || ''} value={options.basicAuthUser || ''}
/> />
</InlineField> </InlineField>
<InlineField label="Password" labelWidth={14} grow> <InlineField label="Password" labelWidth={DB_SETTINGS_LABEL_WIDTH} grow>
<SecretInput <SecretInput
placeholder="Password" placeholder="Password"
isConfigured={options.secureJsonFields.basicAuthPassword || false} isConfigured={options.secureJsonFields.basicAuthPassword || false}
@ -171,10 +179,38 @@ export const AuthSettings = (props: Props) => {
</InlineField> </InlineField>
</Box> </Box>
</> </>
)}
</Box> </Box>
)}
{/* TLS Client Auth */} <Box display="flex" direction="row" alignItems="center" marginBottom={2}>
<InlineLabel
style={{ width: '150px' }}
tooltip={'Whether credentials such as cookies or auth headers should be sent with cross-site requests.'}
>
With Credentials
</InlineLabel>
<InlineSwitch
data-testid="influxdb-v2-config-auth-settings-with-credentials"
value={authOptions.withCredentials}
onChange={(e) => {
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,
});
}}
/>
</Box>
<Box marginBottom={2}> <Box marginBottom={2}>
<Field noMargin> <Field noMargin>
<> <>
@ -209,6 +245,7 @@ export const AuthSettings = (props: Props) => {
onChange={(e) => authProps.TLS?.TLSClientAuth.onClientCertificateChange(e.currentTarget.value)} onChange={(e) => authProps.TLS?.TLSClientAuth.onClientCertificateChange(e.currentTarget.value)}
hasCert={!!authProps.TLS?.TLSClientAuth.clientCertificateConfigured} hasCert={!!authProps.TLS?.TLSClientAuth.clientCertificateConfigured}
onClick={() => authProps.TLS?.TLSClientAuth.onClientCertificateReset()} onClick={() => authProps.TLS?.TLSClientAuth.onClientCertificateReset()}
useGrow={config.featureToggles.newInfluxDSConfigPageDesign}
/> />
<CertificationKey <CertificationKey
label="Client Key" label="Client Key"
@ -216,6 +253,7 @@ export const AuthSettings = (props: Props) => {
onChange={(e) => authProps.TLS?.TLSClientAuth.onClientKeyChange(e.currentTarget.value)} onChange={(e) => authProps.TLS?.TLSClientAuth.onClientKeyChange(e.currentTarget.value)}
hasCert={!!authProps.TLS?.TLSClientAuth.clientKeyConfigured} hasCert={!!authProps.TLS?.TLSClientAuth.clientKeyConfigured}
onClick={() => authProps.TLS?.TLSClientAuth.onClientKeyReset()} onClick={() => authProps.TLS?.TLSClientAuth.onClientKeyReset()}
useGrow={config.featureToggles.newInfluxDSConfigPageDesign}
/> />
</Box> </Box>
)} )}
@ -223,7 +261,6 @@ export const AuthSettings = (props: Props) => {
</Field> </Field>
</Box> </Box>
{/* CA Cert */}
<Box marginBottom={2}> <Box marginBottom={2}>
<Field noMargin> <Field noMargin>
<> <>
@ -244,6 +281,7 @@ export const AuthSettings = (props: Props) => {
onChange={(e) => authProps.TLS?.selfSignedCertificate.onCertificateChange(e.currentTarget.value)} onChange={(e) => authProps.TLS?.selfSignedCertificate.onCertificateChange(e.currentTarget.value)}
hasCert={!!authProps.TLS?.selfSignedCertificate.certificateConfigured} hasCert={!!authProps.TLS?.selfSignedCertificate.certificateConfigured}
onClick={() => authProps.TLS?.selfSignedCertificate.onCertificateReset()} onClick={() => authProps.TLS?.selfSignedCertificate.onCertificateReset()}
useGrow={config.featureToggles.newInfluxDSConfigPageDesign}
/> />
</Box> </Box>
)} )}
@ -251,9 +289,8 @@ export const AuthSettings = (props: Props) => {
</Field> </Field>
</Box> </Box>
{/* Skip TLS verify */}
<Box display="flex" direction="row" alignItems="center"> <Box display="flex" direction="row" alignItems="center">
<Label style={{ width: '125px' }}>Skip TLS Verify</Label> <InlineLabel style={{ width: '150px' }}>Skip TLS Verify</InlineLabel>
<InlineSwitch <InlineSwitch
data-testid="influxdb-v2-config-auth-settings-skip-tls-verify" data-testid="influxdb-v2-config-auth-settings-skip-tls-verify"
value={authOptions.skipTLS} value={authOptions.skipTLS}

View File

@ -1,32 +1,37 @@
import { css } from '@emotion/css';
import React from 'react'; import React from 'react';
import { Alert, Box, Stack, TextLink } from '@grafana/ui'; import { GrafanaTheme2 } from '@grafana/data';
import { Alert, Box, Stack, TextLink, useStyles2 } from '@grafana/ui';
import { DatabaseConnectionSection } from './DatabaseConnectionSection'; import { DatabaseConnectionSection } from './DatabaseConnectionSection';
import { LeftSideBar } from './LeftSideBar'; import { LeftSideBar } from './LeftSideBar';
import { UrlAndAuthenticationSection } from './UrlAndAuthenticationSection'; import { UrlAndAuthenticationSection } from './UrlAndAuthenticationSection';
import { CONTAINER_MIN_WIDTH } from './constants';
import { trackInfluxDBConfigV2FeedbackButtonClicked } from './tracking'; import { trackInfluxDBConfigV2FeedbackButtonClicked } from './tracking';
import { Props } from './types'; import { Props } from './types';
export const ConfigEditor: React.FC<Props> = ({ onOptionsChange, options }: Props) => { export const ConfigEditor: React.FC<Props> = ({ onOptionsChange, options }: Props) => {
const styles = useStyles2(getStyles);
return ( return (
<Stack justifyContent="space-between"> <Stack justifyContent="space-between">
<Box width="250px" flex="0 0 250px"> <div className={styles.hideOnSmallScreen}>
<Box width="100%" flex="1 1 auto">
<LeftSideBar pdcInjected={options?.jsonData?.pdcInjected!!} /> <LeftSideBar pdcInjected={options?.jsonData?.pdcInjected!!} />
</Box> </Box>
<Box width="60%" flex="1 1 auto"> </div>
<Box width="60%" flex="1 1 auto" minWidth={CONTAINER_MIN_WIDTH}>
<Stack direction="column"> <Stack direction="column">
<Alert severity="info" title="You are viewing a new design for the InfluxDB configuration settings."> <Alert severity="info" title="You are viewing a new design for the InfluxDB configuration settings.">
<> <>
If something isn't working correctly, you can revert to the original configuration page design by
disabling the <code>newInfluxDSConfigPageDesign</code> feature flag.{' '}
<TextLink <TextLink
href="https://docs.google.com/forms/d/e/1FAIpQLSdi-zyX3c51vh937UKhNYYxhljUnFi6dQSlZv50mES9NrK-ig/viewform" href="https://docs.google.com/forms/d/e/1FAIpQLSdi-zyX3c51vh937UKhNYYxhljUnFi6dQSlZv50mES9NrK-ig/viewform"
external external
onClick={trackInfluxDBConfigV2FeedbackButtonClicked} onClick={trackInfluxDBConfigV2FeedbackButtonClicked}
> >
Submit feedback. Share your thoughts
</TextLink> </TextLink>{' '}
to help us make it even better.
</> </>
</Alert> </Alert>
<UrlAndAuthenticationSection options={options} onOptionsChange={onOptionsChange} /> <UrlAndAuthenticationSection options={options} onOptionsChange={onOptionsChange} />
@ -39,3 +44,15 @@ export const ConfigEditor: React.FC<Props> = ({ onOptionsChange, options }: Prop
</Stack> </Stack>
); );
}; };
const getStyles = (theme: GrafanaTheme2) => {
return {
hideOnSmallScreen: css({
width: '250px',
flex: '0 0 250px',
[theme.breakpoints.down('sm')]: {
display: 'none',
},
}),
};
};

View File

@ -6,12 +6,19 @@ import { AdvancedDbConnectionSettings } from './AdvancedDBConnectionSettings';
import { InfluxFluxDBConnection } from './InfluxFluxDBConnection'; import { InfluxFluxDBConnection } from './InfluxFluxDBConnection';
import { InfluxInfluxQLDBConnection } from './InfluxInfluxQLDBConnection'; import { InfluxInfluxQLDBConnection } from './InfluxInfluxQLDBConnection';
import { InfluxSQLDBConnection } from './InfluxSQLDBConnection'; import { InfluxSQLDBConnection } from './InfluxSQLDBConnection';
import { CONFIG_SECTION_HEADERS } from './constants'; import { CONFIG_SECTION_HEADERS, CONTAINER_MIN_WIDTH } from './constants';
import { Props } from './types'; import { Props } from './types';
export const DatabaseConnectionSection = ({ options, onOptionsChange }: Props) => ( export const DatabaseConnectionSection = ({ options, onOptionsChange }: Props) => (
<> <>
<Box borderStyle="solid" borderColor="weak" padding={2} marginBottom={4} id={`${CONFIG_SECTION_HEADERS[1].id}`}> <Box
borderStyle="solid"
borderColor="weak"
padding={2}
marginBottom={4}
id={`${CONFIG_SECTION_HEADERS[1].id}`}
minWidth={CONTAINER_MIN_WIDTH}
>
<CollapsableSection <CollapsableSection
label={<Text element="h3">2. {CONFIG_SECTION_HEADERS[1].label}</Text>} label={<Text element="h3">2. {CONFIG_SECTION_HEADERS[1].label}</Text>}
isOpen={CONFIG_SECTION_HEADERS[1].isOpen} isOpen={CONFIG_SECTION_HEADERS[1].isOpen}
@ -26,12 +33,8 @@ export const DatabaseConnectionSection = ({ options, onOptionsChange }: Props) =
<Alert severity="info" title="Database Access"> <Alert severity="info" title="Database Access">
<p> <p>
Setting the database for this datasource does not deny access to other databases. The InfluxDB query 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: syntax allows switching the database in the query. To support data isolation and security, make sure
<code>SHOW MEASUREMENTS ON _internal</code> or appropriate permissions are configured in InfluxDB.
<code>SELECT * FROM &quot;_internal&quot;..&quot;database&quot; LIMIT 10</code>
<br />
<br />
To support data isolation and security, make sure appropriate permissions are configured in InfluxDB.
</p> </p>
</Alert> </Alert>
</> </>

View File

@ -5,6 +5,7 @@ import {
} from '@grafana/data'; } from '@grafana/data';
import { InlineFieldRow, InlineField, Input, SecretInput } from '@grafana/ui'; import { InlineFieldRow, InlineField, Input, SecretInput } from '@grafana/ui';
import { DB_SETTINGS_LABEL_WIDTH } from './constants';
import { import {
trackInfluxDBConfigV2FluxDBDetailsDefaultBucketInputField, trackInfluxDBConfigV2FluxDBDetailsDefaultBucketInputField,
trackInfluxDBConfigV2FluxDBDetailsOrgInputField, trackInfluxDBConfigV2FluxDBDetailsOrgInputField,
@ -20,7 +21,7 @@ export const InfluxFluxDBConnection = (props: Props) => {
return ( return (
<> <>
<InlineFieldRow> <InlineFieldRow>
<InlineField label="Organization" labelWidth={30} grow> <InlineField label="Organization" labelWidth={DB_SETTINGS_LABEL_WIDTH} grow>
<Input <Input
id="organization" id="organization"
placeholder="myorg" placeholder="myorg"
@ -31,7 +32,7 @@ export const InfluxFluxDBConnection = (props: Props) => {
</InlineField> </InlineField>
</InlineFieldRow> </InlineFieldRow>
<InlineFieldRow> <InlineFieldRow>
<InlineField labelWidth={30} label="Default Bucket" grow> <InlineField labelWidth={DB_SETTINGS_LABEL_WIDTH} label="Default Bucket" grow>
<Input <Input
id="default-bucket" id="default-bucket"
onBlur={trackInfluxDBConfigV2FluxDBDetailsDefaultBucketInputField} onBlur={trackInfluxDBConfigV2FluxDBDetailsDefaultBucketInputField}
@ -42,7 +43,7 @@ export const InfluxFluxDBConnection = (props: Props) => {
</InlineField> </InlineField>
</InlineFieldRow> </InlineFieldRow>
<InlineFieldRow> <InlineFieldRow>
<InlineField labelWidth={30} label="Token" grow> <InlineField labelWidth={DB_SETTINGS_LABEL_WIDTH} label="Token" grow>
<SecretInput <SecretInput
id="token" id="token"
isConfigured={Boolean(secureJsonFields && secureJsonFields.token)} isConfigured={Boolean(secureJsonFields && secureJsonFields.token)}

View File

@ -6,6 +6,7 @@ import {
} from '@grafana/data'; } from '@grafana/data';
import { InlineFieldRow, InlineField, Input, SecretInput } from '@grafana/ui'; import { InlineFieldRow, InlineField, Input, SecretInput } from '@grafana/ui';
import { DB_SETTINGS_LABEL_WIDTH } from './constants';
import { import {
trackInfluxDBConfigV2InfluxQLDBDetailsDatabaseInputField, trackInfluxDBConfigV2InfluxQLDBDetailsDatabaseInputField,
trackInfluxDBConfigV2InfluxQLDBDetailsPasswordInputField, trackInfluxDBConfigV2InfluxQLDBDetailsPasswordInputField,
@ -19,7 +20,7 @@ export const InfluxInfluxQLDBConnection = (props: Props) => {
return ( return (
<> <>
<InlineFieldRow> <InlineFieldRow>
<InlineField label="Database" labelWidth={30} grow> <InlineField label="Database" labelWidth={DB_SETTINGS_LABEL_WIDTH} grow>
<Input <Input
id="database" id="database"
placeholder="mydb" placeholder="mydb"
@ -30,7 +31,7 @@ export const InfluxInfluxQLDBConnection = (props: Props) => {
</InlineField> </InlineField>
</InlineFieldRow> </InlineFieldRow>
<InlineFieldRow> <InlineFieldRow>
<InlineField label="User" labelWidth={30} grow> <InlineField label="User" labelWidth={DB_SETTINGS_LABEL_WIDTH} grow>
<Input <Input
id="user" id="user"
placeholder="myuser" placeholder="myuser"
@ -41,7 +42,7 @@ export const InfluxInfluxQLDBConnection = (props: Props) => {
</InlineField> </InlineField>
</InlineFieldRow> </InlineFieldRow>
<InlineFieldRow> <InlineFieldRow>
<InlineField label="Password" labelWidth={30} grow> <InlineField label="Password" labelWidth={DB_SETTINGS_LABEL_WIDTH} grow>
<SecretInput <SecretInput
id="password" id="password"
isConfigured={Boolean(options.secureJsonFields && options.secureJsonFields.password)} isConfigured={Boolean(options.secureJsonFields && options.secureJsonFields.password)}

View File

@ -5,6 +5,7 @@ import {
} from '@grafana/data'; } from '@grafana/data';
import { InlineFieldRow, InlineField, Input, SecretInput } from '@grafana/ui'; import { InlineFieldRow, InlineField, Input, SecretInput } from '@grafana/ui';
import { DB_SETTINGS_LABEL_WIDTH } from './constants';
import { import {
trackInfluxDBConfigV2SQLDBDetailsDatabaseInputField, trackInfluxDBConfigV2SQLDBDetailsDatabaseInputField,
trackInfluxDBConfigV2SQLDBDetailsTokenInputField, trackInfluxDBConfigV2SQLDBDetailsTokenInputField,
@ -18,7 +19,7 @@ export const InfluxSQLDBConnection = (props: Props) => {
return ( return (
<> <>
<InlineFieldRow> <InlineFieldRow>
<InlineField label="Database" labelWidth={30} grow> <InlineField label="Database" labelWidth={DB_SETTINGS_LABEL_WIDTH} grow>
<Input <Input
id="database" id="database"
placeholder="mydb" placeholder="mydb"
@ -29,7 +30,7 @@ export const InfluxSQLDBConnection = (props: Props) => {
</InlineField> </InlineField>
</InlineFieldRow> </InlineFieldRow>
<InlineFieldRow> <InlineFieldRow>
<InlineField labelWidth={30} label="Token" grow> <InlineField labelWidth={DB_SETTINGS_LABEL_WIDTH} label="Token" grow>
<SecretInput <SecretInput
id="token" id="token"
isConfigured={Boolean(secureJsonFields && secureJsonFields.token)} isConfigured={Boolean(secureJsonFields && secureJsonFields.token)}

View File

@ -10,7 +10,7 @@ export const LeftSideBar = ({ pdcInjected }: LeftSideBarProps) => {
const headers = pdcInjected ? CONFIG_SECTION_HEADERS_WITH_PDC : CONFIG_SECTION_HEADERS; const headers = pdcInjected ? CONFIG_SECTION_HEADERS_WITH_PDC : CONFIG_SECTION_HEADERS;
return ( return (
<Stack> <Stack>
<Box flex={1} marginY={5}> <Box flex={1} marginY={10}>
<Text element="h4">InfluxDB</Text> <Text element="h4">InfluxDB</Text>
<Box paddingTop={2}> <Box paddingTop={2}>
{headers.map((header, index) => ( {headers.map((header, index) => (

View File

@ -17,7 +17,7 @@ import { InfluxVersion } from '../../../types';
import { AdvancedHttpSettings } from './AdvancedHttpSettings'; import { AdvancedHttpSettings } from './AdvancedHttpSettings';
import { AuthSettings } from './AuthSettings'; import { AuthSettings } from './AuthSettings';
import { CONFIG_SECTION_HEADERS } from './constants'; import { CONFIG_SECTION_HEADERS, CONTAINER_MIN_WIDTH } from './constants';
import { import {
trackInfluxDBConfigV2ProductSelected, trackInfluxDBConfigV2ProductSelected,
trackInfluxDBConfigV2QueryLanguageSelected, trackInfluxDBConfigV2QueryLanguageSelected,
@ -66,7 +66,14 @@ export const UrlAndAuthenticationSection = (props: Props) => {
const onUrlChange = (event: React.ChangeEvent<HTMLInputElement>) => onUpdateDatasourceOption(props, 'url')(event); const onUrlChange = (event: React.ChangeEvent<HTMLInputElement>) => onUpdateDatasourceOption(props, 'url')(event);
return ( return (
<Box borderStyle="solid" borderColor="weak" padding={2} marginBottom={4} id={`${CONFIG_SECTION_HEADERS[0].id}`}> <Box
borderStyle="solid"
borderColor="weak"
padding={2}
marginBottom={4}
id={`${CONFIG_SECTION_HEADERS[0].id}`}
minWidth={CONTAINER_MIN_WIDTH}
>
<CollapsableSection <CollapsableSection
label={<Text element="h3">1. {CONFIG_SECTION_HEADERS[0].label}</Text>} label={<Text element="h3">1. {CONFIG_SECTION_HEADERS[0].label}</Text>}
isOpen={CONFIG_SECTION_HEADERS[0].isOpen} 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, available settings and authentication methods in the next steps. If you are unsure what product you are using,
view the{' '} view the{' '}
<TextLink href="https://docs.influxdata.com/" external> <TextLink href="https://docs.influxdata.com/" external>
InfluxDB Docs. InfluxDB Docs
</TextLink> </TextLink>
. .
</Text> </Text>
@ -85,7 +92,7 @@ export const UrlAndAuthenticationSection = (props: Props) => {
<Field label={<div style={{ marginBottom: '5px' }}>URL</div>} noMargin> <Field label={<div style={{ marginBottom: '5px' }}>URL</div>} noMargin>
<Input <Input
data-testid="influxdb-v2-config-url-input" data-testid="influxdb-v2-config-url-input"
placeholder="http://localhost:3000/" placeholder="http://localhost:8086/"
onChange={onUrlChange} onChange={onUrlChange}
value={options.url || ''} value={options.url || ''}
onBlur={trackInfluxDBConfigV2URLInputField} onBlur={trackInfluxDBConfigV2URLInputField}

View File

@ -50,8 +50,11 @@ export const getInlineLabelStyles = (theme: GrafanaTheme2, transparent = false,
marginRight: theme.spacing(0.5), marginRight: theme.spacing(0.5),
borderRadius: theme.shape.radius.default, borderRadius: theme.shape.radius.default,
border: 'none', border: 'none',
width: '240px', width: '220px',
color: theme.colors.text.primary, color: theme.colors.text.primary,
}), }),
}; };
}; };
export const DB_SETTINGS_LABEL_WIDTH = 18;
export const CONTAINER_MIN_WIDTH = '450px';

View File

@ -18,6 +18,7 @@ export interface InfluxOptions extends DataSourceJsonData {
dbName?: string; dbName?: string;
product?: string; product?: string;
pdcInjected?: boolean; pdcInjected?: boolean;
oauthPassThru?: boolean;
// With Flux // With Flux
organization?: string; organization?: string;