= ({
{connections.length > 0 && (
<>
-
-
-
- {excludeInactive ? (
-
- ) : (
-
- )}
-
- }
- >
- {excludeInactive
- ? 'Showing active connections'
- : 'Showing all connections'}
-
-
+
(undefined);
- const [activeConnectionsFilterRegex, setActiveConnectionsFilterRegex] =
- useState(null);
+ const [connectionsFilter, setConnectionsFilter] = useState(
+ { regex: null, excludeInactive: false }
+ );
const [connectionInfoModalConnectionId, setConnectionInfoModalConnectionId] =
useState();
- const [excludeInactive, setExcludeInactiveConnections] = useState(false);
- const toggleExcludeInactiveConnections = useCallback(() => {
- setExcludeInactiveConnections((previous) => !previous);
- }, []);
const formPreferences = useConnectionFormPreferences();
const maybeProtectConnectionString = useMaybeProtectConnectionString();
@@ -142,12 +141,6 @@ export function MultipleConnectionSidebar({
)?.connectionInfo;
};
- const onActiveConnectionFilterChange = useCallback(
- (filterRegex: RegExp | null) =>
- setActiveConnectionsFilterRegex(filterRegex),
- [setActiveConnectionsFilterRegex]
- );
-
const onOpenConnectionInfo = useCallback((connectionId: string) => {
return setConnectionInfoModalConnectionId(connectionId);
}, []);
@@ -207,10 +200,8 @@ export function MultipleConnectionSidebar({
{
void connect(connectionInfo);
}}
diff --git a/packages/compass-sidebar/src/components/navigation-items-filter.tsx b/packages/compass-sidebar/src/components/navigation-items-filter.tsx
index 98c7abf4e57..558ba8948e5 100644
--- a/packages/compass-sidebar/src/components/navigation-items-filter.tsx
+++ b/packages/compass-sidebar/src/components/navigation-items-filter.tsx
@@ -1,43 +1,63 @@
-import React, { useCallback } from 'react';
-import { TextInput } from '@mongodb-js/compass-components';
+import React, { useCallback, useState } from 'react';
+import { css, spacing, TextInput } from '@mongodb-js/compass-components';
+import type { ConnectionsFilter } from './use-filtered-connections';
+import ConnectionsFilterPopover from './connections-filter-popover';
+
+const filterContainerStyles = css({
+ display: 'flex',
+ flexDirection: 'row',
+ alignItems: 'center',
+ gap: spacing[200],
+ paddingLeft: spacing[400],
+ paddingRight: spacing[400],
+});
+
+const textInputStyles = css({
+ flexGrow: 1,
+});
+
+function createRegExp(input: string) {
+ try {
+ return input ? new RegExp(input, 'i') : null;
+ } catch (e) {
+ return null;
+ }
+}
export default function NavigationItemsFilter({
placeholder = 'Search',
ariaLabel = 'Search',
title = 'Search',
+ filter,
onFilterChange,
- className,
}: {
placeholder?: string;
ariaLabel?: string;
title?: string;
- onFilterChange(regex: RegExp | null): void;
- className?: string;
+ filter: ConnectionsFilter;
+ onFilterChange(
+ updater: (filter: ConnectionsFilter) => ConnectionsFilter
+ ): void;
}): React.ReactElement {
- const onChange = useCallback(
+ const onChange = useCallback>(
(event) => {
- const searchString: string = event.target.value;
-
- let re;
-
- try {
- re = searchString ? new RegExp(searchString, 'i') : null;
- } catch (e) {
- re = null;
- }
-
- onFilterChange(re);
+ onFilterChange((filter) => ({
+ ...filter,
+ regex: createRegExp(event.target.value),
+ }));
},
[onFilterChange]
);
+ const [isPopoverOpen, setPopoverOpen] = useState(false);
+
const onSubmit = useCallback((evt) => {
evt.preventDefault();
evt.stopPropagation();
}, []);
return (
-
);
diff --git a/packages/compass-sidebar/src/components/use-filtered-connections.spec.ts b/packages/compass-sidebar/src/components/use-filtered-connections.spec.ts
index 5d93c0b65e1..8a9aa3e71e3 100644
--- a/packages/compass-sidebar/src/components/use-filtered-connections.spec.ts
+++ b/packages/compass-sidebar/src/components/use-filtered-connections.spec.ts
@@ -137,10 +137,9 @@ describe('useFilteredConnections', function () {
const { result } = renderHookWithContext(useFilteredConnections, {
initialProps: {
connections: mockSidebarConnections,
- filterRegex: null,
+ filter: { regex: null, excludeInactive: false },
fetchAllCollections: fetchAllCollectionsStub,
onDatabaseExpand: onDatabaseExpandStub,
- excludeInactive: false,
},
});
@@ -153,10 +152,9 @@ describe('useFilteredConnections', function () {
const { result } = renderHookWithContext(useFilteredConnections, {
initialProps: {
connections: mockSidebarConnections,
- filterRegex: null,
+ filter: { regex: null, excludeInactive: false },
fetchAllCollections: fetchAllCollectionsStub,
onDatabaseExpand: onDatabaseExpandStub,
- excludeInactive: false,
},
});
@@ -176,10 +174,9 @@ describe('useFilteredConnections', function () {
{
initialProps: {
connections: mockSidebarConnections,
- filterRegex: null,
+ filter: { regex: null, excludeInactive: true },
fetchAllCollections: fetchAllCollectionsStub,
onDatabaseExpand: onDatabaseExpandStub,
- excludeInactive: true,
},
}
);
@@ -191,10 +188,9 @@ describe('useFilteredConnections', function () {
rerender({
connections: mockSidebarConnections,
- filterRegex: null,
+ filter: { regex: null, excludeInactive: false },
fetchAllCollections: fetchAllCollectionsStub,
onDatabaseExpand: onDatabaseExpandStub,
- excludeInactive: false,
});
expect(result.current.filtered).to.be.undefined;
@@ -206,10 +202,9 @@ describe('useFilteredConnections', function () {
const { result } = renderHookWithContext(useFilteredConnections, {
initialProps: {
connections: mockSidebarConnections,
- filterRegex: null,
+ filter: { regex: null, excludeInactive: false },
fetchAllCollections: fetchAllCollectionsStub,
onDatabaseExpand: onDatabaseExpandStub,
- excludeInactive: false,
},
});
@@ -238,10 +233,9 @@ describe('useFilteredConnections', function () {
const { result } = renderHookWithContext(useFilteredConnections, {
initialProps: {
connections: mockSidebarConnections,
- filterRegex: null,
+ filter: { regex: null, excludeInactive: false },
fetchAllCollections: fetchAllCollectionsStub,
onDatabaseExpand: onDatabaseExpandStub,
- excludeInactive: false,
},
});
@@ -291,10 +285,9 @@ describe('useFilteredConnections', function () {
const { result } = renderHookWithContext(useFilteredConnections, {
initialProps: {
connections: mockSidebarConnections,
- filterRegex: null,
+ filter: { regex: null, excludeInactive: false },
fetchAllCollections: fetchAllCollectionsStub,
onDatabaseExpand: onDatabaseExpandStub,
- excludeInactive: false,
},
});
@@ -328,10 +321,9 @@ describe('useFilteredConnections', function () {
const { result } = renderHookWithContext(useFilteredConnections, {
initialProps: {
connections: mockSidebarConnections,
- filterRegex: null,
+ filter: { regex: null, excludeInactive: false },
fetchAllCollections: fetchAllCollectionsStub,
onDatabaseExpand: onDatabaseExpandStub,
- excludeInactive: false,
},
});
@@ -375,10 +367,9 @@ describe('useFilteredConnections', function () {
const { result } = renderHookWithContext(useFilteredConnections, {
initialProps: {
connections: mockSidebarConnections,
- filterRegex: null,
+ filter: { regex: null, excludeInactive: false },
fetchAllCollections: fetchAllCollectionsStub,
onDatabaseExpand: onDatabaseExpandStub,
- excludeInactive: false,
},
});
@@ -409,10 +400,9 @@ describe('useFilteredConnections', function () {
{
initialProps: {
connections: mockSidebarConnections,
- filterRegex: null,
+ filter: { regex: null, excludeInactive: false },
fetchAllCollections: fetchAllCollectionsStub,
onDatabaseExpand: onDatabaseExpandStub,
- excludeInactive: false,
},
}
);
@@ -444,10 +434,9 @@ describe('useFilteredConnections', function () {
];
rerender({
connections: newConnections,
- filterRegex: null,
+ filter: { regex: null, excludeInactive: false },
fetchAllCollections: fetchAllCollectionsStub,
onDatabaseExpand: onDatabaseExpandStub,
- excludeInactive: false,
});
// should remove the expanded state of connection 2
await waitFor(() => {
@@ -461,10 +450,9 @@ describe('useFilteredConnections', function () {
// now pretend again that connection2 is connected
rerender({
connections: mockSidebarConnections,
- filterRegex: null,
+ filter: { regex: null, excludeInactive: false },
fetchAllCollections: fetchAllCollectionsStub,
onDatabaseExpand: onDatabaseExpandStub,
- excludeInactive: false,
});
await waitFor(() => {
expect(result.current.expanded).to.deep.equal({
@@ -484,10 +472,12 @@ describe('useFilteredConnections', function () {
{
initialProps: {
connections: mockSidebarConnections,
- filterRegex: new RegExp('_connection', 'i'), // match everything basically
+ filter: {
+ regex: new RegExp('_connection', 'i'), // match everything basically
+ excludeInactive: false,
+ },
fetchAllCollections: fetchAllCollectionsStub,
onDatabaseExpand: onDatabaseExpandStub,
- excludeInactive: false,
},
}
);
@@ -500,10 +490,12 @@ describe('useFilteredConnections', function () {
rerender({
connections: mockSidebarConnections,
- filterRegex: new RegExp('disconnected_connection', 'i'), // match disconnected one
+ filter: {
+ regex: new RegExp('disconnected_connection', 'i'), // match disconnected one
+ excludeInactive: false,
+ },
fetchAllCollections: fetchAllCollectionsStub,
onDatabaseExpand: onDatabaseExpandStub,
- excludeInactive: false,
});
await waitFor(() => {
expect(result.current.filtered).to.be.deep.equal([
@@ -516,10 +508,12 @@ describe('useFilteredConnections', function () {
const { result } = renderHookWithContext(useFilteredConnections, {
initialProps: {
connections: mockSidebarConnections,
- filterRegex: new RegExp('db_ready_1_1', 'i'), // match first database basically
+ filter: {
+ regex: new RegExp('db_ready_1_1', 'i'), // match first database basically
+ excludeInactive: false,
+ },
fetchAllCollections: fetchAllCollectionsStub,
onDatabaseExpand: onDatabaseExpandStub,
- excludeInactive: false,
},
});
@@ -557,10 +551,12 @@ describe('useFilteredConnections', function () {
],
} as SidebarConnectedConnection,
],
- filterRegex: new RegExp('Matching', 'i'), // this matches connection as well as database
+ filter: {
+ regex: new RegExp('Matching', 'i'), // this matches connection as well as database
+ excludeInactive: false,
+ },
fetchAllCollections: fetchAllCollectionsStub,
onDatabaseExpand: onDatabaseExpandStub,
- excludeInactive: false,
},
});
@@ -575,10 +571,12 @@ describe('useFilteredConnections', function () {
const { result } = renderHookWithContext(useFilteredConnections, {
initialProps: {
connections: mockSidebarConnections,
- filterRegex: new RegExp('coll_ready_2_1', 'i'), // match second db's collection
+ filter: {
+ regex: new RegExp('coll_ready_2_1', 'i'), // match second db's collection
+ excludeInactive: false,
+ },
fetchAllCollections: fetchAllCollectionsStub,
onDatabaseExpand: onDatabaseExpandStub,
- excludeInactive: false,
},
});
@@ -604,10 +602,12 @@ describe('useFilteredConnections', function () {
const { result } = renderHookWithContext(useFilteredConnections, {
initialProps: {
connections: mockSidebarConnections,
- filterRegex: new RegExp('ready_2_1', 'i'), // this matches 1 database and 1 collection
+ filter: {
+ regex: new RegExp('ready_2_1', 'i'), // this matches 1 database and 1 collection
+ excludeInactive: false,
+ },
fetchAllCollections: fetchAllCollectionsStub,
onDatabaseExpand: onDatabaseExpandStub,
- excludeInactive: false,
},
});
@@ -623,10 +623,12 @@ describe('useFilteredConnections', function () {
const { result } = renderHookWithContext(useFilteredConnections, {
initialProps: {
connections: mockSidebarConnections,
- filterRegex: new RegExp('coll_ready_1_1', 'i'),
+ filter: {
+ regex: new RegExp('coll_ready_1_1', 'i'),
+ excludeInactive: false,
+ },
fetchAllCollections: fetchAllCollectionsStub,
onDatabaseExpand: onDatabaseExpandStub,
- excludeInactive: false,
},
});
@@ -648,10 +650,12 @@ describe('useFilteredConnections', function () {
{
initialProps: {
connections: mockSidebarConnections,
- filterRegex: new RegExp('connection_1'),
+ filter: {
+ regex: new RegExp('connection_1'),
+ excludeInactive: true,
+ },
fetchAllCollections: fetchAllCollectionsStub,
onDatabaseExpand: onDatabaseExpandStub,
- excludeInactive: true,
},
}
);
@@ -662,10 +666,12 @@ describe('useFilteredConnections', function () {
rerender({
connections: mockSidebarConnections,
- filterRegex: new RegExp('connection_1'),
+ filter: {
+ regex: new RegExp('connection_1'),
+ excludeInactive: false,
+ },
fetchAllCollections: fetchAllCollectionsStub,
onDatabaseExpand: onDatabaseExpandStub,
- excludeInactive: false,
});
expect(result.current.filtered).to.be.deep.equal([
@@ -682,10 +688,12 @@ describe('useFilteredConnections', function () {
{
initialProps: {
connections: mockSidebarConnections,
- filterRegex: null as RegExp | null,
+ filter: {
+ regex: null as RegExp | null,
+ excludeInactive: false,
+ },
fetchAllCollections: fetchAllCollectionsStub,
onDatabaseExpand: onDatabaseExpandStub,
- excludeInactive: false,
},
}
);
@@ -701,10 +709,12 @@ describe('useFilteredConnections', function () {
rerender({
connections: mockSidebarConnections,
- filterRegex: new RegExp('coll_ready_1_1', 'i'),
+ filter: {
+ regex: new RegExp('coll_ready_1_1', 'i'),
+ excludeInactive: false,
+ },
fetchAllCollections: fetchAllCollectionsStub,
onDatabaseExpand: onDatabaseExpandStub,
- excludeInactive: false,
});
await waitFor(() => {
expect(result.current.expanded).to.deep.equal({
@@ -725,10 +735,12 @@ describe('useFilteredConnections', function () {
{
initialProps: {
connections: mockSidebarConnections,
- filterRegex: new RegExp('coll_ready_1_1', 'i') as RegExp | null,
+ filter: {
+ regex: new RegExp('coll_ready_1_1', 'i') as RegExp | null,
+ excludeInactive: false,
+ },
fetchAllCollections: fetchAllCollectionsStub,
onDatabaseExpand: onDatabaseExpandStub,
- excludeInactive: false,
},
}
);
@@ -744,10 +756,12 @@ describe('useFilteredConnections', function () {
rerender({
connections: mockSidebarConnections,
- filterRegex: null,
+ filter: {
+ regex: null,
+ excludeInactive: false,
+ },
fetchAllCollections: fetchAllCollectionsStub,
onDatabaseExpand: onDatabaseExpandStub,
- excludeInactive: false,
});
await waitFor(() => {
expect(result.current.expanded).to.deep.equal({
@@ -766,10 +780,12 @@ describe('useFilteredConnections', function () {
const { result } = renderHookWithContext(useFilteredConnections, {
initialProps: {
connections: mockSidebarConnections,
- filterRegex: new RegExp('coll_ready_1_1', 'i'),
+ filter: {
+ regex: new RegExp('coll_ready_1_1', 'i'),
+ excludeInactive: false,
+ },
fetchAllCollections: fetchAllCollectionsStub,
onDatabaseExpand: onDatabaseExpandStub,
- excludeInactive: false,
},
});
@@ -799,10 +815,12 @@ describe('useFilteredConnections', function () {
const { result } = renderHookWithContext(useFilteredConnections, {
initialProps: {
connections: mockSidebarConnections,
- filterRegex: new RegExp('coll_ready_1_1', 'i'),
+ filter: {
+ regex: new RegExp('coll_ready_1_1', 'i'),
+ excludeInactive: false,
+ },
fetchAllCollections: fetchAllCollectionsStub,
onDatabaseExpand: onDatabaseExpandStub,
- excludeInactive: false,
},
});
@@ -838,10 +856,12 @@ describe('useFilteredConnections', function () {
const { result } = renderHookWithContext(useFilteredConnections, {
initialProps: {
connections: mockSidebarConnections,
- filterRegex: new RegExp('coll_ready_1_1', 'i'),
+ filter: {
+ regex: new RegExp('coll_ready_1_1', 'i'),
+ excludeInactive: false,
+ },
fetchAllCollections: fetchAllCollectionsStub,
onDatabaseExpand: onDatabaseExpandStub,
- excludeInactive: false,
},
});
diff --git a/packages/compass-sidebar/src/components/use-filtered-connections.ts b/packages/compass-sidebar/src/components/use-filtered-connections.ts
index a90c6589c9f..e2ffb8b1384 100644
--- a/packages/compass-sidebar/src/components/use-filtered-connections.ts
+++ b/packages/compass-sidebar/src/components/use-filtered-connections.ts
@@ -388,16 +388,19 @@ function filteredConnectionsToSidebarConnection(
return sidebarConnections;
}
+export type ConnectionsFilter = {
+ regex: RegExp | null;
+ excludeInactive: boolean;
+};
+
export const useFilteredConnections = ({
connections,
- filterRegex,
- excludeInactive,
+ filter,
fetchAllCollections,
onDatabaseExpand,
}: {
connections: SidebarConnection[];
- filterRegex: RegExp | null;
- excludeInactive: boolean;
+ filter: ConnectionsFilter;
fetchAllCollections: () => void;
onDatabaseExpand: (connectionId: string, databaseId: string) => void;
}): UseFilteredConnectionsHookResult => {
@@ -423,9 +426,9 @@ export const useFilteredConnections = ({
// connections change often, but the effect only uses connections if the filter is active
// so we use this conditional dependency to avoid too many calls
const connectionsWhenFiltering =
- (filterRegex || excludeInactive) && connections;
+ (filter.regex || filter.excludeInactive) && connections;
useEffect(() => {
- if (!filterRegex && !excludeInactive) {
+ if (!filter.regex && !filter.excludeInactive) {
dispatch({ type: CLEAR_FILTER });
} else if (connectionsWhenFiltering) {
// the above check is extra just to please TS
@@ -438,13 +441,13 @@ export const useFilteredConnections = ({
dispatch({
type: FILTER_CONNECTIONS,
connections: connectionsWhenFiltering,
- filterRegex,
- excludeInactive,
+ filterRegex: filter.regex,
+ excludeInactive: filter.excludeInactive,
});
}
}, [
- filterRegex,
- excludeInactive,
+ filter.regex,
+ filter.excludeInactive,
connectionsWhenFiltering,
fetchAllCollections,
]);