From 06d78ea904fe227fc31cd6fb19d64677f93f4a7f Mon Sep 17 00:00:00 2001 From: Josh Hunt Date: Fri, 29 Jul 2022 16:29:55 +0100 Subject: [PATCH] Dashboards: Disable variable pickers for snapshots (#52827) * user essentials mob! :trident: lastFile:public/app/features/variables/textbox/TextBoxVariablePicker.tsx * user essentials mob! :trident: * user essentials mob! :trident: lastFile:public/app/features/variables/adhoc/picker/AdHocFilter.tsx * finish up disabling variables in snapshots * remove accident * use theme.spacing instead of the v1 shim Co-authored-by: Joao Silva Co-authored-by: Leodegario Pasakdal --- .../dashboard/components/SubMenu/SubMenu.tsx | 4 ++- .../components/SubMenu/SubMenuItems.tsx | 5 +-- .../variables/adhoc/picker/AdHocFilter.tsx | 32 ++++++++++++------- .../variables/adhoc/picker/AdHocFilterKey.tsx | 5 ++- .../adhoc/picker/AdHocFilterRenderer.tsx | 6 +++- .../adhoc/picker/AdHocFilterValue.tsx | 11 ++++++- .../variables/adhoc/picker/AdHocPicker.tsx | 1 + .../adhoc/picker/OperatorSegment.tsx | 13 ++++++-- .../OptionsPicker/OptionPicker.test.tsx | 1 + .../pickers/OptionsPicker/OptionsPicker.tsx | 1 + .../variables/pickers/PickerRenderer.tsx | 3 +- .../variables/pickers/shared/VariableLink.tsx | 32 ++++++++++++------- .../app/features/variables/pickers/types.ts | 1 + .../textbox/TextBoxVariablePicker.tsx | 3 +- 14 files changed, 85 insertions(+), 33 deletions(-) diff --git a/public/app/features/dashboard/components/SubMenu/SubMenu.tsx b/public/app/features/dashboard/components/SubMenu/SubMenu.tsx index a12ceca2bdd..a7e6479c1a2 100644 --- a/public/app/features/dashboard/components/SubMenu/SubMenu.tsx +++ b/public/app/features/dashboard/components/SubMenu/SubMenu.tsx @@ -49,10 +49,12 @@ class SubMenuUnConnected extends PureComponent { return null; } + const readOnlyVariables = dashboard.meta.isSnapshot ?? false; + return (
- + = ({ variables }) => { +export const SubMenuItems: FunctionComponent = ({ variables, readOnly }) => { const [visibleVariables, setVisibleVariables] = useState([]); useEffect(() => { @@ -29,7 +30,7 @@ export const SubMenuItems: FunctionComponent = ({ variables }) => { className="submenu-item gf-form-inline" data-testid={selectors.pages.Dashboard.SubMenu.submenuItem} > - +
); })} diff --git a/public/app/features/variables/adhoc/picker/AdHocFilter.tsx b/public/app/features/variables/adhoc/picker/AdHocFilter.tsx index a7300a4177f..0198b340878 100644 --- a/public/app/features/variables/adhoc/picker/AdHocFilter.tsx +++ b/public/app/features/variables/adhoc/picker/AdHocFilter.tsx @@ -1,6 +1,7 @@ import React, { PureComponent, ReactNode } from 'react'; import { DataSourceRef, SelectableValue } from '@grafana/data'; +import { Segment } from '@grafana/ui'; import { AdHocVariableFilter } from 'app/features/variables/types'; import { AdHocFilterBuilder } from './AdHocFilterBuilder'; @@ -17,6 +18,7 @@ interface Props { // Passes options to the datasources getTagKeys(options?: any) method // which is called to fetch the available filter key options in AdHocFilterKey.tsx getTagKeysOptions?: any; + disabled?: boolean; } /** @@ -47,35 +49,43 @@ export class AdHocFilter extends PureComponent { }; render() { - const { filters } = this.props; + const { filters, disabled } = this.props; return (
- {this.renderFilters(filters)} - 0 ? : null} - onCompleted={this.appendFilterToVariable} - getTagKeysOptions={this.props.getTagKeysOptions} - /> + {this.renderFilters(filters, disabled)} + + {!disabled && ( + 0 ? : null} + onCompleted={this.appendFilterToVariable} + getTagKeysOptions={this.props.getTagKeysOptions} + /> + )}
); } - renderFilters(filters: AdHocVariableFilter[]) { + renderFilters(filters: AdHocVariableFilter[], disabled?: boolean) { + if (filters.length === 0 && disabled) { + return {}} />; + } + return filters.reduce((segments: ReactNode[], filter, index) => { if (segments.length > 0) { segments.push(); } - segments.push(this.renderFilterSegments(filter, index)); + segments.push(this.renderFilterSegments(filter, index, disabled)); return segments; }, []); } - renderFilterSegments(filter: AdHocVariableFilter, index: number) { + renderFilterSegments(filter: AdHocVariableFilter, index: number, disabled?: boolean) { return ( ) => void; getTagKeysOptions?: any; + disabled?: boolean; } const MIN_WIDTH = 90; -export const AdHocFilterKey: FC = ({ datasource, onChange, filterKey, getTagKeysOptions }) => { +export const AdHocFilterKey: FC = ({ datasource, onChange, disabled, filterKey, getTagKeysOptions }) => { const loadKeys = () => fetchFilterKeys(datasource, getTagKeysOptions); const loadKeysWithRemove = () => fetchFilterKeysWithRemove(datasource, getTagKeysOptions); @@ -21,6 +22,7 @@ export const AdHocFilterKey: FC = ({ datasource, onChange, filterKey, get return (
= ({ datasource, onChange, filterKey, get return (
) => void; placeHolder?: string; getTagKeysOptions?: any; + disabled?: boolean; } export const AdHocFilterRenderer: FC = ({ @@ -25,19 +26,22 @@ export const AdHocFilterRenderer: FC = ({ onValueChange, placeHolder, getTagKeysOptions, + disabled, }) => { return ( <>
- +
) => void; placeHolder?: string; + disabled?: boolean; } -export const AdHocFilterValue: FC = ({ datasource, onChange, filterKey, filterValue, placeHolder }) => { +export const AdHocFilterValue: FC = ({ + datasource, + disabled, + onChange, + filterKey, + filterValue, + placeHolder, +}) => { const loadValues = () => fetchFilterValues(datasource, filterKey); return (
{ ) => void; + disabled?: boolean; } const options = ['=', '!=', '<', '>', '=~', '!~'].map>((value) => ({ @@ -13,6 +14,14 @@ const options = ['=', '!=', '<', '>', '=~', '!~'].map>(( value, })); -export const OperatorSegment: FC = ({ value, onChange }) => { - return ; +export const OperatorSegment: FC = ({ value, disabled, onChange }) => { + return ( + + ); }; diff --git a/public/app/features/variables/pickers/OptionsPicker/OptionPicker.test.tsx b/public/app/features/variables/pickers/OptionsPicker/OptionPicker.test.tsx index 60b42c963dd..5b5f932aef0 100644 --- a/public/app/features/variables/pickers/OptionsPicker/OptionPicker.test.tsx +++ b/public/app/features/variables/pickers/OptionsPicker/OptionPicker.test.tsx @@ -37,6 +37,7 @@ function setupTestContext({ pickerState = {}, variable = {} }: Args = {}) { const props: VariablePickerProps = { variable: v, onVariableChange, + readOnly: false, }; const Picker = optionPickerFactory(); const optionsPicker: OptionsPickerState = { ...initialOptionPickerState, ...pickerState }; diff --git a/public/app/features/variables/pickers/OptionsPicker/OptionsPicker.tsx b/public/app/features/variables/pickers/OptionsPicker/OptionsPicker.tsx index 6d264cd06ef..244e0d461d5 100644 --- a/public/app/features/variables/pickers/OptionsPicker/OptionsPicker.tsx +++ b/public/app/features/variables/pickers/OptionsPicker/OptionsPicker.tsx @@ -130,6 +130,7 @@ export const optionPickerFactory = ); } diff --git a/public/app/features/variables/pickers/PickerRenderer.tsx b/public/app/features/variables/pickers/PickerRenderer.tsx index 583fbb570a8..d02150ff04d 100644 --- a/public/app/features/variables/pickers/PickerRenderer.tsx +++ b/public/app/features/variables/pickers/PickerRenderer.tsx @@ -8,6 +8,7 @@ import { VariableHide, VariableModel } from '../types'; interface Props { variable: VariableModel; + readOnly?: boolean; } export const PickerRenderer: FunctionComponent = (props) => { @@ -21,7 +22,7 @@ export const PickerRenderer: FunctionComponent = (props) => {
{props.variable.hide !== VariableHide.hideVariable && PickerToRender && ( - + )}
); diff --git a/public/app/features/variables/pickers/shared/VariableLink.tsx b/public/app/features/variables/pickers/shared/VariableLink.tsx index dcdf06f73b9..21f2d5c60b0 100644 --- a/public/app/features/variables/pickers/shared/VariableLink.tsx +++ b/public/app/features/variables/pickers/shared/VariableLink.tsx @@ -1,23 +1,24 @@ import { css } from '@emotion/css'; import React, { FC, MouseEvent, useCallback } from 'react'; -import { GrafanaTheme } from '@grafana/data'; +import { GrafanaTheme2 } from '@grafana/data'; import { selectors } from '@grafana/e2e-selectors'; -import { Icon, Tooltip, useStyles } from '@grafana/ui'; +import { Icon, Tooltip, useStyles2 } from '@grafana/ui'; interface Props { onClick: () => void; text: string; loading: boolean; onCancel: () => void; + disabled: boolean; // todo: optional? /** * htmlFor, needed for the label */ id: string; } -export const VariableLink: FC = ({ loading, onClick: propsOnClick, text, onCancel, id }) => { - const styles = useStyles(getStyles); +export const VariableLink: FC = ({ loading, disabled, onClick: propsOnClick, text, onCancel, id }) => { + const styles = useStyles2(getStyles); const onClick = useCallback( (event: MouseEvent) => { event.stopPropagation(); @@ -50,6 +51,7 @@ export const VariableLink: FC = ({ loading, onClick: propsOnClick, text, aria-controls={`options-${id}`} id={id} title={text} + disabled={disabled} > @@ -62,7 +64,7 @@ interface VariableLinkTextProps { } const VariableLinkText: FC = ({ text }) => { - const styles = useStyles(getStyles); + const styles = useStyles2(getStyles); return {text}; }; @@ -88,28 +90,34 @@ const LoadingIndicator: FC> = ({ onCancel }) => { ); }; -const getStyles = (theme: GrafanaTheme) => ({ +const getStyles = (theme: GrafanaTheme2) => ({ container: css` max-width: 500px; padding-right: 10px; - padding: 0 ${theme.spacing.sm}; - background-color: ${theme.colors.formInputBg}; - border: 1px solid ${theme.colors.formInputBorder}; - border-radius: ${theme.border.radius.sm}; + padding: 0 ${theme.spacing(1)}; + background-color: ${theme.components.input.background}; + border: 1px solid ${theme.components.input.borderColor}; + border-radius: ${theme.shape.borderRadius(1)}; display: flex; align-items: center; color: ${theme.colors.text}; - height: ${theme.height.md}px; + height: ${theme.spacing(theme.components.height.md)}; .label-tag { margin: 0 5px; } + + &:disabled { + background-color: ${theme.colors.action.disabledBackground}; + color: ${theme.colors.action.disabledText}; + border: 1px solid ${theme.colors.action.disabledBackground}; + } `, textAndTags: css` overflow: hidden; text-overflow: ellipsis; white-space: nowrap; - margin-right: ${theme.spacing.xxs}; + margin-right: ${theme.spacing(0.25)}; user-select: none; `, }); diff --git a/public/app/features/variables/pickers/types.ts b/public/app/features/variables/pickers/types.ts index b992f303b9e..adc87001c3e 100644 --- a/public/app/features/variables/pickers/types.ts +++ b/public/app/features/variables/pickers/types.ts @@ -2,6 +2,7 @@ import { VariableModel } from '../types'; export interface VariablePickerProps { variable: Model; + readOnly: boolean; onVariableChange?: (variable: Model) => void; } diff --git a/public/app/features/variables/textbox/TextBoxVariablePicker.tsx b/public/app/features/variables/textbox/TextBoxVariablePicker.tsx index e52d4dcdde2..1cc3765fde5 100644 --- a/public/app/features/variables/textbox/TextBoxVariablePicker.tsx +++ b/public/app/features/variables/textbox/TextBoxVariablePicker.tsx @@ -12,7 +12,7 @@ import { toVariablePayload } from '../utils'; export interface Props extends VariablePickerProps {} -export function TextBoxVariablePicker({ variable, onVariableChange }: Props): ReactElement { +export function TextBoxVariablePicker({ variable, onVariableChange, readOnly }: Props): ReactElement { const dispatch = useDispatch(); const [updatedValue, setUpdatedValue] = useState(variable.current.value); useEffect(() => { @@ -68,6 +68,7 @@ export function TextBoxVariablePicker({ variable, onVariableChange }: Props): Re value={updatedValue} onChange={onChange} onBlur={onBlur} + disabled={readOnly} onKeyDown={onKeyDown} placeholder="Enter variable value" id={`var-${variable.id}`}