mirror of
https://github.com/pybricks/pybricks-code.git
synced 2026-09-12 01:23:52 +00:00
drop use of decorator for editor context menu
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2021 The Pybricks Authors
|
||||
|
||||
import { I18nContext, I18nManager } from '@shopify/react-i18n';
|
||||
import {
|
||||
fireEvent,
|
||||
render,
|
||||
screen,
|
||||
waitForElementToBeRemoved,
|
||||
} from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import React from 'react';
|
||||
import { Provider } from 'react-redux';
|
||||
import { Store } from 'redux';
|
||||
import Editor from './Editor';
|
||||
|
||||
function getTextArea(): HTMLTextAreaElement {
|
||||
// the textarea in ace editor doesn't actually have any contents, but
|
||||
// it gets the focus for input.
|
||||
return screen.getByDisplayValue('') as HTMLTextAreaElement;
|
||||
}
|
||||
|
||||
it('should focus the text area', () => {
|
||||
const store = ({
|
||||
getState: jest.fn(() => ({ settings: { darkMode: false, showDocs: false } })),
|
||||
dispatch: jest.fn(),
|
||||
subscribe: jest.fn(),
|
||||
} as unknown) as Store;
|
||||
const i18n = new I18nManager({ locale: 'en' });
|
||||
render(
|
||||
<Provider store={store}>
|
||||
<I18nContext.Provider value={i18n}>
|
||||
<Editor />
|
||||
</I18nContext.Provider>
|
||||
</Provider>,
|
||||
);
|
||||
|
||||
expect(getTextArea()).toHaveFocus();
|
||||
});
|
||||
|
||||
describe('context menu', () => {
|
||||
it('should show the context menu', () => {
|
||||
const store = ({
|
||||
getState: jest.fn(() => ({
|
||||
settings: { darkMode: false, showDocs: false },
|
||||
})),
|
||||
dispatch: jest.fn(),
|
||||
subscribe: jest.fn(),
|
||||
} as unknown) as Store;
|
||||
const i18n = new I18nManager({ locale: 'en' });
|
||||
|
||||
render(
|
||||
<Provider store={store}>
|
||||
<I18nContext.Provider value={i18n}>
|
||||
<Editor />
|
||||
</I18nContext.Provider>
|
||||
</Provider>,
|
||||
);
|
||||
|
||||
fireEvent.contextMenu(screen.getByText('Write your program here...'));
|
||||
|
||||
expect(screen.getByText('Copy')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should hide the context menu when Escape is pressed', async () => {
|
||||
const store = ({
|
||||
getState: jest.fn(() => ({
|
||||
settings: { darkMode: false, showDocs: false },
|
||||
})),
|
||||
dispatch: jest.fn(),
|
||||
subscribe: jest.fn(),
|
||||
} as unknown) as Store;
|
||||
const i18n = new I18nManager({ locale: 'en' });
|
||||
|
||||
render(
|
||||
<Provider store={store}>
|
||||
<I18nContext.Provider value={i18n}>
|
||||
<Editor />
|
||||
</I18nContext.Provider>
|
||||
</Provider>,
|
||||
);
|
||||
|
||||
fireEvent.contextMenu(screen.getByText('Write your program here...'));
|
||||
|
||||
expect(screen.getByText('Copy')).toBeInTheDocument();
|
||||
|
||||
userEvent.type(document.activeElement || document.body, '{esc}');
|
||||
|
||||
await waitForElementToBeRemoved(() => screen.queryByText('Copy'));
|
||||
|
||||
// editor should be focused after context menu closes
|
||||
expect(document.activeElement).toBe(getTextArea());
|
||||
});
|
||||
});
|
||||
+33
-3
@@ -2,7 +2,7 @@
|
||||
// Copyright (c) 2020-2021 The Pybricks Authors
|
||||
|
||||
import {
|
||||
ContextMenuTarget,
|
||||
ContextMenu,
|
||||
Menu,
|
||||
MenuDivider,
|
||||
MenuItem,
|
||||
@@ -47,7 +47,6 @@ type DispatchProps = {
|
||||
|
||||
type EditorProps = StateProps & DispatchProps & WithI18nProps;
|
||||
|
||||
@ContextMenuTarget
|
||||
class Editor extends React.Component<EditorProps> {
|
||||
private editorRef: React.RefObject<AceEditor>;
|
||||
private keyBindings?: Array<{ key: string; command: string }>;
|
||||
@@ -83,7 +82,34 @@ class Editor extends React.Component<EditorProps> {
|
||||
render(): JSX.Element {
|
||||
const { i18n, darkMode, onSessionChanged, onCheck, onToggleDocs } = this.props;
|
||||
return (
|
||||
<div className="h-100">
|
||||
<div
|
||||
className="h-100"
|
||||
onContextMenu={(e) => {
|
||||
// istanbul ignore if: not expected
|
||||
if (e.defaultPrevented) {
|
||||
return;
|
||||
}
|
||||
|
||||
e.preventDefault();
|
||||
const menu = this.renderContextMenu();
|
||||
const listener = (e: KeyboardEvent) => {
|
||||
if (e.key === 'Escape') {
|
||||
// istanbul ignore if: not expected
|
||||
if (e.defaultPrevented) {
|
||||
return;
|
||||
}
|
||||
|
||||
e.preventDefault();
|
||||
ContextMenu.hide();
|
||||
}
|
||||
};
|
||||
window.addEventListener('keydown', listener);
|
||||
ContextMenu.show(menu, { left: e.clientX, top: e.clientY }, () => {
|
||||
window.removeEventListener('keydown', listener);
|
||||
this.onContextMenuClose();
|
||||
});
|
||||
}}
|
||||
>
|
||||
<ResizeSensor onResize={(): void => this.editor?.resize()}>
|
||||
<AceEditor
|
||||
ref={this.editorRef}
|
||||
@@ -213,6 +239,10 @@ class Editor extends React.Component<EditorProps> {
|
||||
</Menu>
|
||||
);
|
||||
}
|
||||
|
||||
onContextMenuClose = () => {
|
||||
this.editorRef.current?.editor.focus();
|
||||
};
|
||||
}
|
||||
|
||||
const mapStateToProps = (state: RootState): StateProps => ({
|
||||
|
||||
Reference in New Issue
Block a user