Skip to content

Commit f73bab7

Browse files
authored
LG-5446: CodeEditor QA Fixes (#3261)
* Vertically align panel buttons * Fix persistent copy button styles * More complete live example * Decrease tooltip delay * Add comment on decrease in tooltip speed * Fix gutter padding when line numbers disabled * Fix close chevron alignment * Remove fold placeholder color * Remove some default imports * Make Panel respect editor width props * Some more default imports * Disable undo/redo on readonly * Add change depth states (broken) * Fix re-init editor bug on prop change * Remove caret when readonly * Refactor editor initialization check to remove unnecessary dependencies * Update inert attribute handling in SearchPanel for better type compatibility * Add context menu interaction handling to prevent selection changes * Fix font size and dark mode * Enhance undo/redo functionality to focus editor and scroll cursor into view after actions * Add extension initialization tracking to CodeEditor component to prevent unstyled render * Refactor editor initialization to use requestAnimationFrame for better rendering control and optimize extension return with useMemo for performance * Update contact form header by removing emoji for a cleaner presentation * Dynamically calculate height of loader based on future content * Add additional configuration options to PanelTestContextConfig for enhanced testing capabilities * Add loading indicator integration to CodeEditor component, including styles and package configuration * Fix container width * Update gutter position style in useThemeExtension to improve shadow visibility * feat(code-editor): render top and bottom shadows * fix(code-editor): update selector for editor styles to improve specificity * feat(code-editor): add configurable top and bottom shadows to editor styles * refactor(code-editor): rename InnerEditor selector to Scroller for consistency * chore(code-editor): remove polished dependency and related imports from package.json and styles * feat(code-editor): implement scroll shadows for overflow indication in editor * feat(code-editor): integrate lodash for debounced scroll shadow updates * refactor(code-editor): remove lodash dependency and optimize scroll shadow handling * refactor(code-editor): remove unused styles from useThemeExtension for cleaner code * Fix inner height issue * feat(code-editor): add LoadingWithPanel story and adjust styles for panel integration * Fix panel tests * Remove Spinner * WIP: Fix tests * Remove unused logs * Refactor tests * All tests passing * Linting passed * refactor(code-editor): remove overflow shadow styles and update gutter background color * refactor(code-editor): remove unused theme prop from CodeEditor styles and component * Fix interaction tests by using preloaded modules to prevent loading * changeset * Misc comments and cleanup * copilot review fixes * Fix copy tooltip in darkmode * CR feedback * Use test utils to get panel * Update changeset to major release * CR feedback * Fix build * Fix darkMode again on prettify button
1 parent 888a37d commit f73bab7

31 files changed

+1574
-1335
lines changed

.changeset/bumpy-turtles-start.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
---
2+
'@leafygreen-ui/code-editor': major
3+
---
4+
5+
This release marks the V1.0.0 release of `@leafygreen-ui/code-editor`.
6+
7+
Fixes remaining known of bugs. These include:
8+
9+
- Adds gutter padding between the border and the caret when line numbers are turned off
10+
- Fixes icon button vertical alignment in the panel
11+
- Adds comprehensive story, that also include tooltips
12+
- Increases tooltip speed
13+
- Fixes collapsed chevron alignment
14+
- Darkens unfold icon in dark mode
15+
- Fixes widths. This includes making sure the panel and editor containers respect any set widths
16+
- Corrects the color of the copied icon checkmark
17+
- Disabled undo/redo when no undo/redo actions are available
18+
- Hides blinking cursor when editor is in read only mode
19+
- Allows for CTRL+click to pull up the context menu without losing the selection.
20+
- Insert the cursor in the location of the undone action when the undo button is pressed in the panel menu
21+
- Sets the loading height to be the same height as the future rendered editor
22+
- Persists editor when props change
23+
- Assures that font size and dark mode are respected in the panel
24+
- Enhances tests

packages/code-editor/src/CodeEditor.interactions.stories.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
CopyButtonAppearance,
1717
IndentUnits,
1818
} from './CodeEditor/CodeEditor.types';
19+
import { preLoadedModules } from './testing/preLoadedModules';
1920
import { CodeEditor, LanguageName } from '.';
2021

