From 0989452dd314781d46ccfb79e41760ac2791fc56 Mon Sep 17 00:00:00 2001 From: Terrence Keane Date: Tue, 13 May 2025 09:26:48 -0400 Subject: [PATCH 01/31] feat(code-editor): initialize code editor package with README, configuration, and basic components --- packages/code-editor/README.md | 26 +++ packages/code-editor/package.json | 30 +++ .../code-editor/src/CodeEditor.stories.tsx | 20 ++ .../src/CodeEditor/CodeEditor.spec.tsx | 11 + .../src/CodeEditor/CodeEditor.styles.ts | 4 + .../code-editor/src/CodeEditor/CodeEditor.tsx | 24 +++ .../src/CodeEditor/CodeEditor.types.ts | 1 + packages/code-editor/src/CodeEditor/index.ts | 2 + packages/code-editor/src/index.ts | 1 + packages/code-editor/tsconfig.json | 23 +++ pnpm-lock.yaml | 189 ++++++++++++++++++ 11 files changed, 331 insertions(+) create mode 100644 packages/code-editor/README.md create mode 100644 packages/code-editor/package.json create mode 100644 packages/code-editor/src/CodeEditor.stories.tsx create mode 100644 packages/code-editor/src/CodeEditor/CodeEditor.spec.tsx create mode 100644 packages/code-editor/src/CodeEditor/CodeEditor.styles.ts create mode 100644 packages/code-editor/src/CodeEditor/CodeEditor.tsx create mode 100644 packages/code-editor/src/CodeEditor/CodeEditor.types.ts create mode 100644 packages/code-editor/src/CodeEditor/index.ts create mode 100644 packages/code-editor/src/index.ts create mode 100644 packages/code-editor/tsconfig.json diff --git a/packages/code-editor/README.md b/packages/code-editor/README.md new file mode 100644 index 0000000000..60f6912036 --- /dev/null +++ b/packages/code-editor/README.md @@ -0,0 +1,26 @@ + +# Code Editor + +![npm (scoped)](https://img.shields.io/npm/v/@leafygreen-ui/code-editor.svg) +#### [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 +``` + diff --git a/packages/code-editor/package.json b/packages/code-editor/package.json new file mode 100644 index 0000000000..e8a1dac885 --- /dev/null +++ b/packages/code-editor/package.json @@ -0,0 +1,30 @@ +{ + "name": "@leafygreen-ui/code-editor", + "version": "0.1.0", + "description": "LeafyGreen UI Kit Code Editor", + "main": "./dist/index.js", + "module": "./dist/esm/index.js", + "types": "./dist/index.d.ts", + "license": "Apache-2.0", + "scripts": { + "build": "lg build-package", + "tsc": "lg build-ts", + "docs": "lg build-tsdoc" + }, + "publishConfig": { + "access": "public" + }, + "dependencies": { + "@leafygreen-ui/emotion": "workspace:^", + "@leafygreen-ui/lib": "workspace:^", + "@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" + } +} diff --git a/packages/code-editor/src/CodeEditor.stories.tsx b/packages/code-editor/src/CodeEditor.stories.tsx new file mode 100644 index 0000000000..7eb48a5bfb --- /dev/null +++ b/packages/code-editor/src/CodeEditor.stories.tsx @@ -0,0 +1,20 @@ +import React from 'react'; +import { StoryFn } from '@storybook/react'; + +import { CodeEditor } from '.'; + +export default { + title: 'Components/CodeEditor', + component: CodeEditor, + decorators: [ + Story => ( +
+ +
+ ), + ], +}; + +const Template: StoryFn = () => ; + +export const Basic = Template.bind({}); diff --git a/packages/code-editor/src/CodeEditor/CodeEditor.spec.tsx b/packages/code-editor/src/CodeEditor/CodeEditor.spec.tsx new file mode 100644 index 0000000000..cc94549a73 --- /dev/null +++ b/packages/code-editor/src/CodeEditor/CodeEditor.spec.tsx @@ -0,0 +1,11 @@ + +import React from 'react'; +import { render } from '@testing-library/react'; + +import { CodeEditor } from '.'; + +describe('packages/code-editor', () => { + test('condition', () => { + + }) +}) diff --git a/packages/code-editor/src/CodeEditor/CodeEditor.styles.ts b/packages/code-editor/src/CodeEditor/CodeEditor.styles.ts new file mode 100644 index 0000000000..928608f58d --- /dev/null +++ b/packages/code-editor/src/CodeEditor/CodeEditor.styles.ts @@ -0,0 +1,4 @@ + +import { css } from '@leafygreen-ui/emotion'; + +export const baseStyles = css``; diff --git a/packages/code-editor/src/CodeEditor/CodeEditor.tsx b/packages/code-editor/src/CodeEditor/CodeEditor.tsx new file mode 100644 index 0000000000..1f6c2c9a1b --- /dev/null +++ b/packages/code-editor/src/CodeEditor/CodeEditor.tsx @@ -0,0 +1,24 @@ +import React from 'react'; +import CodeMirror from '@uiw/react-codemirror'; + +export function CodeEditor() { + const [value, setValue] = React.useState("console.log('hello world!');"); + const onChange = React.useCallback((val: string) => { + setValue(val); + }, []); + + return ( + + ); +} + +CodeEditor.displayName = 'CodeEditor'; diff --git a/packages/code-editor/src/CodeEditor/CodeEditor.types.ts b/packages/code-editor/src/CodeEditor/CodeEditor.types.ts new file mode 100644 index 0000000000..99f574361c --- /dev/null +++ b/packages/code-editor/src/CodeEditor/CodeEditor.types.ts @@ -0,0 +1 @@ +export interface CodeEditorProps {} \ No newline at end of file diff --git a/packages/code-editor/src/CodeEditor/index.ts b/packages/code-editor/src/CodeEditor/index.ts new file mode 100644 index 0000000000..27f0cc3fa6 --- /dev/null +++ b/packages/code-editor/src/CodeEditor/index.ts @@ -0,0 +1,2 @@ +export { CodeEditor } from './CodeEditor'; +// export { CodeEditorProps } from './CodeEditor.types'; diff --git a/packages/code-editor/src/index.ts b/packages/code-editor/src/index.ts new file mode 100644 index 0000000000..9f6648589e --- /dev/null +++ b/packages/code-editor/src/index.ts @@ -0,0 +1 @@ +export { CodeEditor } from './CodeEditor'; diff --git a/packages/code-editor/tsconfig.json b/packages/code-editor/tsconfig.json new file mode 100644 index 0000000000..fe8d59a9ea --- /dev/null +++ b/packages/code-editor/tsconfig.json @@ -0,0 +1,23 @@ +{ + "extends": "@lg-tools/build/config/package.tsconfig.json", + "compilerOptions": { + "declarationDir": "dist", + "outDir": "dist", + "rootDir": "src", + "baseUrl": ".", + "paths": { + "@leafygreen-ui/icon/dist/*": ["../icon/src/generated/*"], + "@leafygreen-ui/*": ["../*/src"] + } + }, + "include": ["src/**/*"], + "exclude": ["**/*.spec.*", "**/*.stories.*"], + "references": [ + { + "path": "../emotion" + }, + { + "path": "../lib" + } + ] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f216121eec..d8eb62579f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1181,6 +1181,18 @@ importers: specifier: ^4.2.2 version: 4.3.1 + packages/code-editor: + dependencies: + '@leafygreen-ui/emotion': + specifier: workspace:^ + version: link:../emotion + '@leafygreen-ui/lib': + specifier: workspace:^ + version: link:../lib + '@uiw/react-codemirror': + specifier: ^4.23.10 + version: 4.23.10(@babel/runtime@7.24.1)(@codemirror/autocomplete@6.18.6)(@codemirror/language@6.11.0)(@codemirror/lint@6.8.5)(@codemirror/search@6.5.10)(@codemirror/state@6.5.2)(@codemirror/theme-one-dark@6.1.2)(@codemirror/view@6.36.5)(codemirror@6.0.1)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + packages/combobox: dependencies: '@leafygreen-ui/checkbox': @@ -4919,6 +4931,30 @@ packages: '@changesets/write@0.2.3': resolution: {integrity: sha512-Dbamr7AIMvslKnNYsLFafaVORx4H0pvCA2MHqgtNCySMe1blImEyAEOzDmcgKAkgz4+uwoLz7demIrX+JBr/Xw==} + '@codemirror/autocomplete@6.18.6': + resolution: {integrity: sha512-PHHBXFomUs5DF+9tCOM/UoW6XQ4R44lLNNhRaW9PKPTU0D7lIjRg3ElxaJnTwsl/oHiR93WSXDBrekhoUGCPtg==} + + '@codemirror/commands@6.8.1': + resolution: {integrity: sha512-KlGVYufHMQzxbdQONiLyGQDUW0itrLZwq3CcY7xpv9ZLRHqzkBSoteocBHtMCoY7/Ci4xhzSrToIeLg7FxHuaw==} + + '@codemirror/language@6.11.0': + resolution: {integrity: sha512-A7+f++LodNNc1wGgoRDTt78cOwWm9KVezApgjOMp1W4hM0898nsqBXwF+sbePE7ZRcjN7Sa1Z5m2oN27XkmEjQ==} + + '@codemirror/lint@6.8.5': + resolution: {integrity: sha512-s3n3KisH7dx3vsoeGMxsbRAgKe4O1vbrnKBClm99PU0fWxmxsx5rR2PfqQgIt+2MMJBHbiJ5rfIdLYfB9NNvsA==} + + '@codemirror/search@6.5.10': + resolution: {integrity: sha512-RMdPdmsrUf53pb2VwflKGHEe1XVM07hI7vV2ntgw1dmqhimpatSJKva4VA9h4TLUDOD4EIF02201oZurpnEFsg==} + + '@codemirror/state@6.5.2': + resolution: {integrity: sha512-FVqsPqtPWKVVL3dPSxy8wEF/ymIEuVzF1PK3VbUgrxXpJUSHQWWZz4JMToquRxnkw+36LTamCZG2iua2Ptq0fA==} + + '@codemirror/theme-one-dark@6.1.2': + resolution: {integrity: sha512-F+sH0X16j/qFLMAfbciKTxVOwkdAS336b7AXTKOZhy8BR3eH/RelsnLgLFINrpST63mmN2OuwUt0W2ndUgYwUA==} + + '@codemirror/view@6.36.5': + resolution: {integrity: sha512-cd+FZEUlu3GQCYnguYm3EkhJ8KJVisqqUsCOKedBoAt/d9c76JUUap6U0UrpElln5k6VyrEOYliMuDAKIeDQLg==} + '@cspotcode/source-map-support@0.8.1': resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} engines: {node: '>=12'} @@ -5407,12 +5443,24 @@ packages: '@juggle/resize-observer@3.4.0': resolution: {integrity: sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA==} + '@lezer/common@1.2.3': + resolution: {integrity: sha512-w7ojc8ejBqr2REPsWxJjrMFsA/ysDCFICn8zEOR9mrqzOu2amhITYuLD8ag6XZf0CFXDrhKqw7+tW8cX66NaDA==} + + '@lezer/highlight@1.2.1': + resolution: {integrity: sha512-Z5duk4RN/3zuVO7Jq0pGLJ3qynpxUVsh7IbUbGj88+uV2ApSAn6kWg2au3iJb+0Zi7kKtqffIESgNcRXWZWmSA==} + + '@lezer/lr@1.4.2': + resolution: {integrity: sha512-pu0K1jCIdnQ12aWNaAVU5bzi7Bd1w54J3ECgANPmYLtQKP0HBj2cE/5coBD66MT10xbtIuUr7tg0Shbsvk0mDA==} + '@manypkg/find-root@1.1.0': resolution: {integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==} '@manypkg/get-packages@1.1.3': resolution: {integrity: sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A==} + '@marijn/find-cluster-break@1.0.2': + resolution: {integrity: sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==} + '@mdx-js/react@2.3.0': resolution: {integrity: sha512-zQH//gdOmuu7nt2oJR29vFhDv88oGPmVw6BggmrHeMI+xgEkp1B2dX9/bMBSYtK0dyLX/aOmesKS09g222K1/g==} peerDependencies: @@ -6460,6 +6508,28 @@ packages: resolution: {integrity: sha512-1Hm7THLpO6ww5QU6H/Qp+AusUUl+z/CAm3cNZZ0jQvon9yicgO7Rwd+/WWRpMKLYV6p2UvdbR27c86rzCPpreg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@uiw/codemirror-extensions-basic-setup@4.23.10': + resolution: {integrity: sha512-zpbmSeNs3OU/f/Eyd6brFnjsBUYwv2mFjWxlAsIRSwTlW+skIT60rQHFBSfsj/5UVSxSLWVeUYczN7AyXvgTGQ==} + peerDependencies: + '@codemirror/autocomplete': '>=6.0.0' + '@codemirror/commands': '>=6.0.0' + '@codemirror/language': '>=6.0.0' + '@codemirror/lint': '>=6.0.0' + '@codemirror/search': '>=6.0.0' + '@codemirror/state': '>=6.0.0' + '@codemirror/view': '>=6.0.0' + + '@uiw/react-codemirror@4.23.10': + resolution: {integrity: sha512-AbN4eVHOL4ckRuIXpZxkzEqL/1ChVA+BSdEnAKjIB68pLQvKsVoYbiFP8zkXkYc4+Fcgq5KbAjvYqdo4ewemKw==} + peerDependencies: + '@babel/runtime': '>=7.11.0' + '@codemirror/state': '>=6.0.0' + '@codemirror/theme-one-dark': '>=6.0.0' + '@codemirror/view': '>=6.0.0' + codemirror: '>=6.0.0' + react: '>=16.8.0' + react-dom: '>=16.8.0' + '@vitest/expect@2.0.5': resolution: {integrity: sha512-yHZtwuP7JZivj65Gxoi8upUN2OzHTi3zVfjwdpu2WrvCZPLwsJ2Ey5ILIPccoW23dd/zQBlJ4/dhi7DWNyXCpA==} @@ -7065,6 +7135,9 @@ packages: resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} + codemirror@6.0.1: + resolution: {integrity: sha512-J8j+nZ+CdWmIeFIGXEFbFPtpiYacFMDR8GlHK3IyHQJMCaVRfGx9NT+Hxivv1ckLWPvNdZqndbr/7lVhrf/Svg==} + collect-v8-coverage@1.0.2: resolution: {integrity: sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==} @@ -7169,6 +7242,9 @@ packages: create-require@1.1.1: resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} + crelt@1.0.6: + resolution: {integrity: sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==} + cross-spawn-async@2.2.5: resolution: {integrity: sha512-snteb3aVrxYYOX9e8BabYFK9WhCDhTlw1YQktfTthBogxri4/2r9U2nQc0ffY73ZAxezDc+U8gvHAeU1wy1ubQ==} deprecated: cross-spawn no longer requires a build toolchain, use it instead @@ -10457,6 +10533,9 @@ packages: peerDependencies: webpack: ^5.0.0 + style-mod@4.1.2: + resolution: {integrity: sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw==} + style-to-object@0.4.4: resolution: {integrity: sha512-HYNoHZa2GorYNyqiCaBgsxvcJIn7OHq6inEga+E6Ke3m5JkoqpQbnFssk4jwe+K7AhGa2fcha4wSOf1Kn01dMg==} @@ -10951,6 +11030,9 @@ packages: vm-browserify@1.1.2: resolution: {integrity: sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==} + w3c-keyname@2.2.8: + resolution: {integrity: sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==} + w3c-xmlserializer@4.0.0: resolution: {integrity: sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==} engines: {node: '>=14'} @@ -13077,6 +13159,58 @@ snapshots: human-id: 1.0.2 prettier: 2.8.8 + '@codemirror/autocomplete@6.18.6': + dependencies: + '@codemirror/language': 6.11.0 + '@codemirror/state': 6.5.2 + '@codemirror/view': 6.36.5 + '@lezer/common': 1.2.3 + + '@codemirror/commands@6.8.1': + dependencies: + '@codemirror/language': 6.11.0 + '@codemirror/state': 6.5.2 + '@codemirror/view': 6.36.5 + '@lezer/common': 1.2.3 + + '@codemirror/language@6.11.0': + dependencies: + '@codemirror/state': 6.5.2 + '@codemirror/view': 6.36.5 + '@lezer/common': 1.2.3 + '@lezer/highlight': 1.2.1 + '@lezer/lr': 1.4.2 + style-mod: 4.1.2 + + '@codemirror/lint@6.8.5': + dependencies: + '@codemirror/state': 6.5.2 + '@codemirror/view': 6.36.5 + crelt: 1.0.6 + + '@codemirror/search@6.5.10': + dependencies: + '@codemirror/state': 6.5.2 + '@codemirror/view': 6.36.5 + crelt: 1.0.6 + + '@codemirror/state@6.5.2': + dependencies: + '@marijn/find-cluster-break': 1.0.2 + + '@codemirror/theme-one-dark@6.1.2': + dependencies: + '@codemirror/language': 6.11.0 + '@codemirror/state': 6.5.2 + '@codemirror/view': 6.36.5 + '@lezer/highlight': 1.2.1 + + '@codemirror/view@6.36.5': + dependencies: + '@codemirror/state': 6.5.2 + style-mod: 4.1.2 + w3c-keyname: 2.2.8 + '@cspotcode/source-map-support@0.8.1': dependencies: '@jridgewell/trace-mapping': 0.3.9 @@ -13677,6 +13811,16 @@ snapshots: '@juggle/resize-observer@3.4.0': {} + '@lezer/common@1.2.3': {} + + '@lezer/highlight@1.2.1': + dependencies: + '@lezer/common': 1.2.3 + + '@lezer/lr@1.4.2': + dependencies: + '@lezer/common': 1.2.3 + '@manypkg/find-root@1.1.0': dependencies: '@babel/runtime': 7.24.1 @@ -13693,6 +13837,8 @@ snapshots: globby: 11.1.0 read-yaml-file: 1.1.0 + '@marijn/find-cluster-break@1.0.2': {} + '@mdx-js/react@2.3.0(react@18.2.0)': dependencies: '@types/mdx': 2.0.5 @@ -15034,6 +15180,33 @@ snapshots: '@typescript-eslint/types': 8.17.0 eslint-visitor-keys: 4.2.0 + '@uiw/codemirror-extensions-basic-setup@4.23.10(@codemirror/autocomplete@6.18.6)(@codemirror/commands@6.8.1)(@codemirror/language@6.11.0)(@codemirror/lint@6.8.5)(@codemirror/search@6.5.10)(@codemirror/state@6.5.2)(@codemirror/view@6.36.5)': + dependencies: + '@codemirror/autocomplete': 6.18.6 + '@codemirror/commands': 6.8.1 + '@codemirror/language': 6.11.0 + '@codemirror/lint': 6.8.5 + '@codemirror/search': 6.5.10 + '@codemirror/state': 6.5.2 + '@codemirror/view': 6.36.5 + + '@uiw/react-codemirror@4.23.10(@babel/runtime@7.24.1)(@codemirror/autocomplete@6.18.6)(@codemirror/language@6.11.0)(@codemirror/lint@6.8.5)(@codemirror/search@6.5.10)(@codemirror/state@6.5.2)(@codemirror/theme-one-dark@6.1.2)(@codemirror/view@6.36.5)(codemirror@6.0.1)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@babel/runtime': 7.24.1 + '@codemirror/commands': 6.8.1 + '@codemirror/state': 6.5.2 + '@codemirror/theme-one-dark': 6.1.2 + '@codemirror/view': 6.36.5 + '@uiw/codemirror-extensions-basic-setup': 4.23.10(@codemirror/autocomplete@6.18.6)(@codemirror/commands@6.8.1)(@codemirror/language@6.11.0)(@codemirror/lint@6.8.5)(@codemirror/search@6.5.10)(@codemirror/state@6.5.2)(@codemirror/view@6.36.5) + codemirror: 6.0.1 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + transitivePeerDependencies: + - '@codemirror/autocomplete' + - '@codemirror/language' + - '@codemirror/lint' + - '@codemirror/search' + '@vitest/expect@2.0.5': dependencies: '@vitest/spy': 2.0.5 @@ -15806,6 +15979,16 @@ snapshots: co@4.6.0: {} + codemirror@6.0.1: + dependencies: + '@codemirror/autocomplete': 6.18.6 + '@codemirror/commands': 6.8.1 + '@codemirror/language': 6.11.0 + '@codemirror/lint': 6.8.5 + '@codemirror/search': 6.5.10 + '@codemirror/state': 6.5.2 + '@codemirror/view': 6.36.5 + collect-v8-coverage@1.0.2: {} color-convert@1.9.3: @@ -15912,6 +16095,8 @@ snapshots: create-require@1.1.1: {} + crelt@1.0.6: {} + cross-spawn-async@2.2.5: dependencies: lru-cache: 4.1.5 @@ -20217,6 +20402,8 @@ snapshots: dependencies: webpack: 5.88.0(@swc/core@1.4.2(@swc/helpers@0.5.1))(esbuild@0.24.2) + style-mod@4.1.2: {} + style-to-object@0.4.4: dependencies: inline-style-parser: 0.1.1 @@ -20757,6 +20944,8 @@ snapshots: vm-browserify@1.1.2: {} + w3c-keyname@2.2.8: {} + w3c-xmlserializer@4.0.0: dependencies: xml-name-validator: 4.0.0 From f17150ab5492c6b9fb68b758bf66a7fd88bb18e8 Mon Sep 17 00:00:00 2001 From: Terrence Keane Date: Tue, 13 May 2025 10:50:45 -0400 Subject: [PATCH 02/31] feat(code-editor): enhance CodeEditor component with new props and dependencies --- packages/code-editor/package.json | 4 ++ .../code-editor/src/CodeEditor/CodeEditor.tsx | 70 +++++++++++++++++-- .../src/CodeEditor/CodeEditor.types.ts | 14 +++- packages/code-editor/src/CodeEditor/index.ts | 2 +- packages/code-editor/src/index.ts | 2 +- pnpm-lock.yaml | 23 ++++++ tools/install/src/ALL_PACKAGES.ts | 2 + 7 files changed, 107 insertions(+), 10 deletions(-) diff --git a/packages/code-editor/package.json b/packages/code-editor/package.json index e8a1dac885..3fdc74678e 100644 --- a/packages/code-editor/package.json +++ b/packages/code-editor/package.json @@ -15,8 +15,12 @@ "access": "public" }, "dependencies": { + "@codemirror/language": "^6.11.0", "@leafygreen-ui/emotion": "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", diff --git a/packages/code-editor/src/CodeEditor/CodeEditor.tsx b/packages/code-editor/src/CodeEditor/CodeEditor.tsx index 1f6c2c9a1b..531744bec1 100644 --- a/packages/code-editor/src/CodeEditor/CodeEditor.tsx +++ b/packages/code-editor/src/CodeEditor/CodeEditor.tsx @@ -1,22 +1,78 @@ -import React from 'react'; -import CodeMirror from '@uiw/react-codemirror'; +import React, { useCallback, useMemo, useRef, useState } from 'react'; +import { forceParsing } from '@codemirror/language'; +import { hyperLink } from '@uiw/codemirror-extensions-hyper-link'; +import CodeMirror, { + EditorView, + type Extension, + type ReactCodeMirrorRef, +} from '@uiw/react-codemirror'; -export function CodeEditor() { - const [value, setValue] = React.useState("console.log('hello world!');"); - const onChange = React.useCallback((val: string) => { +import { CodeEditorProps } from './CodeEditor.types'; + +export function CodeEditor({ + enableActiveLineHighlighting = true, + enableClickableUrls = true, + enableCodeFolding = true, + enableLineNumbers = true, + enableLineWrapping = true, + forceParsing: forceParsingProp = false, + placeholder, + readOnly = false, + value: valueProp, + ...rest +}: CodeEditorProps) { + const [value, setValue] = useState(valueProp || ''); + const editorRef = useRef(null); + + const onChange = useCallback((val: string) => { setValue(val); }, []); + const onCreateEditor = useCallback( + (editorView: EditorView) => { + if (forceParsingProp) { + const { state } = editorView; + + if (state.doc.length > 0) { + forceParsing(editorView, state.doc.length, 150); + } + } + }, + [forceParsingProp], + ); + + const extensions = useMemo(() => { + const extensions: Array = []; + + if (enableClickableUrls) { + extensions.push(hyperLink); + } + + if (enableLineWrapping) { + extensions.push(EditorView.lineWrapping); + } + + return extensions; + }, [enableClickableUrls, enableLineWrapping]); + return ( ); } diff --git a/packages/code-editor/src/CodeEditor/CodeEditor.types.ts b/packages/code-editor/src/CodeEditor/CodeEditor.types.ts index 99f574361c..dce4a5ddb4 100644 --- a/packages/code-editor/src/CodeEditor/CodeEditor.types.ts +++ b/packages/code-editor/src/CodeEditor/CodeEditor.types.ts @@ -1 +1,13 @@ -export interface CodeEditorProps {} \ No newline at end of file +import { DarkModeProps } from '@leafygreen-ui/lib'; + +export interface CodeEditorProps extends DarkModeProps { + enableActiveLineHighlighting?: boolean; + enableClickableUrls?: boolean; + enableCodeFolding?: boolean; + enableLineNumbers?: boolean; + enableLineWrapping?: boolean; + forceParsing?: boolean; + placeholder?: HTMLElement | string; + readOnly?: boolean; + value?: string; +} diff --git a/packages/code-editor/src/CodeEditor/index.ts b/packages/code-editor/src/CodeEditor/index.ts index 27f0cc3fa6..0638cb6ce7 100644 --- a/packages/code-editor/src/CodeEditor/index.ts +++ b/packages/code-editor/src/CodeEditor/index.ts @@ -1,2 +1,2 @@ export { CodeEditor } from './CodeEditor'; -// export { CodeEditorProps } from './CodeEditor.types'; +export { CodeEditorProps } from './CodeEditor.types'; diff --git a/packages/code-editor/src/index.ts b/packages/code-editor/src/index.ts index 9f6648589e..0f666f848e 100644 --- a/packages/code-editor/src/index.ts +++ b/packages/code-editor/src/index.ts @@ -1 +1 @@ -export { CodeEditor } from './CodeEditor'; +export { CodeEditor, type CodeEditorProps } from './CodeEditor'; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d8eb62579f..fad51c282d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1183,12 +1183,24 @@ importers: packages/code-editor: dependencies: + '@codemirror/language': + specifier: ^6.11.0 + version: 6.11.0 '@leafygreen-ui/emotion': specifier: workspace:^ version: link:../emotion + '@leafygreen-ui/leafygreen-provider': + specifier: workspace:^ + version: link:../leafygreen-provider '@leafygreen-ui/lib': specifier: workspace:^ version: link:../lib + '@leafygreen-ui/tokens': + specifier: workspace:^ + version: link:../tokens + '@uiw/codemirror-extensions-hyper-link': + specifier: ^4.23.12 + version: 4.23.12(@codemirror/state@6.5.2)(@codemirror/view@6.36.5) '@uiw/react-codemirror': specifier: ^4.23.10 version: 4.23.10(@babel/runtime@7.24.1)(@codemirror/autocomplete@6.18.6)(@codemirror/language@6.11.0)(@codemirror/lint@6.8.5)(@codemirror/search@6.5.10)(@codemirror/state@6.5.2)(@codemirror/theme-one-dark@6.1.2)(@codemirror/view@6.36.5)(codemirror@6.0.1)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) @@ -6519,6 +6531,12 @@ packages: '@codemirror/state': '>=6.0.0' '@codemirror/view': '>=6.0.0' + '@uiw/codemirror-extensions-hyper-link@4.23.12': + resolution: {integrity: sha512-PVknlqnZNXD9klgYgzCoeCV6yyr7wCrs61igXgavMn2U6MaOuTFsJW6i8PUQc/+8YRGnRzrWyTu5hp8ljqhiDg==} + peerDependencies: + '@codemirror/state': '>=6.0.0' + '@codemirror/view': '>=6.0.0' + '@uiw/react-codemirror@4.23.10': resolution: {integrity: sha512-AbN4eVHOL4ckRuIXpZxkzEqL/1ChVA+BSdEnAKjIB68pLQvKsVoYbiFP8zkXkYc4+Fcgq5KbAjvYqdo4ewemKw==} peerDependencies: @@ -15190,6 +15208,11 @@ snapshots: '@codemirror/state': 6.5.2 '@codemirror/view': 6.36.5 + '@uiw/codemirror-extensions-hyper-link@4.23.12(@codemirror/state@6.5.2)(@codemirror/view@6.36.5)': + dependencies: + '@codemirror/state': 6.5.2 + '@codemirror/view': 6.36.5 + '@uiw/react-codemirror@4.23.10(@babel/runtime@7.24.1)(@codemirror/autocomplete@6.18.6)(@codemirror/language@6.11.0)(@codemirror/lint@6.8.5)(@codemirror/search@6.5.10)(@codemirror/state@6.5.2)(@codemirror/theme-one-dark@6.1.2)(@codemirror/view@6.36.5)(codemirror@6.0.1)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': dependencies: '@babel/runtime': 7.24.1 diff --git a/tools/install/src/ALL_PACKAGES.ts b/tools/install/src/ALL_PACKAGES.ts index e0ab5f78e4..bd1d809ab3 100644 --- a/tools/install/src/ALL_PACKAGES.ts +++ b/tools/install/src/ALL_PACKAGES.ts @@ -13,6 +13,7 @@ export const ALL_PACKAGES = [ '@leafygreen-ui/checkbox', '@leafygreen-ui/chip', '@leafygreen-ui/code', + '@leafygreen-ui/code-editor', '@leafygreen-ui/combobox', '@leafygreen-ui/confirmation-modal', '@leafygreen-ui/copyable', @@ -92,6 +93,7 @@ export const ALL_PACKAGES = [ '@lg-tools/build', '@lg-tools/cli', '@lg-tools/codemods', + '@lg-tools/crawler', '@lg-tools/create', '@lg-tools/install', '@lg-tools/link', From 73d1ac52f8325b7184a2bf448a6c2f032daaf3c7 Mon Sep 17 00:00:00 2001 From: Terrence Keane Date: Tue, 13 May 2025 10:59:42 -0400 Subject: [PATCH 03/31] refactor(code-editor): change import statements to use 'type' for CodeEditorProps --- packages/code-editor/src/CodeEditor/CodeEditor.styles.ts | 4 ---- packages/code-editor/src/CodeEditor/CodeEditor.tsx | 2 +- packages/code-editor/src/CodeEditor/index.ts | 2 +- 3 files changed, 2 insertions(+), 6 deletions(-) delete mode 100644 packages/code-editor/src/CodeEditor/CodeEditor.styles.ts diff --git a/packages/code-editor/src/CodeEditor/CodeEditor.styles.ts b/packages/code-editor/src/CodeEditor/CodeEditor.styles.ts deleted file mode 100644 index 928608f58d..0000000000 --- a/packages/code-editor/src/CodeEditor/CodeEditor.styles.ts +++ /dev/null @@ -1,4 +0,0 @@ - -import { css } from '@leafygreen-ui/emotion'; - -export const baseStyles = css``; diff --git a/packages/code-editor/src/CodeEditor/CodeEditor.tsx b/packages/code-editor/src/CodeEditor/CodeEditor.tsx index 531744bec1..016a99c4a9 100644 --- a/packages/code-editor/src/CodeEditor/CodeEditor.tsx +++ b/packages/code-editor/src/CodeEditor/CodeEditor.tsx @@ -7,7 +7,7 @@ import CodeMirror, { type ReactCodeMirrorRef, } from '@uiw/react-codemirror'; -import { CodeEditorProps } from './CodeEditor.types'; +import { type CodeEditorProps } from './CodeEditor.types'; export function CodeEditor({ enableActiveLineHighlighting = true, diff --git a/packages/code-editor/src/CodeEditor/index.ts b/packages/code-editor/src/CodeEditor/index.ts index 0638cb6ce7..7bfc2e0125 100644 --- a/packages/code-editor/src/CodeEditor/index.ts +++ b/packages/code-editor/src/CodeEditor/index.ts @@ -1,2 +1,2 @@ export { CodeEditor } from './CodeEditor'; -export { CodeEditorProps } from './CodeEditor.types'; +export { type CodeEditorProps } from './CodeEditor.types'; From 5809ce3a19213455c1666325d37ac040e3b8c268 Mon Sep 17 00:00:00 2001 From: Terrence Keane Date: Tue, 13 May 2025 11:23:21 -0400 Subject: [PATCH 04/31] feat(code-editor): update CodeEditor stories with args and argTypes for better customization --- .../code-editor/src/CodeEditor.stories.tsx | 41 ++++++++++++++++++- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/packages/code-editor/src/CodeEditor.stories.tsx b/packages/code-editor/src/CodeEditor.stories.tsx index 7eb48a5bfb..8ddebba5d3 100644 --- a/packages/code-editor/src/CodeEditor.stories.tsx +++ b/packages/code-editor/src/CodeEditor.stories.tsx @@ -13,8 +13,45 @@ export default { ), ], + args: { + enableActiveLineHighlighting: true, + enableClickableUrls: true, + enableCodeFolding: true, + enableLineNumbers: true, + enableLineWrapping: true, + forceParsing: false, + placeholder: 'Type your code here...', + readOnly: false, + value: 'console.log("Hello, world!");', + }, + 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' }, + }, + value: { + control: { type: 'text' }, + }, + }, }; -const Template: StoryFn = () => ; +const Template: StoryFn = args => ; -export const Basic = Template.bind({}); +export const LiveExample = Template.bind({}); From ae315faad22852a4ac3403e7adff7a373a991645 Mon Sep 17 00:00:00 2001 From: Terrence Keane Date: Wed, 14 May 2025 15:03:05 -0400 Subject: [PATCH 05/31] Start test suite --- packages/code-editor/package.json | 1 + .../src/CodeEditor/CodeEditor.spec.tsx | 52 ++++++- .../code-editor/src/CodeEditor/CodeEditor.tsx | 143 ++++++++++-------- .../src/CodeEditor/CodeEditor.types.ts | 5 + pnpm-lock.yaml | 23 +++ 5 files changed, 154 insertions(+), 70 deletions(-) diff --git a/packages/code-editor/package.json b/packages/code-editor/package.json index 3fdc74678e..4fb6a97a76 100644 --- a/packages/code-editor/package.json +++ b/packages/code-editor/package.json @@ -17,6 +17,7 @@ "dependencies": { "@codemirror/language": "^6.11.0", "@leafygreen-ui/emotion": "workspace:^", + "@leafygreen-ui/hooks": "^8.4.1", "@leafygreen-ui/leafygreen-provider": "workspace:^", "@leafygreen-ui/lib": "workspace:^", "@leafygreen-ui/tokens": "workspace:^", diff --git a/packages/code-editor/src/CodeEditor/CodeEditor.spec.tsx b/packages/code-editor/src/CodeEditor/CodeEditor.spec.tsx index cc94549a73..5d1d2f36db 100644 --- a/packages/code-editor/src/CodeEditor/CodeEditor.spec.tsx +++ b/packages/code-editor/src/CodeEditor/CodeEditor.spec.tsx @@ -1,11 +1,53 @@ - import React from 'react'; import { render } from '@testing-library/react'; +import { EditorView } from '@uiw/react-codemirror'; + +import { CodeEditor, CodeEditorProps } from '.'; + +global.MutationObserver = jest.fn().mockImplementation(() => ({ + observe: jest.fn(), + unobserve: jest.fn(), + disconnect: jest.fn(), + takeRecords: jest.fn().mockReturnValue([]), +})); + +let editorView: EditorView; + +const selectorMap = { + foldGutter: '.cm-foldGutter', +}; + +function expectSelectorInDocument(selector: string, inDocument = true) { + const element = editorView.view.dom.querySelector(selector); + + if (inDocument) { + expect(element).toBeInTheDocument(); + } else { + expect(element).not.toBeInTheDocument(); + } +} + +function renderCodeEditor(props: Partial = {}) { + const { container } = render( + { + editorView = ref as EditorView; + }} + />, + ); -import { CodeEditor } from '.'; + return { container }; +} describe('packages/code-editor', () => { - test('condition', () => { + test('Fold gutter renders when enabled', () => { + renderCodeEditor({ enableCodeFolding: true }); + expectSelectorInDocument(selectorMap.foldGutter); + }); - }) -}) + test('Fold gutter does not renders when disabled', () => { + renderCodeEditor({ enableCodeFolding: false }); + expectSelectorInDocument(selectorMap.foldGutter, false); + }); +}); diff --git a/packages/code-editor/src/CodeEditor/CodeEditor.tsx b/packages/code-editor/src/CodeEditor/CodeEditor.tsx index 016a99c4a9..88c708bbd8 100644 --- a/packages/code-editor/src/CodeEditor/CodeEditor.tsx +++ b/packages/code-editor/src/CodeEditor/CodeEditor.tsx @@ -1,80 +1,93 @@ -import React, { useCallback, useMemo, useRef, useState } from 'react'; +import React, { + forwardRef, + useCallback, + useMemo, + useRef, + useState, +} from 'react'; import { forceParsing } from '@codemirror/language'; import { hyperLink } from '@uiw/codemirror-extensions-hyper-link'; -import CodeMirror, { - EditorView, - type Extension, - type ReactCodeMirrorRef, -} from '@uiw/react-codemirror'; +import CodeMirror, { EditorView } from '@uiw/react-codemirror'; + +import { useMergeRefs } from '@leafygreen-ui/hooks'; -import { type CodeEditorProps } from './CodeEditor.types'; +import { + type CodeEditorProps, + type CodeMirrorExtension, + type ReactCodeMirrorRef, +} from './CodeEditor.types'; -export function CodeEditor({ - enableActiveLineHighlighting = true, - enableClickableUrls = true, - enableCodeFolding = true, - enableLineNumbers = true, - enableLineWrapping = true, - forceParsing: forceParsingProp = false, - placeholder, - readOnly = false, - value: valueProp, - ...rest -}: CodeEditorProps) { - const [value, setValue] = useState(valueProp || ''); - const editorRef = useRef(null); +export const CodeEditor = forwardRef( + ( + { + enableActiveLineHighlighting = true, + enableClickableUrls = true, + enableCodeFolding = true, + enableLineNumbers = true, + enableLineWrapping = true, + forceParsing: forceParsingProp = false, + placeholder, + readOnly = false, + value: valueProp, + ...rest + }, + forwardedRef, + ) => { + const [value, setValue] = useState(valueProp || ''); + const editorRef = useRef(null); - const onChange = useCallback((val: string) => { - setValue(val); - }, []); + const onChange = useCallback((val: string) => { + setValue(val); + }, []); - const onCreateEditor = useCallback( - (editorView: EditorView) => { - if (forceParsingProp) { - const { state } = editorView; + const onCreateEditor = useCallback( + (editorView: EditorView) => { + if (forceParsingProp) { + const { state } = editorView; - if (state.doc.length > 0) { - forceParsing(editorView, state.doc.length, 150); + if (state.doc.length > 0) { + forceParsing(editorView, state.doc.length, 150); + } } - } - }, - [forceParsingProp], - ); + }, + [forceParsingProp], + ); - const extensions = useMemo(() => { - const extensions: Array = []; + const extensions = useMemo(() => { + const extensions: Array = []; - if (enableClickableUrls) { - extensions.push(hyperLink); - } + if (enableClickableUrls) { + extensions.push(hyperLink); + } - if (enableLineWrapping) { - extensions.push(EditorView.lineWrapping); - } + if (enableLineWrapping) { + extensions.push(EditorView.lineWrapping); + } - return extensions; - }, [enableClickableUrls, enableLineWrapping]); + return extensions; + }, [enableClickableUrls, enableLineWrapping]); - return ( - - ); -} + return ( + + ); + }, +); CodeEditor.displayName = 'CodeEditor'; diff --git a/packages/code-editor/src/CodeEditor/CodeEditor.types.ts b/packages/code-editor/src/CodeEditor/CodeEditor.types.ts index dce4a5ddb4..090ca8c52b 100644 --- a/packages/code-editor/src/CodeEditor/CodeEditor.types.ts +++ b/packages/code-editor/src/CodeEditor/CodeEditor.types.ts @@ -1,5 +1,10 @@ +import { type Extension } from '@uiw/react-codemirror'; + import { DarkModeProps } from '@leafygreen-ui/lib'; +export type CodeMirrorExtension = Extension; +export type { ReactCodeMirrorRef } from '@uiw/react-codemirror'; + export interface CodeEditorProps extends DarkModeProps { enableActiveLineHighlighting?: boolean; enableClickableUrls?: boolean; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index fad51c282d..6cd1152f7a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1189,6 +1189,9 @@ importers: '@leafygreen-ui/emotion': specifier: workspace:^ version: link:../emotion + '@leafygreen-ui/hooks': + specifier: ^8.4.1 + version: 8.4.1(react@18.2.0) '@leafygreen-ui/leafygreen-provider': specifier: workspace:^ version: link:../leafygreen-provider @@ -5455,6 +5458,14 @@ packages: '@juggle/resize-observer@3.4.0': resolution: {integrity: sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA==} + '@leafygreen-ui/hooks@8.4.1': + resolution: {integrity: sha512-WZ1p+HeYqqbWVDGTffkRLDE83K/GbjRDYW8jcSYgznba0NAkOkWT9n/+MJp83rd55iyPhBopOKx7270s/sIH4A==} + + '@leafygreen-ui/lib@14.2.0': + resolution: {integrity: sha512-JWHFwtWXY52YL1uNFpHWvRUWVl5tkXQzyq2uEMFHyZQKYUG0of9o5V+Zc6vAXdMvvAhE3DeYvDjTpaQbUk1PrQ==} + peerDependencies: + react: ^17.0.0 || ^18.0.0 + '@lezer/common@1.2.3': resolution: {integrity: sha512-w7ojc8ejBqr2REPsWxJjrMFsA/ysDCFICn8zEOR9mrqzOu2amhITYuLD8ag6XZf0CFXDrhKqw7+tW8cX66NaDA==} @@ -13829,6 +13840,18 @@ snapshots: '@juggle/resize-observer@3.4.0': {} + '@leafygreen-ui/hooks@8.4.1(react@18.2.0)': + dependencies: + '@leafygreen-ui/lib': 14.2.0(react@18.2.0) + lodash: 4.17.21 + transitivePeerDependencies: + - react + + '@leafygreen-ui/lib@14.2.0(react@18.2.0)': + dependencies: + lodash: 4.17.21 + react: 18.2.0 + '@lezer/common@1.2.3': {} '@lezer/highlight@1.2.1': From 815fff18ed5336d5c4d25244b8af7db1ac2691ac Mon Sep 17 00:00:00 2001 From: Terrence Keane Date: Wed, 14 May 2025 15:06:41 -0400 Subject: [PATCH 06/31] Fix deps --- packages/code-editor/package.json | 2 +- .../src/CodeEditor/CodeEditor.spec.tsx | 1 + packages/code-editor/tsconfig.json | 3 +++ pnpm-lock.yaml | 24 ++----------------- 4 files changed, 7 insertions(+), 23 deletions(-) diff --git a/packages/code-editor/package.json b/packages/code-editor/package.json index 4fb6a97a76..224efd73d1 100644 --- a/packages/code-editor/package.json +++ b/packages/code-editor/package.json @@ -17,7 +17,7 @@ "dependencies": { "@codemirror/language": "^6.11.0", "@leafygreen-ui/emotion": "workspace:^", - "@leafygreen-ui/hooks": "^8.4.1", + "@leafygreen-ui/hooks": "workspace:^", "@leafygreen-ui/leafygreen-provider": "workspace:^", "@leafygreen-ui/lib": "workspace:^", "@leafygreen-ui/tokens": "workspace:^", diff --git a/packages/code-editor/src/CodeEditor/CodeEditor.spec.tsx b/packages/code-editor/src/CodeEditor/CodeEditor.spec.tsx index 5d1d2f36db..df771f5c53 100644 --- a/packages/code-editor/src/CodeEditor/CodeEditor.spec.tsx +++ b/packages/code-editor/src/CodeEditor/CodeEditor.spec.tsx @@ -18,6 +18,7 @@ const selectorMap = { }; function expectSelectorInDocument(selector: string, inDocument = true) { + // TODO: wait for editor view const element = editorView.view.dom.querySelector(selector); if (inDocument) { diff --git a/packages/code-editor/tsconfig.json b/packages/code-editor/tsconfig.json index fe8d59a9ea..4f743d4f83 100644 --- a/packages/code-editor/tsconfig.json +++ b/packages/code-editor/tsconfig.json @@ -16,6 +16,9 @@ { "path": "../emotion" }, + { + "path": "../hooks" + }, { "path": "../lib" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6cd1152f7a..5376c4bb91 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1190,8 +1190,8 @@ importers: specifier: workspace:^ version: link:../emotion '@leafygreen-ui/hooks': - specifier: ^8.4.1 - version: 8.4.1(react@18.2.0) + specifier: workspace:^ + version: link:../hooks '@leafygreen-ui/leafygreen-provider': specifier: workspace:^ version: link:../leafygreen-provider @@ -5458,14 +5458,6 @@ packages: '@juggle/resize-observer@3.4.0': resolution: {integrity: sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA==} - '@leafygreen-ui/hooks@8.4.1': - resolution: {integrity: sha512-WZ1p+HeYqqbWVDGTffkRLDE83K/GbjRDYW8jcSYgznba0NAkOkWT9n/+MJp83rd55iyPhBopOKx7270s/sIH4A==} - - '@leafygreen-ui/lib@14.2.0': - resolution: {integrity: sha512-JWHFwtWXY52YL1uNFpHWvRUWVl5tkXQzyq2uEMFHyZQKYUG0of9o5V+Zc6vAXdMvvAhE3DeYvDjTpaQbUk1PrQ==} - peerDependencies: - react: ^17.0.0 || ^18.0.0 - '@lezer/common@1.2.3': resolution: {integrity: sha512-w7ojc8ejBqr2REPsWxJjrMFsA/ysDCFICn8zEOR9mrqzOu2amhITYuLD8ag6XZf0CFXDrhKqw7+tW8cX66NaDA==} @@ -13840,18 +13832,6 @@ snapshots: '@juggle/resize-observer@3.4.0': {} - '@leafygreen-ui/hooks@8.4.1(react@18.2.0)': - dependencies: - '@leafygreen-ui/lib': 14.2.0(react@18.2.0) - lodash: 4.17.21 - transitivePeerDependencies: - - react - - '@leafygreen-ui/lib@14.2.0(react@18.2.0)': - dependencies: - lodash: 4.17.21 - react: 18.2.0 - '@lezer/common@1.2.3': {} '@lezer/highlight@1.2.1': From 3d48e454f7eb6a21cc64dec8027fd9ea767c567c Mon Sep 17 00:00:00 2001 From: Terrence Keane Date: Thu, 15 May 2025 09:32:03 -0400 Subject: [PATCH 07/31] feat(code-editor): refactor extensions handling using Compartment for dynamic configuration --- .../code-editor/src/CodeEditor/CodeEditor.tsx | 23 +++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/packages/code-editor/src/CodeEditor/CodeEditor.tsx b/packages/code-editor/src/CodeEditor/CodeEditor.tsx index 88c708bbd8..70bed17012 100644 --- a/packages/code-editor/src/CodeEditor/CodeEditor.tsx +++ b/packages/code-editor/src/CodeEditor/CodeEditor.tsx @@ -7,7 +7,7 @@ import React, { } from 'react'; import { forceParsing } from '@codemirror/language'; import { hyperLink } from '@uiw/codemirror-extensions-hyper-link'; -import CodeMirror, { EditorView } from '@uiw/react-codemirror'; +import CodeMirror, { Compartment, EditorView } from '@uiw/react-codemirror'; import { useMergeRefs } from '@leafygreen-ui/hooks'; @@ -56,13 +56,22 @@ export const CodeEditor = forwardRef( const extensions = useMemo(() => { const extensions: Array = []; - if (enableClickableUrls) { - extensions.push(hyperLink); - } + /** + * CodeMirror state is immutable. Once configuration is set, the entire + * state would need to be updated to update one facet. Compartments allow + * us to dynamically change parts of the configuration after + * initialization, without needing to recreate the entire editor state. + * See https://codemirror.net/examples/config/#dynamic-configuration + */ + const hyperLinkCompartment = new Compartment(); + const lineWrappingCompartment = new Compartment(); - if (enableLineWrapping) { - extensions.push(EditorView.lineWrapping); - } + extensions.push( + hyperLinkCompartment.of(enableClickableUrls ? hyperLink : []), + lineWrappingCompartment.of( + enableLineWrapping ? EditorView.lineWrapping : [], + ), + ); return extensions; }, [enableClickableUrls, enableLineWrapping]); From 5759b82d145178457b5ffa5e3d5f03c5eee9c938 Mon Sep 17 00:00:00 2001 From: Terrence Keane Date: Thu, 15 May 2025 11:26:39 -0400 Subject: [PATCH 08/31] feat(tests): enhance CodeEditor tests with improved selector handling and new assertions --- .../src/CodeEditor/CodeEditor.spec.tsx | 154 +++++++++++++++--- 1 file changed, 134 insertions(+), 20 deletions(-) diff --git a/packages/code-editor/src/CodeEditor/CodeEditor.spec.tsx b/packages/code-editor/src/CodeEditor/CodeEditor.spec.tsx index df771f5c53..c66ab6a9ae 100644 --- a/packages/code-editor/src/CodeEditor/CodeEditor.spec.tsx +++ b/packages/code-editor/src/CodeEditor/CodeEditor.spec.tsx @@ -1,6 +1,6 @@ import React from 'react'; -import { render } from '@testing-library/react'; -import { EditorView } from '@uiw/react-codemirror'; +import { act, render } from '@testing-library/react'; +import { ChangeSpec, EditorView } from '@uiw/react-codemirror'; import { CodeEditor, CodeEditorProps } from '.'; @@ -13,27 +13,104 @@ global.MutationObserver = jest.fn().mockImplementation(() => ({ let editorView: EditorView; -const selectorMap = { - foldGutter: '.cm-foldGutter', -}; +const EditorSelectors = { + FoldGutter: '.cm-foldGutter', + GutterElement: '.cm-gutterElement', +} as const; +type EditorSelectors = (typeof EditorSelectors)[keyof typeof EditorSelectors]; -function expectSelectorInDocument(selector: string, inDocument = true) { - // TODO: wait for editor view - const element = editorView.view.dom.querySelector(selector); +const editor = { + getBySelector: (selector: EditorSelectors, options?: { text?: string }) => { + const elements = editorView.dom.querySelectorAll(selector); - if (inDocument) { - expect(element).toBeInTheDocument(); - } else { - expect(element).not.toBeInTheDocument(); - } -} + if (!elements || elements.length === 0) { + throw new Error(`Element with selector "${selector}" not found`); + } + + if (elements.length > 1) { + if (!options?.text) { + throw new Error(`Multiple elements with selector "${selector}" found`); + } + + const matchingElements = Array.from(elements).filter(element => { + return options.text && element.textContent?.includes(options.text); + }); + + if (!matchingElements || matchingElements.length === 0) { + throw new Error( + `Element with selector "${selector}" and text "${options.text}" not found`, + ); + } + + if (matchingElements.length > 1) { + throw new Error( + `Multiple elements with selector "${selector}" and text "${options.text}" found`, + ); + } + + return matchingElements[0]; + } + + return elements[0]; + }, + + queryBySelector: (selector: EditorSelectors, options?: { text?: string }) => { + const elements = editorView.dom.querySelectorAll(selector); + + if (!elements || elements.length === 0) { + return null; + } + + if (elements.length > 1) { + if (!options?.text) { + return null; + } + + const matchingElements = Array.from(elements).filter(element => { + return options.text && element.textContent?.includes(options.text); + }); + + if ( + !matchingElements || + matchingElements.length === 0 || + matchingElements.length > 1 + ) { + return null; + } + + return matchingElements[0]; + } + + return elements[0]; + }, -function renderCodeEditor(props: Partial = {}) { + insertText: (text: string, options?: { from?: number; to?: number }) => { + if (!editorView) { + throw new Error('Editor view is not initialized'); + } + + const changes: ChangeSpec = { insert: text, from: options?.from || 0 }; + + if (options?.to) { + changes.to = options.to; + } + + const transaction = editorView.state.update({ + changes, + }); + + editorView.dispatch(transaction); + }, +}; + +function renderEditor(props: Partial = {}) { const { container } = render( { - editorView = ref as EditorView; + if (ref && ref.view) { + editorView = ref.view; + } }} />, ); @@ -42,13 +119,50 @@ function renderCodeEditor(props: Partial = {}) { } describe('packages/code-editor', () => { + test('Renders value in editor', () => { + const { container } = renderEditor({ value: 'content' }); + expect(container).toHaveTextContent('content'); + }); + + test('Updates value on text insertion', async () => { + const onChange = jest.fn(); + const { container } = renderEditor({ + onChange, + }); + expect(container).not.toHaveTextContent('new content'); + act(() => { + editor.insertText('new content'); + }); + expect(container).toHaveTextContent('new content'); + }); + test('Fold gutter renders when enabled', () => { - renderCodeEditor({ enableCodeFolding: true }); - expectSelectorInDocument(selectorMap.foldGutter); + renderEditor({ enableCodeFolding: true }); + expect( + editor.getBySelector(EditorSelectors.FoldGutter), + ).toBeInTheDocument(); }); test('Fold gutter does not renders when disabled', () => { - renderCodeEditor({ enableCodeFolding: false }); - expectSelectorInDocument(selectorMap.foldGutter, false); + renderEditor({ enableCodeFolding: false }); + expect( + editor.queryBySelector(EditorSelectors.FoldGutter), + ).not.toBeInTheDocument(); + }); + + test('Line numbers render when enabled', () => { + renderEditor({ value: 'content', enableLineNumbers: true }); + expect( + editor.getBySelector(EditorSelectors.GutterElement, { + text: '1', + }), + ).toBeInTheDocument(); + }); + + test('Line numbers does not renders when disabled', () => { + renderEditor({ enableLineNumbers: false }); + expect( + editor.queryBySelector(EditorSelectors.GutterElement, { text: '1' }), + ).not.toBeInTheDocument(); }); }); From 67e2a5fe071253bce83ae3b1db1321b98342ccc4 Mon Sep 17 00:00:00 2001 From: Terrence Keane Date: Thu, 15 May 2025 12:06:42 -0400 Subject: [PATCH 09/31] feat(code-editor): rename 'value' prop to 'initialValue' for consistency and clarity --- .../code-editor/src/CodeEditor.stories.tsx | 4 +- .../src/CodeEditor/CodeEditor.spec.tsx | 4 +- .../code-editor/src/CodeEditor/CodeEditor.tsx | 24 +++++++---- .../src/CodeEditor/CodeEditor.types.ts | 41 +++++++++++++++++-- 4 files changed, 58 insertions(+), 15 deletions(-) diff --git a/packages/code-editor/src/CodeEditor.stories.tsx b/packages/code-editor/src/CodeEditor.stories.tsx index 8ddebba5d3..e327eb02fe 100644 --- a/packages/code-editor/src/CodeEditor.stories.tsx +++ b/packages/code-editor/src/CodeEditor.stories.tsx @@ -22,7 +22,7 @@ export default { forceParsing: false, placeholder: 'Type your code here...', readOnly: false, - value: 'console.log("Hello, world!");', + initialValue: 'console.log("Hello, world!");', }, argTypes: { enableActiveLineHighlighting: { @@ -46,7 +46,7 @@ export default { readOnly: { control: { type: 'boolean' }, }, - value: { + initialValue: { control: { type: 'text' }, }, }, diff --git a/packages/code-editor/src/CodeEditor/CodeEditor.spec.tsx b/packages/code-editor/src/CodeEditor/CodeEditor.spec.tsx index c66ab6a9ae..6c8c899491 100644 --- a/packages/code-editor/src/CodeEditor/CodeEditor.spec.tsx +++ b/packages/code-editor/src/CodeEditor/CodeEditor.spec.tsx @@ -120,7 +120,7 @@ function renderEditor(props: Partial = {}) { describe('packages/code-editor', () => { test('Renders value in editor', () => { - const { container } = renderEditor({ value: 'content' }); + const { container } = renderEditor({ intialValue: 'content' }); expect(container).toHaveTextContent('content'); }); @@ -151,7 +151,7 @@ describe('packages/code-editor', () => { }); test('Line numbers render when enabled', () => { - renderEditor({ value: 'content', enableLineNumbers: true }); + renderEditor({ intialValue: 'content', enableLineNumbers: true }); expect( editor.getBySelector(EditorSelectors.GutterElement, { text: '1', diff --git a/packages/code-editor/src/CodeEditor/CodeEditor.tsx b/packages/code-editor/src/CodeEditor/CodeEditor.tsx index 70bed17012..672a03d77f 100644 --- a/packages/code-editor/src/CodeEditor/CodeEditor.tsx +++ b/packages/code-editor/src/CodeEditor/CodeEditor.tsx @@ -14,10 +14,10 @@ import { useMergeRefs } from '@leafygreen-ui/hooks'; import { type CodeEditorProps, type CodeMirrorExtension, - type ReactCodeMirrorRef, + type CodeMirrorRef, } from './CodeEditor.types'; -export const CodeEditor = forwardRef( +export const CodeEditor = forwardRef( ( { enableActiveLineHighlighting = true, @@ -26,19 +26,27 @@ export const CodeEditor = forwardRef( enableLineNumbers = true, enableLineWrapping = true, forceParsing: forceParsingProp = false, + intialValue, + onChange: onChangeProp, placeholder, readOnly = false, - value: valueProp, ...rest }, forwardedRef, ) => { - const [value, setValue] = useState(valueProp || ''); - const editorRef = useRef(null); + const [value, setValue] = useState(intialValue || ''); + const editorRef = useRef(null); - const onChange = useCallback((val: string) => { - setValue(val); - }, []); + const onChange = useCallback( + (val: string) => { + setValue(val); + + if (onChangeProp) { + onChangeProp(val); + } + }, + [onChangeProp], + ); const onCreateEditor = useCallback( (editorView: EditorView) => { diff --git a/packages/code-editor/src/CodeEditor/CodeEditor.types.ts b/packages/code-editor/src/CodeEditor/CodeEditor.types.ts index 090ca8c52b..b1edc571e1 100644 --- a/packages/code-editor/src/CodeEditor/CodeEditor.types.ts +++ b/packages/code-editor/src/CodeEditor/CodeEditor.types.ts @@ -1,18 +1,53 @@ -import { type Extension } from '@uiw/react-codemirror'; +import type { Extension, ReactCodeMirrorRef } from '@uiw/react-codemirror'; import { DarkModeProps } from '@leafygreen-ui/lib'; export type CodeMirrorExtension = Extension; -export type { ReactCodeMirrorRef } from '@uiw/react-codemirror'; +export type CodeMirrorRef = ReactCodeMirrorRef; export interface CodeEditorProps extends DarkModeProps { + /** + * Enables highlighting of the active line. + */ enableActiveLineHighlighting?: boolean; + /** + * Renders URLs as clickable links in the editor. + */ enableClickableUrls?: boolean; + /** + * Enables code folding arrows in the gutter. + */ enableCodeFolding?: boolean; + /** + * Enables line numbers in the editor’s gutter. + */ enableLineNumbers?: boolean; + /** + * Enables line wrapping when the text exceeds the editor’s width. + */ enableLineWrapping?: boolean; + /** + * Forces parsing of complete document, even if it is not visible. USE WITH CAUTION! + * By default the editor only parses the visible part of the code on the + * screen. This significantly increases performance when there is a lot of + * code rendered. When enabled, this forces parsing of non-visible code. + * This should only be used in exceptional cases. + */ forceParsing?: boolean; + /** + * Initial value to render in the editor. + */ + intialValue?: string; + /** + * Callback that receives the updated editor value when changes are made. + */ + onChange?: (value: string) => void; + /** + * Value to display in the editor when it is empty. + */ placeholder?: HTMLElement | string; + /** + * Enables read only mode, making the contents uneditable. + */ readOnly?: boolean; - value?: string; } From 4a9abe4c4ea787c231b131bef71ca8ccab4d65c5 Mon Sep 17 00:00:00 2001 From: Terrence Keane Date: Thu, 15 May 2025 12:14:12 -0400 Subject: [PATCH 10/31] feat(code-editor): rename 'initialValue' prop to 'defaultValue' for consistency and update related tests --- packages/code-editor/src/CodeEditor.stories.tsx | 4 ++-- packages/code-editor/src/CodeEditor/CodeEditor.spec.tsx | 4 ++-- packages/code-editor/src/CodeEditor/CodeEditor.tsx | 4 ++-- packages/code-editor/src/CodeEditor/CodeEditor.types.ts | 8 ++++---- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/code-editor/src/CodeEditor.stories.tsx b/packages/code-editor/src/CodeEditor.stories.tsx index e327eb02fe..adae9d9793 100644 --- a/packages/code-editor/src/CodeEditor.stories.tsx +++ b/packages/code-editor/src/CodeEditor.stories.tsx @@ -19,10 +19,10 @@ export default { enableCodeFolding: true, enableLineNumbers: true, enableLineWrapping: true, + defaultValue: '', forceParsing: false, placeholder: 'Type your code here...', readOnly: false, - initialValue: 'console.log("Hello, world!");', }, argTypes: { enableActiveLineHighlighting: { @@ -46,7 +46,7 @@ export default { readOnly: { control: { type: 'boolean' }, }, - initialValue: { + defaultValue: { control: { type: 'text' }, }, }, diff --git a/packages/code-editor/src/CodeEditor/CodeEditor.spec.tsx b/packages/code-editor/src/CodeEditor/CodeEditor.spec.tsx index 6c8c899491..b545bd9201 100644 --- a/packages/code-editor/src/CodeEditor/CodeEditor.spec.tsx +++ b/packages/code-editor/src/CodeEditor/CodeEditor.spec.tsx @@ -120,7 +120,7 @@ function renderEditor(props: Partial = {}) { describe('packages/code-editor', () => { test('Renders value in editor', () => { - const { container } = renderEditor({ intialValue: 'content' }); + const { container } = renderEditor({ defaultValue: 'content' }); expect(container).toHaveTextContent('content'); }); @@ -151,7 +151,7 @@ describe('packages/code-editor', () => { }); test('Line numbers render when enabled', () => { - renderEditor({ intialValue: 'content', enableLineNumbers: true }); + renderEditor({ defaultValue: 'content', enableLineNumbers: true }); expect( editor.getBySelector(EditorSelectors.GutterElement, { text: '1', diff --git a/packages/code-editor/src/CodeEditor/CodeEditor.tsx b/packages/code-editor/src/CodeEditor/CodeEditor.tsx index 672a03d77f..6ad4d95ac8 100644 --- a/packages/code-editor/src/CodeEditor/CodeEditor.tsx +++ b/packages/code-editor/src/CodeEditor/CodeEditor.tsx @@ -20,13 +20,13 @@ import { export const CodeEditor = forwardRef( ( { + defaultValue, enableActiveLineHighlighting = true, enableClickableUrls = true, enableCodeFolding = true, enableLineNumbers = true, enableLineWrapping = true, forceParsing: forceParsingProp = false, - intialValue, onChange: onChangeProp, placeholder, readOnly = false, @@ -34,7 +34,7 @@ export const CodeEditor = forwardRef( }, forwardedRef, ) => { - const [value, setValue] = useState(intialValue || ''); + const [value, setValue] = useState(defaultValue || ''); const editorRef = useRef(null); const onChange = useCallback( diff --git a/packages/code-editor/src/CodeEditor/CodeEditor.types.ts b/packages/code-editor/src/CodeEditor/CodeEditor.types.ts index b1edc571e1..c7f1345ce7 100644 --- a/packages/code-editor/src/CodeEditor/CodeEditor.types.ts +++ b/packages/code-editor/src/CodeEditor/CodeEditor.types.ts @@ -6,6 +6,10 @@ export type CodeMirrorExtension = Extension; export type CodeMirrorRef = ReactCodeMirrorRef; export interface CodeEditorProps extends DarkModeProps { + /** + * Initial value to render in the editor. + */ + defaultValue?: string; /** * Enables highlighting of the active line. */ @@ -34,10 +38,6 @@ export interface CodeEditorProps extends DarkModeProps { * This should only be used in exceptional cases. */ forceParsing?: boolean; - /** - * Initial value to render in the editor. - */ - intialValue?: string; /** * Callback that receives the updated editor value when changes are made. */ From bee8b1d012cf83d98a00cf426b9b069a019f87b0 Mon Sep 17 00:00:00 2001 From: Terrence Keane Date: Thu, 15 May 2025 13:24:27 -0400 Subject: [PATCH 11/31] feat(code-editor): enable active line highlighting based on prop for improved editor usability --- .../src/CodeEditor/CodeEditor.spec.tsx | 145 ++++++++++++++---- .../code-editor/src/CodeEditor/CodeEditor.tsx | 1 + 2 files changed, 120 insertions(+), 26 deletions(-) diff --git a/packages/code-editor/src/CodeEditor/CodeEditor.spec.tsx b/packages/code-editor/src/CodeEditor/CodeEditor.spec.tsx index b545bd9201..ff66c671ce 100644 --- a/packages/code-editor/src/CodeEditor/CodeEditor.spec.tsx +++ b/packages/code-editor/src/CodeEditor/CodeEditor.spec.tsx @@ -1,6 +1,8 @@ import React from 'react'; -import { act, render } from '@testing-library/react'; -import { ChangeSpec, EditorView } from '@uiw/react-codemirror'; +// import { forceParsing } from '@codemirror/language'; +import { act, render, waitFor } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import { EditorView } from '@uiw/react-codemirror'; import { CodeEditor, CodeEditorProps } from '.'; @@ -14,8 +16,13 @@ global.MutationObserver = jest.fn().mockImplementation(() => ({ let editorView: EditorView; const EditorSelectors = { + ActiveLine: '.cm-activeLine', + ActiveLineGutter: '.cm-activeLineGutter', + Content: '.cm-content', FoldGutter: '.cm-foldGutter', GutterElement: '.cm-gutterElement', + HyperLink: '.cm-hyper-link-icon', + LineWrapping: '.cm-lineWrapping', } as const; type EditorSelectors = (typeof EditorSelectors)[keyof typeof EditorSelectors]; @@ -84,22 +91,18 @@ const editor = { return elements[0]; }, - insertText: (text: string, options?: { from?: number; to?: number }) => { - if (!editorView) { - throw new Error('Editor view is not initialized'); - } - - const changes: ChangeSpec = { insert: text, from: options?.from || 0 }; - - if (options?.to) { - changes.to = options.to; - } + isLineWrappingEnabled: () => { + return !!editor.queryBySelector(EditorSelectors.LineWrapping); + }, - const transaction = editorView.state.update({ - changes, - }); + isReadOnly: () => { + return editorView.state.readOnly; + }, - editorView.dispatch(transaction); + type: (text: string) => { + const content = editor.getBySelector(EditorSelectors.Content); + userEvent.click(content); + userEvent.type(content, text); }, }; @@ -118,22 +121,31 @@ function renderEditor(props: Partial = {}) { return { container }; } +// jest.mock('@codemirror/language', () => { +// const actualModule = jest.requireActual('@codemirror/language'); +// return { +// ...actualModule, +// forceParsing: jest.fn(), +// }; +// }); + describe('packages/code-editor', () => { - test('Renders value in editor', () => { + test('Renders default value in editor', () => { const { container } = renderEditor({ defaultValue: 'content' }); expect(container).toHaveTextContent('content'); }); - test('Updates value on text insertion', async () => { - const onChange = jest.fn(); - const { container } = renderEditor({ - onChange, - }); - expect(container).not.toHaveTextContent('new content'); + test('Updates value on when user types', async () => { + renderEditor(); + expect(editor.getBySelector(EditorSelectors.Content)).not.toHaveTextContent( + 'new content', + ); act(() => { - editor.insertText('new content'); + editor.type('new content'); }); - expect(container).toHaveTextContent('new content'); + expect(editor.getBySelector(EditorSelectors.Content)).toHaveTextContent( + 'new content', + ); }); test('Fold gutter renders when enabled', () => { @@ -159,10 +171,91 @@ describe('packages/code-editor', () => { ).toBeInTheDocument(); }); - test('Line numbers does not renders when disabled', () => { + test('Line numbers do not renders when disabled', () => { renderEditor({ enableLineNumbers: false }); expect( editor.queryBySelector(EditorSelectors.GutterElement, { text: '1' }), ).not.toBeInTheDocument(); }); + + test('Active line highlighting renders when enabled', () => { + renderEditor({ enableActiveLineHighlighting: true }); + expect( + editor.getBySelector(EditorSelectors.ActiveLine), + ).toBeInTheDocument(); + expect( + editor.getBySelector(EditorSelectors.ActiveLineGutter, { text: '1' }), + ).toBeInTheDocument(); + }); + + test('Active line highlighting does not render when disabled', () => { + renderEditor({ enableActiveLineHighlighting: false }); + expect( + editor.queryBySelector(EditorSelectors.ActiveLine), + ).not.toBeInTheDocument(); + expect( + editor.queryBySelector(EditorSelectors.ActiveLineGutter, { text: '1' }), + ).not.toBeInTheDocument(); + }); + + test('Clickable URLs render when enabled', () => { + renderEditor({ + defaultValue: 'https://mongodb.design', + enableClickableUrls: true, + }); + expect(editor.getBySelector(EditorSelectors.HyperLink)).toBeInTheDocument(); + }); + + test('Clickable URLs do not render when disable', () => { + renderEditor({ + defaultValue: 'https://mongodb.design', + enableClickableUrls: false, + }); + expect( + editor.queryBySelector(EditorSelectors.HyperLink), + ).not.toBeInTheDocument(); + }); + + test('Read-only set on editor state when enabled', () => { + renderEditor({ readOnly: true }); + expect(editor.isReadOnly()).toBe(true); + }); + + test('Read-only not set on editor state when disabled', () => { + renderEditor({ readOnly: false }); + expect(editor.isReadOnly()).toBe(false); + }); + + test('Line wrapping enabled when enabled', () => { + renderEditor({ enableLineWrapping: true }); + expect(editor.isLineWrappingEnabled()).toBe(true); + }); + + test('Line wrapping not enabled when disabled', () => { + renderEditor({ enableLineWrapping: false }); + expect(editor.isLineWrappingEnabled()).toBe(false); + }); + + test('Editor displays placeholder when empty', () => { + renderEditor({ placeholder: 'Type your code here...' }); + expect(editor.getBySelector(EditorSelectors.Content)).toHaveTextContent( + 'Type your code here...', + ); + }); + + test('Editor displays HTMLElement placeholder when empty', () => { + const placeholderElement = document.createElement('div'); + placeholderElement.textContent = 'Type your code here...'; + renderEditor({ placeholder: placeholderElement }); + expect(editor.getBySelector(EditorSelectors.Content)).toHaveTextContent( + 'Type your code here...', + ); + }); + + // test('the forceParsing() method is called when enabled', async () => { + // renderEditor({ forceParsing: true }); + // await waitFor(() => { + // expect(forceParsing as jest.Mock).toHaveBeenCalledTimes(1); + // }); + // }); }); diff --git a/packages/code-editor/src/CodeEditor/CodeEditor.tsx b/packages/code-editor/src/CodeEditor/CodeEditor.tsx index 6ad4d95ac8..094b1ecabe 100644 --- a/packages/code-editor/src/CodeEditor/CodeEditor.tsx +++ b/packages/code-editor/src/CodeEditor/CodeEditor.tsx @@ -97,6 +97,7 @@ export const CodeEditor = forwardRef( basicSetup={{ allowMultipleSelections: true, foldGutter: enableCodeFolding, + highlightActiveLine: enableActiveLineHighlighting, highlightActiveLineGutter: enableActiveLineHighlighting, lineNumbers: enableLineNumbers, }} From 2f21453f903cbec135f4308482822b97a4185e44 Mon Sep 17 00:00:00 2001 From: Terrence Keane Date: Thu, 15 May 2025 15:32:25 -0400 Subject: [PATCH 12/31] feat(tests): enable forceParsing test and update mock implementation for consistency --- .../src/CodeEditor/CodeEditor.spec.tsx | 28 +++++++++---------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/packages/code-editor/src/CodeEditor/CodeEditor.spec.tsx b/packages/code-editor/src/CodeEditor/CodeEditor.spec.tsx index ff66c671ce..2d008a9cb2 100644 --- a/packages/code-editor/src/CodeEditor/CodeEditor.spec.tsx +++ b/packages/code-editor/src/CodeEditor/CodeEditor.spec.tsx @@ -1,6 +1,6 @@ import React from 'react'; -// import { forceParsing } from '@codemirror/language'; -import { act, render, waitFor } from '@testing-library/react'; +import { forceParsing } from '@codemirror/language'; +import { act, render } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { EditorView } from '@uiw/react-codemirror'; @@ -121,13 +121,13 @@ function renderEditor(props: Partial = {}) { return { container }; } -// jest.mock('@codemirror/language', () => { -// const actualModule = jest.requireActual('@codemirror/language'); -// return { -// ...actualModule, -// forceParsing: jest.fn(), -// }; -// }); +jest.mock('@codemirror/language', () => { + const actualModule = jest.requireActual('@codemirror/language'); + return { + ...actualModule, + forceParsing: jest.fn(), + }; +}); describe('packages/code-editor', () => { test('Renders default value in editor', () => { @@ -252,10 +252,8 @@ describe('packages/code-editor', () => { ); }); - // test('the forceParsing() method is called when enabled', async () => { - // renderEditor({ forceParsing: true }); - // await waitFor(() => { - // expect(forceParsing as jest.Mock).toHaveBeenCalledTimes(1); - // }); - // }); + test('the forceParsing() method is called when enabled', async () => { + renderEditor({ forceParsing: true, defaultValue: 'content' }); + expect(forceParsing as jest.Mock).toHaveBeenCalledTimes(1); + }); }); From b82df4c0cd0023657c3dd1163bb12c921e40cae9 Mon Sep 17 00:00:00 2001 From: Terrence Keane Date: Thu, 15 May 2025 16:04:15 -0400 Subject: [PATCH 13/31] feat(tests): refactor CodeEditor tests to use new renderCodeEditor utility and update selectors --- .../src/CodeEditor/CodeEditor.spec.tsx | 200 ++++-------------- .../src/CodeEditor/CodeEditor.types.ts | 21 +- packages/code-editor/src/CodeEditor/index.ts | 10 +- .../src/CodeEditor/utils/testUtils.tsx | 126 +++++++++++ 4 files changed, 202 insertions(+), 155 deletions(-) create mode 100644 packages/code-editor/src/CodeEditor/utils/testUtils.tsx diff --git a/packages/code-editor/src/CodeEditor/CodeEditor.spec.tsx b/packages/code-editor/src/CodeEditor/CodeEditor.spec.tsx index 2d008a9cb2..eb8ae42685 100644 --- a/packages/code-editor/src/CodeEditor/CodeEditor.spec.tsx +++ b/packages/code-editor/src/CodeEditor/CodeEditor.spec.tsx @@ -1,125 +1,8 @@ -import React from 'react'; import { forceParsing } from '@codemirror/language'; -import { act, render } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; -import { EditorView } from '@uiw/react-codemirror'; +import { act } from '@testing-library/react'; -import { CodeEditor, CodeEditorProps } from '.'; - -global.MutationObserver = jest.fn().mockImplementation(() => ({ - observe: jest.fn(), - unobserve: jest.fn(), - disconnect: jest.fn(), - takeRecords: jest.fn().mockReturnValue([]), -})); - -let editorView: EditorView; - -const EditorSelectors = { - ActiveLine: '.cm-activeLine', - ActiveLineGutter: '.cm-activeLineGutter', - Content: '.cm-content', - FoldGutter: '.cm-foldGutter', - GutterElement: '.cm-gutterElement', - HyperLink: '.cm-hyper-link-icon', - LineWrapping: '.cm-lineWrapping', -} as const; -type EditorSelectors = (typeof EditorSelectors)[keyof typeof EditorSelectors]; - -const editor = { - getBySelector: (selector: EditorSelectors, options?: { text?: string }) => { - const elements = editorView.dom.querySelectorAll(selector); - - if (!elements || elements.length === 0) { - throw new Error(`Element with selector "${selector}" not found`); - } - - if (elements.length > 1) { - if (!options?.text) { - throw new Error(`Multiple elements with selector "${selector}" found`); - } - - const matchingElements = Array.from(elements).filter(element => { - return options.text && element.textContent?.includes(options.text); - }); - - if (!matchingElements || matchingElements.length === 0) { - throw new Error( - `Element with selector "${selector}" and text "${options.text}" not found`, - ); - } - - if (matchingElements.length > 1) { - throw new Error( - `Multiple elements with selector "${selector}" and text "${options.text}" found`, - ); - } - - return matchingElements[0]; - } - - return elements[0]; - }, - - queryBySelector: (selector: EditorSelectors, options?: { text?: string }) => { - const elements = editorView.dom.querySelectorAll(selector); - - if (!elements || elements.length === 0) { - return null; - } - - if (elements.length > 1) { - if (!options?.text) { - return null; - } - - const matchingElements = Array.from(elements).filter(element => { - return options.text && element.textContent?.includes(options.text); - }); - - if ( - !matchingElements || - matchingElements.length === 0 || - matchingElements.length > 1 - ) { - return null; - } - - return matchingElements[0]; - } - - return elements[0]; - }, - - isLineWrappingEnabled: () => { - return !!editor.queryBySelector(EditorSelectors.LineWrapping); - }, - - isReadOnly: () => { - return editorView.state.readOnly; - }, - - type: (text: string) => { - const content = editor.getBySelector(EditorSelectors.Content); - userEvent.click(content); - userEvent.type(content, text); - }, -}; - -function renderEditor(props: Partial = {}) { - const { container } = render( - { - if (ref && ref.view) { - editorView = ref.view; - } - }} - />, - ); - - return { container }; -} +import { renderCodeEditor } from './utils/testUtils'; +import { CodeEditorSelectors } from '.'; jest.mock('@codemirror/language', () => { const actualModule = jest.requireActual('@codemirror/language'); @@ -131,114 +14,125 @@ jest.mock('@codemirror/language', () => { describe('packages/code-editor', () => { test('Renders default value in editor', () => { - const { container } = renderEditor({ defaultValue: 'content' }); + const { container } = renderCodeEditor({ defaultValue: 'content' }); expect(container).toHaveTextContent('content'); }); test('Updates value on when user types', async () => { - renderEditor(); - expect(editor.getBySelector(EditorSelectors.Content)).not.toHaveTextContent( - 'new content', - ); + const { editor } = renderCodeEditor(); + expect( + editor.getBySelector(CodeEditorSelectors.Content), + ).not.toHaveTextContent('new content'); act(() => { editor.type('new content'); }); - expect(editor.getBySelector(EditorSelectors.Content)).toHaveTextContent( + expect(editor.getBySelector(CodeEditorSelectors.Content)).toHaveTextContent( 'new content', ); }); test('Fold gutter renders when enabled', () => { - renderEditor({ enableCodeFolding: true }); + const { editor } = renderCodeEditor({ enableCodeFolding: true }); expect( - editor.getBySelector(EditorSelectors.FoldGutter), + editor.getBySelector(CodeEditorSelectors.FoldGutter), ).toBeInTheDocument(); }); test('Fold gutter does not renders when disabled', () => { - renderEditor({ enableCodeFolding: false }); + const { editor } = renderCodeEditor({ enableCodeFolding: false }); expect( - editor.queryBySelector(EditorSelectors.FoldGutter), + editor.queryBySelector(CodeEditorSelectors.FoldGutter), ).not.toBeInTheDocument(); }); test('Line numbers render when enabled', () => { - renderEditor({ defaultValue: 'content', enableLineNumbers: true }); + const { editor } = renderCodeEditor({ + defaultValue: 'content', + enableLineNumbers: true, + }); expect( - editor.getBySelector(EditorSelectors.GutterElement, { + editor.getBySelector(CodeEditorSelectors.GutterElement, { text: '1', }), ).toBeInTheDocument(); }); test('Line numbers do not renders when disabled', () => { - renderEditor({ enableLineNumbers: false }); + const { editor } = renderCodeEditor({ enableLineNumbers: false }); expect( - editor.queryBySelector(EditorSelectors.GutterElement, { text: '1' }), + editor.queryBySelector(CodeEditorSelectors.GutterElement, { text: '1' }), ).not.toBeInTheDocument(); }); test('Active line highlighting renders when enabled', () => { - renderEditor({ enableActiveLineHighlighting: true }); + const { editor } = renderCodeEditor({ enableActiveLineHighlighting: true }); expect( - editor.getBySelector(EditorSelectors.ActiveLine), + editor.getBySelector(CodeEditorSelectors.ActiveLine), ).toBeInTheDocument(); expect( - editor.getBySelector(EditorSelectors.ActiveLineGutter, { text: '1' }), + editor.getBySelector(CodeEditorSelectors.ActiveLineGutter, { text: '1' }), ).toBeInTheDocument(); }); test('Active line highlighting does not render when disabled', () => { - renderEditor({ enableActiveLineHighlighting: false }); + const { editor } = renderCodeEditor({ + enableActiveLineHighlighting: false, + }); expect( - editor.queryBySelector(EditorSelectors.ActiveLine), + editor.queryBySelector(CodeEditorSelectors.ActiveLine), ).not.toBeInTheDocument(); expect( - editor.queryBySelector(EditorSelectors.ActiveLineGutter, { text: '1' }), + editor.queryBySelector(CodeEditorSelectors.ActiveLineGutter, { + text: '1', + }), ).not.toBeInTheDocument(); }); test('Clickable URLs render when enabled', () => { - renderEditor({ + const { editor } = renderCodeEditor({ defaultValue: 'https://mongodb.design', enableClickableUrls: true, }); - expect(editor.getBySelector(EditorSelectors.HyperLink)).toBeInTheDocument(); + expect( + editor.getBySelector(CodeEditorSelectors.HyperLink), + ).toBeInTheDocument(); }); test('Clickable URLs do not render when disable', () => { - renderEditor({ + const { editor } = renderCodeEditor({ defaultValue: 'https://mongodb.design', enableClickableUrls: false, }); expect( - editor.queryBySelector(EditorSelectors.HyperLink), + editor.queryBySelector(CodeEditorSelectors.HyperLink), ).not.toBeInTheDocument(); }); test('Read-only set on editor state when enabled', () => { - renderEditor({ readOnly: true }); + const { editor } = renderCodeEditor({ readOnly: true }); expect(editor.isReadOnly()).toBe(true); }); test('Read-only not set on editor state when disabled', () => { - renderEditor({ readOnly: false }); + const { editor } = renderCodeEditor({ readOnly: false }); expect(editor.isReadOnly()).toBe(false); }); test('Line wrapping enabled when enabled', () => { - renderEditor({ enableLineWrapping: true }); + const { editor } = renderCodeEditor({ enableLineWrapping: true }); expect(editor.isLineWrappingEnabled()).toBe(true); }); test('Line wrapping not enabled when disabled', () => { - renderEditor({ enableLineWrapping: false }); + const { editor } = renderCodeEditor({ enableLineWrapping: false }); expect(editor.isLineWrappingEnabled()).toBe(false); }); test('Editor displays placeholder when empty', () => { - renderEditor({ placeholder: 'Type your code here...' }); - expect(editor.getBySelector(EditorSelectors.Content)).toHaveTextContent( + const { editor } = renderCodeEditor({ + placeholder: 'Type your code here...', + }); + expect(editor.getBySelector(CodeEditorSelectors.Content)).toHaveTextContent( 'Type your code here...', ); }); @@ -246,14 +140,14 @@ describe('packages/code-editor', () => { test('Editor displays HTMLElement placeholder when empty', () => { const placeholderElement = document.createElement('div'); placeholderElement.textContent = 'Type your code here...'; - renderEditor({ placeholder: placeholderElement }); - expect(editor.getBySelector(EditorSelectors.Content)).toHaveTextContent( + const { editor } = renderCodeEditor({ placeholder: placeholderElement }); + expect(editor.getBySelector(CodeEditorSelectors.Content)).toHaveTextContent( 'Type your code here...', ); }); test('the forceParsing() method is called when enabled', async () => { - renderEditor({ forceParsing: true, defaultValue: 'content' }); + renderCodeEditor({ forceParsing: true, defaultValue: 'content' }); expect(forceParsing as jest.Mock).toHaveBeenCalledTimes(1); }); }); diff --git a/packages/code-editor/src/CodeEditor/CodeEditor.types.ts b/packages/code-editor/src/CodeEditor/CodeEditor.types.ts index c7f1345ce7..1c6c64c29c 100644 --- a/packages/code-editor/src/CodeEditor/CodeEditor.types.ts +++ b/packages/code-editor/src/CodeEditor/CodeEditor.types.ts @@ -1,9 +1,28 @@ -import type { Extension, ReactCodeMirrorRef } from '@uiw/react-codemirror'; +import type { + EditorState, + EditorView, + Extension, + ReactCodeMirrorRef, +} from '@uiw/react-codemirror'; import { DarkModeProps } from '@leafygreen-ui/lib'; export type CodeMirrorExtension = Extension; export type CodeMirrorRef = ReactCodeMirrorRef; +export type CodeMirrorState = EditorState; +export type CodeMirrorView = EditorView; + +export const CodeEditorSelectors = { + ActiveLine: '.cm-activeLine', + ActiveLineGutter: '.cm-activeLineGutter', + Content: '.cm-content', + FoldGutter: '.cm-foldGutter', + GutterElement: '.cm-gutterElement', + HyperLink: '.cm-hyper-link-icon', + LineWrapping: '.cm-lineWrapping', +} as const; +export type CodeEditorSelectors = + (typeof CodeEditorSelectors)[keyof typeof CodeEditorSelectors]; export interface CodeEditorProps extends DarkModeProps { /** diff --git a/packages/code-editor/src/CodeEditor/index.ts b/packages/code-editor/src/CodeEditor/index.ts index 7bfc2e0125..d1ffd5545d 100644 --- a/packages/code-editor/src/CodeEditor/index.ts +++ b/packages/code-editor/src/CodeEditor/index.ts @@ -1,2 +1,10 @@ export { CodeEditor } from './CodeEditor'; -export { type CodeEditorProps } from './CodeEditor.types'; +export { + type CodeEditorProps, + CodeEditorSelectors, + type CodeMirrorExtension, + type CodeMirrorRef, + type CodeMirrorState, + type CodeMirrorView, +} from './CodeEditor.types'; +export { renderCodeEditor } from './utils/testUtils'; diff --git a/packages/code-editor/src/CodeEditor/utils/testUtils.tsx b/packages/code-editor/src/CodeEditor/utils/testUtils.tsx new file mode 100644 index 0000000000..36f7561099 --- /dev/null +++ b/packages/code-editor/src/CodeEditor/utils/testUtils.tsx @@ -0,0 +1,126 @@ +import React from 'react'; +import { render } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; + +import { + CodeEditor, + CodeEditorProps, + CodeEditorSelectors, + CodeMirrorView, +} from '..'; + +global.MutationObserver = jest.fn().mockImplementation(() => ({ + observe: jest.fn(), + unobserve: jest.fn(), + disconnect: jest.fn(), + takeRecords: jest.fn().mockReturnValue([]), +})); + +let editorView: CodeMirrorView; + +function getBySelector( + selector: CodeEditorSelectors, + options?: { text?: string }, +) { + const elements = editorView.dom.querySelectorAll(selector); + + if (!elements || elements.length === 0) { + throw new Error(`Element with selector "${selector}" not found`); + } + + if (elements.length > 1) { + if (!options?.text) { + throw new Error(`Multiple elements with selector "${selector}" found`); + } + + const matchingElements = Array.from(elements).filter(element => { + return options.text && element.textContent?.includes(options.text); + }); + + if (!matchingElements || matchingElements.length === 0) { + throw new Error( + `Element with selector "${selector}" and text "${options.text}" not found`, + ); + } + + if (matchingElements.length > 1) { + throw new Error( + `Multiple elements with selector "${selector}" and text "${options.text}" found`, + ); + } + + return matchingElements[0]; + } + + return elements[0]; +} + +function queryBySelector( + selector: CodeEditorSelectors, + options?: { text?: string }, +) { + const elements = editorView.dom.querySelectorAll(selector); + + if (!elements || elements.length === 0) { + return null; + } + + if (elements.length > 1) { + if (!options?.text) { + return null; + } + + const matchingElements = Array.from(elements).filter(element => { + return options.text && element.textContent?.includes(options.text); + }); + + if ( + !matchingElements || + matchingElements.length === 0 || + matchingElements.length > 1 + ) { + return null; + } + + return matchingElements[0]; + } + + return elements[0]; +} + +function isReadOnly() { + return editorView.state.readOnly; +} + +function isLineWrappingEnabled() { + return !!queryBySelector(CodeEditorSelectors.LineWrapping); +} + +function type(text: string) { + const content = getBySelector(CodeEditorSelectors.Content); + userEvent.click(content); + userEvent.type(content, text); +} + +const editor = { + getBySelector, + queryBySelector, + isLineWrappingEnabled, + isReadOnly, + type, +}; + +export function renderCodeEditor(props: Partial = {}) { + const { container } = render( + { + if (ref && ref.view) { + editorView = ref.view; + } + }} + />, + ); + + return { container, editor }; +} From 0a846db2b23e095e7fd1d71944eacb8446816476 Mon Sep 17 00:00:00 2001 From: Terrence Keane Date: Thu, 15 May 2025 16:09:07 -0400 Subject: [PATCH 14/31] refactor(tests): move MutationObserver mock to testUtils for better organization --- packages/code-editor/src/CodeEditor/CodeEditor.spec.tsx | 7 +++++++ packages/code-editor/src/CodeEditor/utils/testUtils.tsx | 7 ------- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/code-editor/src/CodeEditor/CodeEditor.spec.tsx b/packages/code-editor/src/CodeEditor/CodeEditor.spec.tsx index eb8ae42685..72963ecba5 100644 --- a/packages/code-editor/src/CodeEditor/CodeEditor.spec.tsx +++ b/packages/code-editor/src/CodeEditor/CodeEditor.spec.tsx @@ -4,6 +4,13 @@ import { act } from '@testing-library/react'; import { renderCodeEditor } from './utils/testUtils'; import { CodeEditorSelectors } from '.'; +global.MutationObserver = jest.fn().mockImplementation(() => ({ + observe: jest.fn(), + unobserve: jest.fn(), + disconnect: jest.fn(), + takeRecords: jest.fn().mockReturnValue([]), +})); + jest.mock('@codemirror/language', () => { const actualModule = jest.requireActual('@codemirror/language'); return { diff --git a/packages/code-editor/src/CodeEditor/utils/testUtils.tsx b/packages/code-editor/src/CodeEditor/utils/testUtils.tsx index 36f7561099..b2632502bc 100644 --- a/packages/code-editor/src/CodeEditor/utils/testUtils.tsx +++ b/packages/code-editor/src/CodeEditor/utils/testUtils.tsx @@ -9,13 +9,6 @@ import { CodeMirrorView, } from '..'; -global.MutationObserver = jest.fn().mockImplementation(() => ({ - observe: jest.fn(), - unobserve: jest.fn(), - disconnect: jest.fn(), - takeRecords: jest.fn().mockReturnValue([]), -})); - let editorView: CodeMirrorView; function getBySelector( From de05360a5fc41f63270cd66d7947afdf8063611d Mon Sep 17 00:00:00 2001 From: Terrence Keane Date: Fri, 16 May 2025 10:03:52 -0400 Subject: [PATCH 15/31] feat(docs): update README to include new CodeEditor properties and test utilities --- packages/code-editor/README.md | 140 ++++++++++++++++++++++++++++++++- 1 file changed, 139 insertions(+), 1 deletion(-) diff --git a/packages/code-editor/README.md b/packages/code-editor/README.md index 60f6912036..cc2defb246 100644 --- a/packages/code-editor/README.md +++ b/packages/code-editor/README.md @@ -1,7 +1,7 @@ - # Code Editor ![npm (scoped)](https://img.shields.io/npm/v/@leafygreen-ui/code-editor.svg) + #### [View on MongoDB.design](https://www.mongodb.design/component/code-editor/live-example/) ## Installation @@ -24,3 +24,141 @@ yarn add @leafygreen-ui/code-editor npm install @leafygreen-ui/code-editor ``` +## Component + +### `` + +#### Example + +```ts +import { CodeEditor } from '@leafygreen-ui/code-editor'; + +; +``` + +#### 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!**_

