Skip to content

feat(connections-navigation): connect in new window via connect split-button COMPASS-8473 #6577

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 20 commits into from
Feb 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
216 changes: 164 additions & 52 deletions package-lock.json

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion packages/compass-components/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
"@leafygreen-ui/guide-cue": "^7.0.2",
"@leafygreen-ui/hooks": "^8.3.4",
"@leafygreen-ui/icon": "^13.1.2",
"@leafygreen-ui/icon-button": "^16.0.2",
"@leafygreen-ui/icon-button": "16.0.2",
Copy link
Collaborator

Choose a reason for hiding this comment

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

Any reason this is pinned now?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yep. I meant to capture an issue on upgrading this as well, the v16.0.3 release is updating the types of the component, heres a comment on the original LG PR introducing the breaking change: mongodb/leafygreen-ui#2703 (comment)

Copy link
Collaborator

Choose a reason for hiding this comment

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

Do we have a ticket to follow up with a fix for that?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Do we have a ticket to follow up with a fix for that?

Not yet. I'll create that as part of merging this PR - this is what I meant by:

I meant to capture an issue on upgrading this as well

Copy link
Contributor Author

@kraenhansen kraenhansen Feb 14, 2025

Choose a reason for hiding this comment

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

I've created COMPASS-8973 now.