2122
const meta: StoryMetaType<typeof CodeEditor> = {
@@ -196,6 +197,7 @@ export const ErrorTooltipOnHover: StoryObj<{}> = {
196197
return (
197198
<CodeEditor
198199
defaultValue={'test\n'.repeat(5)}
200+
preLoadedModules={preLoadedModules}
199201
tooltips={[
200202
{
201203
line: 2,
@@ -227,6 +229,7 @@ export const HintTooltipOnHover: StoryObj<{}> = {
227229
return (
228230
<CodeEditor
229231
defaultValue={'test\n'.repeat(5)}
232+
preLoadedModules={preLoadedModules}
230233
tooltips={[
231234
{
232235
line: 2,
@@ -258,6 +261,7 @@ export const InfoTooltipOnHover: StoryObj<{}> = {
258261
return (
259262
<CodeEditor
260263
defaultValue={'test\n'.repeat(5)}
264+
preLoadedModules={preLoadedModules}
261265
tooltips={[
262266
{
263267
line: 2,
@@ -289,6 +293,7 @@ export const WarningTooltipOnHover: StoryObj<{}> = {
289293
return (
290294
<CodeEditor
291295
defaultValue={'test\n'.repeat(5)}
296+
preLoadedModules={preLoadedModules}
292297
tooltips={[
293298
{
294299
line: 2,
@@ -319,6 +324,7 @@ export const ContextMenuOnRightClick: StoryObj<{}> = {
319324
render: () => {
320325
return (
321326
<CodeEditor
327+
preLoadedModules={preLoadedModules}
322328
customContextMenuItems={[{ label: 'Custom Action', action: () => {} }]}
323329
/>
324330
);

packages/code-editor/src/CodeEditor.stories.tsx

Lines changed: 85 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,10 @@ import LeafyGreenProvider from '@leafygreen-ui/leafygreen-provider';
2525
import { Modal } from '@leafygreen-ui/modal';
2626
import { BaseFontSize } from '@leafygreen-ui/tokens';
2727

28-
import { CopyButtonAppearance } from './CodeEditor/CodeEditor.types';
28+
import {
29+
CodeEditorTooltipSeverity,
30+
CopyButtonAppearance,
31+
} from './CodeEditor/CodeEditor.types';
2932
import { LanguageName } from './CodeEditor/hooks/extensions/useLanguageExtension';
3033
import { IndentUnits } from './CodeEditor';
3134
import { ShortcutTable } from './ShortcutTable';
@@ -172,12 +175,64 @@ export default meta;
172175
const Template: StoryFn<typeof CodeEditor> = args => <CodeEditor {...args} />;
173176

174177
export const LiveExample = Template.bind({});
175-
176-
export const WithPanel = Template.bind({});
177178
const language = LanguageName.tsx;
178-
WithPanel.args = {
179+
LiveExample.args = {
179180
language,
180181
defaultValue: codeSnippets[language],
182+
tooltips: [
183+
{
184+
line: 4,
185+
column: 11,
186+
length: 11,
187+
messages: ['This is an error tooltip'],
188+
links: [
189+
{
190+
label: 'External Link',
191+
href: 'https://mongodb.com',
192+
},
193+
],
194+
severity: CodeEditorTooltipSeverity.Error,
195+
},
196+
{
197+
line: 10,
198+
column: 25,
199+
length: 8,
200+
messages: ['This is an info tooltip'],
201+
links: [
202+
{
203+
label: 'External Link',
204+
href: 'https://mongodb.com',
205+
},
206+
],
207+
severity: CodeEditorTooltipSeverity.Info,
208+
},
209+
{
210+
line: 12,
211+
column: 14,
212+
length: 7,
213+
messages: ['This is an hint tooltip'],
214+
links: [
215+
{
216+
label: 'External Link',
217+
href: 'https://mongodb.com',
218+
},
219+
],
220+
severity: CodeEditorTooltipSeverity.Hint,
221+
},
222+
{
223+
line: 28,
224+
column: 11,
225+
length: 11,
226+
messages: ['This is an warning tooltip'],
227+
links: [
228+
{
229+
label: 'External Link',
230+
href: 'https://mongodb.com',
231+
},
232+
],
233+
severity: CodeEditorTooltipSeverity.Warning,
234+
},
235+
],
181236
children: (
182237
<CodeEditor.Panel
183238
showCopyButton
@@ -196,11 +251,37 @@ WithPanel.args = {
196251
),
197252
};
198253

254+
export const Minimal = Template.bind({});
255+
Minimal.args = {
256+
enableLineNumbers: false,
257+
};
258+
199259
export const Loading = Template.bind({});
200260
Loading.args = {
201261
isLoading: true,
202262
};
203263

264+
export const LoadingWithPanel = Template.bind({});
265+
LoadingWithPanel.args = {
266+
isLoading: true,
267+
children: (
268+
<CodeEditor.Panel
269+
showCopyButton
270+
showFormatButton
271+
showSecondaryMenuButton
272+
customSecondaryButtons={[
273+
{
274+
label: 'Custom Button',
275+
onClick: () => {},
276+
'aria-label': 'Custom Button',
277+
glyph: <CloudIcon />,
278+
},
279+
]}
280+
title={`index.${language}`}
281+
/>
282+
),
283+
};
284+
204285
/**
205286
* Syntax Highlighting Examples / Regressions
206287
* These have been hardcoded for now, but we could potentially determine a good
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
import { renderCodeEditor } from './CodeEditor.testUtils';
2+
import { LanguageName } from './hooks';
3+
4+
// Enhanced MutationObserver mock for CodeMirror compatibility
5+
global.MutationObserver = jest.fn().mockImplementation(() => ({
6+
observe: jest.fn(),
7+
unobserve: jest.fn(),
8+
disconnect: jest.fn(),
9+
takeRecords: jest.fn().mockReturnValue([]),
10+
}));
11+
12+
// Mock ResizeObserver which is used by CodeMirror
13+
global.ResizeObserver = jest.fn().mockImplementation(() => ({
14+
observe: jest.fn(),
15+
unobserve: jest.fn(),
16+
disconnect: jest.fn(),
17+
}));
18+
19+
// Mock IntersectionObserver which may be used by CodeMirror
20+
global.IntersectionObserver = jest.fn().mockImplementation(() => ({
21+
observe: jest.fn(),
22+
unobserve: jest.fn(),
23+
disconnect: jest.fn(),
24+
root: null,
25+
rootMargin: '',
26+
thresholds: [],
27+
}));
28+
29+
describe('packages/code-editor/CodeEditor imperative handle', () => {
30+
test('getContents works', () => {
31+
const { editor } = renderCodeEditor({
32+
defaultValue: 'test',
33+
});
34+
expect(editor.getHandle().getContents()).toBe('test');
35+
});
36+
37+
test('getEditorViewInstance works', () => {
38+
const { editor } = renderCodeEditor({
39+
defaultValue: 'console.log("hello")',
40+
});
41+
expect(editor.getHandle().getEditorViewInstance()).toBeDefined();
42+
});
43+
44+
test('undo and redo works', async () => {
45+
const initialContent = 'console.log("hello");';
46+
const { editor } = renderCodeEditor({
47+
defaultValue: initialContent,
48+
});
49+
const handle = editor.getHandle();
50+
expect(handle.getContents()).toBe(initialContent);
51+
52+
// Make a change
53+
editor.interactions.insertText('\nconsole.log("world");');
54+
const content = handle.getContents();
55+
expect(handle.getContents()).toBe(
56+
'console.log("hello");\nconsole.log("world");',
57+
);
58+
59+
// Undo the change
60+
const undoResult = handle.undo();
61+
expect(undoResult).toBe(true);
62+
expect(handle.getContents()).toBe(initialContent);
63+
64+
// Redo the change
65+
const redoResult = editor.interactions.redo();
66+
expect(redoResult).toBe(true);
67+
expect(handle.getContents()).toBe(content);
68+
});
69+
70+
test('undo returns false when there is nothing to undo', async () => {
71+
const { editor } = renderCodeEditor({
72+
defaultValue: 'test',
73+
});
74+
const undoResult = editor.getHandle().undo();
75+
expect(undoResult).toBe(false);
76+
});
77+
78+
test('redo returns false when there is nothing to redo', async () => {
79+
const { editor } = renderCodeEditor({
80+
defaultValue: 'test',
81+
});
82+
const redoResult = editor.getHandle().redo();
83+
expect(redoResult).toBe(false);
84+
});
85+
86+
test('formatCode works', async () => {
87+
const { editor } = renderCodeEditor({
88+
language: LanguageName.javascript,
89+
defaultValue: "console.log('hello')",
90+
});
91+
const handle = editor.getHandle();
92+
await handle.formatCode();
93+
expect(handle.getContents()).toBe("console.log('hello');\n");
94+
});
95+
96+
test('isFormattingAvailable returns true when formatting is available', () => {
97+
const { editor } = renderCodeEditor({
98+
language: LanguageName.javascript,
99+
defaultValue: "console.log('hello')",
100+
});
101+
const handle = editor.getHandle();
102+
expect(handle.isFormattingAvailable).toBe(true);
103+
});
104+
105+
test('isFormattingAvailable returns false when formatting is not available', () => {
106+
const { editor } = renderCodeEditor({
107+
defaultValue: 'plain text',
108+
});
109+
const handle = editor.getHandle();
110+
expect(handle.isFormattingAvailable).toBe(false);
111+
});
112+
});

0 commit comments

Comments
 (0)