Skip to content

Commit 754ab49

Browse files
committed
Refactor ConnectionsNavigation into NavigationItemsFilter
1 parent 117c32f commit 754ab49

File tree

4 files changed

+116
-104
lines changed

4 files changed

+116
-104
lines changed

packages/compass-sidebar/src/components/multiple-connections/connections-navigation.tsx

Lines changed: 16 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,6 @@ import {
1212
Button,
1313
Icon,
1414
ButtonVariant,
15-
IconButton,
16-
ConnectedPlugsIcon,
17-
DisconnectedPlugIcon,
18-
Tooltip,
1915
} from '@mongodb-js/compass-components';
2016
import { ConnectionsNavigationTree } from '@mongodb-js/compass-connections-navigation';
2117
import type { MapDispatchToProps, MapStateToProps } from 'react-redux';
@@ -46,7 +42,10 @@ import {
4642
fetchAllCollections,
4743
type Database,
4844
} from '../../modules/databases';
49-
import { useFilteredConnections } from '../use-filtered-connections';
45+
import {
46+
type ConnectionsFilter,
47+
useFilteredConnections,
48+
} from '../use-filtered-connections';
5049
import NavigationItemsFilter from '../navigation-items-filter';
5150
import {
5251
type ConnectionImportExportAction,
@@ -84,15 +83,6 @@ const connectionCountStyles = css({
8483
marginLeft: spacing[100],
8584
});
8685

87-
const filterContainerStyles = css({
88-
display: 'flex',
89-
flexDirection: 'row',
90-
alignItems: 'center',
91-
gap: spacing[200],
92-
paddingLeft: spacing[400],
93-
paddingRight: spacing[400],
94-
});
95-
9686
const searchFormStyles = css({
9787
flexGrow: 1,
9888
});
@@ -123,10 +113,10 @@ type ConnectionListTitleActions =
123113
type ConnectionsNavigationComponentProps = {
124114
connectionsWithStatus: ReturnType<typeof useConnectionsWithStatus>;
125115
activeWorkspace: WorkspaceTab | null;
126-
filterRegex: RegExp | null;
127-
excludeInactive: boolean;
128-
onFilterChange(regex: RegExp | null): void;
129-
onToggleExcludeInactive(): void;
116+
filter: ConnectionsFilter;
117+
onFilterChange(
118+
updater: (filter: ConnectionsFilter) => ConnectionsFilter
119+
): void;
130120
onConnect(info: ConnectionInfo): void;
131121
onNewConnection(): void;
132122
onEditConnection(info: ConnectionInfo): void;
@@ -167,13 +157,11 @@ type ConnectionsNavigationProps = ConnectionsNavigationComponentProps &
167157
const ConnectionsNavigation: React.FC<ConnectionsNavigationProps> = ({
168158
connectionsWithStatus,
169159
activeWorkspace,
170-
filterRegex,
171-
excludeInactive,
160+
filter,
172161
instances,
173162
databases,
174163
isPerformanceTabSupported,
175164
onFilterChange,
176-
onToggleExcludeInactive,
177165
onConnect,
178166
onNewConnection,
179167
onEditConnection,
@@ -270,10 +258,9 @@ const ConnectionsNavigation: React.FC<ConnectionsNavigationProps> = ({
270258
onDatabaseToggle,
271259
} = useFilteredConnections({
272260
connections,
273-
filterRegex,
261+
filter,
274262
fetchAllCollections,
275263
onDatabaseExpand,
276-
excludeInactive,
277264
});
278265

279266
const connectionListTitleActions =
@@ -519,37 +506,12 @@ const ConnectionsNavigation: React.FC<ConnectionsNavigationProps> = ({
519506
</div>
520507
{connections.length > 0 && (
521508
<>
522-
<div className={filterContainerStyles}>
523-
<NavigationItemsFilter
524-
className={searchFormStyles}
525-
placeholder="Search connections"
526-
onFilterChange={onFilterChange}
527-
/>
528-
<Tooltip
529-
justify="middle"
530-
trigger={
531-
<IconButton
532-
onClick={onToggleExcludeInactive}
533-
active={excludeInactive}
534-
aria-label={
535-
excludeInactive
536-
? 'Showing active connections'
537-
: 'Showing all connections'
538-
}
539-
>
540-
{excludeInactive ? (
541-
<ConnectedPlugsIcon />
542-
) : (
543-
<DisconnectedPlugIcon />
544-
)}
545-
</IconButton>
546-
}
547-
>
548-
{excludeInactive
549-
? 'Showing active connections'
550-
: 'Showing all connections'}
551-
</Tooltip>
552-
</div>
509+
<NavigationItemsFilter
510+
className={searchFormStyles}
511+
placeholder="Search connections"
512+
filter={filter}
513+
onFilterChange={onFilterChange}
514+
/>
553515
<ConnectionsNavigationTree
554516
connections={filtered || connections}
555517
activeWorkspace={activeWorkspace}

packages/compass-sidebar/src/components/multiple-connections/sidebar.tsx

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,10 @@ import ConnectionsNavigation from './connections-navigation';
2525
import CSFLEConnectionModal, {
2626
type CSFLEConnectionModalProps,
2727
} from '../csfle-connection-modal';
28+
import type { ConnectionsFilter } from '../use-filtered-connections';
2829
import { setConnectionIsCSFLEEnabled } from '../../modules/data-service';
2930
import { useGlobalAppRegistry } from 'hadron-app-registry';
31+
3032
const TOAST_TIMEOUT_MS = 5000; // 5 seconds.
3133

3234
type MappedCsfleModalProps = {
@@ -99,14 +101,11 @@ export function MultipleConnectionSidebar({
99101
const [csfleModalConnectionId, setCsfleModalConnectionId] = useState<
100102
string | undefined
101103
>(undefined);
102-
const [activeConnectionsFilterRegex, setActiveConnectionsFilterRegex] =
103-
useState<RegExp | null>(null);
104+
const [connectionsFilter, setConnectionsFilter] = useState<ConnectionsFilter>(
105+
{ regex: null, excludeInactive: false }
106+
);
104107
const [connectionInfoModalConnectionId, setConnectionInfoModalConnectionId] =
105108
useState<string | undefined>();
106-
const [excludeInactive, setExcludeInactiveConnections] = useState(false);
107-
const toggleExcludeInactiveConnections = useCallback(() => {
108-
setExcludeInactiveConnections((previous) => !previous);
109-
}, []);
110109

111110
const formPreferences = useConnectionFormPreferences();
112111
const maybeProtectConnectionString = useMaybeProtectConnectionString();
@@ -142,12 +141,6 @@ export function MultipleConnectionSidebar({
142141
)?.connectionInfo;
143142
};
144143

145-
const onActiveConnectionFilterChange = useCallback(
146-
(filterRegex: RegExp | null) =>
147-
setActiveConnectionsFilterRegex(filterRegex),
148-
[setActiveConnectionsFilterRegex]
149-
);
150-
151144
const onOpenConnectionInfo = useCallback((connectionId: string) => {
152145
return setConnectionInfoModalConnectionId(connectionId);
153146
}, []);
@@ -207,10 +200,8 @@ export function MultipleConnectionSidebar({
207200
<ConnectionsNavigation
208201
connectionsWithStatus={connectionsWithStatus}
209202
activeWorkspace={activeWorkspace}
210-
filterRegex={activeConnectionsFilterRegex}
211-
excludeInactive={excludeInactive}
212-
onFilterChange={onActiveConnectionFilterChange}
213-
onToggleExcludeInactive={toggleExcludeInactiveConnections}
203+
filter={connectionsFilter}
204+
onFilterChange={setConnectionsFilter}
214205
onConnect={(connectionInfo) => {
215206
void connect(connectionInfo);
216207
}}
Lines changed: 80 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,107 @@
11
import React, { useCallback } from 'react';
2-
import { TextInput } from '@mongodb-js/compass-components';
2+
import {
3+
ConnectedPlugsIcon,
4+
css,
5+
DisconnectedPlugIcon,
6+
IconButton,
7+
spacing,
8+
TextInput,
9+
Tooltip,
10+
} from '@mongodb-js/compass-components';
11+
import type { ConnectionsFilter } from './use-filtered-connections';
12+
13+
const filterContainerStyles = css({
14+
display: 'flex',
15+
flexDirection: 'row',
16+
alignItems: 'center',
17+
gap: spacing[200],
18+
paddingLeft: spacing[400],
19+
paddingRight: spacing[400],
20+
});
21+
22+
function createRegExp(input: string) {
23+
try {
24+
return input ? new RegExp(input, 'i') : null;
25+
} catch (e) {
26+
return null;
27+
}
28+
}
329

430
export default function NavigationItemsFilter({
531
placeholder = 'Search',
632
ariaLabel = 'Search',
733
title = 'Search',
34+
filter,
835
onFilterChange,
936
className,
1037
}: {
1138
placeholder?: string;
1239
ariaLabel?: string;
1340
title?: string;
14-
onFilterChange(regex: RegExp | null): void;
41+
filter: ConnectionsFilter;
42+
onFilterChange(
43+
updater: (filter: ConnectionsFilter) => ConnectionsFilter
44+
): void;
1545
className?: string;
1646
}): React.ReactElement {
17-
const onChange = useCallback(
47+
const onChange = useCallback<React.ChangeEventHandler<HTMLInputElement>>(
1848
(event) => {
19-
const searchString: string = event.target.value;
20-
21-
let re;
22-
23-
try {
24-
re = searchString ? new RegExp(searchString, 'i') : null;
25-
} catch (e) {
26-
re = null;
27-
}
28-
29-
onFilterChange(re);
49+
onFilterChange((filter) => ({
50+
...filter,
51+
regex: createRegExp(event.target.value),
52+
}));
3053
},
3154
[onFilterChange]
3255
);
3356

57+
const toggleExcludeInactive = useCallback(() => {
58+
onFilterChange((filter) => ({
59+
...filter,
60+
excludeInactive: !filter.excludeInactive,
61+
}));
62+
}, [onFilterChange]);
63+
3464
const onSubmit = useCallback((evt) => {
3565
evt.preventDefault();
3666
evt.stopPropagation();
3767
}, []);
3868

3969
return (
40-
<form noValidate className={className} onSubmit={onSubmit}>
41-
<TextInput
42-
data-testid="sidebar-filter-input"
43-
placeholder={placeholder}
44-
type="search"
45-
aria-label={ariaLabel}
46-
title={title}
47-
onChange={onChange}
48-
/>
49-
</form>
70+
<div className={filterContainerStyles}>
71+
<form noValidate className={className} onSubmit={onSubmit}>
72+
<TextInput
73+
data-testid="sidebar-filter-input"
74+
placeholder={placeholder}
75+
type="search"
76+
aria-label={ariaLabel}
77+
title={title}
78+
onChange={onChange}
79+
/>
80+
</form>
81+
<Tooltip
82+
justify="middle"
83+
trigger={
84+
<IconButton
85+
onClick={toggleExcludeInactive}
86+
active={filter.excludeInactive}
87+
aria-label={
88+
filter.excludeInactive
89+
? 'Showing active connections'
90+
: 'Showing all connections'
91+
}
92+
>
93+
{filter.excludeInactive ? (
94+
<ConnectedPlugsIcon />
95+
) : (
96+
<DisconnectedPlugIcon />
97+
)}
98+
</IconButton>
99+
}
100+
>
101+
{filter.excludeInactive
102+
? 'Showing active connections'
103+
: 'Showing all connections'}
104+
</Tooltip>
105+
</div>
50106
);
51107
}

packages/compass-sidebar/src/components/use-filtered-connections.ts

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -388,16 +388,19 @@ function filteredConnectionsToSidebarConnection(
388388
return sidebarConnections;
389389
}
390390

391+
export type ConnectionsFilter = {
392+
regex: RegExp | null;
393+
excludeInactive: boolean;
394+
};
395+
391396
export const useFilteredConnections = ({
392397
connections,
393-
filterRegex,
394-
excludeInactive,
398+
filter,
395399
fetchAllCollections,
396400
onDatabaseExpand,
397401
}: {
398402
connections: SidebarConnection[];
399-
filterRegex: RegExp | null;
400-
excludeInactive: boolean;
403+
filter: ConnectionsFilter;
401404
fetchAllCollections: () => void;
402405
onDatabaseExpand: (connectionId: string, databaseId: string) => void;
403406
}): UseFilteredConnectionsHookResult => {
@@ -423,9 +426,9 @@ export const useFilteredConnections = ({
423426
// connections change often, but the effect only uses connections if the filter is active
424427
// so we use this conditional dependency to avoid too many calls
425428
const connectionsWhenFiltering =
426-
(filterRegex || excludeInactive) && connections;
429+
(filter.regex || filter.excludeInactive) && connections;
427430
useEffect(() => {
428-
if (!filterRegex && !excludeInactive) {
431+
if (!filter.regex && !filter.excludeInactive) {
429432
dispatch({ type: CLEAR_FILTER });
430433
} else if (connectionsWhenFiltering) {
431434
// the above check is extra just to please TS
@@ -438,13 +441,13 @@ export const useFilteredConnections = ({
438441
dispatch({
439442
type: FILTER_CONNECTIONS,
440443
connections: connectionsWhenFiltering,
441-
filterRegex,
442-
excludeInactive,
444+
filterRegex: filter.regex,
445+
excludeInactive: filter.excludeInactive,
443446
});
444447
}
445448
}, [
446-
filterRegex,
447-
excludeInactive,
449+
filter.regex,
450+
filter.excludeInactive,
448451
connectionsWhenFiltering,
449452
fetchAllCollections,
450453
]);

0 commit comments

Comments
 (0)