Skip to content
Merged
12 changes: 12 additions & 0 deletions configs/testing-library-compass/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -732,6 +732,17 @@ const testingLibrary = {
renderHook,
};

/**
* Expects an element (queried test id) to visible or missing from the screen
*/
function expectElementByTestId(testId: string, state: 'visible' | 'missing') {
if (state === 'visible') {
expect(screen.queryByTestId(testId)).to.be.visible;
} else if (state === 'missing') {
expect(screen.queryByTestId(testId)).to.be.null;
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

If we want a generalized helper I think it would make a bit more sense so add this as an extra chai assertion instead of a function here, it's more effort but way more consistent with how we assert, which is always done with chai library asserts

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I did experiment a bit with it. The question is what we'd want that expect to take as an argument.
If it takes the test-id string attribute, I don't know what to call that chai assert property 🙈 expect("some-test-id").to.be.testIdOfElementInDocument?

Thinking more about it, perhaps it's overkill with this helper 🤔 I mostly wanted to find a way to codify the pattern.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've reverted the helper and cleaned up the test instead: 4450dc7 - I think that's better 🤞

Copy link
Collaborator

Choose a reason for hiding this comment

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

I think it's a good idea, but I'd still have a strong preference to stick to the interface of chai asserts we are accustomed to using if we want to pursue this. And yeah, just a test-id is probably not a great argument for chai here. Maybe for case that you were running in something like expect(elementOrNull).to.be.in.dom that checks that the element is not null and window.document.contains is true can be a new interface for what you're trying to check here. Then you'd be able to combine then with visible check too


export {
// There is never a good reason not to have these wrapper providers when
// rendering something in compass for testing. Using these render methods
Expand All @@ -756,4 +767,5 @@ export {
within,
fireEvent,
testingLibrary,
expectElementByTestId,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { palette } from '@leafygreen-ui/palette';
import React, { useMemo } from 'react';

import { useDarkMode } from '../../hooks/use-theme';

const ConnectedPlugsIcon: React.FunctionComponent = () => {
const darkMode = useDarkMode();

const fillColor = useMemo(
() => (darkMode ? palette.white : palette.black),
[darkMode]
);

const sparkColor = palette.green.dark1;

return (
<svg
width="14"
height="14"
viewBox="0 0 14 14"
fill={fillColor}
xmlns="http://www.w3.org/2000/svg"
>
<path d="M13.8694 0.157499C13.8235 0.108563 13.7683 0.0693554 13.7069 0.0422086C13.6456 0.0150618 13.5794 0.000531054 13.5124 -0.000518593C13.4453 -0.00156824 13.3787 0.0108846 13.3166 0.0360987C13.2544 0.0613128 13.198 0.0987728 13.1506 0.146249L9.87498 3.42187L9.53873 3.08562C9.35298 2.89967 9.13241 2.75215 8.88961 2.6515C8.64682 2.55086 8.38656 2.49905 8.12373 2.49905C7.8609 2.49905 7.60065 2.55086 7.35785 2.6515C7.11505 2.75215 6.89448 2.89967 6.70873 3.08562L5.63373 4.15875C5.61049 4.18197 5.59205 4.20954 5.57947 4.23989C5.56688 4.27024 5.56041 4.30277 5.56041 4.33562C5.56041 4.36848 5.56688 4.40101 5.57947 4.43136C5.59205 4.46171 5.61049 4.48928 5.63373 4.5125L9.48998 8.36625C9.5132 8.38949 9.54077 8.40793 9.57112 8.42051C9.60147 8.43309 9.634 8.43957 9.66686 8.43957C9.69971 8.43957 9.73224 8.43309 9.76259 8.42051C9.79294 8.40793 9.82051 8.38949 9.84373 8.36625L10.89 7.32C11.2692 6.94811 11.489 6.44343 11.5031 5.9125C11.5083 5.64409 11.4591 5.37741 11.3584 5.12853C11.2578 4.87965 11.1078 4.65373 10.9175 4.46437L10.5812 4.12875L13.8437 0.868124C13.9388 0.77584 13.9946 0.650384 13.9994 0.517961C14.0041 0.385539 13.9576 0.256392 13.8694 0.157499ZM4.35248 4.64625C4.30512 4.59887 4.24872 4.56149 4.18663 4.53633C4.12453 4.51118 4.05802 4.49875 3.99104 4.4998C3.92405 4.50085 3.85796 4.51535 3.79669 4.54244C3.73542 4.56953 3.68021 4.60866 3.63436 4.6575C3.54616 4.75639 3.49958 4.88554 3.50436 5.01796C3.50913 5.15038 3.56489 5.27584 3.65998 5.36812L4.04186 5.75L3.08373 6.70687C2.89384 6.89631 2.74424 7.12221 2.64392 7.37097C2.5436 7.61972 2.49464 7.8862 2.49998 8.15437C2.51318 8.68518 2.73213 9.19009 3.11061 9.5625L3.42311 9.87125L0.155606 13.1331C0.0634122 13.2229 0.00832355 13.3441 0.00126193 13.4726C-0.00579969 13.6011 0.0356812 13.7275 0.117481 13.8269C0.162402 13.879 0.217595 13.9213 0.279606 13.9511C0.341618 13.9809 0.409113 13.9976 0.477869 14.0001C0.546624 14.0026 0.615161 13.9909 0.67919 13.9657C0.743219 13.9406 0.801362 13.9024 0.849981 13.8537L4.12498 10.5781L4.46061 10.9144C4.83658 11.2886 5.34545 11.4987 5.87592 11.4987C6.40638 11.4987 6.91526 11.2886 7.29123 10.9144L8.24998 9.95687L8.64623 10.3537C8.69269 10.4002 8.74784 10.4371 8.80853 10.4622C8.86923 10.4873 8.93428 10.5003 8.99998 10.5003C9.06568 10.5003 9.13073 10.4873 9.19143 10.4622C9.25213 10.4371 9.30728 10.4002 9.35373 10.3537C9.40019 10.3073 9.43704 10.2521 9.46218 10.1914C9.48732 10.1307 9.50026 10.0657 9.50026 10C9.50026 9.9343 9.48732 9.86925 9.46218 9.80855C9.43704 9.74785 9.40019 9.6927 9.35373 9.64625L4.35248 4.64625Z" />

<path
d="M4.539 0.804287C4.48708 0.926537 4.48584 1.06441 4.53557 1.18757L5.03557 2.43757C5.06019 2.49855 5.09659 2.55409 5.14267 2.601C5.18876 2.64792 5.24363 2.6853 5.30417 2.71101C5.3647 2.73672 5.4297 2.75026 5.49546 2.75085C5.56123 2.75144 5.62646 2.73907 5.68745 2.71444C5.74843 2.68982 5.80396 2.65343 5.85088 2.60734C5.8978 2.56125 5.93518 2.50638 5.96089 2.44585C5.9866 2.38532 6.00014 2.32031 6.00073 2.25455C6.00131 2.18879 5.98894 2.12355 5.96432 2.06257L5.46432 0.812567C5.41459 0.689407 5.31798 0.591046 5.19573 0.539122C5.07348 0.487198 4.93561 0.485964 4.81245 0.535692C4.68929 0.58542 4.59092 0.682037 4.539 0.804287Z"
fill={sparkColor}
/>
<path
d="M2.06245 5.96444L0.812445 5.46444C0.689285 5.41471 0.590924 5.3181 0.539 5.19585C0.487076 5.0736 0.485842 4.93573 0.53557 4.81257C0.585298 4.68941 0.681915 4.59105 0.804165 4.53912C0.926415 4.4872 1.06429 4.48596 1.18745 4.53569L2.43745 5.03569C2.49843 5.06032 2.55396 5.09671 2.60088 5.14279C2.6478 5.18888 2.68518 5.24375 2.71089 5.30429C2.7366 5.36482 2.75014 5.42982 2.75072 5.49559C2.75131 5.56135 2.73894 5.62658 2.71432 5.68757C2.6897 5.74855 2.6533 5.80409 2.60722 5.851C2.56113 5.89792 2.50626 5.9353 2.44573 5.96101C2.38519 5.98672 2.32019 6.00026 2.25443 6.00085C2.18866 6.00144 2.12343 5.98907 2.06245 5.96444Z"
fill={sparkColor}
/>
<path
d="M11.9374 8.03569L13.1874 8.53569C13.3106 8.58542 13.409 8.68204 13.4609 8.80429C13.5128 8.92654 13.514 9.06441 13.4643 9.18757C13.4146 9.31073 13.318 9.40909 13.1957 9.46101C13.0735 9.51294 12.9356 9.51417 12.8124 9.46444L11.5624 8.96444C11.4393 8.91471 11.3409 8.8181 11.289 8.69585C11.2371 8.5736 11.2358 8.43573 11.2856 8.31257C11.3353 8.18941 11.4319 8.09105 11.5542 8.03912C11.6764 7.9872 11.8143 7.98596 11.9374 8.03569Z"
fill={sparkColor}
/>
<path
d="M8.69573 11.2891C8.81798 11.341 8.91459 11.4394 8.96432 11.5626L9.46432 12.8126C9.51405 12.9357 9.51281 13.0736 9.46089 13.1958C9.40897 13.3181 9.31061 13.4147 9.18745 13.4644C9.06429 13.5142 8.92642 13.5129 8.80417 13.461C8.68192 13.4091 8.5853 13.3107 8.53557 13.1876L8.03557 11.9376C7.98584 11.8144 7.98708 11.6765 8.039 11.5543C8.09092 11.432 8.18929 11.3354 8.31245 11.2857C8.43561 11.236 8.57347 11.2372 8.69573 11.2891Z"
fill={sparkColor}
/>
</svg>
);
};

export { ConnectedPlugsIcon };
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { palette } from '@leafygreen-ui/palette';
import React, { useMemo } from 'react';

import { useDarkMode } from '../../hooks/use-theme';

const DisconnectedPlugIcon: React.FunctionComponent = () => {
const darkMode = useDarkMode();

const fillColor = useMemo(
() => (darkMode ? palette.white : palette.black),
[darkMode]
);

return (
<svg
width="15"
height="15"
viewBox="0 0 15 15"
fill={fillColor}
xmlns="http://www.w3.org/2000/svg"
>
<path d="M14.0306 3.96938C13.961 3.89946 13.8782 3.84398 13.787 3.80613C13.6958 3.76827 13.5981 3.74879 13.4994 3.74879C13.4007 3.74879 13.3029 3.76827 13.2118 3.80613C13.1206 3.84398 13.0378 3.89946 12.9681 3.96938L11 5.9375L9.0625 4L11.0325 2.03063C11.1734 1.88973 11.2526 1.69864 11.2526 1.49938C11.2526 1.30012 11.1734 1.10902 11.0325 0.968128C10.8916 0.827232 10.7005 0.748077 10.5013 0.748077C10.302 0.748077 10.1109 0.827232 9.97001 0.968128L8 2.9375L6.53063 1.46938C6.46086 1.39961 6.37804 1.34427 6.28689 1.30652C6.19574 1.26876 6.09804 1.24933 5.99938 1.24933C5.80012 1.24933 5.60903 1.32848 5.46813 1.46938C5.32723 1.61027 5.24808 1.80137 5.24808 2.00063C5.24808 2.19989 5.32723 2.39098 5.46813 2.53188L5.6875 2.75L2.55563 5.88375C2.30023 6.13912 2.09763 6.4423 1.9594 6.77597C1.82117 7.10964 1.75003 7.46727 1.75003 7.82844C1.75003 8.18961 1.82117 8.54724 1.9594 8.88091C2.09763 9.21459 2.30023 9.51776 2.55563 9.77313L3.36063 10.5781L0.46938 13.4694C0.399615 13.5391 0.344274 13.622 0.306518 13.7131C0.268762 13.8043 0.249329 13.902 0.249329 14.0006C0.249329 14.0993 0.268762 14.197 0.306518 14.2881C0.344274 14.3793 0.399615 14.4621 0.46938 14.5319C0.610276 14.6728 0.801372 14.7519 1.00063 14.7519C1.09929 14.7519 1.19699 14.7325 1.28814 14.6947C1.37929 14.657 1.46211 14.6016 1.53188 14.5319L4.42313 11.6406L5.22813 12.4456C5.4835 12.701 5.78667 12.9036 6.12034 13.0419C6.45402 13.1801 6.81165 13.2512 7.17282 13.2512C7.53399 13.2512 7.89162 13.1801 8.22529 13.0419C8.55896 12.9036 8.86214 12.701 9.11751 12.4456L12.25 9.3125L12.4694 9.5325C12.5391 9.60227 12.622 9.65761 12.7131 9.69536C12.8043 9.73312 12.902 9.75255 13.0006 9.75255C13.0993 9.75255 13.197 9.73312 13.2881 9.69536C13.3793 9.65761 13.4621 9.60227 13.5319 9.5325C13.6016 9.46274 13.657 9.37992 13.6947 9.28876C13.7325 9.19761 13.7519 9.09992 13.7519 9.00125C13.7519 8.90259 13.7325 8.80489 13.6947 8.71374C13.657 8.62259 13.6016 8.53977 13.5319 8.47L12.0625 7L14.0325 5.03063C14.1021 4.96085 14.1573 4.87804 14.1949 4.78692C14.2325 4.69581 14.2517 4.59818 14.2515 4.49961C14.2514 4.40105 14.2318 4.30349 14.1939 4.21251C14.156 4.12153 14.1005 4.03891 14.0306 3.96938ZM8.05563 11.3838C7.93955 11.4999 7.80173 11.592 7.65004 11.6549C7.49835 11.7177 7.33576 11.75 7.17157 11.75C7.00737 11.75 6.84479 11.7177 6.6931 11.6549C6.54141 11.592 6.40358 11.4999 6.2875 11.3838L3.61625 8.7125C3.50013 8.59642 3.40801 8.4586 3.34516 8.30691C3.28231 8.15522 3.24996 7.99264 3.24996 7.82844C3.24996 7.66425 3.28231 7.50166 3.34516 7.34997C3.40801 7.19828 3.50013 7.06046 3.61625 6.94438L6.75 3.8125L11.1875 8.25L8.05563 11.3838Z" />
</svg>
);
};

export { DisconnectedPlugIcon };
2 changes: 2 additions & 0 deletions packages/compass-components/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ export { DocumentIcon } from './components/icons/document-icon';
export { FavoriteIcon } from './components/icons/favorite-icon';
export { ServerIcon } from './components/icons/server-icon';
export { NoSavedItemsIcon } from './components/icons/no-saved-items-icon';
export { ConnectedPlugsIcon } from './components/icons/connected-plugs';
export { DisconnectedPlugIcon } from './components/icons/disconnected-plug';
export { GuideCue as LGGuideCue } from '@leafygreen-ui/guide-cue';
export { Variant as BadgeVariant } from '@leafygreen-ui/badge';
export { Variant as BannerVariant } from '@leafygreen-ui/banner';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ import {
Button,
Icon,
ButtonVariant,
IconButton,
ConnectedPlugsIcon,
DisconnectedPlugIcon,
Tooltip,
} from '@mongodb-js/compass-components';
import { ConnectionsNavigationTree } from '@mongodb-js/compass-connections-navigation';
import type { MapDispatchToProps, MapStateToProps } from 'react-redux';
Expand Down Expand Up @@ -80,11 +84,19 @@ const connectionCountStyles = css({
marginLeft: spacing[100],
});

const searchStyles = css({
const filterContainerStyles = css({
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
gap: spacing[200],
paddingLeft: spacing[400],
paddingRight: spacing[400],
});

const searchFormStyles = css({
flexGrow: 1,
});

const noDeploymentStyles = css({
paddingLeft: spacing[400],
paddingRight: spacing[400],
Expand Down Expand Up @@ -112,7 +124,9 @@ type ConnectionsNavigationComponentProps = {
connectionsWithStatus: ReturnType<typeof useConnectionsWithStatus>;
activeWorkspace: WorkspaceTab | null;
filterRegex: RegExp | null;
excludeInactive: boolean;
onFilterChange(regex: RegExp | null): void;
onToggleExcludeInactive(): void;
onConnect(info: ConnectionInfo): void;
onNewConnection(): void;
onEditConnection(info: ConnectionInfo): void;
Expand Down Expand Up @@ -154,10 +168,12 @@ const ConnectionsNavigation: React.FC<ConnectionsNavigationProps> = ({
connectionsWithStatus,
activeWorkspace,
filterRegex,
excludeInactive,
instances,
databases,
isPerformanceTabSupported,
onFilterChange,
onToggleExcludeInactive,
onConnect,
onNewConnection,
onEditConnection,
Expand Down Expand Up @@ -257,6 +273,7 @@ const ConnectionsNavigation: React.FC<ConnectionsNavigationProps> = ({
filterRegex,
fetchAllCollections,
onDatabaseExpand,
excludeInactive,
});

const connectionListTitleActions =
Expand Down Expand Up @@ -502,11 +519,37 @@ const ConnectionsNavigation: React.FC<ConnectionsNavigationProps> = ({
</div>
{connections.length > 0 && (
<>
<NavigationItemsFilter
searchInputClassName={searchStyles}
placeholder="Search connections"
onFilterChange={onFilterChange}
/>
<div className={filterContainerStyles}>
<NavigationItemsFilter
className={searchFormStyles}
placeholder="Search connections"
onFilterChange={onFilterChange}
/>
<Tooltip
justify="middle"
trigger={
<IconButton
onClick={onToggleExcludeInactive}
active={excludeInactive}
aria-label={
excludeInactive
? 'Showing active connections'
: 'Showing all connections'
}
>
{excludeInactive ? (
<ConnectedPlugsIcon />
) : (
<DisconnectedPlugIcon />
)}
</IconButton>
}
>
{excludeInactive
? 'Showing active connections'
: 'Showing all connections'}
</Tooltip>
</div>
<ConnectionsNavigationTree
connections={filtered || connections}
activeWorkspace={activeWorkspace}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
waitFor,
within,
userEvent,
expectElementByTestId,
} from '@mongodb-js/testing-library-compass';
import MultipleConnectionSidebar from './sidebar';
import type { WorkspaceTab } from '@mongodb-js/compass-workspaces';
Expand Down Expand Up @@ -357,6 +358,33 @@ describe('Multiple Connections Sidebar Component', function () {
expect(screen.getByText('Remove')).to.be.visible;
});

it('should render the only connected connections when toggled', async () => {
await renderAndWaitForNavigationTree();

const activeConnectionsToggleButton = screen.getByLabelText(
'Showing all connections'
);

expectElementByTestId(savedFavoriteConnection.id, 'visible');
expectElementByTestId(savedRecentConnection.id, 'visible');

userEvent.click(activeConnectionsToggleButton);
expect(activeConnectionsToggleButton.ariaLabel).equals(
'Showing active connections'
);

expectElementByTestId(savedFavoriteConnection.id, 'missing');
expectElementByTestId(savedRecentConnection.id, 'missing');

await connectAndNotifyInstanceManager(savedFavoriteConnection);
expectElementByTestId(savedFavoriteConnection.id, 'visible');
expectElementByTestId(savedRecentConnection.id, 'missing');

await connectAndNotifyInstanceManager(savedRecentConnection);
expectElementByTestId(savedFavoriteConnection.id, 'visible');
expectElementByTestId(savedRecentConnection.id, 'visible');
});

context('and performing actions', function () {
beforeEach(async function () {
await renderAndWaitForNavigationTree({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,10 @@ export function MultipleConnectionSidebar({
useState<RegExp | null>(null);
const [connectionInfoModalConnectionId, setConnectionInfoModalConnectionId] =
useState<string | undefined>();
const [excludeInactive, setExcludeInactiveConnections] = useState(false);
const toggleExcludeInactiveConnections = useCallback(() => {
setExcludeInactiveConnections((previous) => !previous);
}, []);

const formPreferences = useConnectionFormPreferences();
const maybeProtectConnectionString = useMaybeProtectConnectionString();
Expand Down Expand Up @@ -204,7 +208,9 @@ export function MultipleConnectionSidebar({
connectionsWithStatus={connectionsWithStatus}
activeWorkspace={activeWorkspace}
filterRegex={activeConnectionsFilterRegex}
excludeInactive={excludeInactive}
onFilterChange={onActiveConnectionFilterChange}
onToggleExcludeInactive={toggleExcludeInactiveConnections}
onConnect={(connectionInfo) => {
void connect(connectionInfo);
}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ export default function NavigationItemsFilter({
ariaLabel = 'Search',
title = 'Search',
onFilterChange,
searchInputClassName,
className,
}: {
placeholder?: string;
ariaLabel?: string;
title?: string;
searchInputClassName?: string;
onFilterChange(regex: RegExp | null): void;
className?: string;
}): React.ReactElement {
const onChange = useCallback(
(event) => {
Expand All @@ -37,15 +37,14 @@ export default function NavigationItemsFilter({
}, []);

return (
<form noValidate onSubmit={onSubmit}>
<form noValidate className={className} onSubmit={onSubmit}>
<TextInput
data-testid="sidebar-filter-input"
placeholder={placeholder}
type="search"
aria-label={ariaLabel}
title={title}
onChange={onChange}
className={searchInputClassName}
/>
</form>
);
Expand Down
Loading
Loading