import { css, cx } from '@emotion/css'; import * as React from 'react'; import { ReactNode, useState } from 'react'; import { DataQuery, DataSourceInstanceSettings, GrafanaTheme2 } from '@grafana/data'; import { selectors } from '@grafana/e2e-selectors'; import { FieldValidationMessage, Icon, Input, useStyles2 } from '@grafana/ui'; import { t, Trans } from 'app/core/internationalization'; import { DataSourcePicker } from 'app/features/datasources/components/picker/DataSourcePicker'; export interface Props { query: TQuery; queries: TQuery[]; hidden?: boolean; dataSource: DataSourceInstanceSettings; renderExtras?: () => ReactNode; onChangeDataSource?: (settings: DataSourceInstanceSettings) => void; onChange: (query: TQuery) => void; collapsedText: string | null; alerting?: boolean; hideRefId?: boolean; } export const QueryEditorRowHeader = (props: Props) => { const { query, queries, onChange, collapsedText, renderExtras, hidden, hideRefId = false } = props; const styles = useStyles2(getStyles); const [isEditing, setIsEditing] = useState(false); const [validationError, setValidationError] = useState(null); const onEditQuery = (event: React.SyntheticEvent) => { setIsEditing(true); }; const onEndEditName = (newName: string) => { setIsEditing(false); // Ignore change if invalid if (validationError) { setValidationError(null); return; } if (query.refId !== newName) { onChange({ ...query, refId: newName, }); } }; const onInputChange = (event: React.SyntheticEvent) => { const newName = event.currentTarget.value.trim(); if (newName.length === 0) { setValidationError('An empty query name is not allowed'); return; } for (const otherQuery of queries) { if (otherQuery !== query && newName === otherQuery.refId) { setValidationError('Query name already exists'); return; } } if (validationError) { setValidationError(null); } }; const onEditQueryBlur = (event: React.SyntheticEvent) => { onEndEditName(event.currentTarget.value.trim()); }; const onKeyDown = (event: React.KeyboardEvent) => { if (event.key === 'Enter') { onEndEditName(event.currentTarget.value); } }; const onFocus = (event: React.FocusEvent) => { event.target.select(); }; return ( <>
{!hideRefId && !isEditing && ( )} {!hideRefId && isEditing && ( <> {validationError && {validationError}} )} {renderDataSource(props, styles)} {renderExtras &&
{renderExtras()}
} {hidden && ( Hidden )}
{collapsedText &&
{collapsedText}
} ); }; const renderDataSource = ( props: Props, styles: ReturnType ): ReactNode => { const { alerting, dataSource, onChangeDataSource } = props; if (!onChangeDataSource) { // eslint-disable-next-line @grafana/no-untranslated-strings return ({dataSource.name}); } return (
); }; const getStyles = (theme: GrafanaTheme2) => { return { wrapper: css({ label: 'Wrapper', display: 'flex', alignItems: 'center', marginLeft: theme.spacing(0.5), overflow: 'hidden', }), queryNameWrapper: css({ display: 'flex', cursor: 'pointer', border: '1px solid transparent', borderRadius: theme.shape.radius.default, alignItems: 'center', padding: theme.spacing(0, 0, 0, 0.5), margin: 0, background: 'transparent', overflow: 'hidden', '&:hover': { background: theme.colors.action.hover, border: `1px dashed ${theme.colors.border.strong}`, }, '&:focus': { border: `2px solid ${theme.colors.primary.border}`, }, '&:hover, &:focus': { '.query-name-edit-icon': { visibility: 'visible', }, }, }), queryName: css({ fontWeight: theme.typography.fontWeightMedium, color: theme.colors.primary.text, cursor: 'pointer', overflow: 'hidden', marginLeft: theme.spacing(0.5), }), queryEditIcon: cx( css({ marginLeft: theme.spacing(2), visibility: 'hidden', }), 'query-name-edit-icon' ), queryNameInput: css({ maxWidth: '300px', margin: '-4px 0', }), collapsedText: css({ fontWeight: theme.typography.fontWeightRegular, fontSize: theme.typography.bodySmall.fontSize, color: theme.colors.text.secondary, paddingLeft: theme.spacing(1), alignItems: 'center', overflow: 'hidden', fontStyle: 'italic', whiteSpace: 'nowrap', textOverflow: 'ellipsis', }), contextInfo: css({ fontSize: theme.typography.bodySmall.fontSize, fontStyle: 'italic', color: theme.colors.text.secondary, paddingLeft: '10px', paddingRight: '10px', }), itemWrapper: css({ display: 'flex', marginLeft: '4px', }), }; };