Skip to content

Commit 44a1cd5

Browse files
lerouxbAnemy
andauthored
feat(sidebar): Expand/Collapse the new sidebar COMPASS-5967 (#3430)
* Expand/Collapse the sidebar * update test * Update packages/compass-sidebar/src/components/sidebar.tsx Co-authored-by: Rhys <Anemy@users.noreply.github.com> * Update packages/compass-components/src/components/resizeable-sidebar.tsx Co-authored-by: Rhys <Anemy@users.noreply.github.com> * and useEffect * and prevWidth * reformat Co-authored-by: Rhys <Anemy@users.noreply.github.com>
1 parent 4b7d5c3 commit 44a1cd5

File tree

10 files changed

+150
-22
lines changed

10 files changed

+150
-22
lines changed

packages/compass-components/src/components/resizeable-sidebar.tsx

Lines changed: 56 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useCallback, useState } from 'react';
1+
import React, { useCallback, useState, useEffect } from 'react';
22
import {
33
ResizeHandle,
44
ResizeDirection,
@@ -57,24 +57,70 @@ const containerStylesLight = css({
5757
backgroundColor: 'var(--bg-color)',
5858
});
5959

60-
const initialSidebarWidth = spacing[6] * 4 - spacing[1]; // 252px
61-
const minSidebarWidth = spacing[4] * 9; // 216px
62-
6360
const ResizableSidebar = ({
64-
initialWidth = initialSidebarWidth,
65-
minWidth = minSidebarWidth,
61+
collapsable = false,
62+
expanded = true,
63+
setExpanded = () => {
64+
return;
65+
},
66+
initialWidth = spacing[6] * 4,
67+
minWidth = 210,
68+
collapsedWidth = 48,
6669
children,
6770
}: {
71+
collapsable?: boolean;
72+
expanded?: boolean;
73+
setExpanded?: (isExpaned: boolean) => void;
6874
initialWidth?: number;
6975
minWidth?: number;
76+
collapsedWidth?: number;
7077
children: JSX.Element;
7178
}): JSX.Element => {
7279
const [width, setWidth] = useState(initialWidth);
80+
const [prevWidth, setPrevWidth] = useState(initialWidth);
7381

7482
const getMaxSidebarWidth = useCallback(() => {
7583
return Math.max(minWidth, window.innerWidth - 100);
7684
}, [minWidth]);
7785

86+
// Apply bounds to the sidebar width when resizing to ensure it's always
87+
// visible and usable to the user.
88+
const boundSidebarWidth = useCallback(
89+
(attemptedWidth: number) => {
90+
const maxWidth = getMaxSidebarWidth();
91+
92+
return Math.min(maxWidth, Math.max(minWidth, attemptedWidth));
93+
},
94+
[getMaxSidebarWidth, minWidth]
95+
);
96+
97+
const updateWidth = useCallback(
98+
(newWidth: number) => {
99+
setWidth(newWidth);
100+
101+
const expectedExpanded = width > minWidth || !collapsable;
102+
if (expanded !== expectedExpanded) {
103+
setExpanded(expectedExpanded);
104+
}
105+
106+
// Keep the most recent expanded width in case the sidebar suddenly gets
107+
// collapsed (via a button or keyboard shortcut or similar) so that if it
108+
// gets similarly expanded later we can restore the width.
109+
if (expectedExpanded) {
110+
setPrevWidth(newWidth);
111+
}
112+
},
113+
[collapsable, expanded, minWidth, setExpanded, width]
114+
);
115+
116+
const renderedWidth = expanded ? boundSidebarWidth(width) : collapsedWidth;
117+
118+
useEffect(() => {
119+
if (expanded && width === collapsedWidth) {
120+
setWidth(prevWidth);
121+
}
122+
}, [setWidth, prevWidth, expanded, width, collapsedWidth]);
123+
78124
const { theme } = useTheme();
79125

80126
return (
@@ -84,17 +130,17 @@ const ResizableSidebar = ({
84130
theme === Theme.Dark ? containerStylesDark : containerStylesLight
85131
)}
86132
style={{
87-
minWidth: minWidth,
88-
width: width,
133+
minWidth: collapsable ? collapsedWidth : minWidth,
134+
width: renderedWidth,
89135
flex: 'none',
90136
}}
91137
>
92138
{children}
93139
<ResizeHandle
94-
onChange={(newWidth) => setWidth(newWidth)}
140+
onChange={updateWidth}
95141
direction={ResizeDirection.RIGHT}
96142
value={width}
97-
minValue={minWidth}
143+
minValue={collapsable ? collapsedWidth : minWidth}
98144
maxValue={getMaxSidebarWidth()}
99145
title="sidebar"
100146
/>

packages/compass-sidebar/src/components/navigation-items.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,8 +151,10 @@ export function NavigationItems({
151151
actions={databasesActions}
152152
isActive={currentLocation === 'Databases'}
153153
/>
154-
<DatabaseCollectionFilter changeFilterRegex={changeFilterRegex} />
155-
<SidebarDatabasesNavigation />
154+
{isExpanded && (
155+
<DatabaseCollectionFilter changeFilterRegex={changeFilterRegex} />
156+
)}
157+
{isExpanded && <SidebarDatabasesNavigation />}
156158
</>
157159
);
158160
}

packages/compass-sidebar/src/components/sidebar-title.tsx

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,12 @@ import {
1313

1414
import type { ItemAction } from '@mongodb-js/compass-components';
1515

16-
type Actions =
16+
type Action =
1717
| 'open-instance-workspace'
1818
| 'copy-connection-string'
1919
| 'edit-favorite'
2020
| 'open-connection-info'
21+
| 'expand-sidebar'
2122
| 'refresh-data';
2223

2324
const titleLabel = css({
@@ -44,7 +45,6 @@ const TitleLabel: React.FunctionComponent<React.HTMLProps<HTMLSpanElement>> = ({
4445

4546
const titleLogo = css({
4647
width: spacing[5],
47-
paddingLeft: spacing[1],
4848
marginTop: '6px', // hardcoded to try and match the design
4949
flexShrink: 0,
5050
});
@@ -100,10 +100,10 @@ function SidebarTitle({
100100
title: string;
101101
isFavorite: boolean;
102102
isExpanded?: boolean;
103-
onAction(actionName: Actions, ...rest: any[]): void;
103+
onAction(actionName: Action, ...rest: any[]): void;
104104
}) {
105105
const actions = useMemo(() => {
106-
const actions: ItemAction<Actions>[] = [];
106+
const actions: ItemAction<Action>[] = [];
107107

108108
actions.push({
109109
action: 'copy-connection-string',
@@ -135,16 +135,20 @@ function SidebarTitle({
135135
const { theme } = useTheme();
136136

137137
const onClick = useCallback(() => {
138-
onAction('open-instance-workspace', 'My Queries');
139-
}, [onAction]);
138+
if (isExpanded) {
139+
onAction('open-instance-workspace', 'My Queries');
140+
} else {
141+
onAction('expand-sidebar');
142+
}
143+
}, [isExpanded, onAction]);
140144

141145
return (
142146
// eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions
143147
<div className={cx(sidebarTitle)} onClick={onClick}>
144148
<TitleLogo />
145149
{isExpanded && <TitleLabel title={title}>{title}</TitleLabel>}
146150
{isExpanded && (
147-
<ItemActionControls<Actions>
151+
<ItemActionControls<Action>
148152
onAction={onAction}
149153
iconSize="small"
150154
actions={actions}

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

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { cloneDeep } from 'lodash';
33
import { connect } from 'react-redux';
44
import { getConnectionTitle } from 'mongodb-data-service';
55
import type { ConnectionInfo } from 'mongodb-data-service';
6+
import type { MongoDBInstance } from 'mongodb-instance-model';
67
import {
78
css,
89
spacing,
@@ -24,7 +25,7 @@ import NonGenuineMarker from './non-genuine-marker';
2425

2526
import { updateAndSaveConnectionInfo } from '../modules/connection-info';
2627
import { toggleIsGenuineMongoDBVisible } from '../modules/is-genuine-mongodb-visible';
27-
import type { MongoDBInstance } from 'mongodb-instance-model';
28+
import { setIsExpanded } from '../modules/is-expanded';
2829

2930
const TOAST_TIMEOUT_MS = 5000; // 5 seconds.
3031

@@ -35,26 +36,29 @@ const badgesPlaceholderStyles = css({
3536

3637
// eslint-disable-next-line no-empty-pattern
3738
export function Sidebar({
39+
isExpanded,
3840
connectionInfo,
3941
globalAppRegistryEmit,
4042
updateAndSaveConnectionInfo,
4143
isGenuineMongoDBVisible,
4244
toggleIsGenuineMongoDBVisible,
45+
setIsExpanded,
4346
isGenuine,
4447
csfleMode,
4548
}: {
49+
isExpanded: boolean;
4650
connectionInfo: ConnectionInfo;
4751
globalAppRegistryEmit: any;
4852
updateAndSaveConnectionInfo: any;
4953
isGenuineMongoDBVisible: boolean;
5054
toggleIsGenuineMongoDBVisible: (isVisible: boolean) => void;
55+
setIsExpanded: (isExpanded: boolean) => void;
5156
isGenuine?: boolean;
5257
csfleMode?: 'enabled' | 'disabled' | 'unavailable';
5358
}) {
5459
const [isFavoriteModalVisible, setIsFavoriteModalVisible] = useState(false);
5560
const [isConnectionInfoModalVisible, setIsConnectionInfoModalVisible] =
5661
useState(false);
57-
const [isExpanded] = useState(true);
5862

5963
const onClickSaveFavorite = useCallback(
6064
(newFavoriteInfo) => {
@@ -108,10 +112,16 @@ export function Sidebar({
108112
return;
109113
}
110114

115+
if (action === 'expand-sidebar') {
116+
setIsExpanded(true);
117+
return;
118+
}
119+
111120
globalAppRegistryEmit(action, ...rest);
112121
},
113122
[
114123
connectionInfo.connectionOptions.connectionString,
124+
setIsExpanded,
115125
globalAppRegistryEmit,
116126
openToast,
117127
]
@@ -135,7 +145,11 @@ export function Sidebar({
135145
);
136146

137147
return (
138-
<ResizableSidebar>
148+
<ResizableSidebar
149+
collapsable={true}
150+
expanded={isExpanded}
151+
setExpanded={setIsExpanded}
152+
>
139153
<>
140154
<SidebarTitle
141155
title={getConnectionTitle(connectionInfo)}
@@ -190,12 +204,14 @@ export function Sidebar({
190204
}
191205

192206
const mapStateToProps = (state: {
207+
isExpanded: boolean;
193208
connectionInfo: {
194209
connectionInfo: ConnectionInfo;
195210
};
196211
isGenuineMongoDBVisible: boolean;
197212
instance?: MongoDBInstance;
198213
}) => ({
214+
isExpanded: state.isExpanded,
199215
connectionInfo: state.connectionInfo.connectionInfo,
200216
isGenuineMongoDBVisible: state.isGenuineMongoDBVisible,
201217
isGenuine: state.instance?.genuineMongoDB.isGenuine,
@@ -206,6 +222,7 @@ const MappedSidebar = connect(mapStateToProps, {
206222
globalAppRegistryEmit,
207223
updateAndSaveConnectionInfo,
208224
toggleIsGenuineMongoDBVisible,
225+
setIsExpanded,
209226
})(Sidebar);
210227

211228
export default MappedSidebar;

packages/compass-sidebar/src/modules/index.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import connectionOptions, {
1919
INITIAL_STATE as CONNECTION_OPTIONS_IS,
2020
} from './connection-options';
2121
import location, { INITIAL_STATE as LOCATION_IS } from './location';
22+
import isExpanded, { INITIAL_STATE as IS_EXPANDED_IS } from './is-expanded';
2223

2324
/**
2425
* The reducer.
@@ -32,6 +33,7 @@ const reducer = combineReducers({
3233
isDetailsExpanded,
3334
isGenuineMongoDBVisible,
3435
location,
36+
isExpanded,
3537
});
3638

3739
/**
@@ -53,6 +55,7 @@ const rootReducer = (state, action) => {
5355
isDetailsExpanded: IS_DETAILS_EXPANDED_INITIAL_STATE,
5456
isGenuineMongoDBVisible: IS_VISIBLE_IS,
5557
location: LOCATION_IS,
58+
isExpanded: IS_EXPANDED_IS,
5659
};
5760
}
5861
return reducer(state, action);
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import type { AnyAction } from 'redux';
2+
3+
export const TOGGLE = 'sidebar/TOGGLE';
4+
export const SET_EXPANDED = 'sidebar/SET_EXPANDED';
5+
6+
export const INITIAL_STATE = true;
7+
8+
export default function reducer(state = INITIAL_STATE, action: AnyAction) {
9+
if (action.type === TOGGLE) {
10+
return !state;
11+
}
12+
if (action.type === SET_EXPANDED) {
13+
return action.isExpanded;
14+
}
15+
return state;
16+
}
17+
18+
export const toggleSidebar = () => ({
19+
type: TOGGLE,
20+
});
21+
22+
export const setIsExpanded = (isExpanded: boolean) => ({
23+
type: SET_EXPANDED,
24+
isExpanded,
25+
});

packages/compass-sidebar/src/stores/store.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { reset } from '../modules/reset';
1010
import { toggleIsGenuineMongoDBVisible } from '../modules/is-genuine-mongodb-visible';
1111
import { changeConnectionInfo } from '../modules/connection-info';
1212
import { changeConnectionOptions } from '../modules/connection-options';
13+
import { toggleSidebar } from '../modules/is-expanded';
1314

1415
// We use these symbols so that nothing from outside can access these values on
1516
// the store
@@ -191,6 +192,10 @@ store.onActivated = (appRegistry) => {
191192
appRegistry.on('data-service-disconnected', () => {
192193
store.dispatch(reset());
193194
});
195+
196+
appRegistry.on('toggle-sidebar', () => {
197+
store.dispatch(toggleSidebar());
198+
});
194199
};
195200

196201
export default store;

packages/compass/src/app/index.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,10 @@ app.extend({
341341
ipc.on('app:refresh-data', () =>
342342
global.hadronApp.appRegistry.emit('refresh-data')
343343
);
344+
// catch a toggle sidebar coming from window-manager
345+
ipc.on('app:toggle-sidebar', () =>
346+
global.hadronApp.appRegistry.emit('toggle-sidebar')
347+
);
344348
// as soon as dom is ready, render and set up the rest
345349
state.render();
346350
marky.stop('Time to Connect rendered');

packages/compass/src/main/menu.spec.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,13 @@ describe('CompassMenu', function () {
136136
{
137137
type: 'separator',
138138
},
139+
{
140+
accelerator: 'CmdOrCtrl+Shift+D',
141+
label: '&Toggle Sidebar',
142+
},
143+
{
144+
type: 'separator'
145+
},
139146
{
140147
accelerator: 'CmdOrCtrl+0',
141148
label: 'Actual Size',
@@ -182,6 +189,13 @@ describe('CompassMenu', function () {
182189
{
183190
type: 'separator',
184191
},
192+
{
193+
accelerator: 'CmdOrCtrl+Shift+D',
194+
label: '&Toggle Sidebar',
195+
},
196+
{
197+
type: 'separator'
198+
},
185199
{
186200
accelerator: 'CmdOrCtrl+0',
187201
label: 'Actual Size',

packages/compass/src/main/menu.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,14 @@ function viewSubMenu(
395395
},
396396
},
397397
separator(),
398+
{
399+
label: '&Toggle Sidebar',
400+
accelerator: 'CmdOrCtrl+Shift+D',
401+
click() {
402+
ipcMain.broadcast('app:toggle-sidebar');
403+
},
404+
},
405+
separator(),
398406
{
399407
label: 'Actual Size',
400408
accelerator: 'CmdOrCtrl+0',

0 commit comments

Comments
 (0)