diff --git a/packages/grafana-ui/src/components/ConfirmModal/ConfirmModal.tsx b/packages/grafana-ui/src/components/ConfirmModal/ConfirmModal.tsx index 8139228a81c..0251fbcfd0c 100644 --- a/packages/grafana-ui/src/components/ConfirmModal/ConfirmModal.tsx +++ b/packages/grafana-ui/src/components/ConfirmModal/ConfirmModal.tsx @@ -1,4 +1,4 @@ -import { css } from '@emotion/css'; +import { css, cx } from '@emotion/css'; import React, { useEffect, useRef, useState } from 'react'; import { GrafanaTheme2 } from '@grafana/data'; @@ -25,6 +25,8 @@ export interface ConfirmModalProps { dismissText?: string; /** Icon for the modal header */ icon?: IconName; + /** Additional styling for modal container */ + modalClass?: string; /** Text user needs to fill in before confirming */ confirmationText?: string; /** Text for alternative button */ @@ -46,6 +48,7 @@ export const ConfirmModal = ({ confirmationText, dismissText = 'Cancel', alternativeText, + modalClass, icon = 'exclamation-triangle', onConfirm, onDismiss, @@ -66,7 +69,7 @@ export const ConfirmModal = ({ }, [isOpen]); return ( - +
{body} {description ?
{description}
: null} diff --git a/public/app/features/variables/editor/ConfirmDeleteModal.tsx b/public/app/features/variables/editor/ConfirmDeleteModal.tsx new file mode 100644 index 00000000000..2e412d3f776 --- /dev/null +++ b/public/app/features/variables/editor/ConfirmDeleteModal.tsx @@ -0,0 +1,34 @@ +import { css } from '@emotion/css'; +import React from 'react'; + +import { ConfirmModal } from '@grafana/ui'; + +interface Props { + varName: string; + isOpen: boolean; + onConfirm: () => void; + onDismiss: () => void; +} + +export function ConfirmDeleteModal({ varName, isOpen = false, onConfirm, onDismiss }: Props) { + return ( + + ); +} + +const styles = { + modal: css({ + width: 'max-content', + maxWidth: '80vw', + }), +}; diff --git a/public/app/features/variables/editor/VariableEditorContainer.tsx b/public/app/features/variables/editor/VariableEditorContainer.tsx index 17ca426b9da..08cd5a564fe 100644 --- a/public/app/features/variables/editor/VariableEditorContainer.tsx +++ b/public/app/features/variables/editor/VariableEditorContainer.tsx @@ -14,6 +14,7 @@ import { changeVariableOrder, duplicateVariable, removeVariable } from '../state import { KeyedVariableIdentifier } from '../state/types'; import { toKeyedVariableIdentifier, toVariablePayload } from '../utils'; +import { ConfirmDeleteModal } from './ConfirmDeleteModal'; import { VariableEditorEditor } from './VariableEditorEditor'; import { VariableEditorList } from './VariableEditorList'; import { createNewVariable, initListMode } from './actions'; @@ -60,7 +61,15 @@ const connector = connect(mapStateToProps, mapDispatchToProps); type Props = OwnProps & ConnectedProps; -class VariableEditorContainerUnconnected extends PureComponent { +interface State { + variableId?: KeyedVariableIdentifier; +} + +class VariableEditorContainerUnconnected extends PureComponent { + state: State = { + variableId: undefined, + }; + componentDidMount() { this.props.initListMode(this.props.dashboard.uid); } @@ -82,8 +91,17 @@ class VariableEditorContainerUnconnected extends PureComponent { this.props.duplicateVariable(identifier); }; - onRemoveVariable = (identifier: KeyedVariableIdentifier) => { - this.props.removeVariable(identifier); + onModalOpen = (identifier: KeyedVariableIdentifier) => { + this.setState({ variableId: identifier }); + }; + + onModalClose = () => { + this.setState({ variableId: undefined }); + }; + + onRemoveVariable = () => { + this.props.removeVariable(this.state.variableId!); + this.onModalClose(); }; render() { @@ -100,7 +118,7 @@ class VariableEditorContainerUnconnected extends PureComponent { onEdit={this.onEditVariable} onChangeOrder={this.onChangeVariableOrder} onDuplicate={this.onDuplicateVariable} - onDelete={this.onRemoveVariable} + onDelete={this.onModalOpen} usages={this.props.usages} usagesNetwork={this.props.usagesNetwork} /> @@ -109,6 +127,12 @@ class VariableEditorContainerUnconnected extends PureComponent { )} {variableToEdit && } + ); } diff --git a/public/app/features/variables/editor/VariableEditorEditor.tsx b/public/app/features/variables/editor/VariableEditorEditor.tsx index f1887042616..b00d2b8727f 100644 --- a/public/app/features/variables/editor/VariableEditorEditor.tsx +++ b/public/app/features/variables/editor/VariableEditorEditor.tsx @@ -20,6 +20,7 @@ import { KeyedVariableIdentifier } from '../state/types'; import { VariableHide } from '../types'; import { toKeyedVariableIdentifier, toVariablePayload } from '../utils'; +import { ConfirmDeleteModal } from './ConfirmDeleteModal'; import { VariableHideSelect } from './VariableHideSelect'; import { VariableSectionHeader } from './VariableSectionHeader'; import { VariableTextField } from './VariableTextField'; @@ -61,7 +62,15 @@ export interface OwnProps { type Props = OwnProps & ConnectedProps; -export class VariableEditorEditorUnConnected extends PureComponent { +interface State { + showDeleteModal: boolean; +} + +export class VariableEditorEditorUnConnected extends PureComponent { + state: State = { + showDeleteModal: false, + }; + componentDidMount(): void { this.props.variableEditorMount(this.props.identifier); } @@ -120,8 +129,17 @@ export class VariableEditorEditorUnConnected extends PureComponent { this.props.updateOptions(toKeyedVariableIdentifier(this.props.variable)); }; + onModalOpen = () => { + this.setState({ showDeleteModal: true }); + }; + + onModalClose = () => { + this.setState({ showDeleteModal: false }); + }; + onDelete = () => { this.props.removeVariable(this.props.identifier); + this.onModalClose(); locationService.partial({ editIndex: null }); }; @@ -192,7 +210,7 @@ export class VariableEditorEditorUnConnected extends PureComponent { {hasOptions(this.props.variable) ? : null} -
); } diff --git a/public/app/features/variables/editor/VariableEditorList.tsx b/public/app/features/variables/editor/VariableEditorList.tsx index 98634fc4b02..ef3b8ff2d42 100644 --- a/public/app/features/variables/editor/VariableEditorList.tsx +++ b/public/app/features/variables/editor/VariableEditorList.tsx @@ -40,7 +40,7 @@ export function VariableEditorList({ } reportInteraction('Variable drag and drop'); const identifier = JSON.parse(result.draggableId); - onChangeOrder(identifier, result.source.index, result.destination.index); + onChangeOrder(identifier, variables[result.source.index].index, variables[result.destination.index].index); }; return ( diff --git a/public/app/features/variables/editor/actions.ts b/public/app/features/variables/editor/actions.ts index e2cd953072b..ef42be3456d 100644 --- a/public/app/features/variables/editor/actions.ts +++ b/public/app/features/variables/editor/actions.ts @@ -90,7 +90,8 @@ export const createNewVariable = (key: string | null | undefined, type: VariableType = 'query'): ThunkResult => (dispatch, getState) => { const rootStateKey = toStateKey(key); - const id = getNextAvailableId(type, getVariablesByKey(rootStateKey, getState())); + const varsByKey = getVariablesByKey(rootStateKey, getState()); + const id = getNextAvailableId(type, varsByKey); const identifier: VariableIdentifier = { type, id }; const global = false; const index = getNewVariableIndex(rootStateKey, getState()); @@ -102,7 +103,7 @@ export const createNewVariable = toKeyedAction(rootStateKey, addVariable(toVariablePayload(identifier, { global, model, index }))) ); - locationService.partial({ editIndex: index }); + locationService.partial({ editIndex: varsByKey.length }); }; export const initListMode = diff --git a/public/app/features/variables/state/sharedReducer.ts b/public/app/features/variables/state/sharedReducer.ts index 0d75e937031..4c5bda5f013 100644 --- a/public/app/features/variables/state/sharedReducer.ts +++ b/public/app/features/variables/state/sharedReducer.ts @@ -67,9 +67,9 @@ const sharedReducerSlice = createSlice({ return; } - const variableStates = Object.values(state); - for (let index = 0; index < variableStates.length; index++) { - variableStates[index].index = index; + const variableStates = Object.values(state).sort((a, b) => a.index - b.index); + for (let i = 0; i < variableStates.length; i++) { + variableStates[i].index = i; } }, duplicateVariable: (state: VariablesState, action: PayloadAction>) => {