Skip to content

Commit 3c2baa0

Browse files
gratcliffGabe Ratcliffrunnabrojulshotal
authored
feat: code editor export (#5)
* feat(codeeditor): tree restructuring - Adding codeEditor structure from ui lib - Restructuring src to be more comprehensive * feat(codeeditor): full integration of ui codeeditor - Added sass support in webpack - Consolidating utils for shared use - Porting ui code editor * chore: lint cleanup * feat(codeeditor): geeneral polish - Tweaking linters for new file structure - Test updates - Prettyfying * feat(codeeditor): moving to single configurable export * feat(editor): adding support for linenumbers in runmode * feat(editor): beginning conditional line styling" * style(code editor): matching parent classes * feat(editor): wrapping lines in divs for styling * style(code mirror): add remaining styles and highlight styles * style(code mirror): use proper highlight/overlay classes * fix: blocking linenumbers from selection * style(code mirror): preserve highlight on hover * feat(syntaxhighlighter): line highlighting (#6) * feat: attempt at line highlighting w comments * feat: it works and is less ugly now * fix: blocking linenumbers from selection * chore: small eslint fix to pass tests * feat(highlighting): minor refactor - Updated name schemes - Reuse components * feat(highlighting): further refactoring - Moving standalone function calls outside parent - Consolidating component declaration * fix(highlighting): applying overlay to deadspace * chore(coremirror): remove extra class * chore: adding variable Co-authored-by: Gabriel Ratcliff <gaberatcliff@gmail.com> Co-authored-by: Tony Li <runnabro@users.noreply.github.com> * chore: tests and docs * chore: documentation - Updating readme.md to provide proper use cases and option documentation - Cleanup per review comments * chore: documenting editable use case * fix: canonical named export * chore: adding homepage * chore: dev and prod optimization * chore: webpack externals - Excluding React and react-dom from webpack built as to avoid conflicts - Replicating import syntax in test bed - Cleaning up package.json * chore: fixing pr comment * fix: breaking out styled syntax highlighter into seperate function * chore: removing comment Co-authored-by: Gabe Ratcliff <gaberatcliff@Meghans-MBP.attlocal.net> Co-authored-by: Tony Li <runnabro@users.noreply.github.com> Co-authored-by: Julia Hotaling <42787244+julshotal@users.noreply.github.com>
1 parent 6819099 commit 3c2baa0

25 files changed

+1795
-234
lines changed

.eslintignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
node_modules/
22
coverage/
3+
dist/
34
__tests__/__fixtures__/**/sample.*

.jsinspectrc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"threshold": 30,
33
"identifiers": true,
4-
"ignore": "test",
4+
"ignore": "test|dist|webpack",
55
"reporter": "default",
66
"suppress": 100
77
}

.prettierignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
__tests__/__fixtures__/
22
__tests__/__snapshots__/
33
coverage/
4+
dist/

README.md

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# @readme/syntax-highlighter
22

3-
ReadMe's React-based syntax highlighter
3+
ReadMe's React-based syntax highlighter based on [CodeMirror](https://github.com/codemirror/CodeMirror) and [react-codemirror2](https://github.com/scniro/react-codemirror2).
44

