Prometheus: Disabled inputs when settings are read-only (#60354)

This commit is contained in:
Ludovic Viaud 2023-01-10 16:13:42 +01:00 committed by GitHub
parent 5d725d22ad
commit 220ee3d1d4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 135 additions and 68 deletions

View File

@ -19,7 +19,7 @@ export function AlertingSettings<T extends AlertingConfig>({ options, onOptionsC
<div className="gf-form-group"> <div className="gf-form-group">
<div className="gf-form-inline"> <div className="gf-form-inline">
<div className="gf-form"> <div className="gf-form">
<InlineField labelWidth={26} label="Manage alerts via Alerting UI"> <InlineField labelWidth={26} label="Manage alerts via Alerting UI" disabled={options.readOnly}>
<InlineSwitch <InlineSwitch
value={options.jsonData.manageAlerts !== false} value={options.jsonData.manageAlerts !== false}
onChange={(event) => onChange={(event) =>

View File

@ -35,7 +35,7 @@ export const BasicAuthSettings: React.FC<HttpSettingsProps> = ({ dataSourceConfi
return ( return (
<> <>
<InlineField> <InlineField disabled={dataSourceConfig.readOnly}>
<FormField <FormField
label="User" label="User"
labelWidth={10} labelWidth={10}
@ -45,7 +45,7 @@ export const BasicAuthSettings: React.FC<HttpSettingsProps> = ({ dataSourceConfi
onChange={(event) => onChange({ ...dataSourceConfig, basicAuthUser: event.currentTarget.value })} onChange={(event) => onChange({ ...dataSourceConfig, basicAuthUser: event.currentTarget.value })}
/> />
</InlineField> </InlineField>
<InlineField> <InlineField disabled={dataSourceConfig.readOnly}>
<SecretFormField <SecretFormField
isConfigured={!!(dataSourceConfig.secureJsonFields && dataSourceConfig.secureJsonFields.basicAuthPassword)} isConfigured={!!(dataSourceConfig.secureJsonFields && dataSourceConfig.secureJsonFields.basicAuthPassword)}
value={password || ''} value={password || ''}

View File

@ -32,7 +32,7 @@ const setup = (propOverrides?: object) => {
password: true, password: true,
}, },
secureJsonFields: {}, secureJsonFields: {},
readOnly: true, readOnly: false,
}, },
onChange, onChange,
...propOverrides, ...propOverrides,

View File

@ -196,6 +196,8 @@ export class CustomHeadersSettings extends PureComponent<Props, State> {
render() { render() {
const { headers } = this.state; const { headers } = this.state;
const { dataSourceConfig } = this.props;
return ( return (
<div className={'gf-form-group'}> <div className={'gf-form-group'}>
<div className="gf-form"> <div className="gf-form">
@ -215,18 +217,20 @@ export class CustomHeadersSettings extends PureComponent<Props, State> {
/> />
))} ))}
</div> </div>
<div className="gf-form"> {!dataSourceConfig.readOnly && (
<Button <div className="gf-form">
variant="secondary" <Button
icon="plus" variant="secondary"
type="button" icon="plus"
onClick={(e) => { type="button"
this.onHeaderAdd(); onClick={(e) => {
}} this.onHeaderAdd();
> }}
Add header >
</Button> Add header
</div> </Button>
</div>
)}
</div> </div>
); );
} }

View File

@ -113,6 +113,7 @@ export const DataSourceHttpSettings: React.FC<HttpSettingsProps> = (props) => {
options={ACCESS_OPTIONS} options={ACCESS_OPTIONS}
value={ACCESS_OPTIONS.filter((o) => o.value === dataSourceConfig.access)[0] || DEFAULT_ACCESS_OPTION} value={ACCESS_OPTIONS.filter((o) => o.value === dataSourceConfig.access)[0] || DEFAULT_ACCESS_OPTION}
onChange={(selectedValue) => onSettingsChange({ access: selectedValue.value })} onChange={(selectedValue) => onSettingsChange({ access: selectedValue.value })}
disabled={dataSourceConfig.readOnly}
/> />
); );
@ -133,6 +134,7 @@ export const DataSourceHttpSettings: React.FC<HttpSettingsProps> = (props) => {
value={dataSourceConfig.url} value={dataSourceConfig.url}
aria-label={selectors.components.DataSource.DataSourceHttpSettings.urlInput} aria-label={selectors.components.DataSource.DataSourceHttpSettings.urlInput}
onChange={(event) => onSettingsChange({ url: event.currentTarget.value })} onChange={(event) => onSettingsChange({ url: event.currentTarget.value })}
disabled={dataSourceConfig.readOnly}
/> />
); );
@ -183,6 +185,7 @@ export const DataSourceHttpSettings: React.FC<HttpSettingsProps> = (props) => {
onChange={(cookies) => onChange={(cookies) =>
onSettingsChange({ jsonData: { ...dataSourceConfig.jsonData, keepCookies: cookies } }) onSettingsChange({ jsonData: { ...dataSourceConfig.jsonData, keepCookies: cookies } })
} }
disabled={dataSourceConfig.readOnly}
/> />
</div> </div>
<div className="gf-form"> <div className="gf-form">
@ -200,6 +203,7 @@ export const DataSourceHttpSettings: React.FC<HttpSettingsProps> = (props) => {
jsonData: { ...dataSourceConfig.jsonData, timeout: parseInt(event.currentTarget.value, 10) }, jsonData: { ...dataSourceConfig.jsonData, timeout: parseInt(event.currentTarget.value, 10) },
}); });
}} }}
disabled={dataSourceConfig.readOnly}
/> />
</div> </div>
</div> </div>
@ -211,7 +215,7 @@ export const DataSourceHttpSettings: React.FC<HttpSettingsProps> = (props) => {
<h3 className="page-heading">Auth</h3> <h3 className="page-heading">Auth</h3>
<div className="gf-form-group"> <div className="gf-form-group">
<div className="gf-form-inline"> <div className="gf-form-inline">
<InlineField label="Basic auth" labelWidth={LABEL_WIDTH}> <InlineField label="Basic auth" labelWidth={LABEL_WIDTH} disabled={dataSourceConfig.readOnly}>
<InlineSwitch <InlineSwitch
id="http-settings-basic-auth" id="http-settings-basic-auth"
value={dataSourceConfig.basicAuth} value={dataSourceConfig.basicAuth}
@ -225,6 +229,7 @@ export const DataSourceHttpSettings: React.FC<HttpSettingsProps> = (props) => {
label="With Credentials" label="With Credentials"
tooltip="Whether credentials such as cookies or auth headers should be sent with cross-site requests." tooltip="Whether credentials such as cookies or auth headers should be sent with cross-site requests."
labelWidth={LABEL_WIDTH} labelWidth={LABEL_WIDTH}
disabled={dataSourceConfig.readOnly}
> >
<InlineSwitch <InlineSwitch
id="http-settings-with-credentials" id="http-settings-with-credentials"
@ -242,6 +247,7 @@ export const DataSourceHttpSettings: React.FC<HttpSettingsProps> = (props) => {
label="Azure Authentication" label="Azure Authentication"
tooltip="Use Azure authentication for Azure endpoint." tooltip="Use Azure authentication for Azure endpoint."
labelWidth={LABEL_WIDTH} labelWidth={LABEL_WIDTH}
disabled={dataSourceConfig.readOnly}
> >
<InlineSwitch <InlineSwitch
id="http-settings-azure-auth" id="http-settings-azure-auth"
@ -258,7 +264,7 @@ export const DataSourceHttpSettings: React.FC<HttpSettingsProps> = (props) => {
{sigV4AuthToggleEnabled && ( {sigV4AuthToggleEnabled && (
<div className="gf-form-inline"> <div className="gf-form-inline">
<InlineField label="SigV4 auth" labelWidth={LABEL_WIDTH}> <InlineField label="SigV4 auth" labelWidth={LABEL_WIDTH} disabled={dataSourceConfig.readOnly}>
<InlineSwitch <InlineSwitch
id="http-settings-sigv4-auth" id="http-settings-sigv4-auth"
value={dataSourceConfig.jsonData.sigV4Auth || false} value={dataSourceConfig.jsonData.sigV4Auth || false}

View File

@ -15,14 +15,19 @@ export const HttpProxySettings: React.FC<HttpSettingsBaseProps> = ({
return ( return (
<> <>
<div className="gf-form-inline"> <div className="gf-form-inline">
<InlineField label="TLS Client Auth" labelWidth={LABEL_WIDTH}> <InlineField label="TLS Client Auth" labelWidth={LABEL_WIDTH} disabled={dataSourceConfig.readOnly}>
<InlineSwitch <InlineSwitch
id="http-settings-tls-client-auth" id="http-settings-tls-client-auth"
value={dataSourceConfig.jsonData.tlsAuth || false} value={dataSourceConfig.jsonData.tlsAuth || false}
onChange={(event) => onChange({ ...dataSourceConfig.jsonData, tlsAuth: event!.currentTarget.checked })} onChange={(event) => onChange({ ...dataSourceConfig.jsonData, tlsAuth: event!.currentTarget.checked })}
/> />
</InlineField> </InlineField>
<InlineField label="With CA Cert" tooltip="Needed for verifying self-signed TLS Certs" labelWidth={LABEL_WIDTH}> <InlineField
label="With CA Cert"
tooltip="Needed for verifying self-signed TLS Certs"
labelWidth={LABEL_WIDTH}
disabled={dataSourceConfig.readOnly}
>
<InlineSwitch <InlineSwitch
id="http-settings-ca-cert" id="http-settings-ca-cert"
value={dataSourceConfig.jsonData.tlsAuthWithCACert || false} value={dataSourceConfig.jsonData.tlsAuthWithCACert || false}
@ -33,7 +38,7 @@ export const HttpProxySettings: React.FC<HttpSettingsBaseProps> = ({
</InlineField> </InlineField>
</div> </div>
<div className="gf-form-inline"> <div className="gf-form-inline">
<InlineField label="Skip TLS Verify" labelWidth={LABEL_WIDTH}> <InlineField label="Skip TLS Verify" labelWidth={LABEL_WIDTH} disabled={dataSourceConfig.readOnly}>
<InlineSwitch <InlineSwitch
id="http-settings-skip-tls-verify" id="http-settings-skip-tls-verify"
value={dataSourceConfig.jsonData.tlsSkipVerify || false} value={dataSourceConfig.jsonData.tlsSkipVerify || false}
@ -49,6 +54,7 @@ export const HttpProxySettings: React.FC<HttpSettingsBaseProps> = ({
label="Forward OAuth Identity" label="Forward OAuth Identity"
tooltip="Forward the user's upstream OAuth identity to the data source (Their access token gets passed along)." tooltip="Forward the user's upstream OAuth identity to the data source (Their access token gets passed along)."
labelWidth={LABEL_WIDTH} labelWidth={LABEL_WIDTH}
disabled={dataSourceConfig.readOnly}
> >
<InlineSwitch <InlineSwitch
id="http-settings-forward-oauth" id="http-settings-forward-oauth"

View File

@ -41,7 +41,12 @@ export const FormField: FunctionComponent<Props> = ({
{label} {label}
</InlineFormLabel> </InlineFormLabel>
{inputEl || ( {inputEl || (
<input type="text" className={`gf-form-input ${inputWidth ? `width-${inputWidth}` : ''}`} {...inputProps} /> <input
type="text"
className={`gf-form-input ${inputWidth ? `width-${inputWidth}` : ''}`}
{...inputProps}
disabled={inputProps.disabled}
/>
)} )}
</div> </div>
); );

View File

@ -14,7 +14,7 @@ export interface Props extends Omit<HTMLProps<HTMLInputElement>, 'value'> {
} }
export const Switch = React.forwardRef<HTMLInputElement, Props>( export const Switch = React.forwardRef<HTMLInputElement, Props>(
({ value, checked, disabled, onChange, id, label, ...inputProps }, ref) => { ({ value, checked, onChange, id, label, disabled, ...inputProps }, ref) => {
if (checked) { if (checked) {
deprecationWarning('Switch', 'checked prop', 'value'); deprecationWarning('Switch', 'checked prop', 'value');
} }
@ -30,7 +30,7 @@ export const Switch = React.forwardRef<HTMLInputElement, Props>(
disabled={disabled} disabled={disabled}
checked={value} checked={value}
onChange={(event) => { onChange={(event) => {
onChange?.(event); !disabled && onChange?.(event);
}} }}
id={switchIdRef.current} id={switchIdRef.current}
{...inputProps} {...inputProps}
@ -52,8 +52,9 @@ export const InlineSwitch = React.forwardRef<HTMLInputElement, InlineSwitchProps
({ transparent, className, showLabel, label, value, id, ...props }, ref) => { ({ transparent, className, showLabel, label, value, id, ...props }, ref) => {
const theme = useTheme2(); const theme = useTheme2();
const styles = getSwitchStyles(theme, transparent); const styles = getSwitchStyles(theme, transparent);
return ( return (
<div className={cx(styles.inlineContainer, className)}> <div className={cx(styles.inlineContainer, className, props.disabled && styles.disabled)}>
{showLabel && ( {showLabel && (
<label <label
htmlFor={id} htmlFor={id}
@ -158,6 +159,11 @@ const getSwitchStyles = stylesFactory((theme: GrafanaTheme2, transparent?: boole
} }
} }
`, `,
disabled: css`
background-color: rgba(204, 204, 220, 0.04);
color: rgba(204, 204, 220, 0.6);
border: 1px solid rgba(204, 204, 220, 0.04);
`,
inlineLabel: css` inlineLabel: css`
cursor: pointer; cursor: pointer;
padding-right: ${theme.spacing(1)}; padding-right: ${theme.spacing(1)};

View File

@ -11,12 +11,21 @@ export interface Props {
onNameChange: (name: string) => void; onNameChange: (name: string) => void;
onDefaultChange: (value: boolean) => void; onDefaultChange: (value: boolean) => void;
alertingSupported: boolean; alertingSupported: boolean;
disabled?: boolean;
} }
export function BasicSettings({ dataSourceName, isDefault, onDefaultChange, onNameChange, alertingSupported }: Props) { export function BasicSettings({
dataSourceName,
isDefault,
onDefaultChange,
onNameChange,
alertingSupported,
disabled,
}: Props) {
return ( return (
<> <>
{<AlertingEnabled enabled={alertingSupported} />} <AlertingEnabled enabled={alertingSupported} />
<div className="gf-form-group" aria-label="Datasource settings page basic settings"> <div className="gf-form-group" aria-label="Datasource settings page basic settings">
<div className="gf-form-inline"> <div className="gf-form-inline">
{/* Name */} {/* Name */}
@ -26,6 +35,7 @@ export function BasicSettings({ dataSourceName, isDefault, onDefaultChange, onNa
tooltip="The name is used when you select the data source in panels. The default data source is tooltip="The name is used when you select the data source in panels. The default data source is
'preselected in new panels." 'preselected in new panels."
grow grow
disabled={disabled}
> >
<Input <Input
id="basic-settings-name" id="basic-settings-name"
@ -40,7 +50,7 @@ export function BasicSettings({ dataSourceName, isDefault, onDefaultChange, onNa
</div> </div>
{/* Is Default */} {/* Is Default */}
<InlineField label="Default" labelWidth={8}> <InlineField label="Default" labelWidth={8} disabled={disabled}>
<InlineSwitch <InlineSwitch
id="basic-settings-default" id="basic-settings-default"
value={isDefault} value={isDefault}

View File

@ -159,6 +159,7 @@ export function EditDataSourceView({
onDefaultChange={onDefaultChange} onDefaultChange={onDefaultChange}
onNameChange={onNameChange} onNameChange={onNameChange}
alertingSupported={alertingSupported} alertingSupported={alertingSupported}
disabled={readOnly || !hasWriteRights}
/> />
{plugin && ( {plugin && (

View File

@ -51,19 +51,20 @@ export const AzureAuthSettings: FunctionComponent<HttpSettingsBaseProps> = (prop
credentials={credentials} credentials={credentials}
azureCloudOptions={KnownAzureClouds} azureCloudOptions={KnownAzureClouds}
onCredentialsChange={onCredentialsChange} onCredentialsChange={onCredentialsChange}
disabled={dataSourceConfig.readOnly}
/> />
{overrideAudienceAllowed && ( {overrideAudienceAllowed && (
<> <>
<h6>Azure Configuration</h6> <h6>Azure Configuration</h6>
<div className="gf-form-group"> <div className="gf-form-group">
<InlineFieldRow> <InlineFieldRow>
<InlineField labelWidth={26} label="Override AAD audience"> <InlineField labelWidth={26} label="Override AAD audience" disabled={dataSourceConfig.readOnly}>
<InlineSwitch value={overrideAudienceChecked} onChange={onOverrideAudienceChange} /> <InlineSwitch value={overrideAudienceChecked} onChange={onOverrideAudienceChange} />
</InlineField> </InlineField>
</InlineFieldRow> </InlineFieldRow>
{overrideAudienceChecked && ( {overrideAudienceChecked && (
<InlineFieldRow> <InlineFieldRow>
<InlineField labelWidth={26} label="Resource ID"> <InlineField labelWidth={26} label="Resource ID" disabled={dataSourceConfig.readOnly}>
<Input <Input
className="width-30" className="width-30"
value={dataSourceConfig.jsonData.azureEndpointResourceId || ''} value={dataSourceConfig.jsonData.azureEndpointResourceId || ''}

View File

@ -13,6 +13,7 @@ export interface Props {
azureCloudOptions?: SelectableValue[]; azureCloudOptions?: SelectableValue[];
onCredentialsChange: (updatedCredentials: AzureCredentials) => void; onCredentialsChange: (updatedCredentials: AzureCredentials) => void;
getSubscriptions?: () => Promise<SelectableValue[]>; getSubscriptions?: () => Promise<SelectableValue[]>;
disabled?: boolean;
} }
const authTypeOptions: Array<SelectableValue<AzureAuthType>> = [ const authTypeOptions: Array<SelectableValue<AzureAuthType>> = [
@ -27,7 +28,7 @@ const authTypeOptions: Array<SelectableValue<AzureAuthType>> = [
]; ];
export const AzureCredentialsForm: FunctionComponent<Props> = (props: Props) => { export const AzureCredentialsForm: FunctionComponent<Props> = (props: Props) => {
const { credentials, azureCloudOptions, onCredentialsChange, getSubscriptions } = props; const { credentials, azureCloudOptions, onCredentialsChange, getSubscriptions, disabled } = props;
const hasRequiredFields = isCredentialsComplete(credentials); const hasRequiredFields = isCredentialsComplete(credentials);
const [subscriptions, setSubscriptions] = useState<Array<SelectableValue<string>>>([]); const [subscriptions, setSubscriptions] = useState<Array<SelectableValue<string>>>([]);
@ -161,6 +162,7 @@ export const AzureCredentialsForm: FunctionComponent<Props> = (props: Props) =>
value={authTypeOptions.find((opt) => opt.value === credentials.authType)} value={authTypeOptions.find((opt) => opt.value === credentials.authType)}
options={authTypeOptions} options={authTypeOptions}
onChange={onAuthTypeChange} onChange={onAuthTypeChange}
isDisabled={disabled}
/> />
</div> </div>
</div> </div>
@ -178,6 +180,7 @@ export const AzureCredentialsForm: FunctionComponent<Props> = (props: Props) =>
value={azureCloudOptions.find((opt) => opt.value === credentials.azureCloud)} value={azureCloudOptions.find((opt) => opt.value === credentials.azureCloud)}
options={azureCloudOptions} options={azureCloudOptions}
onChange={onAzureCloudChange} onChange={onAzureCloudChange}
isDisabled={disabled}
/> />
</div> </div>
</div> </div>
@ -191,6 +194,7 @@ export const AzureCredentialsForm: FunctionComponent<Props> = (props: Props) =>
placeholder="XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" placeholder="XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
value={credentials.tenantId || ''} value={credentials.tenantId || ''}
onChange={onTenantIdChange} onChange={onTenantIdChange}
disabled={disabled}
/> />
</div> </div>
</div> </div>
@ -204,6 +208,7 @@ export const AzureCredentialsForm: FunctionComponent<Props> = (props: Props) =>
placeholder="XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" placeholder="XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
value={credentials.clientId || ''} value={credentials.clientId || ''}
onChange={onClientIdChange} onChange={onClientIdChange}
disabled={disabled}
/> />
</div> </div>
</div> </div>
@ -214,15 +219,17 @@ export const AzureCredentialsForm: FunctionComponent<Props> = (props: Props) =>
<InlineFormLabel htmlFor="azure-client-secret" className="width-12"> <InlineFormLabel htmlFor="azure-client-secret" className="width-12">
Client Secret Client Secret
</InlineFormLabel> </InlineFormLabel>
<Input id="azure-client-secret" className="width-25" placeholder="configured" disabled={true} /> <Input id="azure-client-secret" className="width-25" placeholder="configured" disabled />
</div> </div>
<div className="gf-form"> {!disabled && (
<div className="max-width-30 gf-form-inline"> <div className="gf-form">
<Button variant="secondary" type="button" onClick={onClientSecretReset}> <div className="max-width-30 gf-form-inline">
reset <Button variant="secondary" type="button" onClick={onClientSecretReset}>
</Button> reset
</Button>
</div>
</div> </div>
</div> )}
</div> </div>
) : ( ) : (
<div className="gf-form-inline"> <div className="gf-form-inline">
@ -234,6 +241,7 @@ export const AzureCredentialsForm: FunctionComponent<Props> = (props: Props) =>
placeholder="XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" placeholder="XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
value={credentials.clientSecret || ''} value={credentials.clientSecret || ''}
onChange={onClientSecretChange} onChange={onClientSecretChange}
disabled={disabled}
/> />
</div> </div>
</div> </div>
@ -255,6 +263,7 @@ export const AzureCredentialsForm: FunctionComponent<Props> = (props: Props) =>
} }
options={subscriptions} options={subscriptions}
onChange={onSubscriptionChange} onChange={onSubscriptionChange}
isDisabled={disabled}
/> />
</div> </div>
</div> </div>

View File

@ -11,32 +11,35 @@ type Props = {
value: ExemplarTraceIdDestination; value: ExemplarTraceIdDestination;
onChange: (value: ExemplarTraceIdDestination) => void; onChange: (value: ExemplarTraceIdDestination) => void;
onDelete: () => void; onDelete: () => void;
disabled?: boolean;
}; };
export default function ExemplarSetting({ value, onChange, onDelete }: Props) { export default function ExemplarSetting({ value, onChange, onDelete, disabled }: Props) {
const [isInternalLink, setIsInternalLink] = useState(Boolean(value.datasourceUid)); const [isInternalLink, setIsInternalLink] = useState(Boolean(value.datasourceUid));
return ( return (
<div className="gf-form-group"> <div className="gf-form-group">
<InlineField label="Internal link" labelWidth={24}> <InlineField label="Internal link" labelWidth={24} disabled={disabled}>
<> <>
<InlineSwitch <InlineSwitch
value={isInternalLink} value={isInternalLink}
aria-label={selectors.components.DataSource.Prometheus.configPage.internalLinkSwitch} aria-label={selectors.components.DataSource.Prometheus.configPage.internalLinkSwitch}
onChange={(ev) => setIsInternalLink(ev.currentTarget.checked)} onChange={(ev) => setIsInternalLink(ev.currentTarget.checked)}
/> />
<Button {!disabled && (
variant="destructive" <Button
title="Remove link" variant="destructive"
icon="times" title="Remove link"
onClick={(event) => { icon="times"
event.preventDefault(); onClick={(event) => {
onDelete(); event.preventDefault();
}} onDelete();
className={css` }}
margin-left: 8px; className={css`
`} margin-left: 8px;
/> `}
/>
)}
</> </>
</InlineField> </InlineField>
@ -45,6 +48,7 @@ export default function ExemplarSetting({ value, onChange, onDelete }: Props) {
label="Data source" label="Data source"
labelWidth={24} labelWidth={24}
tooltip="The data source the exemplar is going to navigate to." tooltip="The data source the exemplar is going to navigate to."
disabled={disabled}
> >
<DataSourcePicker <DataSourcePicker
tracing={true} tracing={true}
@ -65,6 +69,7 @@ export default function ExemplarSetting({ value, onChange, onDelete }: Props) {
label="URL" label="URL"
labelWidth={24} labelWidth={24}
tooltip="The URL of the trace backend the user would go to see its trace." tooltip="The URL of the trace backend the user would go to see its trace."
disabled={disabled}
> >
<Input <Input
placeholder="https://example.com/${__value.raw}" placeholder="https://example.com/${__value.raw}"
@ -86,6 +91,7 @@ export default function ExemplarSetting({ value, onChange, onDelete }: Props) {
label="URL Label" label="URL Label"
labelWidth={24} labelWidth={24}
tooltip="Use to override the button label on the exemplar traceID field." tooltip="Use to override the button label on the exemplar traceID field."
disabled={disabled}
> >
<Input <Input
placeholder="Go to example.com" placeholder="Go to example.com"
@ -104,6 +110,7 @@ export default function ExemplarSetting({ value, onChange, onDelete }: Props) {
label="Label name" label="Label name"
labelWidth={24} labelWidth={24}
tooltip="The name of the field in the labels object that should be used to get the traceID." tooltip="The name of the field in the labels object that should be used to get the traceID."
disabled={disabled}
> >
<Input <Input
placeholder="traceID" placeholder="traceID"

View File

@ -11,9 +11,10 @@ import ExemplarSetting from './ExemplarSetting';
type Props = { type Props = {
options?: ExemplarTraceIdDestination[]; options?: ExemplarTraceIdDestination[];
onChange: (value: ExemplarTraceIdDestination[]) => void; onChange: (value: ExemplarTraceIdDestination[]) => void;
disabled?: boolean;
}; };
export function ExemplarsSettings({ options, onChange }: Props) { export function ExemplarsSettings({ options, onChange, disabled }: Props) {
return ( return (
<> <>
<h3 className="page-heading">Exemplars</h3> <h3 className="page-heading">Exemplars</h3>
@ -34,25 +35,28 @@ export function ExemplarsSettings({ options, onChange }: Props) {
newOptions.splice(index, 1); newOptions.splice(index, 1);
onChange(newOptions); onChange(newOptions);
}} }}
disabled={disabled}
/> />
); );
})} })}
<Button {!disabled && (
variant="secondary" <Button
aria-label={selectors.components.DataSource.Prometheus.configPage.exemplarsAddButton} variant="secondary"
className={css` aria-label={selectors.components.DataSource.Prometheus.configPage.exemplarsAddButton}
margin-bottom: 10px; className={css`
`} margin-bottom: 10px;
icon="plus" `}
onClick={(event) => { icon="plus"
event.preventDefault(); onClick={(event) => {
const newOptions = [...(options || []), { name: 'traceID' }]; event.preventDefault();
onChange(newOptions); const newOptions = [...(options || []), { name: 'traceID' }];
}} onChange(newOptions);
> }}
Add >
</Button> Add
</Button>
)}
</> </>
); );
} }

View File

@ -158,6 +158,7 @@ export const PromSettings = (props: Props) => {
placeholder="15s" placeholder="15s"
onChange={onChangeHandler('timeInterval', options, onOptionsChange)} onChange={onChangeHandler('timeInterval', options, onOptionsChange)}
validationEvents={promSettingsValidationEvents} validationEvents={promSettingsValidationEvents}
disabled={options.readOnly}
/> />
} }
tooltip="Set this to the typical scrape and evaluation interval configured in Prometheus. Defaults to 15s." tooltip="Set this to the typical scrape and evaluation interval configured in Prometheus. Defaults to 15s."
@ -178,6 +179,7 @@ export const PromSettings = (props: Props) => {
spellCheck={false} spellCheck={false}
placeholder="60s" placeholder="60s"
validationEvents={promSettingsValidationEvents} validationEvents={promSettingsValidationEvents}
disabled={options.readOnly}
/> />
} }
tooltip="Set the Prometheus query timeout." tooltip="Set the Prometheus query timeout."
@ -198,6 +200,7 @@ export const PromSettings = (props: Props) => {
value={httpOptions.find((o) => o.value === options.jsonData.httpMethod)} value={httpOptions.find((o) => o.value === options.jsonData.httpMethod)}
onChange={onChangeHandler('httpMethod', options, onOptionsChange)} onChange={onChangeHandler('httpMethod', options, onOptionsChange)}
className="width-6" className="width-6"
disabled={options.readOnly}
/> />
</div> </div>
</div> </div>
@ -242,6 +245,7 @@ export const PromSettings = (props: Props) => {
} }
)} )}
width={20} width={20}
disabled={options.readOnly}
/> />
} }
tooltip="Set this to the type of your prometheus database, e.g. Prometheus, Cortex, Mimir or Thanos. Changing this field will save your current settings, and attempt to detect the version." tooltip="Set this to the type of your prometheus database, e.g. Prometheus, Cortex, Mimir or Thanos. Changing this field will save your current settings, and attempt to detect the version."
@ -263,6 +267,7 @@ export const PromSettings = (props: Props) => {
)} )}
onChange={onChangeHandler('prometheusVersion', options, onOptionsChange)} onChange={onChangeHandler('prometheusVersion', options, onOptionsChange)}
width={20} width={20}
disabled={options.readOnly}
/> />
} }
tooltip={`Use this to set the version of your ${options.jsonData.prometheusType} instance if it is not automatically configured.`} tooltip={`Use this to set the version of your ${options.jsonData.prometheusType} instance if it is not automatically configured.`}
@ -279,6 +284,7 @@ export const PromSettings = (props: Props) => {
labelWidth={28} labelWidth={28}
label="Disable metrics lookup" label="Disable metrics lookup"
tooltip="Checking this option will disable the metrics chooser and metric/label support in the query field's autocomplete. This helps if you have performance issues with bigger Prometheus instances." tooltip="Checking this option will disable the metrics chooser and metric/label support in the query field's autocomplete. This helps if you have performance issues with bigger Prometheus instances."
disabled={options.readOnly}
> >
<InlineSwitch <InlineSwitch
value={options.jsonData.disableMetricsLookup ?? false} value={options.jsonData.disableMetricsLookup ?? false}
@ -299,6 +305,7 @@ export const PromSettings = (props: Props) => {
onChange={onChangeHandler('customQueryParameters', options, onOptionsChange)} onChange={onChangeHandler('customQueryParameters', options, onOptionsChange)}
spellCheck={false} spellCheck={false}
placeholder="Example: max_source_resolution=5m&timeout=10" placeholder="Example: max_source_resolution=5m&timeout=10"
disabled={options.readOnly}
/> />
} }
/> />
@ -314,6 +321,7 @@ export const PromSettings = (props: Props) => {
exemplarOptions exemplarOptions
) )
} }
disabled={options.readOnly}
/> />
</> </>
); );