Extension Sidebar: Close sidebar with toolbar button (#104208)

* Extension Sidebar: Improve closing behavior

* Extension Sidebar: Fix title
This commit is contained in:
Sven Grossmann
2025-04-19 22:38:58 +02:00
committed by GitHub
parent 5ec8750875
commit ea450a92f9
3 changed files with 85 additions and 35 deletions
@@ -95,9 +95,9 @@ describe('ExtensionToolbarItem', () => {
it('should render a single button when only one component is available', () => {
setup();
const button = screen.getByTestId('extension-toolbar-button');
const button = screen.getByTestId('extension-toolbar-button-open');
expect(button).toBeInTheDocument();
expect(button).toHaveAttribute('aria-label', mockComponent.description);
expect(button).toHaveAttribute('aria-label', `Open ${mockComponent.title}`);
expect(screen.getByTestId('is-open')).toHaveTextContent('false');
expect(screen.getByTestId('docked-component-id')).toHaveTextContent('');
});
@@ -105,7 +105,7 @@ describe('ExtensionToolbarItem', () => {
it('should toggle the sidebar when clicking a single component button', async () => {
setup();
const button = screen.getByTestId('extension-toolbar-button');
const button = screen.getByTestId('extension-toolbar-button-open');
await userEvent.click(button);
expect(screen.getByTestId('is-open')).toHaveTextContent('true');
@@ -127,7 +127,7 @@ describe('ExtensionToolbarItem', () => {
setup();
const button = screen.getByTestId('extension-toolbar-button');
const button = screen.getByTestId('extension-toolbar-button-open');
expect(button).toBeInTheDocument();
await userEvent.click(button);
@@ -151,7 +151,7 @@ describe('ExtensionToolbarItem', () => {
setup();
const button = screen.getByTestId('extension-toolbar-button');
const button = screen.getByTestId('extension-toolbar-button-open');
await userEvent.click(button);
// Menu items should be visible
@@ -176,7 +176,7 @@ describe('ExtensionToolbarItem', () => {
setup();
// Open the dropdown
const button = screen.getByTestId('extension-toolbar-button');
const button = screen.getByTestId('extension-toolbar-button-open');
await userEvent.click(button);
// Click a menu item
@@ -202,12 +202,13 @@ describe('ExtensionToolbarItem', () => {
setup();
const button = screen.getByTestId('extension-toolbar-button');
await userEvent.click(button);
await userEvent.click(screen.getByText('Component 1'));
const openButton = screen.getByTestId('extension-toolbar-button-open');
await userEvent.click(openButton);
const component1 = screen.getByText('Component 1');
await userEvent.click(component1);
await userEvent.click(button);
await userEvent.click(screen.getByText('Component 1'));
const closeButton = screen.getByTestId('extension-toolbar-button-close');
await userEvent.click(closeButton);
expect(screen.getByTestId('is-open')).toHaveTextContent('false');
});
@@ -1,4 +1,5 @@
import { css, cx } from '@emotion/css';
import { useCallback } from 'react';
import { GrafanaTheme2 } from '@grafana/data';
import { Dropdown, Menu, ToolbarButton, useTheme2 } from '@grafana/ui';
@@ -6,13 +7,25 @@ import { t } from 'app/core/internationalization';
import { NavToolbarSeparator } from '../NavToolbar/NavToolbarSeparator';
import { getComponentIdFromComponentMeta, useExtensionSidebarContext } from './ExtensionSidebarProvider';
import {
getComponentIdFromComponentMeta,
getComponentMetaFromComponentId,
useExtensionSidebarContext,
} from './ExtensionSidebarProvider';
export function ExtensionToolbarItem() {
const styles = getStyles(useTheme2());
const { availableComponents, dockedComponentId, setDockedComponentId, isOpen, isEnabled } =
useExtensionSidebarContext();
let dockedComponentTitle = '';
if (dockedComponentId) {
const dockedComponent = getComponentMetaFromComponentId(dockedComponentId);
if (dockedComponent) {
dockedComponentTitle = dockedComponent.componentTitle;
}
}
if (!isEnabled || availableComponents.size === 0) {
return null;
}
@@ -26,22 +39,52 @@ export function ExtensionToolbarItem() {
return null;
}
// conditionally renders a button to open or close the sidebar
// not using a component to avoid passing refs with the `Dropdown` component
const renderButton = useCallback(
(isOpen: boolean, title?: string, onClick?: () => void) => {
if (isOpen) {
// render button to close the sidebar
return (
<ToolbarButton
className={cx(styles.button, styles.buttonActive)}
icon="ai-sparkle"
data-testid="extension-toolbar-button-close"
variant="default"
onClick={() => setDockedComponentId(undefined)}
tooltip={t('navigation.extension-sidebar.button-tooltip.close', 'Close {{title}}', { title })}
/>
);
}
// if a title is provided, use it in the tooltip
let tooltip = t('navigation.extension-sidebar.button-tooltip.open-all', 'Open AI assistants and sidebar apps');
if (title) {
tooltip = t('navigation.extension-sidebar.button-tooltip.open', 'Open {{title}}', { title });
}
return (
<ToolbarButton
className={cx(styles.button)}
icon="ai-sparkle"
data-testid="extension-toolbar-button-open"
variant="default"
onClick={onClick}
tooltip={tooltip}
/>
);
},
[setDockedComponentId, styles.button, styles.buttonActive]
);
if (components.length === 1) {
return (
<>
<ToolbarButton
icon="ai-sparkle"
data-testid="extension-toolbar-button"
className={cx(styles.button, isOpen && styles.buttonActive)}
tooltip={components[0].description}
onClick={() => {
if (isOpen) {
setDockedComponentId(undefined);
} else {
setDockedComponentId(getComponentIdFromComponentMeta(components[0].pluginId, components[0]));
}
}}
/>
{renderButton(isOpen, components[0].title, () => {
if (isOpen) {
setDockedComponentId(undefined);
} else {
setDockedComponentId(getComponentIdFromComponentMeta(components[0].pluginId, components[0]));
}
})}
<NavToolbarSeparator />
</>
);
@@ -70,15 +113,17 @@ export function ExtensionToolbarItem() {
);
return (
<>
<Dropdown overlay={MenuItems} placement="bottom-end">
<ToolbarButton
className={cx(styles.button, isOpen && styles.buttonActive)}
icon="ai-sparkle"
data-testid="extension-toolbar-button"
variant="default"
tooltip={t('navigation.extension-sidebar.button-tooltip', 'Open AI assistants and sidebar apps')}
/>
</Dropdown>
{isOpen &&
renderButton(isOpen, dockedComponentTitle, () => {
if (isOpen) {
setDockedComponentId(undefined);
}
})}
{!isOpen && (
<Dropdown overlay={MenuItems} placement="bottom-end">
{renderButton(isOpen)}
</Dropdown>
)}
<NavToolbarSeparator />
</>
);
+5 -1
View File
@@ -6489,7 +6489,11 @@
"aria-label": "Breadcrumbs"
},
"extension-sidebar": {
"button-tooltip": "Open AI assistants and sidebar apps"
"button-tooltip": {
"close": "Close {{title}}",
"open": "Open {{title}}",
"open-all": "Open AI assistants and sidebar apps"
}
},
"help": {
"aria-label": "Help"