-
Notifications
You must be signed in to change notification settings - Fork 68
LG-5067: Create basic CodeEditor component/package #2858
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
32 commits
Select commit
Hold shift + click to select a range
0989452
feat(code-editor): initialize code editor package with README, config…
tsck f17150a
feat(code-editor): enhance CodeEditor component with new props and de…
tsck 73d1ac5
refactor(code-editor): change import statements to use 'type' for Cod…
tsck 5809ce3
feat(code-editor): update CodeEditor stories with args and argTypes f…
tsck ae315fa
Start test suite
tsck 815fff1
Fix deps
tsck 3d48e45
feat(code-editor): refactor extensions handling using Compartment for…
tsck 5759b82
feat(tests): enhance CodeEditor tests with improved selector handling…
tsck 67e2a5f
feat(code-editor): rename 'value' prop to 'initialValue' for consiste…
tsck 4a9abe4
feat(code-editor): rename 'initialValue' prop to 'defaultValue' for c…
tsck bee8b1d
feat(code-editor): enable active line highlighting based on prop for …
tsck 2f21453
feat(tests): enable forceParsing test and update mock implementation …
tsck b82df4c
feat(tests): refactor CodeEditor tests to use new renderCodeEditor ut…
tsck 0a846db
refactor(tests): move MutationObserver mock to testUtils for better o…
tsck de05360
feat(docs): update README to include new CodeEditor properties and te…
tsck 1deb79f
refactor(tests): update typing test to use userEvent and export edito…
tsck 3aa98de
refactor(tests): update CodeEditor interactions and test utilities fo…
tsck 55b389a
test: update forceParsing method test to remove async and improve cla…
tsck 647ad60
changeset
tsck c26ce0f
feat(docs): add CodeEditor package information to README
tsck d79e9a2
feat: expand exports in index.ts to include additional CodeEditor typ…
tsck 747f201
refactor(tests): move renderCodeEditor utility to CodeEditor.testUtil…
tsck 5142f97
feat(tests): add TestUtils for improved test rendering utilities
tsck 1553697
fix: correct rendering description in tests and update README for Cod…
tsck 40e1508
docs(code-editor): update README with CodeEditor example and default …
tsck e2f6d50
chore(package): update node engine version to >= 18.20.8 (#2868)
tsck e7165e0
fix(package): update main and types paths in package.json
tsck 8eae613
Merge branch 'main' of github.com:mongodb/leafygreen-ui into LG-5067/…
tsck 4e651f4
chore(package): add @lg-tools/build to devDependencies
tsck d2d186a
chore(package): update build script in package.json
tsck af0b4db
feat(button): add exports field for module resolution
tsck ee82832
feat(code-editor): add exports field for module resolution
tsck File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
--- | ||
'@leafygreen-ui/code-editor': minor | ||
--- | ||
|
||
- Creates package. | ||
- Adds `CodeEditor` component with the following functionality: | ||
- Basic setup | ||
- Line wrapping | ||
- Hyperlink support | ||
- Forced parsing | ||
- Placeholders | ||
- Adds `renderEditor` test utility which exposes a number of helper methods. | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,183 @@ | ||
# Code Editor | ||
|
||
 | ||
|
||
#### [View on MongoDB.design](https://www.mongodb.design/component/code-editor/live-example/) | ||
|
||
## Installation | ||
|
||
### PNPM | ||
|
||
```shell | ||
pnpm add @leafygreen-ui/code-editor | ||
``` | ||
|
||
### Yarn | ||
|
||
```shell | ||
yarn add @leafygreen-ui/code-editor | ||
``` | ||
|
||
### NPM | ||
|
||
```shell | ||
npm install @leafygreen-ui/code-editor | ||
``` | ||
|
||
## Component | ||
|
||
### `<CodeEditor>` | ||
Comment on lines
+27
to
+29
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is this a stub for the future or can this be removed? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nope, that's the component name that's being documented here. |
||
|
||
#### Example | ||
|
||
```tsx | ||
import { CodeEditor } from '@leafygreen-ui/code-editor'; | ||
|
||
const sampleCode = `// Your code goes here | ||
function greet(name: string): string { | ||
return \`Hello, \${name}!\`; | ||
} | ||
|
||
console.log(greet('MongoDB user'));`; | ||
|
||
<CodeEditor defaultValue={sampleCode} />; | ||
``` | ||
|
||
#### Properties | ||
|
||
| Name | Description | Type | Default | | ||
| ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------- | ----------- | | ||
| `defaultValue` _(optional)_ | Initial value to render in the editor. | `string` | `undefined` | | ||
| `enableActiveLineHighlighting` _(optional)_ | Enables highlighting of the active line. | `boolean` | `true` | | ||
| `enableClickableUrls` _(optional)_ | Renders URLs as clickable links in the editor. | `boolean` | `true` | | ||
| `enableCodeFolding` _(optional)_ | Enables code folding arrows in the gutter. | `boolean` | `true` | | ||
| `enableLineNumbers` _(optional)_ | Enables line numbers in the editor’s gutter. | `boolean` | `true` | | ||
| `enableLineWrapping` _(optional)_ | Enables line wrapping when the text exceeds the editor’s width. | `boolean` | `true` | | ||
| `forceParsing` _(optional)_ | _**This should be used with caution as it can significantly impact performance!**_<br><br>Forces the parsing of the complete document, even parts not currently visible.<br><br>By default, the editor optimizes performance by only parsing the code that is visible on the screen, which is especially beneficial when dealing with large amounts of code. Enabling this option overrides this behavior and forces the parsing of all code, visible or not. This should generally be reserved for exceptional circumstances. | `boolean` | `false` | | ||
| `onChange` _(optional)_ | Callback that receives the updated editor value when changes are made. | `(value: string) => void;` | `undefined` | | ||
| `placeholder` _(optional)_ | Value to display in the editor when it is empty. | `HTMLElement \| string` | `undefined` | | ||
| `readOnly` _(optional)_ | Enables read only mode, making the contents uneditable. | `boolean` | `false` | | ||
|
||
## Types | ||
|
||
| Name | Description | | ||
| ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ||
| `CodeEditorProps` | Props that can be passed to the `CodeEditor` component. | | ||
| `CodeEditorSelectors` | Enum-like map of CSS selectors for common elements that make up the code editor. These can be useful in testing. | | ||
| `CodeMirrorExtension` | Underlying CodeMirror editor `Extension` type. For more information see https://codemirror.net/docs/ref/#state.Extension. | | ||
| `CodeMirrorRef` | Underlying CodeMirror editor ref type. When a ref is passed to the `CodeEditor` it will be of this type and give you direct access to CodeMirror internals such as `CodeMirrorState` and `CodeMirrorView` | | ||
| `CodeMirrorState` | Underlying CodeMirror editor `EditorState` type. For more information see https://codemirror.net/docs/ref/#state.EditorState. | | ||
| `CodeMirrorView` | Underlying CodeMirror editor `EditorView` type. For more information see https://codemirror.net/docs/ref/#view.EditorView. | | ||
| `RenderedTestResult` | Type returned by the `renderEditor` test utility. More info in Test Utilities section. | | ||
| `RenderedTestEditorType` | Editor type used to interact with editor in a Jest test. More info in Test Utilities section. | | ||
|
||
## Test Utlities | ||
|
||
### `renderEditor` | ||
|
||
Renders the editor in a Jest test. | ||
|
||
```tsx | ||
function renderEditor(props?: Partial<CodeEditorProps>): RenderResult; | ||
``` | ||
|
||
### `RenderResult` | ||
|
||
#### `container` | ||
|
||
HTML element of the container of the editor that was rendered. | ||
|
||
#### `editor` | ||
|
||
Editor object used for querying and interacting with the rendered editor. | ||
|
||
Has the following interface: | ||
|
||
```tsx | ||
{ | ||
// Used to find single element in the editor. Will throw error if not found. | ||
getBySelector( | ||
selector: CodeEditorSelectors, | ||
options?: { text?: string }, | ||
): Element; | ||
|
||
// Used to find single element in the editor. Will return null if not found. Useful when checking if something has not rendered. | ||
queryBySelector(selector: CodeEditorSelectors, options?: { | ||
text?: string; | ||
}): Element | null; | ||
|
||
// Returns whether editor is in a read only state. | ||
isReadOnly(): boolean; | ||
|
||
// Returns whether line wrapping is enabled in editor. | ||
isLineWrappingEnabled(): boolean; | ||
|
||
// Group of actions that can be performed on the editor. | ||
interactions: { | ||
// Insert text into the editor | ||
insertText(text: string, options?: { to?: number; from?: number; }): undefined; | ||
} | ||
} | ||
``` | ||
|
||
### Examples | ||
|
||
#### Test selector has rendered | ||
|
||
```tsx | ||
import { TestUtils } from '@leafygreen-ui/code-editor'; | ||
|
||
const { renderEditor } = TestUtils; | ||
|
||
test('Line numbers rendered', () => { | ||
const { editor } = renderCodeEditor({ | ||
defaultValue: 'content', | ||
enableLineNumbers: true, | ||
}); | ||
expect( | ||
editor.getBySelector(CodeEditorSelectors.GutterElement, { | ||
text: '1', | ||
}), | ||
).toBeInTheDocument(); | ||
}); | ||
``` | ||
|
||
#### Test selector has not rendered | ||
|
||
```tsx | ||
import { TestUtils } from '@leafygreen-ui/code-editor'; | ||
|
||
const { renderEditor } = TestUtils; | ||
|
||
test('Fold gutter does not render', () => { | ||
const { editor } = renderCodeEditor({ enableCodeFolding: false }); | ||
expect( | ||
// Note use of queryBy instead of getBy when test if not rendered | ||
editor.queryBySelector(CodeEditorSelectors.FoldGutter), | ||
).not.toBeInTheDocument(); | ||
}); | ||
``` | ||
|
||
#### Test user interaction | ||
|
||
```tsx | ||
import { TestUtils } from '@leafygreen-ui/code-editor'; | ||
|
||
const { renderEditor } = TestUtils; | ||
|
||
test('Updates value on when user types', () => { | ||
const { editor } = renderCodeEditor(); | ||
|
||
expect( | ||
editor.getBySelector(CodeEditorSelectors.Content), | ||
).not.toHaveTextContent('new content'); | ||
|
||
act(() => { | ||
editor.interactions.insertText('new content'); | ||
}); | ||
|
||
expect(editor.getBySelector(CodeEditorSelectors.Content)).toHaveTextContent( | ||
'new content', | ||
); | ||
}); | ||
``` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
{ | ||
"name": "@leafygreen-ui/code-editor", | ||
"version": "0.1.0", | ||
"description": "LeafyGreen UI Kit Code Editor", | ||
"main": "./dist/umd/index.js", | ||
"module": "./dist/esm/index.js", | ||
"types": "./dist/types/index.d.ts", | ||
"license": "Apache-2.0", | ||
"exports": { | ||
".": { | ||
"require": "./dist/umd/index.js", | ||
"import": "./dist/esm/index.js", | ||
"types": "./dist/types/index.d.ts" | ||
} | ||
}, | ||
"scripts": { | ||
"build": "lg-build bundle", | ||
"tsc": "lg-build tsc", | ||
"docs": "lg-build docs" | ||
}, | ||
"publishConfig": { | ||
"access": "public" | ||
}, | ||
"dependencies": { | ||
"@codemirror/language": "^6.11.0", | ||
"@leafygreen-ui/emotion": "workspace:^", | ||
"@leafygreen-ui/hooks": "workspace:^", | ||
"@leafygreen-ui/leafygreen-provider": "workspace:^", | ||
"@leafygreen-ui/lib": "workspace:^", | ||
"@leafygreen-ui/tokens": "workspace:^", | ||
"@uiw/codemirror-extensions-hyper-link": "^4.23.12", | ||
"@uiw/react-codemirror": "^4.23.10" | ||
}, | ||
"homepage": "https://github.com/mongodb/leafygreen-ui/tree/main/packages/code-editor", | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/mongodb/leafygreen-ui" | ||
}, | ||
"bugs": { | ||
"url": "https://jira.mongodb.org/projects/LG/summary" | ||
}, | ||
"devDependencies": { | ||
"@lg-tools/build": "workspace:^" | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
import React from 'react'; | ||
import { StoryFn } from '@storybook/react'; | ||
|
||
import { CodeEditor } from '.'; | ||
|
||
export default { | ||
title: 'Components/CodeEditor', | ||
component: CodeEditor, | ||
decorators: [ | ||
Story => ( | ||
<div style={{ width: '100%', height: '100vh' }}> | ||
<Story /> | ||
</div> | ||
), | ||
], | ||
args: { | ||
enableActiveLineHighlighting: true, | ||
enableClickableUrls: true, | ||
enableCodeFolding: true, | ||
enableLineNumbers: true, | ||
enableLineWrapping: true, | ||
defaultValue: '', | ||
forceParsing: false, | ||
placeholder: 'Type your code here...', | ||
readOnly: false, | ||
}, | ||
argTypes: { | ||
enableActiveLineHighlighting: { | ||
control: { type: 'boolean' }, | ||
}, | ||
enableClickableUrls: { | ||
control: { type: 'boolean' }, | ||
}, | ||
enableCodeFolding: { | ||
control: { type: 'boolean' }, | ||
}, | ||
enableLineNumbers: { | ||
control: { type: 'boolean' }, | ||
}, | ||
enableLineWrapping: { | ||
control: { type: 'boolean' }, | ||
}, | ||
placeholder: { | ||
control: { type: 'text' }, | ||
}, | ||
readOnly: { | ||
control: { type: 'boolean' }, | ||
}, | ||
defaultValue: { | ||
control: { type: 'text' }, | ||
}, | ||
}, | ||
}; | ||
|
||
const Template: StoryFn<typeof CodeEditor> = args => <CodeEditor {...args} />; | ||
|
||
export const LiveExample = Template.bind({}); |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is redundant? I believe this will lead to a republish of the same source code in 0.1.0 and 0.2.0
Also, is the plan to do incremental releases or did you want to wrap it all up into an integration branch?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh I see. So on the init package commit we typically don't have a changeset at all? Curious if it would be better then to bump the package down to 0.0.0?
I was just going to do incremental releases but if the team prefers integration branches I'm ok with that as well. What's the benefit of doing an integration branch here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My understanding is that publishing v0.1.0 and using incremental releases can be useful for consumers that need immediate access. Thinking some more on it, I'm not sure it would make sense to allow this to release until it's ready to be consumed which might be after designs/styling are complete?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think where I'm not following is if something is a sub-v1 release, that indicates to consumers that there's substantial risk in using it. That to me is a beta release by definition. So I'm not sure I understand why an integration branch would be necessary here. I could see that being useful when something is already versioned. For example, if this was already a v1 and the work in this epic would bring it to v2, an integration branch would be necessary since incomplete work would minor bump the v1 version, and indicate to consumers that its ready when its not. However, for sub-v1 releases, I'm not sure I feel that applies.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, spoke this one through with the team and am going to rebase this to an integration branch to mitigate the concerns that this isn't styled yet. Thanks for bringing this up!