"@leafygreen-ui/info-sprinkle": "^4.0.2",
"@leafygreen-ui/leafygreen-provider": "^4.0.2",
"@leafygreen-ui/logo": "^10.0.2",
Expand All @@ -63,6 +63,7 @@
"@leafygreen-ui/search-input": "^5.0.2",
"@leafygreen-ui/segmented-control": "^10.0.2",
"@leafygreen-ui/select": "^14.0.2",
"@leafygreen-ui/split-button": "^4.1.5",
"@leafygreen-ui/table": "^13.0.1",
"@leafygreen-ui/tabs": "^14.0.2",
"@leafygreen-ui/text-area": "^10.0.2",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from 'react';
import React, { useState } from 'react';
import { spacing } from '@leafygreen-ui/tokens';
import { css, cx } from '@leafygreen-ui/emotion';
import type { RenderMode } from '@leafygreen-ui/popover';
Expand Down Expand Up @@ -47,8 +47,10 @@ export function ItemActionControls<Action extends string>({
collapseToMenuThreshold = 2,
'data-testid': dataTestId,
}: ItemActionControlsProps<Action>) {
const [isHidable, setHidable] = useState(true);

const sharedProps = {
isVisible,
setHidable,
onAction,
className: cx('item-action-controls', className),
iconClassName,
Expand All @@ -61,7 +63,7 @@ export function ItemActionControls<Action extends string>({
renderMode,
};

if (actions.length === 0) {
if (actions.length === 0 || (isHidable && !isVisible)) {
return null;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,22 +25,28 @@ const containerStyle = css({
export type ItemActionGroupProps<Action extends string> = {
actions: (GroupedItemAction<Action> | ItemSeparator)[];
onAction(actionName: Action): void;
isVisible?: boolean;
/**
* Called to signal to the parent if the component wants to prevent becoming hidden.
* Note: In the current implementation, this is called when a menu is opened.
*/
setHidable?(hidable: boolean): void;
className?: string;
iconClassName?: string;
iconStyle?: React.CSSProperties;
iconSize?: ItemActionButtonSize;
isVisible?: boolean;
'data-testid'?: string;
};

export function ItemActionGroup<Action extends string>({
actions,
onAction,
isVisible = true,
setHidable,
className,
iconClassName,
iconStyle,
iconSize = ItemActionButtonSize.Default,
isVisible = true,
'data-testid': dataTestId,
}: ItemActionGroupProps<Action>) {
const onClick: React.MouseEventHandler<HTMLElement> = useCallback(
Expand All @@ -55,9 +61,7 @@ export function ItemActionGroup<Action extends string>({
[onAction]
);

const shouldRender = isVisible && actions.length > 0;

if (!shouldRender) {
if (!isVisible || actions.length === 0) {
return null;
}

Expand All @@ -84,6 +88,7 @@ export function ItemActionGroup<Action extends string>({
iconClassName={iconClassName}
onClick={onClick}
data-testid={actionTestId(dataTestId, itemProps.action)}
setHidable={setHidable}
/>
);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useCallback, useRef, useState } from 'react';
import React, { useCallback, useEffect, useRef, useState } from 'react';
import { css, cx } from '@leafygreen-ui/emotion';
import type { RenderMode } from '@leafygreen-ui/popover';

Expand Down Expand Up @@ -32,6 +32,12 @@ const containerStyle = css({

export type ItemActionMenuProps<Action extends string> = {
actions: MenuAction<Action>[];
isVisible?: boolean;
/**
* Called to signal to the parent if the component wants to prevent becoming hidden.
* Note: In the current implementation, this is called when a menu is opened.
*/
setHidable?(hideable: boolean): void;
onAction(actionName: Action): void;
// TODO: Merge className and menuClassName
className?: string;
Expand All @@ -40,13 +46,13 @@ export type ItemActionMenuProps<Action extends string> = {
iconClassName?: string;
iconStyle?: React.CSSProperties;
iconSize?: ItemActionButtonSize;
isVisible?: boolean;
'data-testid'?: string;
};

export function ItemActionMenu<Action extends string>({
isVisible = true,
actions,
isVisible = true,
setHidable,
onAction,
className,
menuClassName,
Expand Down Expand Up @@ -79,6 +85,13 @@ export function ItemActionMenu<Action extends string>({
[onAction]
);

// Opening the menu should keep it visible
useEffect(() => {
if (setHidable) {
setHidable(!isMenuOpen);
}
}, [setHidable, isMenuOpen]);

const shouldRender = isMenuOpen || (isVisible && actions.length > 0);

if (!shouldRender) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ export type ItemComponentProps<Action extends string> = Omit<
iconClassName?: string;
iconStyle?: React.CSSProperties;
'data-testid'?: string;
onClick(evt: React.MouseEvent<unknown>): void;
onClick: (evt: React.MouseEvent<HTMLElement>) => void;
setHidable?(hidable: boolean): void;
};

export type ItemAction<Action extends string> = {
Expand Down
2 changes: 2 additions & 0 deletions packages/compass-components/src/components/leafygreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@ import Code, { Language } from '@leafygreen-ui/code';
import ConfirmationModal from '@leafygreen-ui/confirmation-modal';
import { default as LeafyGreenIcon } from '@leafygreen-ui/icon';
import type { Size as LeafyGreenIconSize } from '@leafygreen-ui/icon';
export type { GlyphName } from '@leafygreen-ui/icon';
import {
AtlasNavGraphic,
MongoDBLogoMark,
MongoDBLogo,
} from '@leafygreen-ui/logo';
import { Menu, MenuSeparator, MenuItem } from '@leafygreen-ui/menu';
export type { MenuItemProps } from '@leafygreen-ui/menu';
import { InfoSprinkle } from '@leafygreen-ui/info-sprinkle';

// If a leafygreen Menu (and therefore MenuItems) makes its way into a <form>,
Expand Down
1 change: 1 addition & 0 deletions packages/compass-components/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ export {
Size as ButtonSize,
Variant as ButtonVariant,
} from '@leafygreen-ui/button';
export { SplitButton } from '@leafygreen-ui/split-button';

export { default as LeafyGreenProvider } from '@leafygreen-ui/leafygreen-provider';

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import React, {
useState,
useEffect,
forwardRef,
type ButtonHTMLAttributes,
} from 'react';
import {
css,
Icon,
MenuItem,
SplitButton,
type GlyphName,
type ItemComponentProps,
type MenuItemProps,
} from '@mongodb-js/compass-components';
import type { Actions } from './constants';

const menuItemStyles = css({
minWidth: 'max-content',
});

type ConnectMenuItemProps = {
action: Actions;
glyph: GlyphName;
} & Omit<
MenuItemProps & ButtonHTMLAttributes<HTMLButtonElement>,
'glyph' | 'as'
>;

export const ConnectMenuItem = forwardRef<
HTMLButtonElement,
ConnectMenuItemProps
>(function ConnectMenuItem({ action, glyph, ...rest }, ref) {
return (
<MenuItem
as="button"
data-action={action}
className={menuItemStyles}
glyph={<Icon glyph={glyph} />}
{...rest}
ref={ref}
/>
);
});

// Hack to make SplitButton consider this as a MenuItem
ConnectMenuItem.displayName = 'MenuItem';

export function ConnectButtonWithMenu({
setHidable,
action,
tooltip,
label,
iconSize,
iconStyle,
isDisabled,
onClick,
className,
'data-testid': testId,
}: ItemComponentProps<Actions>) {
const [isOpen, setOpen] = useState(false);

// Opening the menu should keep it visible
useEffect(() => {
if (setHidable) {
setHidable(!isOpen);
}
}, [setHidable, isOpen]);

return (
<SplitButton
key={action}
title={!tooltip ? label : undefined}
label={label}
size={iconSize}
data-action={action}
data-testid={testId}
onClick={onClick}
className={className}
style={iconStyle}
disabled={isDisabled}
renderDarkMenu={false}
darkMode={false}
open={isOpen}
setOpen={setOpen}
triggerAriaLabel="see more connection options"
menuItems={[
<ConnectMenuItem
key="connection-connect"
action="connection-connect"
glyph="Connect"
onClick={onClick}
>
Here
</ConnectMenuItem>,
<ConnectMenuItem
key="connection-connect-in-new-window"
action="connection-connect-in-new-window"
glyph="OpenNewTab"
onClick={onClick}
>
In New Window
</ConnectMenuItem>,
]}
>
{label}
</SplitButton>
);
}
62 changes: 34 additions & 28 deletions packages/compass-connections-navigation/src/connect-button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,33 +4,39 @@ import {
type ItemComponentProps,
} from '@mongodb-js/compass-components';
import type { Actions } from './constants';
import { usePreference } from 'compass-preferences-model/provider';
import { ConnectButtonWithMenu } from './connect-button-with-menu';

type ConnectButtonProps = ItemComponentProps<Actions>;

export function ConnectButton({
action,
tooltip,
label,
iconSize,
iconStyle,
isDisabled,
onClick,
className,
'data-testid': testId,
}: ConnectButtonProps) {
return (
<Button
key={action}
title={!tooltip ? label : undefined}
size={iconSize}
data-action={action}
data-testid={testId}
onClick={onClick}
className={className}
style={iconStyle}
disabled={isDisabled}
>
{label}
</Button>
);
export function ConnectButton(props: ItemComponentProps<Actions>) {
const connectInNewWindowEnabled = usePreference('enableConnectInNewWindow');
if (connectInNewWindowEnabled) {
return <ConnectButtonWithMenu {...props} />;
} else {
const {
action,
tooltip,
label,
iconSize,
iconStyle,
isDisabled,
onClick,
className,
'data-testid': testId,
} = props;
return (
<Button
key={action}
title={!tooltip ? label : undefined}
size={iconSize}
data-action={action}
data-testid={testId}
onClick={onClick}
className={className}
style={iconStyle}
disabled={isDisabled}
>
{label}
</Button>
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export type Actions =
| 'duplicate-connection'
| 'remove-connection'
| 'connection-connect'
| 'connection-connect-in-new-window'
| 'connection-disconnect'
| 'connection-performance-metrics'
| 'open-connection-info'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1420,6 +1420,12 @@ const openConnectionClosedWithNonRetryableErrorToast = (
});
};

export const connectInNewWindow =
(connectionInfo: ConnectionInfo): ConnectionsThunkAction<void> =>
(_dispatch, _getState, { globalAppRegistry }) => {
globalAppRegistry.emit('connect-in-new-window', connectionInfo.id);
};

export const connect = (
connectionInfo: ConnectionInfo
): ConnectionsThunkAction<
Expand Down
4 changes: 4 additions & 0 deletions packages/compass-connections/src/stores/store-context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import type {
import {
cancelEditConnection,
connect as connectionsConnect,
connectInNewWindow,
saveAndConnect,
connectionsEventEmitter,
createNewConnection,
Expand Down Expand Up @@ -88,6 +89,9 @@ function getConnectionsActions(dispatch: ConnectionsStore['dispatch']) {
connect: (connectionInfo: ConnectionInfo) => {
return dispatch(connectionsConnect(connectionInfo));
},
connectInNewWindow: (connectionInfo: ConnectionInfo) => {
return dispatch(connectInNewWindow(connectionInfo));
},
saveAndConnect: (connectionInfo: ConnectionInfo) => {
return dispatch(saveAndConnect(connectionInfo));
},
Expand Down
Loading
Loading