diff --git a/public/app/features/dashboard/components/PanelEditor/OptionsGroup.tsx b/public/app/features/dashboard/components/PanelEditor/OptionsGroup.tsx index cb537ff41d6..02206ebcf0b 100644 --- a/public/app/features/dashboard/components/PanelEditor/OptionsGroup.tsx +++ b/public/app/features/dashboard/components/PanelEditor/OptionsGroup.tsx @@ -1,4 +1,4 @@ -import React, { useState, FC } from 'react'; +import React, { useState, FC, useEffect } from 'react'; import { css, cx } from 'emotion'; import { GrafanaTheme } from '@grafana/data'; import { useTheme, Icon, stylesFactory } from '@grafana/ui'; @@ -9,6 +9,7 @@ interface Props { defaultToClosed?: boolean; className?: string; nested?: boolean; + onToggle?: (isExpanded: boolean) => void; } export const OptionsGroup: FC = ({ @@ -18,10 +19,16 @@ export const OptionsGroup: FC = ({ renderTitle, className, nested = false, + onToggle, }) => { const [isExpanded, toggleExpand] = useState(defaultToClosed ? false : true); const theme = useTheme(); const styles = getStyles(theme, isExpanded, nested); + useEffect(() => { + if (onToggle) { + onToggle(isExpanded); + } + }, [isExpanded]); return (
diff --git a/public/app/features/dashboard/components/PanelEditor/PanelEditorTabs.tsx b/public/app/features/dashboard/components/PanelEditor/PanelEditorTabs.tsx index ea1c988d789..3205874ece0 100644 --- a/public/app/features/dashboard/components/PanelEditor/PanelEditorTabs.tsx +++ b/public/app/features/dashboard/components/PanelEditor/PanelEditorTabs.tsx @@ -8,7 +8,6 @@ import { DashboardModel } from '../../state'; import { QueriesTab } from '../../panel_editor/QueriesTab'; import { PanelModel } from '../../state/PanelModel'; import { AlertTab } from 'app/features/alerting/AlertTab'; -import { VisualizationTab } from './VisualizationTab'; import { TransformationsEditor } from '../TransformationsEditor/TransformationsEditor'; interface PanelEditorTabsProps { @@ -66,7 +65,6 @@ export const PanelEditorTabs: React.FC = ({ panel, dashboa {activeTab.id === PanelEditorTabId.Query && } {activeTab.id === PanelEditorTabId.Alert && } - {activeTab.id === PanelEditorTabId.Visualize && } {activeTab.id === PanelEditorTabId.Transform && data.state !== LoadingState.NotStarted && ( = ({ onPanelOptionsChanged, onFieldConfigsChange, }) => { - const elements: JSX.Element[] = []; + const visTabInputRef = useRef(); const linkVariablesSuggestions = useMemo(() => getPanelLinksVariableSuggestions(), []); + const elements: JSX.Element[] = []; const panelLinksCount = panel && panel.links ? panel.links.length : 0; const variableOptions = getVariableOptions(); @@ -39,9 +41,14 @@ export const PanelOptionsTab: FC = ({ const maxPerRowOptions = [2, 3, 4, 6, 8, 12].map(value => ({ label: value.toString(), value })); + const focusVisPickerInput = (isExpanded: boolean) => { + if (isExpanded && visTabInputRef.current) { + visTabInputRef.current.focus(); + } + }; // Fist common panel settings Title, description elements.push( - + onPanelConfigChange('title', e.currentTarget.value)} /> @@ -57,6 +64,12 @@ export const PanelOptionsTab: FC = ({ ); + elements.push( + + + + ); + // Old legacy react editor if (plugin.editor && panel && !plugin.optionEditors) { elements.push( diff --git a/public/app/features/dashboard/components/PanelEditor/VisualizationTab.tsx b/public/app/features/dashboard/components/PanelEditor/VisualizationTab.tsx index f4c2d6427b4..4fb01efd4a6 100644 --- a/public/app/features/dashboard/components/PanelEditor/VisualizationTab.tsx +++ b/public/app/features/dashboard/components/PanelEditor/VisualizationTab.tsx @@ -1,4 +1,4 @@ -import React, { FC, useCallback, useState } from 'react'; +import React, { useCallback, useState } from 'react'; import { css } from 'emotion'; import { GrafanaTheme, PanelPlugin, PanelPluginMeta } from '@grafana/data'; import { CustomScrollbar, useTheme, stylesFactory, Icon, Input } from '@grafana/ui'; @@ -23,70 +23,71 @@ interface DispatchProps { type Props = OwnProps & ConnectedProps & DispatchProps; -export const VisualizationTabUnconnected: FC = ({ panel, plugin, changePanelPlugin }) => { - const [searchQuery, setSearchQuery] = useState(''); - const theme = useTheme(); - const styles = getStyles(theme); +export const VisualizationTabUnconnected = React.forwardRef( + ({ panel, plugin, changePanelPlugin }, ref) => { + const [searchQuery, setSearchQuery] = useState(''); + const theme = useTheme(); + const styles = getStyles(theme); - if (!plugin) { - return null; - } + if (!plugin) { + return null; + } - const onPluginTypeChange = (meta: PanelPluginMeta) => { - changePanelPlugin(panel, meta.id); - }; + const onPluginTypeChange = (meta: PanelPluginMeta) => { + changePanelPlugin(panel, meta.id); + }; - const onKeyPress = useCallback( - (e: React.KeyboardEvent) => { - if (e.key === 'Enter') { - const query = e.currentTarget.value; - const plugins = getAllPanelPluginMeta(); - const match = filterPluginList(plugins, query, plugin.meta); - if (match && match.length) { - onPluginTypeChange(match[0]); + const onKeyPress = useCallback( + (e: React.KeyboardEvent) => { + if (e.key === 'Enter') { + const query = e.currentTarget.value; + const plugins = getAllPanelPluginMeta(); + const match = filterPluginList(plugins, query, plugin.meta); + if (match && match.length) { + onPluginTypeChange(match[0]); + } } - } - }, - [onPluginTypeChange] - ); + }, + [onPluginTypeChange] + ); - const suffix = - searchQuery !== '' ? ( - setSearchQuery('')}> - - Clear filter - - ) : null; + const suffix = + searchQuery !== '' ? ( + setSearchQuery('')}> + + Clear filter + + ) : null; - return ( -
-
- - setSearchQuery(e.currentTarget.value)} - onKeyPress={onKeyPress} - prefix={} - suffix={suffix} - placeholder="Filter visualisations" - autoFocus - /> - + return ( +
+
+ + setSearchQuery(e.currentTarget.value)} + onKeyPress={onKeyPress} + prefix={} + suffix={suffix} + placeholder="Filter visualisations" + ref={ref} + /> + +
+
+ + {}} + /> + +
-
- - {}} - /> - -
-
- ); -}; - + ); + } +); const getStyles = stylesFactory((theme: GrafanaTheme) => { return { icon: css` @@ -97,7 +98,6 @@ const getStyles = stylesFactory((theme: GrafanaTheme) => { flex-direction: column; flex-grow: 1; max-height: 100%; - padding: ${theme.spacing.md}; `, search: css` flex-grow: 0; @@ -123,4 +123,6 @@ const mapStateToProps: MapStateToProps = ( const mapDispatchToProps: MapDispatchToProps = { changePanelPlugin }; -export const VisualizationTab = connect(mapStateToProps, mapDispatchToProps)(VisualizationTabUnconnected); +export const VisualizationTab = connect(mapStateToProps, mapDispatchToProps, undefined, { forwardRef: true })( + VisualizationTabUnconnected +); diff --git a/public/app/features/dashboard/components/PanelEditor/state/selectors.ts b/public/app/features/dashboard/components/PanelEditor/state/selectors.ts index f57c4cb6ae2..121edcec6bc 100644 --- a/public/app/features/dashboard/components/PanelEditor/state/selectors.ts +++ b/public/app/features/dashboard/components/PanelEditor/state/selectors.ts @@ -30,13 +30,6 @@ export const getPanelEditorTabs = memoizeOne((location: LocationState, plugin?: }); } - tabs.push({ - id: PanelEditorTabId.Visualize, - text: 'Visualize', - icon: 'chart-bar', - active: false, - }); - if (plugin.meta.id === 'graph') { tabs.push({ id: PanelEditorTabId.Alert, diff --git a/public/app/features/dashboard/panel_editor/VizTypePicker.tsx b/public/app/features/dashboard/panel_editor/VizTypePicker.tsx index 93975ba3d63..dc42cf9dc1c 100644 --- a/public/app/features/dashboard/panel_editor/VizTypePicker.tsx +++ b/public/app/features/dashboard/panel_editor/VizTypePicker.tsx @@ -85,24 +85,18 @@ export const VizTypePicker: React.FC = ({ searchQuery, onTypeChange, curr const renderList = filteredPluginList.concat(pluginsList.filter(p => filteredPluginList.indexOf(p) === -1)); return ( -
-
- {hasResults ? ( - renderList.map((plugin, index) => renderVizPlugin(plugin, index)) - ) : ( - Could not find anything matching your query - )} -
+
+ {hasResults ? ( + renderList.map((plugin, index) => renderVizPlugin(plugin, index)) + ) : ( + Could not find anything matching your query + )}
); }; const getStyles = stylesFactory((theme: GrafanaTheme) => { return { - wrapper: css` - // this needed here to make the box shadow not be clicked by the parent scroll container - padding-top: ${theme.spacing.md}; - `, grid: css` max-width: 100%; display: grid;