Skip to content

Commit a203c74

Browse files
feat(connections-navigation): connect in new window via connect split-button COMPASS-8473 (#6577)
* Add dependency on split-button * Make ItemActionControls hidable and use a SplitButton * Fix menu item width * Fix event emitter types on CompassApplication * Propagate from connections-navigation action through main process IPC * Add connection-id support to the auto-connect feature * Wire up the ConnectButton * Propagate from main IPC into window manager * Add a test * Remove an outdated comment * Apply suggestions from code review Co-authored-by: Sergey Petushkov <[email protected]> * Add enableConnectInNewWindow preference controlling connect button * Add test ensuring the right connect button is displayed * Pin [email protected] * Revert use of PolymorphicProps * Fix ConnectMenuItem * Simplify text in the connect menu * Reset windows after each test * Update packages/compass/src/main/window-manager.ts Co-authored-by: Sergey Petushkov <[email protected]> * Add comments mentioning COMPASS-8970 --------- Co-authored-by: Sergey Petushkov <[email protected]>
1 parent e4e64f7 commit a203c74

File tree

28 files changed

+584
-112
lines changed

28 files changed

+584
-112
lines changed

package-lock.json

Lines changed: 164 additions & 52 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/compass-components/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@
4646
"@leafygreen-ui/guide-cue": "^7.0.2",
4747
"@leafygreen-ui/hooks": "^8.3.4",
4848
"@leafygreen-ui/icon": "^13.1.2",
49-
"@leafygreen-ui/icon-button": "^16.0.2",
49+
"@leafygreen-ui/icon-button": "16.0.2",
5050
"@leafygreen-ui/info-sprinkle": "^4.0.2",
5151
"@leafygreen-ui/leafygreen-provider": "^4.0.2",
5252
"@leafygreen-ui/logo": "^10.0.2",
@@ -63,6 +63,7 @@
6363
"@leafygreen-ui/search-input": "^5.0.2",
6464
"@leafygreen-ui/segmented-control": "^10.0.2",
6565
"@leafygreen-ui/select": "^14.0.2",
66+
"@leafygreen-ui/split-button": "^4.1.5",
6667
"@leafygreen-ui/table": "^13.0.1",
6768
"@leafygreen-ui/tabs": "^14.0.2",
6869
"@leafygreen-ui/text-area": "^10.0.2",

packages/compass-components/src/components/actions/item-action-controls.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React from 'react';
1+
import React, { useState } from 'react';
22
import { spacing } from '@leafygreen-ui/tokens';
33
import { css, cx } from '@leafygreen-ui/emotion';
44
import type { RenderMode } from '@leafygreen-ui/popover';
@@ -47,8 +47,10 @@ export function ItemActionControls<Action extends string>({
4747
collapseToMenuThreshold = 2,
4848
'data-testid': dataTestId,
4949
}: ItemActionControlsProps<Action>) {
50+
const [isHidable, setHidable] = useState(true);
51+
5052
const sharedProps = {
51-
isVisible,
53+
setHidable,
5254
onAction,
5355
className: cx('item-action-controls', className),
5456
iconClassName,
@@ -61,7 +63,7 @@ export function ItemActionControls<Action extends string>({
6163
renderMode,
6264
};
6365

64-
if (actions.length === 0) {
66+
if (actions.length === 0 || (isHidable && !isVisible)) {
6567
return null;
6668
}
6769

packages/compass-components/src/components/actions/item-action-group.tsx

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,22 +25,28 @@ const containerStyle = css({
2525
export type ItemActionGroupProps<Action extends string> = {
2626
actions: (GroupedItemAction<Action> | ItemSeparator)[];
2727
onAction(actionName: Action): void;
28+
isVisible?: boolean;
29+
/**
30+
* Called to signal to the parent if the component wants to prevent becoming hidden.
31+
* Note: In the current implementation, this is called when a menu is opened.
32+
*/
33+
setHidable?(hidable: boolean): void;
2834
className?: string;
2935
iconClassName?: string;
3036
iconStyle?: React.CSSProperties;
3137
iconSize?: ItemActionButtonSize;
32-
isVisible?: boolean;
3338
'data-testid'?: string;
3439
};
3540

3641
export function ItemActionGroup<Action extends string>({
3742
actions,
3843
onAction,
44+
isVisible = true,
45+
setHidable,
3946
className,
4047
iconClassName,
4148
iconStyle,
4249
iconSize = ItemActionButtonSize.Default,
43-
isVisible = true,
4450
'data-testid': dataTestId,
4551
}: ItemActionGroupProps<Action>) {
4652
const onClick: React.MouseEventHandler<HTMLElement> = useCallback(
@@ -55,9 +61,7 @@ export function ItemActionGroup<Action extends string>({
5561
[onAction]
5662
);
5763

58-
const shouldRender = isVisible && actions.length > 0;
59-
60-
if (!shouldRender) {
64+
if (!isVisible || actions.length === 0) {
6165
return null;
6266
}
6367

@@ -84,6 +88,7 @@ export function ItemActionGroup<Action extends string>({
8488
iconClassName={iconClassName}
8589
onClick={onClick}
8690
data-testid={actionTestId(dataTestId, itemProps.action)}
91+
setHidable={setHidable}
8792
/>
8893
);
8994

packages/compass-components/src/components/actions/item-action-menu.tsx

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useCallback, useRef, useState } from 'react';
1+
import React, { useCallback, useEffect, useRef, useState } from 'react';
22
import { css, cx } from '@leafygreen-ui/emotion';
33
import type { RenderMode } from '@leafygreen-ui/popover';
44

@@ -32,6 +32,12 @@ const containerStyle = css({
3232

3333
export type ItemActionMenuProps<Action extends string> = {
3434
actions: MenuAction<Action>[];
35+
isVisible?: boolean;
36+
/**
37+
* Called to signal to the parent if the component wants to prevent becoming hidden.
38+
* Note: In the current implementation, this is called when a menu is opened.
39+
*/
40+
setHidable?(hideable: boolean): void;
3541
onAction(actionName: Action): void;
3642
// TODO: Merge className and menuClassName
3743
className?: string;
@@ -40,13 +46,13 @@ export type ItemActionMenuProps<Action extends string> = {
4046
iconClassName?: string;
4147
iconStyle?: React.CSSProperties;
4248
iconSize?: ItemActionButtonSize;
43-
isVisible?: boolean;
4449
'data-testid'?: string;
4550
};
4651

4752
export function ItemActionMenu<Action extends string>({
48-
isVisible = true,
4953
actions,
54+
isVisible = true,
55+
setHidable,
5056
onAction,
5157
className,
5258
menuClassName,
@@ -79,6 +85,13 @@ export function ItemActionMenu<Action extends string>({
7985
[onAction]
8086
);
8187

88+
// Opening the menu should keep it visible
89+
useEffect(() => {
90+
if (setHidable) {
91+
setHidable(!isMenuOpen);
92+
}
93+
}, [setHidable, isMenuOpen]);
94+
8295
const shouldRender = isMenuOpen || (isVisible && actions.length > 0);
8396

8497
if (!shouldRender) {

packages/compass-components/src/components/actions/types.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ export type ItemComponentProps<Action extends string> = Omit<
2727
iconClassName?: string;
2828
iconStyle?: React.CSSProperties;
2929
'data-testid'?: string;
30-
onClick(evt: React.MouseEvent<unknown>): void;
30+
onClick: (evt: React.MouseEvent<HTMLElement>) => void;
31+
setHidable?(hidable: boolean): void;
3132
};
3233

3334
export type ItemAction<Action extends string> = {

packages/compass-components/src/components/leafygreen.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,14 @@ import Code, { Language } from '@leafygreen-ui/code';
1111
import ConfirmationModal from '@leafygreen-ui/confirmation-modal';
1212
import { default as LeafyGreenIcon } from '@leafygreen-ui/icon';
1313
import type { Size as LeafyGreenIconSize } from '@leafygreen-ui/icon';
14+
export type { GlyphName } from '@leafygreen-ui/icon';
1415
import {
1516
AtlasNavGraphic,
1617
MongoDBLogoMark,
1718
MongoDBLogo,
1819
} from '@leafygreen-ui/logo';
1920
import { Menu, MenuSeparator, MenuItem } from '@leafygreen-ui/menu';
21+
export type { MenuItemProps } from '@leafygreen-ui/menu';
2022
import { InfoSprinkle } from '@leafygreen-ui/info-sprinkle';
2123

2224
// If a leafygreen Menu (and therefore MenuItems) makes its way into a <form>,

packages/compass-components/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ export {
6868
Size as ButtonSize,
6969
Variant as ButtonVariant,
7070
} from '@leafygreen-ui/button';
71+
export { SplitButton } from '@leafygreen-ui/split-button';
7172

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

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
import React, {
2+
useState,
3+
useEffect,
4+
forwardRef,
5+
type ButtonHTMLAttributes,
6+
} from 'react';
7+
import {
8+
css,
9+
Icon,
10+
MenuItem,
11+
SplitButton,
12+
type GlyphName,
13+
type ItemComponentProps,
14+
type MenuItemProps,
15+
} from '@mongodb-js/compass-components';
16+
import type { Actions } from './constants';
17+
18+
const menuItemStyles = css({
19+
minWidth: 'max-content',
20+
});
21+
22+
type ConnectMenuItemProps = {
23+
action: Actions;
24+
glyph: GlyphName;
25+
} & Omit<
26+
MenuItemProps & ButtonHTMLAttributes<HTMLButtonElement>,
27+
'glyph' | 'as'
28+
>;
29+
30+
export const ConnectMenuItem = forwardRef<
31+
HTMLButtonElement,
32+
ConnectMenuItemProps
33+
>(function ConnectMenuItem({ action, glyph, ...rest }, ref) {
34+
return (
35+
<MenuItem
36+
as="button"
37+
data-action={action}
38+
className={menuItemStyles}
39+
glyph={<Icon glyph={glyph} />}
40+
{...rest}
41+
ref={ref}
42+
/>
43+
);
44+
});
45+
46+
// Hack to make SplitButton consider this as a MenuItem
47+
ConnectMenuItem.displayName = 'MenuItem';
48+
49+
export function ConnectButtonWithMenu({
50+
setHidable,
51+
action,
52+
tooltip,
53+
label,
54+
iconSize,
55+
iconStyle,
56+
isDisabled,
57+
onClick,
58+
className,
59+
'data-testid': testId,
60+
}: ItemComponentProps<Actions>) {
61+
const [isOpen, setOpen] = useState(false);
62+
63+
// Opening the menu should keep it visible
64+
useEffect(() => {
65+
if (setHidable) {
66+
setHidable(!isOpen);
67+
}
68+
}, [setHidable, isOpen]);
69+
70+
return (
71+
<SplitButton
72+
key={action}
73+
title={!tooltip ? label : undefined}
74+
label={label}
75+
size={iconSize}
76+
data-action={action}
77+
data-testid={testId}
78+
onClick={onClick}
79+
className={className}
80+
style={iconStyle}
81+
disabled={isDisabled}
82+
renderDarkMenu={false}
83+
darkMode={false}
84+
open={isOpen}
85+
setOpen={setOpen}
86+
triggerAriaLabel="see more connection options"
87+
menuItems={[
88+
<ConnectMenuItem
89+
key="connection-connect"
90+
action="connection-connect"
91+
glyph="Connect"
92+
onClick={onClick}
93+
>
94+
Here
95+
</ConnectMenuItem>,
96+
<ConnectMenuItem
97+
key="connection-connect-in-new-window"
98+
action="connection-connect-in-new-window"
99+
glyph="OpenNewTab"
100+
onClick={onClick}
101+
>
102+
In New Window
103+
</ConnectMenuItem>,
104+
]}
105+
>
106+
{label}
107+
</SplitButton>
108+
);
109+
}

packages/compass-connections-navigation/src/connect-button.tsx

Lines changed: 34 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -4,33 +4,39 @@ import {
44
type ItemComponentProps,
55
} from '@mongodb-js/compass-components';
66
import type { Actions } from './constants';
7+
import { usePreference } from 'compass-preferences-model/provider';
8+
import { ConnectButtonWithMenu } from './connect-button-with-menu';
79

8-
type ConnectButtonProps = ItemComponentProps<Actions>;
9-
10-
export function ConnectButton({
11-
action,
12-
tooltip,
13-
label,
14-
iconSize,
15-
iconStyle,
16-
isDisabled,
17-
onClick,
18-
className,
19-
'data-testid': testId,
20-
}: ConnectButtonProps) {
21-
return (
22-
<Button
23-
key={action}
24-
title={!tooltip ? label : undefined}
25-
size={iconSize}
26-
data-action={action}
27-
data-testid={testId}
28-
onClick={onClick}
29-
className={className}
30-
style={iconStyle}
31-
disabled={isDisabled}
32-
>
33-
{label}
34-
</Button>
35-
);
10+
export function ConnectButton(props: ItemComponentProps<Actions>) {
11+
const connectInNewWindowEnabled = usePreference('enableConnectInNewWindow');
12+
if (connectInNewWindowEnabled) {
13+
return <ConnectButtonWithMenu {...props} />;
14+
} else {
15+
const {
16+
action,
17+
tooltip,
18+
label,
19+
iconSize,
20+
iconStyle,
21+
isDisabled,
22+
onClick,
23+
className,
24+
'data-testid': testId,
25+
} = props;
26+
return (
27+
<Button
28+
key={action}
29+
title={!tooltip ? label : undefined}
30+
size={iconSize}
31+
data-action={action}
32+
data-testid={testId}
33+
onClick={onClick}
34+
className={className}
35+
style={iconStyle}
36+
disabled={isDisabled}
37+
>
38+
{label}
39+
</Button>
40+
);
41+
}
3642
}

0 commit comments

Comments
 (0)