Skip to content
Merged
Show file tree
Hide file tree
Changes from 51 commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
3c7976e
Vertically align panel buttons
tsck Oct 7, 2025
30d3182
Fix persistent copy button styles
tsck Oct 7, 2025
340522d
More complete live example
tsck Oct 7, 2025
8bf5633
Decrease tooltip delay
tsck Oct 7, 2025
90340a5
Add comment on decrease in tooltip speed
tsck Oct 7, 2025
0aaa0a0
Fix gutter padding when line numbers disabled
tsck Oct 8, 2025
7c2bd42
Fix close chevron alignment
tsck Oct 8, 2025
fe622fd
Remove fold placeholder color
tsck Oct 8, 2025
d8894c4
Remove some default imports
tsck Oct 8, 2025
95ee7ed
Make Panel respect editor width props
tsck Oct 8, 2025
8ab9587
Some more default imports
tsck Oct 8, 2025
c3467d2
Disable undo/redo on readonly
tsck Oct 8, 2025
95746ba
Add change depth states (broken)
tsck Oct 8, 2025
fa34c79
Fix re-init editor bug on prop change
tsck Oct 8, 2025
806b7b4
Remove caret when readonly
tsck Oct 8, 2025
8e6c004
Refactor editor initialization check to remove unnecessary dependencies
tsck Oct 21, 2025
78f9243
Update inert attribute handling in SearchPanel for better type compat…
tsck Oct 21, 2025
03de69f
Add context menu interaction handling to prevent selection changes
tsck Oct 21, 2025
3a27cc8
Fix font size and dark mode
tsck Oct 21, 2025
50a36ef
Enhance undo/redo functionality to focus editor and scroll cursor int…
tsck Oct 21, 2025
b1d2b5c
Add extension initialization tracking to CodeEditor component to prev…
tsck Oct 22, 2025
6ade369
Refactor editor initialization to use requestAnimationFrame for bette…
tsck Oct 22, 2025
3238c86
Update contact form header by removing emoji for a cleaner presentation
tsck Oct 24, 2025
7b2c951
Dynamically calculate height of loader based on future content
tsck Oct 24, 2025
10560fd
Add additional configuration options to PanelTestContextConfig for en…
tsck Oct 24, 2025
44354c5
Add loading indicator integration to CodeEditor component, including …
tsck Oct 24, 2025
5b75e17
Fix container width
tsck Oct 27, 2025
27c3e98
Update gutter position style in useThemeExtension to improve shadow v…
tsck Oct 28, 2025
d4b6c0d
feat(code-editor): render top and bottom shadows
tsck Oct 28, 2025
7224e10
fix(code-editor): update selector for editor styles to improve specif…
tsck Oct 29, 2025
79186a9
feat(code-editor): add configurable top and bottom shadows to editor …
tsck Oct 29, 2025
67019ea
refactor(code-editor): rename InnerEditor selector to Scroller for co…
tsck Oct 29, 2025
e98aabe
chore(code-editor): remove polished dependency and related imports fr…
tsck Oct 29, 2025
ab0337d
feat(code-editor): implement scroll shadows for overflow indication i…
tsck Oct 29, 2025
2049884
feat(code-editor): integrate lodash for debounced scroll shadow updates
tsck Oct 29, 2025
39ff90b
refactor(code-editor): remove lodash dependency and optimize scroll s…
tsck Oct 29, 2025
ebff315
refactor(code-editor): remove unused styles from useThemeExtension fo…
tsck Oct 29, 2025
e3a8a4d
Fix inner height issue
tsck Oct 29, 2025
cc24e12
feat(code-editor): add LoadingWithPanel story and adjust styles for p…
tsck Oct 29, 2025
dc4822f
Fix panel tests
tsck Oct 29, 2025
7289b02
Remove Spinner
tsck Oct 29, 2025
ba49882
WIP: Fix tests
tsck Oct 29, 2025
35e442e
Remove unused logs
tsck Oct 29, 2025
58cbf7b
Refactor tests
tsck Oct 29, 2025
8043093
All tests passing
tsck Oct 29, 2025
b837759
Linting passed
tsck Oct 29, 2025
b557b0f
refactor(code-editor): remove overflow shadow styles and update gutte…
tsck Oct 30, 2025
7361dbf
refactor(code-editor): remove unused theme prop from CodeEditor style…
tsck Oct 30, 2025
bf5d952
Fix interaction tests by using preloaded modules to prevent loading
tsck Oct 30, 2025
173f4b6
changeset
tsck Oct 30, 2025
0bb0a6b
Misc comments and cleanup
tsck Oct 30, 2025
0d1b9df
copilot review fixes
tsck Oct 30, 2025
46a216e
Fix copy tooltip in darkmode
tsck Oct 30, 2025
b98fd1b
Merge branch 'main' of github.com:mongodb/leafygreen-ui into LG-5446/…
tsck Oct 30, 2025
3997f6a
CR feedback
tsck Oct 30, 2025
cf42e34
Merge branch 'main' of github.com:mongodb/leafygreen-ui into LG-5446/…
tsck Oct 30, 2025
c83e112
Use test utils to get panel
tsck Oct 30, 2025
aa16aef
Merge branch 'main' of github.com:mongodb/leafygreen-ui into LG-5446/…
tsck Oct 30, 2025
ef0b163
Update changeset to major release
tsck Oct 30, 2025
9b08fa9
CR feedback
tsck Oct 30, 2025
e23fb51
Fix build
tsck Oct 31, 2025
abf02e4
Fix darkMode again on prettify button
tsck Oct 31, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions .changeset/bumpy-turtles-start.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
'@leafygreen-ui/code-editor': minor
---

