Skip to content

Commit 56a3539

Browse files
Interactive Window input box tweaks (#10029)
1 parent e7e9de5 commit 56a3539

File tree

12 files changed

+86
-12
lines changed

12 files changed

+86
-12
lines changed

news/2 Fixes/9282.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Clean up interative window styling and set focus to input box if clicking in the interactive window.

src/datascience-ui/history-react/interactivePanel.tsx

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ export class InteractivePanel extends React.Component<IInteractivePanelProps> {
7272
<section id="main-panel-variable" aria-label={getLocString('DataScience.collapseVariableExplorerLabel', 'Variables')}>
7373
{this.renderVariablePanel(this.props.baseTheme)}
7474
</section>
75-
<main id="main-panel-content" onScroll={this.handleScroll}>
75+
<main id="main-panel-content" onClick={this.contentPanelClick} onScroll={this.handleScroll}>
7676
{this.renderContentPanel(this.props.baseTheme)}
7777
</main>
7878
<section id="main-panel-footer" aria-label={getLocString('DataScience.editSection', 'Input new cells here')}>
@@ -82,6 +82,11 @@ export class InteractivePanel extends React.Component<IInteractivePanelProps> {
8282
);
8383
}
8484

85+
// If click is not handled by something else, focus our input box
86+
private contentPanelClick = (_event: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
87+
this.props.focusInput();
88+
};
89+
8590
private renderToolbarPanel() {
8691
const variableExplorerTooltip = this.props.variableState.visible
8792
? getLocString('DataScience.collapseVariableExplorerTooltip', 'Hide variables active in jupyter kernel')
@@ -216,7 +221,7 @@ export class InteractivePanel extends React.Component<IInteractivePanelProps> {
216221
monacoTheme={this.props.monacoTheme}
217222
font={this.props.font}
218223
settings={this.props.settings}
219-
focusPending={this.props.activateCount}
224+
focusPending={this.props.focusPending}
220225
/>
221226
</ErrorBoundary>
222227
</div>
@@ -280,7 +285,7 @@ export class InteractivePanel extends React.Component<IInteractivePanelProps> {
280285
monacoTheme={this.props.monacoTheme}
281286
font={this.props.font}
282287
settings={this.props.settings}
283-
focusPending={this.props.activateCount}
288+
focusPending={this.props.focusPending}
284289
/>
285290
</ErrorBoundary>
286291
</div>

src/datascience-ui/history-react/redux/actions.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import {
2020

2121
// See https://react-redux.js.org/using-react-redux/connect-mapdispatch#defining-mapdispatchtoprops-as-an-object
2222
export const actionCreators = {
23+
focusInput: (): CommonAction<never | undefined> => ({ type: CommonActionType.FOCUS_INPUT }),
2324
refreshVariables: (newExecutionCount?: number): CommonAction<IRefreshVariablesRequest> => ({ type: CommonActionType.REFRESH_VARIABLES, payload: { newExecutionCount } }),
2425
restartKernel: (): CommonAction<never | undefined> => ({ type: CommonActionType.RESTART_KERNEL }),
2526
interruptKernel: (): CommonAction<never | undefined> => ({ type: CommonActionType.INTERRUPT_KERNEL }),

src/datascience-ui/history-react/redux/mapping.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ type InteractiveReducerFunc<T> = ReducerFunc<IMainState, CommonActionType, T>;
2424
export type InteractiveReducerArg<T = never | undefined> = ReducerArg<IMainState, CommonActionType, T>;
2525

2626
export class IInteractiveActionMapping {
27+
public [CommonActionType.FOCUS_INPUT]: InteractiveReducerFunc<never | undefined>;
2728
public [CommonActionType.RESTART_KERNEL]: InteractiveReducerFunc<never | undefined>;
2829
public [CommonActionType.SELECT_KERNEL]: InteractiveReducerFunc<never | undefined>;
2930
public [CommonActionType.SELECT_SERVER]: InteractiveReducerFunc<never | undefined>;

src/datascience-ui/history-react/redux/reducers/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ export const reducerMap: IInteractiveActionMapping = {
4040
[CommonActionType.CLICK_CELL]: Effects.clickCell,
4141
[CommonActionType.UNFOCUS_CELL]: Effects.unfocusCell,
4242
[CommonActionType.UNMOUNT]: Creation.unmount,
43+
[CommonActionType.FOCUS_INPUT]: CommonEffects.focusInput,
4344

4445
// Messages from the webview (some are ignored)
4546
[IncomingMessageActions.STARTCELL]: Creation.startCell,

src/datascience-ui/interactive-common/common.css

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ body, html {
7979
}
8080

8181
.cell-wrapper:focus {
82+
outline-width: 0px;
8283
outline-color:var(--override-selection-background, var(--vscode-editor-selectionBackground));
8384
}
8485

@@ -98,6 +99,13 @@ body, html {
9899
display: block;
99100
}
100101

102+
.edit-panel-colorized .cell-wrapper:focus {
103+
outline-width: 0px;
104+
}
105+
.edit-panel .cell-wrapper:focus {
106+
outline-width: 0px;
107+
}
108+
101109
.cell-outer {
102110
display:grid;
103111
grid-template-columns: auto 1fr;
@@ -252,12 +260,16 @@ body, html {
252260
.code-watermark {
253261
position: absolute;
254262
top: 3px;
255-
left: 30px;
263+
left: 3px;
256264
z-index: 500;
257265
font-style: italic;
258266
color: var(--override-watermark-color, var(--vscode-panelTitle-inactiveForeground));
259267
}
260268

269+
.code-watermark:focus {
270+
outline-width: 0;
271+
}
272+
261273
.collapse-input-svg-rotate {
262274
transform: rotate(45deg);
263275
transform-origin: 0% 100%;

src/datascience-ui/interactive-common/mainState.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ export type IMainState = {
6969
testMode?: boolean;
7070
codeTheme: string;
7171
settings?: IDataScienceExtraSettings;
72-
activateCount: number;
72+
focusPending: number;
7373
monacoReady: boolean;
7474
loaded: boolean;
7575
kernel: IServerState;
@@ -134,7 +134,7 @@ export function generateTestState(filePath: string = '', editable: boolean = fal
134134
},
135135
codeTheme: 'Foo',
136136
settings: defaultSettings,
137-
activateCount: 0,
137+
focusPending: 0,
138138
monacoReady: true,
139139
loaded: false,
140140
testMode: true,

src/datascience-ui/interactive-common/redux/reducers/commonEffects.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,11 @@ export namespace CommonEffects {
3939
}
4040

4141
export function activate<T>(arg: CommonReducerArg<T>): IMainState {
42-
return {
43-
...arg.prevState,
44-
activateCount: arg.prevState.activateCount + 1
45-
};
42+
return focusPending(arg.prevState);
43+
}
44+
45+
export function focusInput<T>(arg: CommonReducerArg<T>): IMainState {
46+
return focusPending(arg.prevState);
4647
}
4748

4849
export function handleLocInit<T>(arg: CommonReducerArg<T, string>): IMainState {
@@ -105,4 +106,11 @@ export namespace CommonEffects {
105106
monacoTheme: Identifiers.GeneratedThemeName
106107
};
107108
}
109+
110+
function focusPending(prevState: IMainState): IMainState {
111+
return {
112+
...prevState,
113+
focusPending: prevState.focusPending + 1
114+
};
115+
}
108116
}

src/datascience-ui/interactive-common/redux/reducers/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ export enum CommonActionType {
4242
EXPAND_ALL = 'action.expand_all',
4343
EXPORT = 'action.export',
4444
FOCUS_CELL = 'action.focus_cell',
45+
FOCUS_INPUT = 'action.focus_input',
4546
GATHER_CELL = 'action.gather_cell',
4647
GET_VARIABLE_DATA = 'action.get_variable_data',
4748
GOTO_CELL = 'action.goto_cell',

src/datascience-ui/interactive-common/redux/store.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ function generateDefaultState(skipDefault: boolean, testMode: boolean, baseTheme
4242
family: "Consolas, 'Courier New', monospace"
4343
},
4444
codeTheme: Identifiers.GeneratedThemeName,
45-
activateCount: 0,
45+
focusPending: 0,
4646
monacoReady: testMode, // When testing, monaco starts out ready
4747
loaded: false,
4848
kernel: {

src/datascience-ui/native-editor/nativeEditor.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -396,7 +396,7 @@ export class NativeEditor extends React.Component<INativeEditorProps> {
396396
editorOptions={this.props.editorOptions}
397397
enableGather={this.props.settings.enableGather}
398398
themeMatplotlibPlots={this.props.settings.themeMatplotlibPlots}
399-
focusPending={this.props.activateCount}
399+
focusPending={this.props.focusPending}
400400
/>
401401
</ErrorBoundary>
402402
{lastLine}

src/test/datascience/interactiveWindow.functional.test.tsx

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,50 @@ suite('DataScience Interactive Window output tests', () => {
187187
}
188188
);
189189

190+
runMountedTest(
191+
'Click outside cells sets focus to input box',
192+
async wrapper => {
193+
// Create an interactive window so that it listens to the results.
194+
const interactiveWindow = await getOrCreateInteractiveWindow(ioc);
195+
await interactiveWindow.show();
196+
197+
// Type in the input box
198+
const editor = getInteractiveEditor(wrapper);
199+
typeCode(editor, 'a=1\na');
200+
201+
// Give focus to a random div
202+
const reactDiv = wrapper
203+
.find('div')
204+
.first()
205+
.getDOMNode();
206+
207+
const domDiv = reactDiv.querySelector('div');
208+
209+
if (domDiv && ioc.postMessage) {
210+
domDiv.tabIndex = -1;
211+
domDiv.focus();
212+
213+
// Click in content-panel-div, since this doesn't click on any valid click handlers this
214+
// should set input back to the input box
215+
wrapper
216+
.find('div#content-panel-div')
217+
.first()
218+
.simulate('click');
219+
220+
// Then enter press shift + enter on the active element
221+
const activeElement = document.activeElement;
222+
if (activeElement) {
223+
await submitInput(ioc, activeElement as HTMLTextAreaElement);
224+
}
225+
}
226+
227+
verifyHtmlOnCell(wrapper, 'InteractiveCell', '<span>1</span>', CellPosition.Last);
228+
},
229+
() => {
230+
return ioc;
231+
}
232+
);
233+
190234
runMountedTest(
191235
'Collapse / expand cell',
192236
async wrapper => {

0 commit comments

Comments
 (0)