Extension Sidebar: Close sidebar with toolbar button (#104208)
* Extension Sidebar: Improve closing behavior * Extension Sidebar: Fix title
This commit is contained in:
+12
-11
@@ -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 />
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user