diff --git a/packages/react-code-editor/package.json b/packages/react-code-editor/package.json index 718a267f7a0..433021f0325 100644 --- a/packages/react-code-editor/package.json +++ b/packages/react-code-editor/package.json @@ -33,12 +33,12 @@ "@patternfly/react-icons": "^5.2.0-prerelease.8", "@patternfly/react-styles": "^5.2.0-prerelease.5", "react-dropzone": "14.2.3", - "tslib": "^2.5.0" + "tslib": "^2.5.0", + "@monaco-editor/react": "^4.6.0" }, "peerDependencies": { "react": "^17 || ^18", - "react-dom": "^17 || ^18", - "react-monaco-editor": "^0.51.0" + "react-dom": "^17 || ^18" }, "devDependencies": { "rimraf": "^2.6.2", diff --git a/packages/react-code-editor/src/components/CodeEditor/CodeEditor.tsx b/packages/react-code-editor/src/components/CodeEditor/CodeEditor.tsx index 2dcbacc83b2..c99608228fc 100644 --- a/packages/react-code-editor/src/components/CodeEditor/CodeEditor.tsx +++ b/packages/react-code-editor/src/components/CodeEditor/CodeEditor.tsx @@ -17,7 +17,7 @@ import { TooltipPosition, EmptyStateHeader } from '@patternfly/react-core'; -import MonacoEditor, { ChangeHandler, EditorDidMount } from 'react-monaco-editor'; +import Editor, { Monaco } from '@monaco-editor/react'; import { editor } from 'monaco-editor/esm/vs/editor/editor.api'; import CopyIcon from '@patternfly/react-icons/dist/esm/icons/copy-icon'; import UploadIcon from '@patternfly/react-icons/dist/esm/icons/upload-icon'; @@ -28,6 +28,9 @@ import Dropzone, { FileRejection } from 'react-dropzone'; import { CodeEditorContext } from './CodeEditorUtils'; import { CodeEditorControl } from './CodeEditorControl'; +export type ChangeHandler = (value: string, event: editor.IModelContentChangedEvent) => void; +export type EditorDidMount = (editor: editor.IStandaloneCodeEditor, monaco: Monaco) => void; + export interface Shortcut { description: string; keys: string[]; @@ -631,7 +634,7 @@ class CodeEditor extends React.Component { const editor = (
- { options={options} overrideServices={overrideServices} onChange={this.onChange} - editorDidMount={this.editorDidMount} + onMount={this.editorDidMount} theme={isDarkTheme ? 'vs-dark' : 'vs-light'} />
diff --git a/packages/react-code-editor/src/components/CodeEditor/__test__/CodeEditor.test.tsx b/packages/react-code-editor/src/components/CodeEditor/__test__/CodeEditor.test.tsx index 3703f1ad875..78e403d5fec 100644 --- a/packages/react-code-editor/src/components/CodeEditor/__test__/CodeEditor.test.tsx +++ b/packages/react-code-editor/src/components/CodeEditor/__test__/CodeEditor.test.tsx @@ -1,46 +1,79 @@ import React from 'react'; -import { render } from '@testing-library/react'; +import { render, screen, act } from '@testing-library/react'; import { CodeEditor, Language } from '../CodeEditor'; +import styles from '@patternfly/react-styles/css/components/CodeEditor/code-editor'; +import fileUploadStyles from '@patternfly/react-styles/css/components/FileUpload/file-upload'; -Object.defineProperty(window, 'matchMedia', { - writable: true, - value: jest.fn().mockImplementation((query) => ({ - matches: false, - media: query, - onchange: null, - addListener: jest.fn(), // Deprecated - removeListener: jest.fn(), // Deprecated - addEventListener: jest.fn(), - removeEventListener: jest.fn(), - dispatchEvent: jest.fn() - })) -}); - -describe('CodeEditor', () => { - beforeAll(() => { - window.HTMLCanvasElement.prototype.getContext = () => ({}) as any; - }); +jest.mock('@monaco-editor/react', () => jest.fn(() =>
)); - test('matches snapshot without props', () => { - const { asFragment } = render(); - expect(asFragment()).toMatchSnapshot(); - }); +test('Matches snapshot without props', () => { + const { asFragment } = render(); + expect(asFragment()).toMatchSnapshot(); +}); + +test('Matches snapshot with control buttons enabled', () => { + const { asFragment } = render(); + expect(asFragment()).toMatchSnapshot(); +}); + +test(`Renders with default classes ${styles.codeEditor}, ${styles.codeEditorMain}, ${styles.codeEditorCode}`, () => { + render(); + expect(screen.getByTestId('mock-editor').parentElement).toHaveClass(styles.codeEditorCode); + expect(screen.getByTestId('mock-editor').parentElement?.parentElement).toHaveClass(styles.codeEditorMain); + expect(screen.getByTestId('mock-editor').parentElement?.parentElement?.parentElement).toHaveClass(styles.codeEditor); +}); + +test('Renders custom class when className is passed', () => { + render(); + expect(screen.getByTestId('mock-editor').parentElement?.parentElement?.parentElement).toHaveClass('custom'); +}); + +test(`Renders with ${styles.modifiers.readOnly} when isReadOnly = true`, () => { + render(); + expect(screen.getByTestId('mock-editor').parentElement?.parentElement?.parentElement).toHaveClass( + styles.modifiers.readOnly + ); +}); + +test(`Renders with ${fileUploadStyles.fileUpload} when isUploadEnabled = true`, () => { + render(); + expect(screen.getByTestId('mock-editor').parentElement?.parentElement?.parentElement).toHaveClass( + fileUploadStyles.fileUpload + ); +}); + +test(`Renders with empty state when code = undefined`, () => { + render(empty} />); + expect(screen.getByText('empty')).toBeInTheDocument(); +}); + +test(`Renders with empty state when isUploadEnabled = true and code = undefined`, () => { + render(empty} isUploadEnabled />); + expect(screen.getByText('empty')).toBeInTheDocument(); +}); + +test(`Renders with language label when isLanguageLabelVisible`, () => { + render(); + expect(screen.getByText('JAVA')).toBeInTheDocument(); +}); + +test(`Renders with custom controls when customControls is passed`, () => { + render(control} />); + expect(screen.getByText('control')).toBeInTheDocument(); +}); + +test(`Renders with custom header content when headerMainContent is passed`, () => { + render(); + expect(screen.getByText('header content')).toBeInTheDocument(); +}); - test('matches snapshot with all props', () => { - const { asFragment } = render( - - ); - expect(asFragment()).toMatchSnapshot(); +test(`Renders with shortcuts when shortcutsPopoverButtonText is passed`, () => { + render( + + ); + expect(screen.getByText('shortcuts-button')).toBeInTheDocument(); + act(() => { + screen.getByText('shortcuts-button').click(); }); + expect(screen.getByText('shortcuts')).toBeInTheDocument(); }); diff --git a/packages/react-code-editor/src/components/CodeEditor/__test__/CodeEditorControl.test.tsx b/packages/react-code-editor/src/components/CodeEditor/__test__/CodeEditorControl.test.tsx new file mode 100644 index 00000000000..847cf6cda33 --- /dev/null +++ b/packages/react-code-editor/src/components/CodeEditor/__test__/CodeEditorControl.test.tsx @@ -0,0 +1,18 @@ +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import { CodeEditorControl } from '../CodeEditorControl'; + +test('Matches snapshot', () => { + const { asFragment } = render(icon} onClick={jest.fn()} />); + expect(asFragment()).toMatchSnapshot(); +}); + +test('Renders with custom class when className is passed', () => { + render(icon} onClick={jest.fn()} />); + expect(screen.getByText('icon').parentElement).toHaveClass('custom'); +}); + +test('Renders with accessible name when aria-label is passed', () => { + render(icon} onClick={jest.fn()} />); + expect(screen.getByLabelText('aria-test')); +}); diff --git a/packages/react-code-editor/src/components/CodeEditor/__test__/__snapshots__/CodeEditor.test.tsx.snap b/packages/react-code-editor/src/components/CodeEditor/__test__/__snapshots__/CodeEditor.test.tsx.snap index a81731992c9..2dfcbd78dd7 100644 --- a/packages/react-code-editor/src/components/CodeEditor/__test__/__snapshots__/CodeEditor.test.tsx.snap +++ b/packages/react-code-editor/src/components/CodeEditor/__test__/__snapshots__/CodeEditor.test.tsx.snap @@ -1,9 +1,9 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`CodeEditor matches snapshot with all props 1`] = ` +exports[`Matches snapshot with control buttons enabled 1`] = `
-
- - - - - JAVASCRIPT - -
-
-
-