Forces the parsing of the complete document, even parts not currently visible.

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. + +```ts +function renderEditor(props?: Partial): 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: + +```ts +{ + // 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. + userEvent: { + // Type into the editor + type(text: string): undefined; + } +} +``` + +### Examples + +#### Test selector has rendered + +```ts +test('Line numbers rendered', () => { + const { editor } = renderCodeEditor({ + defaultValue: 'content', + enableLineNumbers: true, + }); + expect( + editor.getBySelector(CodeEditorSelectors.GutterElement, { + text: '1', + }), + ).toBeInTheDocument(); +}); +``` + +#### Test selector has not rendered + +```ts +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 + +```ts +test('Updates value on when user types', () => { + const { editor } = renderCodeEditor(); + + expect( + editor.getBySelector(CodeEditorSelectors.Content), + ).not.toHaveTextContent('new content'); + + act(() => { + editor.userEvent.type('new content'); + }); + + expect(editor.getBySelector(CodeEditorSelectors.Content)).toHaveTextContent( + 'new content', + ); +}); +``` From 1deb79f6ea1c08cbc2325f32193f7b0a0dce49c4 Mon Sep 17 00:00:00 2001 From: Terrence Keane Date: Fri, 16 May 2025 10:06:45 -0400 Subject: [PATCH 16/31] refactor(tests): update typing test to use userEvent and export editor utilities --- packages/code-editor/src/CodeEditor/CodeEditor.spec.tsx | 4 ++-- packages/code-editor/src/CodeEditor/utils/testUtils.tsx | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/code-editor/src/CodeEditor/CodeEditor.spec.tsx b/packages/code-editor/src/CodeEditor/CodeEditor.spec.tsx index 72963ecba5..e7f870573e 100644 --- a/packages/code-editor/src/CodeEditor/CodeEditor.spec.tsx +++ b/packages/code-editor/src/CodeEditor/CodeEditor.spec.tsx @@ -25,13 +25,13 @@ describe('packages/code-editor', () => { expect(container).toHaveTextContent('content'); }); - test('Updates value on when user types', async () => { + test('Updates value on when user types', () => { const { editor } = renderCodeEditor(); expect( editor.getBySelector(CodeEditorSelectors.Content), ).not.toHaveTextContent('new content'); act(() => { - editor.type('new content'); + editor.userEvent.type('new content'); }); expect(editor.getBySelector(CodeEditorSelectors.Content)).toHaveTextContent( 'new content', diff --git a/packages/code-editor/src/CodeEditor/utils/testUtils.tsx b/packages/code-editor/src/CodeEditor/utils/testUtils.tsx index b2632502bc..a9b73636f7 100644 --- a/packages/code-editor/src/CodeEditor/utils/testUtils.tsx +++ b/packages/code-editor/src/CodeEditor/utils/testUtils.tsx @@ -95,12 +95,14 @@ function type(text: string) { userEvent.type(content, text); } -const editor = { +export const editor = { getBySelector, queryBySelector, isLineWrappingEnabled, isReadOnly, - type, + userEvent: { + type, + }, }; export function renderCodeEditor(props: Partial = {}) { From 3aa98de6cf9b5e94e8d6ba165fd643e7cae937d7 Mon Sep 17 00:00:00 2001 From: Terrence Keane Date: Fri, 16 May 2025 10:17:51 -0400 Subject: [PATCH 17/31] refactor(tests): update CodeEditor interactions and test utilities for improved clarity --- packages/code-editor/README.md | 8 +++--- .../src/CodeEditor/CodeEditor.spec.tsx | 2 +- .../src/CodeEditor/utils/testUtils.tsx | 26 ++++++++++++++----- 3 files changed, 24 insertions(+), 12 deletions(-) diff --git a/packages/code-editor/README.md b/packages/code-editor/README.md index cc2defb246..df6b174f41 100644 --- a/packages/code-editor/README.md +++ b/packages/code-editor/README.md @@ -106,9 +106,9 @@ Has the following interface: isLineWrappingEnabled(): boolean; // Group of actions that can be performed on the editor. - userEvent: { - // Type into the editor - type(text: string): undefined; + interactions: { + // Insert text into the editor + insertText(text: string, options?: { to?: number; from?: number; }): undefined; } } ``` @@ -154,7 +154,7 @@ test('Updates value on when user types', () => { ).not.toHaveTextContent('new content'); act(() => { - editor.userEvent.type('new content'); + editor.interactions.insertText('new content'); }); expect(editor.getBySelector(CodeEditorSelectors.Content)).toHaveTextContent( diff --git a/packages/code-editor/src/CodeEditor/CodeEditor.spec.tsx b/packages/code-editor/src/CodeEditor/CodeEditor.spec.tsx index e7f870573e..f9b1de4ff8 100644 --- a/packages/code-editor/src/CodeEditor/CodeEditor.spec.tsx +++ b/packages/code-editor/src/CodeEditor/CodeEditor.spec.tsx @@ -31,7 +31,7 @@ describe('packages/code-editor', () => { editor.getBySelector(CodeEditorSelectors.Content), ).not.toHaveTextContent('new content'); act(() => { - editor.userEvent.type('new content'); + editor.interactions.insertText('new content'); }); expect(editor.getBySelector(CodeEditorSelectors.Content)).toHaveTextContent( 'new content', diff --git a/packages/code-editor/src/CodeEditor/utils/testUtils.tsx b/packages/code-editor/src/CodeEditor/utils/testUtils.tsx index a9b73636f7..eb572e6958 100644 --- a/packages/code-editor/src/CodeEditor/utils/testUtils.tsx +++ b/packages/code-editor/src/CodeEditor/utils/testUtils.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { render } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; +import { ChangeSpec } from '@uiw/react-codemirror'; import { CodeEditor, @@ -89,10 +89,22 @@ function isLineWrappingEnabled() { return !!queryBySelector(CodeEditorSelectors.LineWrapping); } -function type(text: string) { - const content = getBySelector(CodeEditorSelectors.Content); - userEvent.click(content); - userEvent.type(content, text); +function insertText(text: string, options?: { from?: number; to?: number }) { + if (!editorView) { + throw new Error('Editor view is not initialized'); + } + + const changes: ChangeSpec = { insert: text, from: options?.from || 0 }; + + if (options?.to) { + changes.to = options.to; + } + + const transaction = editorView.state.update({ + changes, + }); + + editorView.dispatch(transaction); } export const editor = { @@ -100,8 +112,8 @@ export const editor = { queryBySelector, isLineWrappingEnabled, isReadOnly, - userEvent: { - type, + interactions: { + insertText, }, }; From 55b389a3225dd27c8bd55f60996973e2e81560bd Mon Sep 17 00:00:00 2001 From: Terrence Keane Date: Fri, 16 May 2025 10:18:10 -0400 Subject: [PATCH 18/31] test: update forceParsing method test to remove async and improve clarity --- packages/code-editor/src/CodeEditor/CodeEditor.spec.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/code-editor/src/CodeEditor/CodeEditor.spec.tsx b/packages/code-editor/src/CodeEditor/CodeEditor.spec.tsx index f9b1de4ff8..17ada83d3c 100644 --- a/packages/code-editor/src/CodeEditor/CodeEditor.spec.tsx +++ b/packages/code-editor/src/CodeEditor/CodeEditor.spec.tsx @@ -153,7 +153,7 @@ describe('packages/code-editor', () => { ); }); - test('the forceParsing() method is called when enabled', async () => { + test('the forceParsing() method is called when enabled', () => { renderCodeEditor({ forceParsing: true, defaultValue: 'content' }); expect(forceParsing as jest.Mock).toHaveBeenCalledTimes(1); }); From 647ad6081d1d99c43829148643bc1de96a93a23d Mon Sep 17 00:00:00 2001 From: Terrence Keane Date: Fri, 16 May 2025 10:26:02 -0400 Subject: [PATCH 19/31] changeset --- .changeset/fair-boats-cheer.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 .changeset/fair-boats-cheer.md diff --git a/.changeset/fair-boats-cheer.md b/.changeset/fair-boats-cheer.md new file mode 100644 index 0000000000..1124e838a9 --- /dev/null +++ b/.changeset/fair-boats-cheer.md @@ -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. From c26ce0f6bf93498d31c80c396115cb588a303e0f Mon Sep 17 00:00:00 2001 From: Terrence Keane Date: Fri, 16 May 2025 10:29:55 -0400 Subject: [PATCH 20/31] feat(docs): add CodeEditor package information to README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index b35d5a9877..966b548c51 100644 --- a/README.md +++ b/README.md @@ -79,6 +79,7 @@ import Button from '@leafygreen-ui/button'; | [@leafygreen-ui/checkbox](./packages/checkbox) | [![version](https://img.shields.io/npm/v/@leafygreen-ui/checkbox)](https://www.npmjs.com/package/@leafygreen-ui/checkbox) | ![downloads](https://img.shields.io/npm/dm/@leafygreen-ui/checkbox?color=white) | [Live Example](http://mongodb.design/component/checkbox/live-example) | | [@leafygreen-ui/chip](./packages/chip) | [![version](https://img.shields.io/npm/v/@leafygreen-ui/chip)](https://www.npmjs.com/package/@leafygreen-ui/chip) | ![downloads](https://img.shields.io/npm/dm/@leafygreen-ui/chip?color=white) | [Live Example](http://mongodb.design/component/chip/live-example) | | [@leafygreen-ui/code](./packages/code) | [![version](https://img.shields.io/npm/v/@leafygreen-ui/code)](https://www.npmjs.com/package/@leafygreen-ui/code) | ![downloads](https://img.shields.io/npm/dm/@leafygreen-ui/code?color=white) | [Live Example](http://mongodb.design/component/code/live-example) | +| [@leafygreen-ui/code-editor](./packages/code-editor) | [![version](https://img.shields.io/npm/v/@leafygreen-ui/code-editor)](https://www.npmjs.com/package/@leafygreen-ui/code-editor) | ![downloads](https://img.shields.io/npm/dm/@leafygreen-ui/code-editor?color=white) | [Live Example](http://mongodb.design/component/code-editor/live-example) | | [@leafygreen-ui/combobox](./packages/combobox) | [![version](https://img.shields.io/npm/v/@leafygreen-ui/combobox)](https://www.npmjs.com/package/@leafygreen-ui/combobox) | ![downloads](https://img.shields.io/npm/dm/@leafygreen-ui/combobox?color=white) | [Live Example](http://mongodb.design/component/combobox/live-example) | | [@leafygreen-ui/confirmation-modal](./packages/confirmation-modal) | [![version](https://img.shields.io/npm/v/@leafygreen-ui/confirmation-modal)](https://www.npmjs.com/package/@leafygreen-ui/confirmation-modal) | ![downloads](https://img.shields.io/npm/dm/@leafygreen-ui/confirmation-modal?color=white) | [Live Example](http://mongodb.design/component/confirmation-modal/live-example) | | [@leafygreen-ui/copyable](./packages/copyable) | [![version](https://img.shields.io/npm/v/@leafygreen-ui/copyable)](https://www.npmjs.com/package/@leafygreen-ui/copyable) | ![downloads](https://img.shields.io/npm/dm/@leafygreen-ui/copyable?color=white) | [Live Example](http://mongodb.design/component/copyable/live-example) | From d79e9a2312a71c4cb25d31f5353ad5ae2302c25b Mon Sep 17 00:00:00 2001 From: Terrence Keane Date: Fri, 16 May 2025 10:41:22 -0400 Subject: [PATCH 21/31] feat: expand exports in index.ts to include additional CodeEditor types and functions --- packages/code-editor/src/index.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/packages/code-editor/src/index.ts b/packages/code-editor/src/index.ts index 0f666f848e..321c4e8466 100644 --- a/packages/code-editor/src/index.ts +++ b/packages/code-editor/src/index.ts @@ -1 +1,10 @@ -export { CodeEditor, type CodeEditorProps } from './CodeEditor'; +export { + CodeEditor, + type CodeEditorProps, + CodeEditorSelectors, + type CodeMirrorExtension, + type CodeMirrorRef, + type CodeMirrorState, + type CodeMirrorView, + renderCodeEditor, +} from './CodeEditor'; From 747f20188f8b0db97619f2a721fcc2fdb7b9368e Mon Sep 17 00:00:00 2001 From: Terrence Keane Date: Fri, 16 May 2025 10:45:26 -0400 Subject: [PATCH 22/31] refactor(tests): move renderCodeEditor utility to CodeEditor.testUtils for better organization --- packages/code-editor/src/CodeEditor/CodeEditor.spec.tsx | 2 +- .../{utils/testUtils.tsx => CodeEditor.testUtils.tsx} | 2 +- packages/code-editor/src/CodeEditor/index.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) rename packages/code-editor/src/CodeEditor/{utils/testUtils.tsx => CodeEditor.testUtils.tsx} (99%) diff --git a/packages/code-editor/src/CodeEditor/CodeEditor.spec.tsx b/packages/code-editor/src/CodeEditor/CodeEditor.spec.tsx index 17ada83d3c..2551410667 100644 --- a/packages/code-editor/src/CodeEditor/CodeEditor.spec.tsx +++ b/packages/code-editor/src/CodeEditor/CodeEditor.spec.tsx @@ -1,7 +1,7 @@ import { forceParsing } from '@codemirror/language'; import { act } from '@testing-library/react'; -import { renderCodeEditor } from './utils/testUtils'; +import { renderCodeEditor } from './CodeEditor.testUtils'; import { CodeEditorSelectors } from '.'; global.MutationObserver = jest.fn().mockImplementation(() => ({ diff --git a/packages/code-editor/src/CodeEditor/utils/testUtils.tsx b/packages/code-editor/src/CodeEditor/CodeEditor.testUtils.tsx similarity index 99% rename from packages/code-editor/src/CodeEditor/utils/testUtils.tsx rename to packages/code-editor/src/CodeEditor/CodeEditor.testUtils.tsx index eb572e6958..cb785b165b 100644 --- a/packages/code-editor/src/CodeEditor/utils/testUtils.tsx +++ b/packages/code-editor/src/CodeEditor/CodeEditor.testUtils.tsx @@ -7,7 +7,7 @@ import { CodeEditorProps, CodeEditorSelectors, CodeMirrorView, -} from '..'; +} from '.'; let editorView: CodeMirrorView; diff --git a/packages/code-editor/src/CodeEditor/index.ts b/packages/code-editor/src/CodeEditor/index.ts index d1ffd5545d..ceeb397210 100644 --- a/packages/code-editor/src/CodeEditor/index.ts +++ b/packages/code-editor/src/CodeEditor/index.ts @@ -1,4 +1,5 @@ export { CodeEditor } from './CodeEditor'; +export { renderCodeEditor } from './CodeEditor.testUtils'; export { type CodeEditorProps, CodeEditorSelectors, @@ -7,4 +8,3 @@ export { type CodeMirrorState, type CodeMirrorView, } from './CodeEditor.types'; -export { renderCodeEditor } from './utils/testUtils'; From 5142f97831375e5dbf25ca91e4ec7908ff59604a Mon Sep 17 00:00:00 2001 From: Terrence Keane Date: Fri, 16 May 2025 10:49:24 -0400 Subject: [PATCH 23/31] feat(tests): add TestUtils for improved test rendering utilities --- packages/code-editor/README.md | 12 ++++++++++++ packages/code-editor/src/index.ts | 7 ++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/packages/code-editor/README.md b/packages/code-editor/README.md index df6b174f41..ba8a8337ab 100644 --- a/packages/code-editor/README.md +++ b/packages/code-editor/README.md @@ -118,6 +118,10 @@ Has the following interface: #### Test selector has rendered ```ts +import { TestUtils } from '@leafygreen-ui/code-editor'; + +const { renderEditor } = TestUtils; + test('Line numbers rendered', () => { const { editor } = renderCodeEditor({ defaultValue: 'content', @@ -134,6 +138,10 @@ test('Line numbers rendered', () => { #### Test selector has not rendered ```ts +import { TestUtils } from '@leafygreen-ui/code-editor'; + +const { renderEditor } = TestUtils; + test('Fold gutter does not render', () => { const { editor } = renderCodeEditor({ enableCodeFolding: false }); expect( @@ -146,6 +154,10 @@ test('Fold gutter does not render', () => { #### Test user interaction ```ts +import { TestUtils } from '@leafygreen-ui/code-editor'; + +const { renderEditor } = TestUtils; + test('Updates value on when user types', () => { const { editor } = renderCodeEditor(); diff --git a/packages/code-editor/src/index.ts b/packages/code-editor/src/index.ts index 321c4e8466..a532790cfe 100644 --- a/packages/code-editor/src/index.ts +++ b/packages/code-editor/src/index.ts @@ -1,3 +1,9 @@ +import { renderCodeEditor } from './CodeEditor'; + +export const TestUtils = { + renderCodeEditor, +}; + export { CodeEditor, type CodeEditorProps, @@ -6,5 +12,4 @@ export { type CodeMirrorRef, type CodeMirrorState, type CodeMirrorView, - renderCodeEditor, } from './CodeEditor'; From 1553697e745cccaf59df06f5b6880f1a8635ee51 Mon Sep 17 00:00:00 2001 From: Terrence Keane Date: Tue, 20 May 2025 12:17:35 -0400 Subject: [PATCH 24/31] fix: correct rendering description in tests and update README for CodeEditor example refactor: extract CodeMirror dimensions into constants for better maintainability docs: enhance CodeMirror types documentation with detailed descriptions chore: remove unused package from ALL_PACKAGES.ts --- packages/code-editor/README.md | 12 +++--- .../src/CodeEditor/CodeEditor.spec.tsx | 2 +- .../code-editor/src/CodeEditor/CodeEditor.tsx | 7 +++- .../src/CodeEditor/CodeEditor.types.ts | 38 +++++++++++++++++++ tools/install/src/ALL_PACKAGES.ts | 1 - 5 files changed, 50 insertions(+), 10 deletions(-) diff --git a/packages/code-editor/README.md b/packages/code-editor/README.md index ba8a8337ab..766324d7da 100644 --- a/packages/code-editor/README.md +++ b/packages/code-editor/README.md @@ -30,7 +30,7 @@ npm install @leafygreen-ui/code-editor #### Example -```ts +```tsx import { CodeEditor } from '@leafygreen-ui/code-editor'; ; @@ -70,7 +70,7 @@ import { CodeEditor } from '@leafygreen-ui/code-editor'; Renders the editor in a Jest test. -```ts +```tsx function renderEditor(props?: Partial): RenderResult; ``` @@ -86,7 +86,7 @@ Editor object used for querying and interacting with the rendered editor. Has the following interface: -```ts +```tsx { // Used to find single element in the editor. Will throw error if not found. getBySelector( @@ -117,7 +117,7 @@ Has the following interface: #### Test selector has rendered -```ts +```tsx import { TestUtils } from '@leafygreen-ui/code-editor'; const { renderEditor } = TestUtils; @@ -137,7 +137,7 @@ test('Line numbers rendered', () => { #### Test selector has not rendered -```ts +```tsx import { TestUtils } from '@leafygreen-ui/code-editor'; const { renderEditor } = TestUtils; @@ -153,7 +153,7 @@ test('Fold gutter does not render', () => { #### Test user interaction -```ts +```tsx import { TestUtils } from '@leafygreen-ui/code-editor'; const { renderEditor } = TestUtils; diff --git a/packages/code-editor/src/CodeEditor/CodeEditor.spec.tsx b/packages/code-editor/src/CodeEditor/CodeEditor.spec.tsx index 2551410667..7bdb4b6918 100644 --- a/packages/code-editor/src/CodeEditor/CodeEditor.spec.tsx +++ b/packages/code-editor/src/CodeEditor/CodeEditor.spec.tsx @@ -45,7 +45,7 @@ describe('packages/code-editor', () => { ).toBeInTheDocument(); }); - test('Fold gutter does not renders when disabled', () => { + test('Fold gutter does not render when disabled', () => { const { editor } = renderCodeEditor({ enableCodeFolding: false }); expect( editor.queryBySelector(CodeEditorSelectors.FoldGutter), diff --git a/packages/code-editor/src/CodeEditor/CodeEditor.tsx b/packages/code-editor/src/CodeEditor/CodeEditor.tsx index 094b1ecabe..24037fb097 100644 --- a/packages/code-editor/src/CodeEditor/CodeEditor.tsx +++ b/packages/code-editor/src/CodeEditor/CodeEditor.tsx @@ -17,6 +17,9 @@ import { type CodeMirrorRef, } from './CodeEditor.types'; +const CODE_MIRROR_HEIGHT = '200px'; +const CODE_MIRROR_WIDTH = '100%'; + export const CodeEditor = forwardRef( ( { @@ -87,8 +90,8 @@ export const CodeEditor = forwardRef( return ( void; + /** * Value to display in the editor when it is empty. */ placeholder?: HTMLElement | string; + /** * Enables read only mode, making the contents uneditable. */ diff --git a/tools/install/src/ALL_PACKAGES.ts b/tools/install/src/ALL_PACKAGES.ts index bd1d809ab3..f627dbd3ed 100644 --- a/tools/install/src/ALL_PACKAGES.ts +++ b/tools/install/src/ALL_PACKAGES.ts @@ -93,7 +93,6 @@ export const ALL_PACKAGES = [ '@lg-tools/build', '@lg-tools/cli', '@lg-tools/codemods', - '@lg-tools/crawler', '@lg-tools/create', '@lg-tools/install', '@lg-tools/link', From 40e15086ed69f2ddacf2635225536de6452e74b1 Mon Sep 17 00:00:00 2001 From: Terrence Keane Date: Tue, 20 May 2025 12:21:33 -0400 Subject: [PATCH 25/31] docs(code-editor): update README with CodeEditor example and default value --- packages/code-editor/README.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/code-editor/README.md b/packages/code-editor/README.md index 766324d7da..69c178cb6c 100644 --- a/packages/code-editor/README.md +++ b/packages/code-editor/README.md @@ -33,7 +33,14 @@ npm install @leafygreen-ui/code-editor ```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'));`; + +; ``` #### Properties From e2f6d50211e26d8fc2f0d31e5085155701467e83 Mon Sep 17 00:00:00 2001 From: Terrence Keane Date: Fri, 23 May 2025 12:59:25 -0400 Subject: [PATCH 26/31] chore(package): update node engine version to >= 18.20.8 (#2868) * chore(package): update node engine version to >= 18.22.0 * chore(package): update node engine version to >= 18.20.8 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3bbbea2924..8fab86bc05 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "license": "Apache-2.0", "private": true, "engines": { - "node": ">= 18.12.0", + "node": ">= 18.20.8", "pnpm": ">= 9.15.0" }, "scripts": { From e7165e056063fb97419861300eba9299621f8fbc Mon Sep 17 00:00:00 2001 From: Terrence Keane Date: Fri, 23 May 2025 13:40:08 -0400 Subject: [PATCH 27/31] fix(package): update main and types paths in package.json --- packages/code-editor/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/code-editor/package.json b/packages/code-editor/package.json index 224efd73d1..d9dfb92744 100644 --- a/packages/code-editor/package.json +++ b/packages/code-editor/package.json @@ -2,9 +2,9 @@ "name": "@leafygreen-ui/code-editor", "version": "0.1.0", "description": "LeafyGreen UI Kit Code Editor", - "main": "./dist/index.js", + "main": "./dist/umd/index.js", "module": "./dist/esm/index.js", - "types": "./dist/index.d.ts", + "types": "./dist/types/index.d.ts", "license": "Apache-2.0", "scripts": { "build": "lg build-package", From 4e651f40f9e271737bfc4a500227fb9349b60aa8 Mon Sep 17 00:00:00 2001 From: Terrence Keane Date: Fri, 23 May 2025 13:50:26 -0400 Subject: [PATCH 28/31] chore(package): add @lg-tools/build to devDependencies --- packages/code-editor/package.json | 3 + pnpm-lock.yaml | 120 +++++++++++++++--------------- 2 files changed, 65 insertions(+), 58 deletions(-) diff --git a/packages/code-editor/package.json b/packages/code-editor/package.json index d9dfb92744..d651c24495 100644 --- a/packages/code-editor/package.json +++ b/packages/code-editor/package.json @@ -31,5 +31,8 @@ }, "bugs": { "url": "https://jira.mongodb.org/projects/LG/summary" + }, + "devDependencies": { + "@lg-tools/build": "workspace:^" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5376c4bb91..a3dbd800c5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1207,6 +1207,10 @@ importers: '@uiw/react-codemirror': specifier: ^4.23.10 version: 4.23.10(@babel/runtime@7.24.1)(@codemirror/autocomplete@6.18.6)(@codemirror/language@6.11.0)(@codemirror/lint@6.8.5)(@codemirror/search@6.5.10)(@codemirror/state@6.5.2)(@codemirror/theme-one-dark@6.1.2)(@codemirror/view@6.36.5)(codemirror@6.0.1)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + devDependencies: + '@lg-tools/build': + specifier: workspace:^ + version: link:../../tools/build packages/combobox: dependencies: @@ -1822,7 +1826,7 @@ importers: version: 11.0.0 ts-node: specifier: ^10.9.2 - version: 10.9.2(@swc/core@1.4.2(@swc/helpers@0.5.1))(@types/node@20.17.12)(typescript@5.9.0-dev.20250521) + version: 10.9.2(@swc/core@1.4.2(@swc/helpers@0.5.1))(@types/node@20.17.12)(typescript@5.9.0-dev.20250523) xml2json: specifier: ^0.12.0 version: 0.12.0 @@ -3717,10 +3721,10 @@ importers: version: 8.6.12(storybook@8.6.12(prettier@3.5.3)) '@storybook/react': specifier: 8.6.12 - version: 8.6.12(@storybook/test@8.6.12(storybook@8.6.12(prettier@3.5.3)))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(storybook@8.6.12(prettier@3.5.3))(typescript@5.9.0-dev.20250521) + version: 8.6.12(@storybook/test@8.6.12(storybook@8.6.12(prettier@3.5.3)))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(storybook@8.6.12(prettier@3.5.3))(typescript@5.9.0-dev.20250523) '@storybook/react-webpack5': specifier: 8.6.12 - version: 8.6.12(@storybook/test@8.6.12(storybook@8.6.12(prettier@3.5.3)))(@swc/core@1.4.2(@swc/helpers@0.5.1))(esbuild@0.24.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(storybook@8.6.12(prettier@3.5.3))(typescript@5.9.0-dev.20250521) + version: 8.6.12(@storybook/test@8.6.12(storybook@8.6.12(prettier@3.5.3)))(@swc/core@1.4.2(@swc/helpers@0.5.1))(esbuild@0.24.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(storybook@8.6.12(prettier@3.5.3))(typescript@5.9.0-dev.20250523) '@storybook/test': specifier: 8.6.12 version: 8.6.12(storybook@8.6.12(prettier@3.5.3)) @@ -3729,7 +3733,7 @@ importers: version: 8.6.12(storybook@8.6.12(prettier@3.5.3)) '@svgr/webpack': specifier: 8.0.1 - version: 8.0.1(typescript@5.9.0-dev.20250521) + version: 8.0.1(typescript@5.9.0-dev.20250523) assert: specifier: ^2.1.0 version: 2.1.0 @@ -3771,7 +3775,7 @@ importers: version: 18.2.0 react-docgen-typescript: specifier: 2.2.2 - version: 2.2.2(typescript@5.9.0-dev.20250521) + version: 2.2.2(typescript@5.9.0-dev.20250523) react-dom: specifier: ^17.0.0 || ^18.0.0 version: 18.2.0(react@18.2.0) @@ -3922,7 +3926,7 @@ importers: version: 11.1.1 jest: specifier: 29.6.2 - version: 29.6.2(@types/node@20.4.8)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.4.2(@swc/helpers@0.5.1))(@types/node@20.4.8)(typescript@5.9.0-dev.20250521)) + version: 29.6.2(@types/node@20.4.8)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.4.2(@swc/helpers@0.5.1))(@types/node@20.4.8)(typescript@5.9.0-dev.20250523)) jest-axe: specifier: 8.0.0 version: 8.0.0 @@ -10857,8 +10861,8 @@ packages: engines: {node: '>=14.17'} hasBin: true - typescript@5.9.0-dev.20250521: - resolution: {integrity: sha512-wxrj9XlTy2D5iqAPWHWKgnXGkHVTgouDmMPhGrxqcX1z5STJKqDCXnebUFKM7xXahfEPtWFIyN7ouyg2G56e7g==} + typescript@5.9.0-dev.20250523: + resolution: {integrity: sha512-igDelnnCrlPHezUmn1QseV7BBn7bITue651a+X+JSKkBDwrvyy5vqd1G6dvM3vrc4PlInB5F6jQDp/23kg9ZEA==} engines: {node: '>=14.17'} hasBin: true @@ -13648,7 +13652,7 @@ snapshots: - ts-node optional: true - '@jest/core@29.6.2(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.4.2(@swc/helpers@0.5.1))(@types/node@20.4.8)(typescript@5.9.0-dev.20250521))': + '@jest/core@29.6.2(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.4.2(@swc/helpers@0.5.1))(@types/node@20.4.8)(typescript@5.9.0-dev.20250523))': dependencies: '@jest/console': 29.6.2 '@jest/reporters': 29.6.2 @@ -13662,7 +13666,7 @@ snapshots: exit: 0.1.2 graceful-fs: 4.2.11 jest-changed-files: 29.5.0 - jest-config: 29.6.2(@types/node@20.4.8)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.4.2(@swc/helpers@0.5.1))(@types/node@20.4.8)(typescript@5.9.0-dev.20250521)) + jest-config: 29.6.2(@types/node@20.4.8)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.4.2(@swc/helpers@0.5.1))(@types/node@20.4.8)(typescript@5.9.0-dev.20250523)) jest-haste-map: 29.7.0 jest-message-util: 29.7.0 jest-regex-util: 29.6.3 @@ -14143,7 +14147,7 @@ snapshots: react: 18.2.0 react-dom: 18.2.0(react@18.2.0) - '@storybook/builder-webpack5@8.6.12(@swc/core@1.4.2(@swc/helpers@0.5.1))(esbuild@0.24.2)(storybook@8.6.12(prettier@3.5.3))(typescript@5.9.0-dev.20250521)': + '@storybook/builder-webpack5@8.6.12(@swc/core@1.4.2(@swc/helpers@0.5.1))(esbuild@0.24.2)(storybook@8.6.12(prettier@3.5.3))(typescript@5.9.0-dev.20250523)': dependencies: '@storybook/core-webpack': 8.6.12(storybook@8.6.12(prettier@3.5.3)) '@types/semver': 7.5.0 @@ -14153,7 +14157,7 @@ snapshots: constants-browserify: 1.0.0 css-loader: 6.8.1(webpack@5.88.0(@swc/core@1.4.2(@swc/helpers@0.5.1))(esbuild@0.24.2)) es-module-lexer: 1.6.0 - fork-ts-checker-webpack-plugin: 8.0.0(typescript@5.9.0-dev.20250521)(webpack@5.88.0(@swc/core@1.4.2(@swc/helpers@0.5.1))(esbuild@0.24.2)) + fork-ts-checker-webpack-plugin: 8.0.0(typescript@5.9.0-dev.20250523)(webpack@5.88.0(@swc/core@1.4.2(@swc/helpers@0.5.1))(esbuild@0.24.2)) html-webpack-plugin: 5.5.3(webpack@5.88.0(@swc/core@1.4.2(@swc/helpers@0.5.1))(esbuild@0.24.2)) magic-string: 0.30.17 path-browserify: 1.0.1 @@ -14171,7 +14175,7 @@ snapshots: webpack-hot-middleware: 2.25.4 webpack-virtual-modules: 0.6.2 optionalDependencies: - typescript: 5.9.0-dev.20250521 + typescript: 5.9.0-dev.20250523 transitivePeerDependencies: - '@swc/core' - esbuild @@ -14289,11 +14293,11 @@ snapshots: dependencies: storybook: 8.6.12(prettier@3.5.3) - '@storybook/preset-react-webpack@8.6.12(@storybook/test@8.6.12(storybook@8.6.12(prettier@3.5.3)))(@swc/core@1.4.2(@swc/helpers@0.5.1))(esbuild@0.24.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(storybook@8.6.12(prettier@3.5.3))(typescript@5.9.0-dev.20250521)': + '@storybook/preset-react-webpack@8.6.12(@storybook/test@8.6.12(storybook@8.6.12(prettier@3.5.3)))(@swc/core@1.4.2(@swc/helpers@0.5.1))(esbuild@0.24.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(storybook@8.6.12(prettier@3.5.3))(typescript@5.9.0-dev.20250523)': dependencies: '@storybook/core-webpack': 8.6.12(storybook@8.6.12(prettier@3.5.3)) - '@storybook/react': 8.6.12(@storybook/test@8.6.12(storybook@8.6.12(prettier@3.5.3)))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(storybook@8.6.12(prettier@3.5.3))(typescript@5.9.0-dev.20250521) - '@storybook/react-docgen-typescript-plugin': 1.0.6--canary.9.0c3f3b7.0(typescript@5.9.0-dev.20250521)(webpack@5.88.0(@swc/core@1.4.2(@swc/helpers@0.5.1))(esbuild@0.24.2)) + '@storybook/react': 8.6.12(@storybook/test@8.6.12(storybook@8.6.12(prettier@3.5.3)))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(storybook@8.6.12(prettier@3.5.3))(typescript@5.9.0-dev.20250523) + '@storybook/react-docgen-typescript-plugin': 1.0.6--canary.9.0c3f3b7.0(typescript@5.9.0-dev.20250523)(webpack@5.88.0(@swc/core@1.4.2(@swc/helpers@0.5.1))(esbuild@0.24.2)) '@types/semver': 7.5.0 find-up: 5.0.0 magic-string: 0.30.17 @@ -14306,7 +14310,7 @@ snapshots: tsconfig-paths: 4.2.0 webpack: 5.88.0(@swc/core@1.4.2(@swc/helpers@0.5.1))(esbuild@0.24.2) optionalDependencies: - typescript: 5.9.0-dev.20250521 + typescript: 5.9.0-dev.20250523 transitivePeerDependencies: - '@storybook/test' - '@swc/core' @@ -14323,16 +14327,16 @@ snapshots: dependencies: storybook: 8.6.12(prettier@3.5.3) - '@storybook/react-docgen-typescript-plugin@1.0.6--canary.9.0c3f3b7.0(typescript@5.9.0-dev.20250521)(webpack@5.88.0(@swc/core@1.4.2(@swc/helpers@0.5.1))(esbuild@0.24.2))': + '@storybook/react-docgen-typescript-plugin@1.0.6--canary.9.0c3f3b7.0(typescript@5.9.0-dev.20250523)(webpack@5.88.0(@swc/core@1.4.2(@swc/helpers@0.5.1))(esbuild@0.24.2))': dependencies: debug: 4.4.0 endent: 2.1.0 find-cache-dir: 3.3.2 flat-cache: 3.0.4 micromatch: 4.0.5 - react-docgen-typescript: 2.2.2(typescript@5.9.0-dev.20250521) + react-docgen-typescript: 2.2.2(typescript@5.9.0-dev.20250523) tslib: 2.6.2 - typescript: 5.9.0-dev.20250521 + typescript: 5.9.0-dev.20250523 webpack: 5.88.0(@swc/core@1.4.2(@swc/helpers@0.5.1))(esbuild@0.24.2) transitivePeerDependencies: - supports-color @@ -14349,16 +14353,16 @@ snapshots: react-dom: 18.2.0(react@18.2.0) storybook: 8.6.12(prettier@3.5.3) - '@storybook/react-webpack5@8.6.12(@storybook/test@8.6.12(storybook@8.6.12(prettier@3.5.3)))(@swc/core@1.4.2(@swc/helpers@0.5.1))(esbuild@0.24.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(storybook@8.6.12(prettier@3.5.3))(typescript@5.9.0-dev.20250521)': + '@storybook/react-webpack5@8.6.12(@storybook/test@8.6.12(storybook@8.6.12(prettier@3.5.3)))(@swc/core@1.4.2(@swc/helpers@0.5.1))(esbuild@0.24.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(storybook@8.6.12(prettier@3.5.3))(typescript@5.9.0-dev.20250523)': dependencies: - '@storybook/builder-webpack5': 8.6.12(@swc/core@1.4.2(@swc/helpers@0.5.1))(esbuild@0.24.2)(storybook@8.6.12(prettier@3.5.3))(typescript@5.9.0-dev.20250521) - '@storybook/preset-react-webpack': 8.6.12(@storybook/test@8.6.12(storybook@8.6.12(prettier@3.5.3)))(@swc/core@1.4.2(@swc/helpers@0.5.1))(esbuild@0.24.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(storybook@8.6.12(prettier@3.5.3))(typescript@5.9.0-dev.20250521) - '@storybook/react': 8.6.12(@storybook/test@8.6.12(storybook@8.6.12(prettier@3.5.3)))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(storybook@8.6.12(prettier@3.5.3))(typescript@5.9.0-dev.20250521) + '@storybook/builder-webpack5': 8.6.12(@swc/core@1.4.2(@swc/helpers@0.5.1))(esbuild@0.24.2)(storybook@8.6.12(prettier@3.5.3))(typescript@5.9.0-dev.20250523) + '@storybook/preset-react-webpack': 8.6.12(@storybook/test@8.6.12(storybook@8.6.12(prettier@3.5.3)))(@swc/core@1.4.2(@swc/helpers@0.5.1))(esbuild@0.24.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(storybook@8.6.12(prettier@3.5.3))(typescript@5.9.0-dev.20250523) + '@storybook/react': 8.6.12(@storybook/test@8.6.12(storybook@8.6.12(prettier@3.5.3)))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(storybook@8.6.12(prettier@3.5.3))(typescript@5.9.0-dev.20250523) react: 18.2.0 react-dom: 18.2.0(react@18.2.0) storybook: 8.6.12(prettier@3.5.3) optionalDependencies: - typescript: 5.9.0-dev.20250521 + typescript: 5.9.0-dev.20250523 transitivePeerDependencies: - '@storybook/test' - '@swc/core' @@ -14397,7 +14401,7 @@ snapshots: '@storybook/test': 8.6.12(storybook@8.6.12(prettier@3.5.3)) typescript: 5.8.3 - '@storybook/react@8.6.12(@storybook/test@8.6.12(storybook@8.6.12(prettier@3.5.3)))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(storybook@8.6.12(prettier@3.5.3))(typescript@5.9.0-dev.20250521)': + '@storybook/react@8.6.12(@storybook/test@8.6.12(storybook@8.6.12(prettier@3.5.3)))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(storybook@8.6.12(prettier@3.5.3))(typescript@5.9.0-dev.20250523)': dependencies: '@storybook/components': 8.6.12(storybook@8.6.12(prettier@3.5.3)) '@storybook/global': 5.0.0 @@ -14410,7 +14414,7 @@ snapshots: storybook: 8.6.12(prettier@3.5.3) optionalDependencies: '@storybook/test': 8.6.12(storybook@8.6.12(prettier@3.5.3)) - typescript: 5.9.0-dev.20250521 + typescript: 5.9.0-dev.20250523 '@storybook/test@8.5.3(storybook@8.6.12(prettier@3.5.3))': dependencies: @@ -14577,12 +14581,12 @@ snapshots: - supports-color - typescript - '@svgr/core@8.0.0(typescript@5.9.0-dev.20250521)': + '@svgr/core@8.0.0(typescript@5.9.0-dev.20250523)': dependencies: '@babel/core': 7.24.3 '@svgr/babel-preset': 8.0.0(@babel/core@7.24.3) camelcase: 6.3.0 - cosmiconfig: 8.3.6(typescript@5.9.0-dev.20250521) + cosmiconfig: 8.3.6(typescript@5.9.0-dev.20250523) snake-case: 3.0.4 transitivePeerDependencies: - supports-color @@ -14627,11 +14631,11 @@ snapshots: transitivePeerDependencies: - supports-color - '@svgr/plugin-jsx@8.0.1(@svgr/core@8.0.0(typescript@5.9.0-dev.20250521))': + '@svgr/plugin-jsx@8.0.1(@svgr/core@8.0.0(typescript@5.9.0-dev.20250523))': dependencies: '@babel/core': 7.24.3 '@svgr/babel-preset': 8.0.0(@babel/core@7.24.3) - '@svgr/core': 8.0.0(typescript@5.9.0-dev.20250521) + '@svgr/core': 8.0.0(typescript@5.9.0-dev.20250523) '@svgr/hast-util-to-babel-ast': 8.0.0 svg-parser: 2.0.4 transitivePeerDependencies: @@ -14662,10 +14666,10 @@ snapshots: transitivePeerDependencies: - typescript - '@svgr/plugin-svgo@8.0.1(@svgr/core@8.0.0(typescript@5.9.0-dev.20250521))(typescript@5.9.0-dev.20250521)': + '@svgr/plugin-svgo@8.0.1(@svgr/core@8.0.0(typescript@5.9.0-dev.20250523))(typescript@5.9.0-dev.20250523)': dependencies: - '@svgr/core': 8.0.0(typescript@5.9.0-dev.20250521) - cosmiconfig: 8.3.6(typescript@5.9.0-dev.20250521) + '@svgr/core': 8.0.0(typescript@5.9.0-dev.20250523) + cosmiconfig: 8.3.6(typescript@5.9.0-dev.20250523) deepmerge: 4.3.1 svgo: 3.0.2 transitivePeerDependencies: @@ -14696,16 +14700,16 @@ snapshots: - supports-color - typescript - '@svgr/webpack@8.0.1(typescript@5.9.0-dev.20250521)': + '@svgr/webpack@8.0.1(typescript@5.9.0-dev.20250523)': dependencies: '@babel/core': 7.24.3 '@babel/plugin-transform-react-constant-elements': 7.22.5(@babel/core@7.24.3) '@babel/preset-env': 7.24.3(@babel/core@7.24.3) '@babel/preset-react': 7.24.1(@babel/core@7.24.3) '@babel/preset-typescript': 7.24.1(@babel/core@7.24.3) - '@svgr/core': 8.0.0(typescript@5.9.0-dev.20250521) - '@svgr/plugin-jsx': 8.0.1(@svgr/core@8.0.0(typescript@5.9.0-dev.20250521)) - '@svgr/plugin-svgo': 8.0.1(@svgr/core@8.0.0(typescript@5.9.0-dev.20250521))(typescript@5.9.0-dev.20250521) + '@svgr/core': 8.0.0(typescript@5.9.0-dev.20250523) + '@svgr/plugin-jsx': 8.0.1(@svgr/core@8.0.0(typescript@5.9.0-dev.20250523)) + '@svgr/plugin-svgo': 8.0.1(@svgr/core@8.0.0(typescript@5.9.0-dev.20250523))(typescript@5.9.0-dev.20250523) transitivePeerDependencies: - supports-color - typescript @@ -16088,14 +16092,14 @@ snapshots: optionalDependencies: typescript: 5.8.3 - cosmiconfig@8.3.6(typescript@5.9.0-dev.20250521): + cosmiconfig@8.3.6(typescript@5.9.0-dev.20250523): dependencies: import-fresh: 3.3.0 js-yaml: 4.1.0 parse-json: 5.2.0 path-type: 4.0.0 optionalDependencies: - typescript: 5.9.0-dev.20250521 + typescript: 5.9.0-dev.20250523 create-ecdh@4.0.4: dependencies: @@ -16493,7 +16497,7 @@ snapshots: dependencies: semver: 7.6.3 shelljs: 0.8.5 - typescript: 5.9.0-dev.20250521 + typescript: 5.9.0-dev.20250523 dunder-proto@1.0.1: dependencies: @@ -17118,7 +17122,7 @@ snapshots: cross-spawn: 7.0.3 signal-exit: 4.1.0 - fork-ts-checker-webpack-plugin@8.0.0(typescript@5.9.0-dev.20250521)(webpack@5.88.0(@swc/core@1.4.2(@swc/helpers@0.5.1))(esbuild@0.24.2)): + fork-ts-checker-webpack-plugin@8.0.0(typescript@5.9.0-dev.20250523)(webpack@5.88.0(@swc/core@1.4.2(@swc/helpers@0.5.1))(esbuild@0.24.2)): dependencies: '@babel/code-frame': 7.24.2 chalk: 4.1.2 @@ -17132,7 +17136,7 @@ snapshots: schema-utils: 3.3.0 semver: 7.6.3 tapable: 2.2.1 - typescript: 5.9.0-dev.20250521 + typescript: 5.9.0-dev.20250523 webpack: 5.88.0(@swc/core@1.4.2(@swc/helpers@0.5.1))(esbuild@0.24.2) form-data@2.5.1: @@ -17812,16 +17816,16 @@ snapshots: - ts-node optional: true - jest-cli@29.6.2(@types/node@20.4.8)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.4.2(@swc/helpers@0.5.1))(@types/node@20.4.8)(typescript@5.9.0-dev.20250521)): + jest-cli@29.6.2(@types/node@20.4.8)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.4.2(@swc/helpers@0.5.1))(@types/node@20.4.8)(typescript@5.9.0-dev.20250523)): dependencies: - '@jest/core': 29.6.2(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.4.2(@swc/helpers@0.5.1))(@types/node@20.4.8)(typescript@5.9.0-dev.20250521)) + '@jest/core': 29.6.2(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.4.2(@swc/helpers@0.5.1))(@types/node@20.4.8)(typescript@5.9.0-dev.20250523)) '@jest/test-result': 29.6.2 '@jest/types': 29.6.3 chalk: 4.1.2 exit: 0.1.2 graceful-fs: 4.2.11 import-local: 3.1.0 - jest-config: 29.6.2(@types/node@20.4.8)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.4.2(@swc/helpers@0.5.1))(@types/node@20.4.8)(typescript@5.9.0-dev.20250521)) + jest-config: 29.6.2(@types/node@20.4.8)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.4.2(@swc/helpers@0.5.1))(@types/node@20.4.8)(typescript@5.9.0-dev.20250523)) jest-util: 29.7.0 jest-validate: 29.6.2 prompts: 2.4.2 @@ -17896,7 +17900,7 @@ snapshots: - supports-color optional: true - jest-config@29.6.2(@types/node@20.4.8)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.4.2(@swc/helpers@0.5.1))(@types/node@20.4.8)(typescript@5.9.0-dev.20250521)): + jest-config@29.6.2(@types/node@20.4.8)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.4.2(@swc/helpers@0.5.1))(@types/node@20.4.8)(typescript@5.9.0-dev.20250523)): dependencies: '@babel/core': 7.24.3 '@jest/test-sequencer': 29.6.2 @@ -17922,7 +17926,7 @@ snapshots: strip-json-comments: 3.1.1 optionalDependencies: '@types/node': 20.4.8 - ts-node: 10.9.2(@swc/core@1.4.2(@swc/helpers@0.5.1))(@types/node@20.4.8)(typescript@5.9.0-dev.20250521) + ts-node: 10.9.2(@swc/core@1.4.2(@swc/helpers@0.5.1))(@types/node@20.4.8)(typescript@5.9.0-dev.20250523) transitivePeerDependencies: - babel-plugin-macros - supports-color @@ -18190,12 +18194,12 @@ snapshots: - ts-node optional: true - jest@29.6.2(@types/node@20.4.8)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.4.2(@swc/helpers@0.5.1))(@types/node@20.4.8)(typescript@5.9.0-dev.20250521)): + jest@29.6.2(@types/node@20.4.8)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.4.2(@swc/helpers@0.5.1))(@types/node@20.4.8)(typescript@5.9.0-dev.20250523)): dependencies: - '@jest/core': 29.6.2(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.4.2(@swc/helpers@0.5.1))(@types/node@20.4.8)(typescript@5.9.0-dev.20250521)) + '@jest/core': 29.6.2(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.4.2(@swc/helpers@0.5.1))(@types/node@20.4.8)(typescript@5.9.0-dev.20250523)) '@jest/types': 29.6.3 import-local: 3.1.0 - jest-cli: 29.6.2(@types/node@20.4.8)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.4.2(@swc/helpers@0.5.1))(@types/node@20.4.8)(typescript@5.9.0-dev.20250521)) + jest-cli: 29.6.2(@types/node@20.4.8)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.4.2(@swc/helpers@0.5.1))(@types/node@20.4.8)(typescript@5.9.0-dev.20250523)) transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -19630,9 +19634,9 @@ snapshots: dependencies: typescript: 5.8.3 - react-docgen-typescript@2.2.2(typescript@5.9.0-dev.20250521): + react-docgen-typescript@2.2.2(typescript@5.9.0-dev.20250523): dependencies: - typescript: 5.9.0-dev.20250521 + typescript: 5.9.0-dev.20250523 react-docgen@7.0.3: dependencies: @@ -20586,7 +20590,7 @@ snapshots: optionalDependencies: '@swc/core': 1.4.2(@swc/helpers@0.5.1) - ts-node@10.9.2(@swc/core@1.4.2(@swc/helpers@0.5.1))(@types/node@20.17.12)(typescript@5.9.0-dev.20250521): + ts-node@10.9.2(@swc/core@1.4.2(@swc/helpers@0.5.1))(@types/node@20.17.12)(typescript@5.9.0-dev.20250523): dependencies: '@cspotcode/source-map-support': 0.8.1 '@tsconfig/node10': 1.0.11 @@ -20600,13 +20604,13 @@ snapshots: create-require: 1.1.1 diff: 4.0.2 make-error: 1.3.6 - typescript: 5.9.0-dev.20250521 + typescript: 5.9.0-dev.20250523 v8-compile-cache-lib: 3.0.1 yn: 3.1.1 optionalDependencies: '@swc/core': 1.4.2(@swc/helpers@0.5.1) - ts-node@10.9.2(@swc/core@1.4.2(@swc/helpers@0.5.1))(@types/node@20.4.8)(typescript@5.9.0-dev.20250521): + ts-node@10.9.2(@swc/core@1.4.2(@swc/helpers@0.5.1))(@types/node@20.4.8)(typescript@5.9.0-dev.20250523): dependencies: '@cspotcode/source-map-support': 0.8.1 '@tsconfig/node10': 1.0.11 @@ -20620,7 +20624,7 @@ snapshots: create-require: 1.1.1 diff: 4.0.2 make-error: 1.3.6 - typescript: 5.9.0-dev.20250521 + typescript: 5.9.0-dev.20250523 v8-compile-cache-lib: 3.0.1 yn: 3.1.1 optionalDependencies: @@ -20747,7 +20751,7 @@ snapshots: typescript@5.8.3: {} - typescript@5.9.0-dev.20250521: {} + typescript@5.9.0-dev.20250523: {} unbox-primitive@1.0.2: dependencies: From d2d186ad4b03bf8eebcbb691140de980905587db Mon Sep 17 00:00:00 2001 From: Terrence Keane Date: Fri, 23 May 2025 14:05:22 -0400 Subject: [PATCH 29/31] chore(package): update build script in package.json --- packages/code-editor/package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/code-editor/package.json b/packages/code-editor/package.json index d651c24495..345829f236 100644 --- a/packages/code-editor/package.json +++ b/packages/code-editor/package.json @@ -7,9 +7,9 @@ "types": "./dist/types/index.d.ts", "license": "Apache-2.0", "scripts": { - "build": "lg build-package", - "tsc": "lg build-ts", - "docs": "lg build-tsdoc" + "build": "lg-build bundle", + "tsc": "lg-build tsc", + "docs": "lg-build docs" }, "publishConfig": { "access": "public" From af0b4dbb718ee0c5bf0878bec69a1c99372cd178 Mon Sep 17 00:00:00 2001 From: Terrence Keane Date: Fri, 23 May 2025 14:33:20 -0400 Subject: [PATCH 30/31] feat(button): add exports field for module resolution --- packages/button/package.json | 7 +++++++ packages/code-editor/tsconfig.json | 6 +----- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/packages/button/package.json b/packages/button/package.json index ce2fceba96..9ed202b9f8 100644 --- a/packages/button/package.json +++ b/packages/button/package.json @@ -6,6 +6,13 @@ "module": "./dist/esm/index.js", "types": "./dist/types/index.d.ts", "typesVersions": {}, + "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", diff --git a/packages/code-editor/tsconfig.json b/packages/code-editor/tsconfig.json index 4f743d4f83..93bd5b7ef8 100644 --- a/packages/code-editor/tsconfig.json +++ b/packages/code-editor/tsconfig.json @@ -1,10 +1,6 @@ { "extends": "@lg-tools/build/config/package.tsconfig.json", - "compilerOptions": { - "declarationDir": "dist", - "outDir": "dist", - "rootDir": "src", - "baseUrl": ".", + "compilerOptions": { "paths": { "@leafygreen-ui/icon/dist/*": ["../icon/src/generated/*"], "@leafygreen-ui/*": ["../*/src"] From ee828325552fdbbafcfecf7d751f9146d3637fc8 Mon Sep 17 00:00:00 2001 From: Terrence Keane Date: Fri, 23 May 2025 14:37:19 -0400 Subject: [PATCH 31/31] feat(code-editor): add exports field for module resolution fix(button): remove exports field from package.json --- packages/button/package.json | 7 ------- packages/code-editor/package.json | 7 +++++++ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/button/package.json b/packages/button/package.json index 9ed202b9f8..ce2fceba96 100644 --- a/packages/button/package.json +++ b/packages/button/package.json @@ -6,13 +6,6 @@ "module": "./dist/esm/index.js", "types": "./dist/types/index.d.ts", "typesVersions": {}, - "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", diff --git a/packages/code-editor/package.json b/packages/code-editor/package.json index 345829f236..09e81f95f6 100644 --- a/packages/code-editor/package.json +++ b/packages/code-editor/package.json @@ -6,6 +6,13 @@ "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",