Fixes a number of bugs. These include:

- Adds gutter padding between the border and the caret when line numbers are turned off
- Fixes icon button vertical alignment in the panel
- Adds comprehensive story, that also include tooltips
- Increases tooltip speed
- Fixes collapsed chevron alignment
- Darkens unfold icon in dark mode
- Fixes widths. This includes making sure the panel and editor containers respect any set widths
- Corrects the color of the copied icon checkmark
- Disabled undo/redo when no undo/redo actions are available
- Hides blinking cursor when editor is in read only mode
- Allows for CTRL+click to pull up the context menu without losing the selection.
- Insert the cursor in the location of the undone action when the undo button is pressed in the panel menu
- Sets the loading height to be the same height as the future rendered editor
- Persists editor when props change
- Assures that font size and dark mode are respected in the panel
- Enhances tests
6 changes: 6 additions & 0 deletions packages/code-editor/src/CodeEditor.interactions.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
CopyButtonAppearance,
IndentUnits,
} from './CodeEditor/CodeEditor.types';
import { preLoadedModules } from './testing/preLoadedModules';
import { CodeEditor, LanguageName } from '.';

const meta: StoryMetaType<typeof CodeEditor> = {
Expand Down Expand Up @@ -196,6 +197,7 @@ export const ErrorTooltipOnHover: StoryObj<{}> = {
return (
<CodeEditor
defaultValue={'test\n'.repeat(5)}
preLoadedModules={preLoadedModules}
tooltips={[
{
line: 2,
Expand Down Expand Up @@ -227,6 +229,7 @@ export const HintTooltipOnHover: StoryObj<{}> = {
return (
<CodeEditor
defaultValue={'test\n'.repeat(5)}
preLoadedModules={preLoadedModules}
tooltips={[
{
line: 2,
Expand Down Expand Up @@ -258,6 +261,7 @@ export const InfoTooltipOnHover: StoryObj<{}> = {
return (
<CodeEditor
defaultValue={'test\n'.repeat(5)}
preLoadedModules={preLoadedModules}
tooltips={[
{
line: 2,
Expand Down Expand Up @@ -289,6 +293,7 @@ export const WarningTooltipOnHover: StoryObj<{}> = {
return (
<CodeEditor
defaultValue={'test\n'.repeat(5)}
preLoadedModules={preLoadedModules}
tooltips={[
{
line: 2,
Expand Down Expand Up @@ -319,6 +324,7 @@ export const ContextMenuOnRightClick: StoryObj<{}> = {
render: () => {
return (
<CodeEditor
preLoadedModules={preLoadedModules}
customContextMenuItems={[{ label: 'Custom Action', action: () => {} }]}
/>
);
Expand Down
89 changes: 85 additions & 4 deletions packages/code-editor/src/CodeEditor.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,10 @@ import LeafyGreenProvider from '@leafygreen-ui/leafygreen-provider';
import Modal from '@leafygreen-ui/modal';
import { BaseFontSize } from '@leafygreen-ui/tokens';

import { CopyButtonAppearance } from './CodeEditor/CodeEditor.types';
import {
CodeEditorTooltipSeverity,
CopyButtonAppearance,
} from './CodeEditor/CodeEditor.types';
import { LanguageName } from './CodeEditor/hooks/extensions/useLanguageExtension';
import { IndentUnits } from './CodeEditor';
import { ShortcutTable } from './ShortcutTable';
Expand Down Expand Up @@ -172,12 +175,64 @@ export default meta;
const Template: StoryFn<typeof CodeEditor> = args => <CodeEditor {...args} />;

export const LiveExample = Template.bind({});

export const WithPanel = Template.bind({});
const language = LanguageName.tsx;
WithPanel.args = {
LiveExample.args = {
language,
defaultValue: codeSnippets[language],
tooltips: [
{
line: 4,
column: 11,
length: 11,
messages: ['This is an error tooltip'],
links: [
{
label: 'External Link',
href: 'https://mongodb.com',
},
],
severity: CodeEditorTooltipSeverity.Error,
},
{
line: 10,
column: 25,
length: 8,
messages: ['This is an info tooltip'],
links: [
{
label: 'External Link',
href: 'https://mongodb.com',
},
],
severity: CodeEditorTooltipSeverity.Info,
},
{
line: 12,
column: 14,
length: 7,
messages: ['This is an hint tooltip'],
links: [
{
label: 'External Link',
href: 'https://mongodb.com',
},
],
severity: CodeEditorTooltipSeverity.Hint,
},
{
line: 28,
column: 11,
length: 11,
messages: ['This is an warning tooltip'],
links: [
{
label: 'External Link',
href: 'https://mongodb.com',
},
],
severity: CodeEditorTooltipSeverity.Warning,
},
],
children: (
<CodeEditor.Panel
showCopyButton
Expand All @@ -196,11 +251,37 @@ WithPanel.args = {
),
};

export const Minimal = Template.bind({});
Minimal.args = {
enableLineNumbers: false,
};

export const Loading = Template.bind({});
Loading.args = {
isLoading: true,
};

export const LoadingWithPanel = Template.bind({});
LoadingWithPanel.args = {
isLoading: true,
children: (
<CodeEditor.Panel
showCopyButton
showFormatButton
showSecondaryMenuButton
customSecondaryButtons={[
{
label: 'Custom Button',
onClick: () => {},
'aria-label': 'Custom Button',
glyph: <CloudIcon />,
},
]}
title={`index.${language}`}
/>
),
};

/**
* Syntax Highlighting Examples / Regressions
* These have been hardcoded for now, but we could potentially determine a good
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import { renderCodeEditor } from './CodeEditor.testUtils';
import { LanguageName } from './hooks';

// Enhanced MutationObserver mock for CodeMirror compatibility
global.MutationObserver = jest.fn().mockImplementation(() => ({
observe: jest.fn(),
unobserve: jest.fn(),
disconnect: jest.fn(),
takeRecords: jest.fn().mockReturnValue([]),
}));

// Mock ResizeObserver which is used by CodeMirror
global.ResizeObserver = jest.fn().mockImplementation(() => ({
observe: jest.fn(),
unobserve: jest.fn(),
disconnect: jest.fn(),
}));

// Mock IntersectionObserver which may be used by CodeMirror
global.IntersectionObserver = jest.fn().mockImplementation(() => ({
observe: jest.fn(),
unobserve: jest.fn(),
disconnect: jest.fn(),
root: null,
rootMargin: '',
thresholds: [],
}));

describe('packages/code-editor/CodeEditor imperative handle', () => {
test('getContents works', () => {
const { editor } = renderCodeEditor({
defaultValue: 'test',
});
expect(editor.getHandle().getContents()).toBe('test');
});

test('getEditorViewInstance works', () => {
const { editor } = renderCodeEditor({
defaultValue: 'console.log("hello")',
});
expect(editor.getHandle().getEditorViewInstance()).toBeDefined();
});

test('undo and redo works', async () => {
const initialContent = 'console.log("hello");';
const { editor } = renderCodeEditor({
defaultValue: initialContent,
});
const handle = editor.getHandle();
expect(handle.getContents()).toBe(initialContent);

// Make a change
editor.interactions.insertText('\nconsole.log("world");');
const content = handle.getContents();
expect(handle.getContents()).toBe(
'console.log("hello");\nconsole.log("world");',
);

// Undo the change
const undoResult = handle.undo();
expect(undoResult).toBe(true);
expect(handle.getContents()).toBe(initialContent);

// Redo the change
const redoResult = editor.interactions.redo();
expect(redoResult).toBe(true);
expect(handle.getContents()).toBe(content);
});

test('undo returns false when there is nothing to undo', async () => {
const { editor } = renderCodeEditor({
defaultValue: 'test',
});
const undoResult = editor.getHandle().undo();
expect(undoResult).toBe(false);
});

test('redo returns false when there is nothing to redo', async () => {
const { editor } = renderCodeEditor({
defaultValue: 'test',
});
const redoResult = editor.getHandle().redo();
expect(redoResult).toBe(false);
});

test('formatCode works', async () => {
const { editor } = renderCodeEditor({
language: LanguageName.javascript,
defaultValue: "console.log('hello')",
});
const handle = editor.getHandle();
await handle.formatCode();
expect(handle.getContents()).toBe("console.log('hello');\n");
});

test('isFormattingAvailable returns true when formatting is available', () => {
const { editor } = renderCodeEditor({
language: LanguageName.javascript,
defaultValue: "console.log('hello')",
});
const handle = editor.getHandle();
expect(handle.isFormattingAvailable).toBe(true);
});

test('isFormattingAvailable returns false when formatting is not available', () => {
const { editor } = renderCodeEditor({
defaultValue: 'plain text',
});
const handle = editor.getHandle();
expect(handle.isFormattingAvailable).toBe(false);
});
});
Loading
Loading