55
[![npm](https://img.shields.io/npm/v/@readme/syntax-highlighter)](https://npm.im/@readme/syntax-highlighter) [![Build](https://github.com/readmeio/syntax-highlighter/workflows/CI/badge.svg)](https://github.com/readmeio/syntax-highlighter)
66

@@ -13,12 +13,50 @@ npm install --save @readme/syntax-highlighter
1313
```
1414

1515
## Usage
16+
### Read Only Mode
17+
```js
18+
const syntaxHighlighter = require('@readme/syntax-highlighter');
19+
const ele = syntaxHighlighter('console.log("Hello, world!");', 'js');
20+
```
21+
22+
### Read Only with Line Numbers
23+
```js
24+
const syntaxHighlighter = require('@readme/syntax-highlighter');
25+
const ele = syntaxHighlighter('console.log("Hello, world!");', 'js', { ...opts, highlightMode: true });
26+
```
1627

28+
### Read Only with Line Numbers and Highlighted Ranges
1729
```js
1830
const syntaxHighlighter = require('@readme/syntax-highlighter');
19-
console.log(syntaxHighlighter('console.log("Hello, world!");', 'js'));
31+
const ele = syntaxHighlighter('console.log("Hello, world!");', 'js', {
32+
...opts,
33+
highlightMode: true,
34+
ranges: [
35+
[
36+
{ ch: 0, line: 0 },
37+
{ ch: 0, line: 1 },
38+
],
39+
],
40+
});
2041
```
2142

43+
### Full CodeMirror
44+
Access to a full code Mirror instance. See configuration settings in the [react-codemirror2 library](https://github.com/scniro/react-codemirror2#props)
45+
46+
```js
47+
const syntaxHighlighter = require('@readme/syntax-highlighter');
48+
const ele = syntaxHighlighter('console.log("Hello, world!");', 'js', { ...opts, editable: true }, { ...editorProps });
49+
```
50+
51+
### Available Options
52+
| Name | Type | Description |
53+
| :--- | :--- | :--- |
54+
| dark | boolean | Enable Dark Mode? |
55+
| highlightMode | boolean | Enable line number display and ability to set highlighted line css |
56+
| tokenizeVariables | boolean | Apply [Variable Component](https://github.com/readmeio/api-explorer/tree/next/packages/variable) to matched Regex |
57+
| ranges | array | Ranges of line numbers to apply highlighting to. Requires `highlightMode` enabled |
58+
| editable | boolean | Enable full CodeMirror Instance |
59+
2260
## Languages Supported
2361

2462
| Language | Available language mode(s) |

__tests__/__snapshots__/index.test.js.snap renamed to __tests__/__snapshots__/codeMirror.test.js.snap

Lines changed: 76 additions & 76 deletions
Large diffs are not rendered by default.

__tests__/codeEditor.test.js

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import React from 'react';
2+
import { mount } from 'enzyme';
3+
import CodeEditor from '../src/codeEditor';
4+
5+
describe('<CodeEditor/>', () => {
6+
const getClientRectSpy = jest.fn(() => ({ width: 100 }));
7+
Range.prototype.getBoundingClientRect = getClientRectSpy;
8+
Range.prototype.getClientRects = getClientRectSpy;
9+
10+
const node = mount(<CodeEditor code="console.log('Hello, world.');" lang="js" />);
11+
const cm = node.find('Controlled');
12+
13+
it('should display a <CodeEditor> element', () => {
14+
expect(node.children('.CodeEditor')).toHaveLength(1);
15+
});
16+
17+
it('should highlight code', () => {
18+
expect(cm.html()).toContain('cm-variable');
19+
});
20+
21+
it('should set CodeMirror options', () => {
22+
expect('options' in cm.props()).toBe(true);
23+
});
24+
25+
it('should set a sanitized language mode', () => {
26+
expect(node.props().lang).toBe('js');
27+
expect(cm.props().options.mode).toBe('javascript');
28+
});
29+
30+
it('should set new language via props', () => {
31+
node.setProps({ lang: 'kotlin' });
32+
expect(node.props().lang).toBe('kotlin');
33+
34+
setTimeout(() => {
35+
expect(cm.props().options.mode).toBe('clike');
36+
});
37+
});
38+
39+
it('should take children as a code value', () => {
40+
const props = {
41+
children: 'const res = true;',
42+
lang: 'js',
43+
};
44+
45+
const n2 = mount(<CodeEditor {...props} />);
46+
const cm2 = n2.find('Controlled');
47+
expect(cm2.prop('value')).toBe('const res = true;');
48+
});
49+
});
Lines changed: 41 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
1-
const { mount, shallow } = require('enzyme');
2-
const path = require('path');
3-
const glob = require('glob');
4-
const fs = require('fs').promises;
5-
6-
const syntaxHighlighter = require('../src');
7-
const uppercase = require('../src/uppercase');
8-
const canoncial = require('../src/canonical');
1+
import { mount, shallow } from 'enzyme';
2+
import path from 'path';
3+
import glob from 'glob';
4+
import { promises as fs } from 'fs';
5+
import syntaxHighlighter, { uppercase, canonical } from '../src';
96

107
const fixtures = glob.sync(path.join(__dirname, '/__fixtures__/*'));
118

@@ -14,7 +11,7 @@ test('should highlight a block of code', () => {
1411

1512
expect(code.hasClass('cm-s-neo')).toBe(true);
1613
expect(code.html()).toBe(
17-
'<span class="cm-s-neo"><span class="cm-keyword">var</span> <span class="cm-def">a</span> <span class="cm-operator">=</span> <span class="cm-number">1</span>;</span>'
14+
'<div class="cm-s-neo"><span class="cm-keyword">var</span> <span class="cm-def">a</span> <span class="cm-operator">=</span> <span class="cm-number">1</span>;</div>'
1815
);
1916
});
2017

@@ -38,7 +35,7 @@ test('should concat the same style items', () => {
3835

3936
test('should work with modes', () => {
4037
expect(shallow(syntaxHighlighter('{ "a": 1 }', 'json')).html()).toBe(
41-
'<span class="cm-s-neo">{ <span class="cm-property">&quot;a&quot;</span>: <span class="cm-number">1</span> }</span>'
38+
'<div class="cm-s-neo">{ <span class="cm-property">&quot;a&quot;</span>: <span class="cm-number">1</span> }</div>'
4239
);
4340
});
4441

@@ -109,11 +106,11 @@ describe('Supported languages', () => {
109106

110107
if ('canonical' in instructions.mode) {
111108
it('should have a canonical directive set up', () => {
112-
expect(canoncial(alias)).toBe(instructions.mode.canonical);
109+
expect(canonical(alias)).toBe(instructions.mode.canonical);
113110
});
114111
} else {
115112
it('should have a canonical directive set up off the primary mode', () => {
116-
expect(canoncial(alias)).toBe(instructions.mode.primary);
113+
expect(canonical(alias)).toBe(instructions.mode.primary);
117114
});
118115
}
119116
});
@@ -132,3 +129,35 @@ describe('Supported languages', () => {
132129
}
133130
});
134131
});
132+
133+
describe('highlight mode', () => {
134+
let node;
135+
const code = `curl --request POST
136+
--url <<url>>
137+
--header 'authorization: Bearer 123'
138+
--header 'content-type: application/json'`;
139+
140+
beforeEach(() => {
141+
node = mount(
142+
syntaxHighlighter(code, 'curl', {
143+
dark: true,
144+
highlightMode: true,
145+
tokenizeVariables: true,
146+
ranges: [
147+
[
148+
{ ch: 0, line: 0 },
149+
{ ch: 0, line: 1 },
150+
],
151+
],
152+
})
153+
);
154+
});
155+
156+
it('should return line numbers by default', () => {
157+
expect(node.find('p').first().hasClass('cm-lineNumber')).toBe(true);
158+
});
159+
160+
it('should highlight based on range input', () => {
161+
expect(node.find('.cm-linerow.cm-highlight')).toHaveLength(4);
162+
});
163+
});

__tests__/uppercase.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
const { uppercase } = require('../src');
1+
import { uppercase } from '../src';
22

33
test('should uppercase known languages', () => {
44
expect(uppercase('http')).toBe('HTTP');

jest.config.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,18 @@ const path = require('path');
22

33
module.exports = {
44
coveragePathIgnorePatterns: ['<rootDir>/__tests__/__fixtures__/'],
5-
collectCoverageFrom: ['src/*.{js,jsx}'],
5+
collectCoverageFrom: ['src/**/*.{js,jsx}'],
66
setupFiles: [path.join(__dirname, '/lib/enzyme')],
77
testPathIgnorePatterns: ['<rootDir>/__tests__/__fixtures__/'],
88
transform: {
99
'^.+\\.jsx?$': path.join(__dirname, '/lib/babel-jest'),
10+
'.+\\.(css|styl|less|sass|scss)$': 'identity-obj-proxy',
1011
},
1112
transformIgnorePatterns: [
1213
// Since `@readme/variable` doesn't ship any transpiled code, we need to transform it as we're running tests.
1314
'<rootDir>/node_modules/@readme/variable/^.+\\.jsx?$',
1415
],
16+
moduleNameMapper: {
17+
'^.+\\.(css|less|scss)$': 'identity-obj-proxy',
18+
},
1519
};

0 commit comments

Comments
 (0)