Skip to content

Commit a1feb18

Browse files
authored
fix: fix select all action always select the editor content (#415)
* fix: fix select all action always select the editor content * test: update the workbench snapshot * test: add focusin test for menubar * fix: fix keybinding didn't work * test: update the snapshot
1 parent 9eb70a1 commit a1feb18

File tree

4 files changed

+75
-10
lines changed

4 files changed

+75
-10
lines changed

src/controller/menuBar.ts

+11-2
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,15 @@ import {
2424
MenuBarService,
2525
LayoutService,
2626
} from 'mo/services';
27-
import { ID_SIDE_BAR } from 'mo/common/id';
27+
import { ID_APP, ID_SIDE_BAR } from 'mo/common/id';
2828
import { IMonacoService, MonacoService } from 'mo/monaco/monacoService';
2929
import { CommandQuickSideBarViewAction } from 'mo/monaco/quickToggleSideBarAction';
3030
import { QuickTogglePanelAction } from 'mo/monaco/quickTogglePanelAction';
3131

3232
export interface IMenuBarController {
3333
onSelect?: (key: string, item?: IActivityBarItem) => void;
3434
onClick: (event: React.MouseEvent<any, any>, item: IMenuBarItem) => void;
35+
updateFocusinEle?: (ele: HTMLElement | null) => void;
3536
updateStatusBar?: () => void;
3637
updateMenuBar?: () => void;
3738
updateActivityBar?: () => void;
@@ -45,6 +46,8 @@ export class MenuBarController
4546
private readonly menuBarService: IMenuBarService;
4647
private readonly layoutService: ILayoutService;
4748
private readonly monacoService: IMonacoService;
49+
private focusinEle: HTMLElement | null = null;
50+
4851
private automation = {
4952
[ACTION_QUICK_CREATE_FILE]: () => this.createFile(),
5053
[ACTION_QUICK_UNDO]: () => this.undo(),
@@ -66,6 +69,11 @@ export class MenuBarController
6669
this.monacoService = container.resolve(MonacoService);
6770
}
6871

72+
public updateFocusinEle = (ele: HTMLElement | null) => {
73+
if (ele?.id == ID_APP) return;
74+
this.focusinEle = ele;
75+
};
76+
6977
public readonly onClick = (event: React.MouseEvent, item: IMenuBarItem) => {
7078
const menuId = item.id || '';
7179

@@ -105,7 +113,8 @@ export class MenuBarController
105113

106114
public selectAll = () => {
107115
this.monacoService.commandService.executeCommand(
108-
ACTION_QUICK_SELECT_ALL
116+
ACTION_QUICK_SELECT_ALL,
117+
this.focusinEle
109118
);
110119
};
111120

src/monaco/quickSelectAllAction.ts

+28-5
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,33 @@ export class QuickSelectAllAction extends Action2 {
3232
this.editorService = container.resolve(EditorService);
3333
}
3434

35-
run(): void {
36-
this.editorService.editorInstance!.focus();
37-
this.editorService.editorInstance!.setSelection(
38-
this.editorService.editorInstance!.getModel()!.getFullModelRange()
39-
);
35+
selectEditorAll() {
36+
const editor = this.editorService.editorInstance;
37+
if (editor) {
38+
editor.focus();
39+
editor.setSelection(editor.getModel()!.getFullModelRange());
40+
}
41+
}
42+
43+
isTextdom(ele: Element): ele is HTMLInputElement {
44+
return typeof (ele as HTMLInputElement).selectionStart === 'number';
45+
}
46+
47+
run(accessor, ...args): void {
48+
const focusinEle = args[0];
49+
// execute the action via shortcut if focusinEle is undefined
50+
const currentFocusinEle: Element | null =
51+
focusinEle || document.activeElement;
52+
if (
53+
currentFocusinEle &&
54+
this.isTextdom(currentFocusinEle) &&
55+
!currentFocusinEle.className.includes('monaco')
56+
) {
57+
// native element can select by the native method
58+
currentFocusinEle.select();
59+
} else {
60+
// monaco component should use the method from instance
61+
this.selectEditorAll();
62+
}
4063
}
4164
}

src/workbench/menuBar/__tests__/menubar.test.tsx

+22-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React from 'react';
22
import renderer from 'react-test-renderer';
3-
import { fireEvent, render } from '@testing-library/react';
3+
import { cleanup, fireEvent, render } from '@testing-library/react';
44
import '@testing-library/jest-dom';
55

66
import MenuBar, { actionClassName } from '../menuBar';
@@ -25,6 +25,8 @@ const menuData = [
2525
const TEST_FN: jest.Mock<any, any> = jest.fn();
2626

2727
describe('Test MenuBar Component', () => {
28+
afterEach(cleanup);
29+
2830
test('Match the MenuBar snapshot', () => {
2931
const component = renderer.create(
3032
<MenuBar data={menuData} onClick={TEST_FN} />
@@ -48,4 +50,23 @@ describe('Test MenuBar Component', () => {
4850
const menuBar = getByRole('menu').firstElementChild as HTMLLIElement;
4951
expect(menuBar).toBeInTheDocument();
5052
});
53+
54+
test('Should support to get the focus element', () => {
55+
const mockFn = jest.fn();
56+
const {} = render(
57+
<MenuBar
58+
data={menuData}
59+
onClick={TEST_FN}
60+
updateFocusinEle={mockFn}
61+
/>
62+
);
63+
64+
const input = document.createElement('input');
65+
document.body.append(input);
66+
67+
input.focus();
68+
69+
expect(mockFn).toBeCalled();
70+
expect(mockFn.mock.calls[0][0]).toEqual(input);
71+
});
5172
});

src/workbench/menuBar/menuBar.tsx

+14-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useRef } from 'react';
1+
import React, { useCallback, useEffect, useRef } from 'react';
22
import { getBEMElement, prefixClaName } from 'mo/common/className';
33
import { IMenuBar, IMenuBarItem } from 'mo/model/workbench/menuBar';
44
import { IMenuBarController } from 'mo/controller/menuBar';
@@ -11,7 +11,7 @@ export const defaultClassName = prefixClaName('menuBar');
1111
export const actionClassName = getBEMElement(defaultClassName, 'action');
1212

1313
export function MenuBar(props: IMenuBar & IMenuBarController) {
14-
const { data, onClick } = props;
14+
const { data, onClick, updateFocusinEle } = props;
1515
const childRef = useRef<DropDownRef>(null);
1616

1717
const addKeybindingForData = (
@@ -50,6 +50,18 @@ export function MenuBar(props: IMenuBar & IMenuBarController) {
5050
data={addKeybindingForData(data)}
5151
/>
5252
);
53+
54+
const handleSaveFocusinEle = useCallback((e: FocusEvent) => {
55+
updateFocusinEle?.(e.target as HTMLElement | null);
56+
}, []);
57+
58+
useEffect(() => {
59+
document.body.addEventListener('focusin', handleSaveFocusinEle);
60+
return () => {
61+
document.body.removeEventListener('focusin', handleSaveFocusinEle);
62+
};
63+
}, []);
64+
5365
return (
5466
<div className={defaultClassName}>
5567
<DropDown

0 commit comments

Comments
 (0)