Skip to content

Commit 90eaccc

Browse files
authored
feat(focus-mode): show guide cue COMPASS-6497 (#4043)
1 parent c840ea2 commit 90eaccc

File tree

15 files changed

+546
-155
lines changed

15 files changed

+546
-155
lines changed

package-lock.json

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

packages/compass-aggregations/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,8 @@
9595
"rimraf": "^3.0.0",
9696
"semver": "^5.7.1",
9797
"sinon": "^9.2.3",
98-
"xvfb-maybe": "^0.2.1"
98+
"xvfb-maybe": "^0.2.1",
99+
"react-intersection-observer": "^8.34.0"
99100
},
100101
"dependencies": {
101102
"@mongodb-js/compass-components": "^1.5.0",

packages/compass-aggregations/src/components/focus-mode/focus-mode-stage-editor.tsx

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
import React from 'react';
1+
import React, { useRef } from 'react';
22
import { css, spacing, Link } from '@mongodb-js/compass-components';
3+
import { type AceEditor } from '@mongodb-js/compass-editor';
34

45
import StageEditor from '../stage-editor/stage-editor';
56
import { getStageHelpLink } from '../../utils/stage';
@@ -37,21 +38,26 @@ export const FocusModeStageEditor = ({
3738
index: number;
3839
operator: string | null;
3940
}) => {
41+
const editorRef = useRef<AceEditor | undefined>(undefined);
4042
if (index === -1) {
4143
return null;
4244
}
4345
const link = getStageHelpLink(operator) || PIPELINE_HELP_URI;
4446
return (
4547
<div className={containerStyles}>
4648
<div className={headerStyles}>
47-
<StageOperatorSelect index={index} />
49+
<StageOperatorSelect editorRef={editorRef} index={index} />
4850
<Link hideExternalIcon={false} href={link} target="_blank">
4951
Open docs
5052
</Link>
5153
</div>
5254
<div className={editorStyles}>
53-
{/* @ts-expect-error requires stage-editor.jsx to be converted */}
54-
<StageEditor index={index} />
55+
<StageEditor
56+
onLoad={(editor) => {
57+
editorRef.current = editor;
58+
}}
59+
index={index}
60+
/>
5561
</div>
5662
</div>
5763
);

packages/compass-aggregations/src/components/loading-overlay/loading-overlay.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ const loadingOverlayStyles = css({
1616
left: 0,
1717
bottom: 0,
1818
right: 0,
19-
zIndex: 1000,
19+
zIndex: 1,
2020
display: 'flex',
2121
alignItems: 'center',
2222
justifyContent: 'center',

packages/compass-aggregations/src/components/stage-editor/stage-editor.spec.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,6 @@ describe('StageEditor [Component]', function () {
2929
});
3030

3131
it('renders the wrapper div', function () {
32-
expect(component.find('UnthemedStageEditor')).to.be.present();
32+
expect(component.find('StageEditor')).to.be.present();
3333
});
3434
});

packages/compass-aggregations/src/components/stage-editor/stage-editor.tsx

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import {
1919
spacing,
2020
palette,
2121
Banner,
22-
withDarkMode,
22+
useDarkMode,
2323
} from '@mongodb-js/compass-components';
2424
import { changeStageValue } from '../../modules/pipeline-builder/stage-editor';
2525
import { mapPipelineModeToEditorViewType } from '../../modules/pipeline-builder/builder-helpers';
@@ -66,7 +66,6 @@ const bannerStyles = css({
6666
});
6767

6868
type StageEditorProps = {
69-
darkMode?: boolean;
7069
index: number;
7170
stageOperator: string | null;
7271
stageValue: string | null;
@@ -76,8 +75,9 @@ type StageEditorProps = {
7675
serverError: MongoServerError | null;
7776
num_stages: number;
7877
editor_view_type: string;
79-
className: string;
78+
className?: string;
8079
onChange: (index: number, value: string) => void;
80+
onLoad?: (editor: AceEditor) => void;
8181
};
8282

8383
function useStageCompleter(
@@ -99,20 +99,21 @@ function useStageCompleter(
9999
return completer.current;
100100
}
101101

102-
const UnthemedStageEditor = ({
102+
export const StageEditor = ({
103103
stageValue,
104104
stageOperator,
105105
index,
106106
onChange,
107107
serverError,
108108
syntaxError,
109-
darkMode,
110109
className,
111110
autocompleteFields,
112111
serverVersion,
113112
num_stages,
114113
editor_view_type,
114+
onLoad,
115115
}: StageEditorProps) => {
116+
const darkMode = useDarkMode();
116117
const editorInitialValueRef = useRef<string | null>(stageValue);
117118
const editorRef = useRef<AceEditor | undefined>(undefined);
118119
const completer = useStageCompleter(
@@ -122,10 +123,6 @@ const UnthemedStageEditor = ({
122123
stageOperator
123124
);
124125

125-
useEffect(() => {
126-
editorRef.current?.focus();
127-
}, [stageOperator]);
128-
129126
useEffect(() => {
130127
let annotations: AceAnnotation[] = [];
131128
if (syntaxError && syntaxError.loc) {
@@ -182,6 +179,7 @@ const UnthemedStageEditor = ({
182179
completer={completer}
183180
onLoad={(editor) => {
184181
editorRef.current = editor;
182+
onLoad?.(editor);
185183
}}
186184
onBlur={onBlurEditor}
187185
/>
@@ -214,9 +212,6 @@ const UnthemedStageEditor = ({
214212
);
215213
};
216214

217-
// exported for tests
218-
export const StageEditor = withDarkMode(UnthemedStageEditor);
219-
220215
export default connect(
221216
(state: RootState, ownProps: { index: number }) => {
222217
const stages = state.pipelineBuilder.stageEditor.stages;

packages/compass-aggregations/src/components/stage-toolbar/index.spec.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,15 @@ const renderStageToolbar = (
2121
})}
2222
>
2323
<StageToolbar
24+
onFocusModeClicked={() => {}}
2425
hasServerError={false}
2526
hasSyntaxError={false}
2627
index={0}
2728
isAutoPreviewing={false}
2829
isCollapsed={false}
2930
isDisabled={false}
30-
onFocusModeEnableClick={() => {}}
31+
onOpenFocusMode={() => {}}
32+
editorRef={React.createRef()}
3133
{...props}
3234
/>
3335
</Provider>

packages/compass-aggregations/src/components/stage-toolbar/index.tsx

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ import {
1010
useDarkMode,
1111
IconButton,
1212
} from '@mongodb-js/compass-components';
13-
import type { RootState } from '../../modules';
13+
import type { PipelineBuilderThunkDispatch, RootState } from '../../modules';
14+
import { type AceEditor } from '@mongodb-js/compass-editor';
1415
import ToggleStage from './toggle-stage';
1516
import StageCollapser from './stage-collapser';
1617
import StageOperatorSelect from './stage-operator-select';
@@ -90,7 +91,9 @@ type StageToolbarProps = {
9091
hasServerError?: boolean;
9192
isCollapsed?: boolean;
9293
isDisabled?: boolean;
93-
onFocusModeEnableClick: (index: number) => void;
94+
onFocusModeClicked: () => void;
95+
onOpenFocusMode: () => void;
96+
editorRef: React.RefObject<AceEditor | undefined>;
9497
};
9598

9699
const DISABLED_TEXT = 'Stage disabled. Results not passed in the pipeline.';
@@ -103,7 +106,8 @@ export function StageToolbar({
103106
hasServerError,
104107
isCollapsed,
105108
isDisabled,
106-
onFocusModeEnableClick,
109+
onOpenFocusMode,
110+
editorRef,
107111
}: StageToolbarProps) {
108112
const darkMode = useDarkMode();
109113
const showFocusMode = usePreference('showFocusMode', React);
@@ -123,7 +127,7 @@ export function StageToolbar({
123127
<StageCollapser index={index} />
124128
<Body weight="medium">Stage {index + 1}</Body>
125129
<div className={selectStyles}>
126-
<StageOperatorSelect index={index} />
130+
<StageOperatorSelect editorRef={editorRef} index={index} />
127131
</div>
128132
<ToggleStage index={index} />
129133
</div>
@@ -133,9 +137,10 @@ export function StageToolbar({
133137
<div className={rightStyles}>
134138
{showFocusMode && (
135139
<IconButton
136-
onClick={() => onFocusModeEnableClick(index)}
140+
onClick={onOpenFocusMode}
137141
aria-label="Open stage in focus mode"
138142
data-testid="focus-mode-button"
143+
data-guide-cue-ref="focus-mode-button"
139144
>
140145
<Icon glyph="FullScreenEnter" size="small"></Icon>
141146
</IconButton>
@@ -146,8 +151,13 @@ export function StageToolbar({
146151
);
147152
}
148153

154+
type StageToolbarOwnProps = Pick<
155+
StageToolbarProps,
156+
'index' | 'onFocusModeClicked'
157+
>;
158+
149159
export default connect(
150-
(state: RootState, ownProps: { index: number }) => {
160+
(state: RootState, ownProps: StageToolbarOwnProps) => {
151161
const stage = state.pipelineBuilder.stageEditor.stages[ownProps.index];
152162
return {
153163
isAutoPreviewing: !!state.autoPreview,
@@ -157,5 +167,10 @@ export default connect(
157167
isDisabled: stage.disabled,
158168
};
159169
},
160-
{ onFocusModeEnableClick: enableFocusMode }
170+
(dispatch: PipelineBuilderThunkDispatch, ownProps: StageToolbarOwnProps) => ({
171+
onOpenFocusMode: () => {
172+
dispatch(enableFocusMode(ownProps.index));
173+
ownProps.onFocusModeClicked();
174+
},
175+
})
161176
)(StageToolbar);

packages/compass-aggregations/src/components/stage-toolbar/stage-operator-select.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import _ from 'lodash';
22
import React, { useCallback, useMemo } from 'react';
33
import { usePreference } from 'compass-preferences-model';
44
import { connect } from 'react-redux';
5+
import { type AceEditor } from '@mongodb-js/compass-editor';
56

67
import {
78
Combobox,
@@ -103,12 +104,14 @@ type EnvAwareStageOperatorSelectProps = {
103104
};
104105
onChange: (index: number, name: string) => void;
105106
index: number;
107+
editorRef: React.RefObject<AceEditor | undefined>;
106108
};
107109
function EnvAwareStageOperatorSelect({
108110
envInfo: { serverVersion, env, isTimeSeries, isReadonly, sourceName },
109111
stage,
110112
onChange,
111113
index,
114+
editorRef,
112115
}: EnvAwareStageOperatorSelectProps) {
113116
const preferencesReadOnly = usePreference('readOnly', React);
114117
const stages = useMemo(() => {
@@ -131,6 +134,7 @@ function EnvAwareStageOperatorSelect({
131134
const onChangeFilter = (index: number, name: string | null) => {
132135
if (name) {
133136
onChange(index, name);
137+
editorRef.current?.focus();
134138
}
135139
};
136140

0 commit comments

Comments
 (0)