Skip to content
7 changes: 6 additions & 1 deletion static/app/components/core/drawer/components.tsx

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More of a general design question - Any reason why we chose to hide the close button text instead of keeping it consistent everywhere?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was matching the figma - personally I think it looks cleaner, but don't have a strong preference

Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,10 @@ interface DrawerHeaderProps {
* If true, hides the close button
*/
hideCloseButton?: boolean;
/**
* If true, hides the label of the close button
*/
hideCloseButtonText?: boolean;
ref?: React.Ref<HTMLHeadingElement>;
}

Expand All @@ -123,6 +127,7 @@ export function DrawerHeader({
children = null,
hideBar = false,
hideCloseButton = false,
hideCloseButtonText = false,
}: DrawerHeaderProps) {
const {onClose} = useDrawerContentContext();
const hasPageFrameFeature = useHasPageFrameFeature();
Expand All @@ -144,7 +149,7 @@ export function DrawerHeader({
icon={<IconClose />}
onClick={onClose}
>
{t('Close')}
{!hideCloseButtonText && t('Close')}
</Button>
{!hideBar && <HeaderBar />}
</Fragment>
Expand Down
23 changes: 19 additions & 4 deletions static/app/components/dropdownMenu/item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ export interface MenuItemProps extends MenuListItemProps {
* Pass a class name to the menu item.
*/
className?: string;
/**
* Whether to close the menu when this item is clicked. Overrides the list-level
* `closeOnSelect` prop when set.
*/
closeOnSelect?: boolean;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this override necessary when we can just set the closeOnSelect on the item?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the item-level closeOnSelect wasn't available before, only an option for closeOnSelect on all items. For all items except history, we want to close on select

/**
* Destination if this menu item is an external link.
*/
Expand Down Expand Up @@ -111,17 +116,27 @@ export function DropdownMenuItem({
const innerWrapRef = useRef<HTMLDivElement | null>(null);
const isDisabled = state.disabledKeys.has(node.key);
const isFocused = state.selectionManager.focusedKey === node.key;
const {key, onAction, to, label, isSubmenu, trailingItems, externalHref, ...itemProps} =
node.value ?? {};
const {
key,
onAction,
to,
label,
isSubmenu,
trailingItems,
externalHref,
closeOnSelect: itemCloseOnSelect,
...itemProps
} = node.value ?? {};
const {size} = node.props;
const {rootOverlayState} = useContext(DropdownMenuContext);
const isLink = to || externalHref;
const resolvedCloseOnSelect = itemCloseOnSelect ?? closeOnSelect;

const actionHandler = () => {
if (isLink) {
// Close the menu after the click event has bubbled to the link
// Only needed on links that do not unmount the menu
if (closeOnSelect) {
if (resolvedCloseOnSelect) {
requestAnimationFrame(() => rootOverlayState?.close());
}
return;
Expand Down Expand Up @@ -180,7 +195,7 @@ export function DropdownMenuItem({
onClose?.();
rootOverlayState?.close();
},
closeOnSelect: isLink ? false : closeOnSelect,
closeOnSelect: isLink ? false : resolvedCloseOnSelect,
isDisabled,
},
state,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,9 @@ import {UserFixture} from 'sentry-fixture/user';
import {render, screen, userEvent, waitFor} from 'sentry-test/reactTestingLibrary';

import {ConfigStore} from 'sentry/stores/configStore';
import {ExplorerDrawerContent} from 'sentry/views/seerExplorer/components/drawer/explorerDrawerContent';
import * as useSeerExplorerModule from 'sentry/views/seerExplorer/hooks/useSeerExplorer';

import {ExplorerDrawerContent} from './explorerDrawerContent';

const mockOnClose = jest.fn();
const mockGetPageReferrer = jest.fn().mockReturnValue('/issues/');

const defaultHookReturn: ReturnType<typeof useSeerExplorerModule.useSeerExplorer> = {
Expand Down Expand Up @@ -59,37 +57,25 @@ describe('ExplorerDrawerContent', () => {

describe('Empty State', () => {
it('renders the drawer root element', () => {
render(
<ExplorerDrawerContent
onClose={mockOnClose}
getPageReferrer={mockGetPageReferrer}
/>,
{organization}
);
render(<ExplorerDrawerContent getPageReferrer={mockGetPageReferrer} />, {
organization,
});
expect(document.querySelector('[data-seer-explorer-root]')).toBeInTheDocument();
});

it('shows empty state when no messages exist', async () => {
render(
<ExplorerDrawerContent
onClose={mockOnClose}
getPageReferrer={mockGetPageReferrer}
/>,
{organization}
);
render(<ExplorerDrawerContent getPageReferrer={mockGetPageReferrer} />, {
organization,
});
expect(
await screen.findByText('Ask Seer anything about your application.')
).toBeInTheDocument();
});

it('shows input', async () => {
render(
<ExplorerDrawerContent
onClose={mockOnClose}
getPageReferrer={mockGetPageReferrer}
/>,
{organization}
);
render(<ExplorerDrawerContent getPageReferrer={mockGetPageReferrer} />, {
organization,
});
expect(
await screen.findByPlaceholderText(
'Ask seer a question, or press / for commands.'
Expand All @@ -104,13 +90,9 @@ describe('ExplorerDrawerContent', () => {
sendMessage,
});

render(
<ExplorerDrawerContent
onClose={mockOnClose}
getPageReferrer={mockGetPageReferrer}
/>,
{organization}
);
render(<ExplorerDrawerContent getPageReferrer={mockGetPageReferrer} />, {
organization,
});

const suggestion = await screen.findByRole('button', {
name: 'Which of my open issues are getting worse, not better?',
Expand All @@ -129,13 +111,9 @@ describe('ExplorerDrawerContent', () => {
isError: true,
});

render(
<ExplorerDrawerContent
onClose={mockOnClose}
getPageReferrer={mockGetPageReferrer}
/>,
{organization}
);
render(<ExplorerDrawerContent getPageReferrer={mockGetPageReferrer} />, {
organization,
});

expect(
await screen.findByText('Error loading this session (ID=123).')
Expand Down Expand Up @@ -171,13 +149,9 @@ describe('ExplorerDrawerContent', () => {
} as useSeerExplorerModule.SeerExplorerResponse['session'],
});

render(
<ExplorerDrawerContent
onClose={mockOnClose}
getPageReferrer={mockGetPageReferrer}
/>,
{organization}
);
render(<ExplorerDrawerContent getPageReferrer={mockGetPageReferrer} />, {
organization,
});

expect(await screen.findByText('What is this error?')).toBeInTheDocument();
expect(screen.getByText('This is a null pointer exception.')).toBeInTheDocument();
Expand All @@ -189,13 +163,9 @@ describe('ExplorerDrawerContent', () => {

describe('Input Handling', () => {
it('can type in the textarea', async () => {
render(
<ExplorerDrawerContent
onClose={mockOnClose}
getPageReferrer={mockGetPageReferrer}
/>,
{organization}
);
render(<ExplorerDrawerContent getPageReferrer={mockGetPageReferrer} />, {
organization,
});
const textarea = await screen.findByTestId('seer-explorer-input');
await userEvent.type(textarea, 'Test message');
expect(textarea).toHaveValue('Test message');
Expand All @@ -208,13 +178,9 @@ describe('ExplorerDrawerContent', () => {
sendMessage,
});

render(
<ExplorerDrawerContent
onClose={mockOnClose}
getPageReferrer={mockGetPageReferrer}
/>,
{organization}
);
render(<ExplorerDrawerContent getPageReferrer={mockGetPageReferrer} />, {
organization,
});

const textarea = await screen.findByTestId('seer-explorer-input');
await userEvent.type(textarea, 'Test message');
Expand Down Expand Up @@ -278,13 +244,9 @@ describe('ExplorerDrawerContent', () => {
},
});

render(
<ExplorerDrawerContent
onClose={mockOnClose}
getPageReferrer={mockGetPageReferrer}
/>,
{organization}
);
render(<ExplorerDrawerContent getPageReferrer={mockGetPageReferrer} />, {
organization,
});

const textarea = await screen.findByTestId('seer-explorer-input');
await userEvent.type(textarea, 'What is this error?');
Expand All @@ -306,13 +268,9 @@ describe('ExplorerDrawerContent', () => {
sendMessage,
});

render(
<ExplorerDrawerContent
onClose={mockOnClose}
getPageReferrer={mockGetPageReferrer}
/>,
{organization}
);
render(<ExplorerDrawerContent getPageReferrer={mockGetPageReferrer} />, {
organization,
});

await screen.findByTestId('seer-explorer-input');
await userEvent.keyboard('{Enter}');
Expand All @@ -328,13 +286,9 @@ describe('ExplorerDrawerContent', () => {
isPolling: true,
});

render(
<ExplorerDrawerContent
onClose={mockOnClose}
getPageReferrer={mockGetPageReferrer}
/>,
{organization}
);
render(<ExplorerDrawerContent getPageReferrer={mockGetPageReferrer} />, {
organization,
});

const textarea = await screen.findByTestId('seer-explorer-input');
await userEvent.type(textarea, 'Test message');
Expand All @@ -358,13 +312,9 @@ describe('ExplorerDrawerContent', () => {
} as useSeerExplorerModule.SeerExplorerResponse['session'],
});

render(
<ExplorerDrawerContent
onClose={mockOnClose}
getPageReferrer={mockGetPageReferrer}
/>,
{organization}
);
render(<ExplorerDrawerContent getPageReferrer={mockGetPageReferrer} />, {
organization,
});

const textarea = await screen.findByTestId('seer-explorer-input');
await waitFor(() => expect(textarea).toBeDisabled());
Expand All @@ -387,13 +337,9 @@ describe('ExplorerDrawerContent', () => {
} as useSeerExplorerModule.SeerExplorerResponse['session'],
});

render(
<ExplorerDrawerContent
onClose={mockOnClose}
getPageReferrer={mockGetPageReferrer}
/>,
{organization}
);
render(<ExplorerDrawerContent getPageReferrer={mockGetPageReferrer} />, {
organization,
});

const textarea = await screen.findByTestId('seer-explorer-input');
await waitFor(() => expect(textarea).toBeEnabled());
Expand All @@ -416,13 +362,9 @@ describe('ExplorerDrawerContent', () => {
} as useSeerExplorerModule.SeerExplorerResponse['session'],
});

render(
<ExplorerDrawerContent
onClose={mockOnClose}
getPageReferrer={mockGetPageReferrer}
/>,
{organization}
);
render(<ExplorerDrawerContent getPageReferrer={mockGetPageReferrer} />, {
organization,
});

const textarea = await screen.findByTestId('seer-explorer-input');
await waitFor(() => expect(textarea).toBeEnabled());
Expand All @@ -436,27 +378,19 @@ describe('ExplorerDrawerContent', () => {
});

it('does not show toggle without the feature flag', async () => {
render(
<ExplorerDrawerContent
onClose={mockOnClose}
getPageReferrer={mockGetPageReferrer}
/>,
{organization}
);
render(<ExplorerDrawerContent getPageReferrer={mockGetPageReferrer} />, {
organization,
});
await screen.findByTestId('seer-explorer-input');
expect(
screen.queryByRole('checkbox', {name: 'Toggle context engine'})
).not.toBeInTheDocument();
});

it('shows toggle when feature flag is enabled', async () => {
render(
<ExplorerDrawerContent
onClose={mockOnClose}
getPageReferrer={mockGetPageReferrer}
/>,
{organization: orgWithFlag}
);
render(<ExplorerDrawerContent getPageReferrer={mockGetPageReferrer} />, {
organization: orgWithFlag,
});
expect(
await screen.findByRole('checkbox', {name: 'Toggle context engine'})
).toBeInTheDocument();
Expand Down
Loading
Loading