Skip to content

Commit

Permalink
feat(code-editor): add package (#3309)
Browse files Browse the repository at this point in the history
* chore: test

* feat(code-editor-library): add package

* feat(code-editor, editable-code-block): add packages

* fix: lint and a11y

* chore: disable landmark-unique rule in a11y test-runner
  • Loading branch information
TheSisb committed Jul 11, 2023
1 parent 6a751f7 commit 61ba09f
Show file tree
Hide file tree
Showing 27 changed files with 1,277 additions and 0 deletions.
6 changes: 6 additions & 0 deletions .changeset/rich-pots-tell.md
@@ -0,0 +1,6 @@
---
'@twilio-paste/core': minor
'@twilio-paste/code-editor-library': major
---

[Code Editor Library] Add new Code Editor library package which wraps the excellent Monaco editor. Enables building full featured code editors in the browser, like Visual Studio code.
6 changes: 6 additions & 0 deletions .changeset/tender-bees-raise.md
@@ -0,0 +1,6 @@
---
'@twilio-paste/editable-code-block': major
'@twilio-paste/core': minor
---

[Editable Code Block] Add the new EditableCodeBlock component. Enables building code editors in the browser, styled with Paste default styles.
2 changes: 2 additions & 0 deletions .codesandbox/ci.json
Expand Up @@ -24,6 +24,7 @@
"/packages/paste-core/components/checkbox",
"/packages/paste-libraries/clipboard-copy",
"/packages/paste-core/components/code-block",
"/packages/paste-libraries/code-editor",
"/packages/paste-color-contrast-utils",
"/packages/paste-core/components/combobox",
"/packages/paste-core/primitives/combobox",
Expand All @@ -39,6 +40,7 @@
"/packages/paste-core/components/display-heading",
"/packages/paste-core/components/display-pill-group",
"/packages/paste-libraries/dropdown",
"/packages/paste-core/components/editable-code-block",
"/packages/paste-core/components/file-picker",
"/packages/paste-core/components/file-uploader",
"/packages/paste-core/layout/flex",
Expand Down
5 changes: 5 additions & 0 deletions .storybook/test-runner.js
Expand Up @@ -16,6 +16,11 @@ const a11yConfig = {

await configureAxe(page, {
rules: [
{
// Page level test that states one main element is present. Not applicable for isolated components.
id: 'landmark-unique',
enabled: false,
},
{
// Page level test that states one main element is present. Not applicable for isolated components.
id: 'landmark-one-main',
Expand Down
1 change: 1 addition & 0 deletions packages/paste-codemods/tools/.cache/mappings.json
Expand Up @@ -99,6 +99,7 @@
"DisplayHeading": "@twilio-paste/core/display-heading",
"DisplayPill": "@twilio-paste/core/display-pill-group",
"DisplayPillGroup": "@twilio-paste/core/display-pill-group",
"EditableCodeBlock": "@twilio-paste/core/editable-code-block",
"FilePicker": "@twilio-paste/core/file-picker",
"FilePickerButton": "@twilio-paste/core/file-picker",
"FileUploader": "@twilio-paste/core/file-uploader",
Expand Down
3 changes: 3 additions & 0 deletions packages/paste-core/components/editable-code-block/build.js
@@ -0,0 +1,3 @@
const {build} = require('../../../../tools/build/esbuild');

build(require('./package.json'));
86 changes: 86 additions & 0 deletions packages/paste-core/components/editable-code-block/package.json
@@ -0,0 +1,86 @@
{
"name": "@twilio-paste/editable-code-block",
"version": "0.0.0",
"category": "data display",
"status": "alpha",
"description": "An Editable Code Block is used to display and make changes to large blocks of code.",
"author": "Twilio Inc.",
"license": "MIT",
"main:dev": "src/index.tsx",
"main": "dist/index.js",
"module": "dist/index.es.js",
"types": "dist/index.d.ts",
"sideEffects": false,
"publishConfig": {
"access": "public"
},
"files": [
"dist"
],
"scripts": {
"build": "yarn clean && NODE_ENV=production node build.js && tsc",
"build:js": "NODE_ENV=development node build.js",
"clean": "rm -rf ./dist",
"tsc": "tsc"
},
"peerDependencies": {
"@twilio-paste/anchor": "^11.0.0",
"@twilio-paste/animation-library": "^1.0.0",
"@twilio-paste/badge": "^7.0.0",
"@twilio-paste/box": "^9.1.0",
"@twilio-paste/button": "^13.0.1",
"@twilio-paste/code-editor-library": "^0.0.0",
"@twilio-paste/color-contrast-utils": "^4.0.0",
"@twilio-paste/customization": "^7.0.0",
"@twilio-paste/design-tokens": "^9.0.0",
"@twilio-paste/disclosure-primitive": "^1.0.0",
"@twilio-paste/icons": "^11.0.0",
"@twilio-paste/reakit-library": "^1.0.0",
"@twilio-paste/screen-reader-only": "^12.0.0",
"@twilio-paste/spinner": "^13.0.0",
"@twilio-paste/stack": "^7.0.0",
"@twilio-paste/style-props": "^8.0.0",
"@twilio-paste/styling-library": "^2.0.0",
"@twilio-paste/text": "^9.0.0",
"@twilio-paste/theme": "^10.0.0",
"@twilio-paste/truncate": "^13.0.0",
"@twilio-paste/types": "^5.0.0",
"@twilio-paste/uid-library": "^1.0.0",
"@twilio-paste/utils": "^4.0.0",
"@types/react": "^16.8.6 || ^17.0.2 || ^18.0.27",
"@types/react-dom": "^16.8.6 || ^17.0.2 || ^18.0.10",
"prop-types": "^15.7.2",
"react": "^16.8.6 || ^17.0.2 || ^18.0.0",
"react-dom": "^16.8.6 || ^17.0.2 || ^18.0.0"
},
"devDependencies": {
"@twilio-paste/anchor": "^11.0.0",
"@twilio-paste/animation-library": "^1.0.0",
"@twilio-paste/badge": "^7.1.0",
"@twilio-paste/box": "^9.1.0",
"@twilio-paste/button": "^13.0.3",
"@twilio-paste/code-editor-library": "^0.0.0",
"@twilio-paste/color-contrast-utils": "^4.0.0",
"@twilio-paste/customization": "^7.0.0",
"@twilio-paste/design-tokens": "^9.2.1",
"@twilio-paste/disclosure-primitive": "^1.0.0",
"@twilio-paste/icons": "^11.2.1",
"@twilio-paste/reakit-library": "^1.0.0",
"@twilio-paste/screen-reader-only": "^12.0.0",
"@twilio-paste/spinner": "^13.0.0",
"@twilio-paste/stack": "^7.0.0",
"@twilio-paste/style-props": "^8.0.0",
"@twilio-paste/styling-library": "^2.0.2",
"@twilio-paste/text": "^9.0.1",
"@twilio-paste/theme": "^10.0.2",
"@twilio-paste/truncate": "^13.0.0",
"@twilio-paste/types": "^5.0.0",
"@twilio-paste/uid-library": "^1.0.0",
"@twilio-paste/utils": "^4.0.0",
"@types/react": "^18.0.27",
"@types/react-dom": "^18.0.10",
"prop-types": "^15.7.2",
"react": "^18.0.0",
"react-dom": "^18.0.0"
}
}
@@ -0,0 +1,41 @@
import * as React from 'react';
import {Box, type BoxProps} from '@twilio-paste/box';
import {
CodeEditor,
CodeEditorPasteTheme,
type CodeEditorProps,
type Editor,
type Monaco,
} from '@twilio-paste/code-editor-library';

export interface EditableCodeBlockProps
extends Omit<CodeEditorProps, 'wrapperProps' | 'className' | 'loading' | 'theme'> {
element?: BoxProps['element'];
}

export const EditableCodeBlock: React.FC<EditableCodeBlockProps> = ({
onMount,
element = 'EDITABLE_CODE_BLOCK',
...props
}) => {
const handleEditorDidMount = (editor: Editor.IStandaloneCodeEditor, monaco: Monaco): void => {
// Sets the Paste theme for the editor
monaco.editor.defineTheme('paste', CodeEditorPasteTheme);
monaco.editor.setTheme('paste');

/*
* Call provided onMount function.
* This can be used to add custom language support, or to grab a
* reference to the ref (monaco) object.
*/
onMount?.(editor, monaco);
};

return (
<Box element={element} borderRadius="borderRadius10" overflow="hidden">
<CodeEditor {...props} onMount={handleEditorDidMount} />
</Box>
);
};

EditableCodeBlock.displayName = 'EditableCodeBlock';
@@ -0,0 +1 @@
export * from './EditableCodeBlock';
@@ -0,0 +1,145 @@
import * as React from 'react';
import {Stack} from '@twilio-paste/stack';
import {RadioButtonGroup, RadioButton} from '@twilio-paste/radio-button-group';

import {EditableCodeBlock} from '../src';

// eslint-disable-next-line import/no-default-export
export default {
title: 'Components/Editable Code Block',
};

export const Default = (): React.ReactNode => {
return (
<EditableCodeBlock
height="70vh"
defaultLanguage="typescript"
defaultValue={`interface User {
name: string;
id: number;
}
class UserAccount {
name: string;
id: number;
constructor(name: string, id: number) {
this.name = name;
this.id = id;
}
}
const user: User = new UserAccount("Murphy", 1);`}
/>
);
};

export const ReadOnly = (): React.ReactNode => {
return (
<EditableCodeBlock
height="70vh"
options={{
readOnly: true,
}}
defaultLanguage="typescript"
defaultValue={`interface User {
name: string;
id: number;
}
class UserAccount {
name: string;
id: number;
constructor(name: string, id: number) {
this.name = name;
this.id = id;
}
}
const user: User = new UserAccount("Murphy", 1);`}
/>
);
};

const Files = {
JS: {
name: 'script.js',
language: 'javascript',
value: `// program that checks if the number is positive, negative or zero
// input from the user
const number = parseInt(prompt("Enter a number: "));
// check if number is greater than 0
if (number > 0) {
console.log("The number is positive");
}
// check if number is 0
else if (number == 0) {
console.log("The number is zero");
}
// if number is less than 0
else {
console.log("The number is negative");
}`,
},
CSS: {
name: 'style.css',
language: 'css',
value: `.content-area {
background-color: #fff;
border: 1px solid #e5e5e5;
border-radius: 3px;
padding: 16px;
margin-bottom: 16px;
}`,
},
HTML: {
name: 'index.html',
language: 'html',
value: `<!DOCTYPE html>
<html>
<head></head>
<body>
<div class="content-area">
<h1>My Website</h1>
<p>Welcome to my website</p>
</div>
</body>
</html>`,
},
};

export const MultiFileEditor = (): React.ReactNode => {
const [fileKey, setFileKey] = React.useState('JS');
return (
<Stack orientation="vertical" spacing="space40">
<RadioButtonGroup
attached
name="file-selector"
legend="Select file"
helpText="Show code for the selected file"
onChange={(_fileKey) => setFileKey(_fileKey)}
>
{Object.keys(Files).map((key) => (
<RadioButton key={key} value={key} defaultChecked={fileKey === key}>
{Files[key].name}
</RadioButton>
))}
</RadioButtonGroup>
<EditableCodeBlock
height="400px"
options={{
minimap: {
enabled: false,
},
}}
path={Files[fileKey].name}
defaultLanguage={Files[fileKey].language}
defaultValue={Files[fileKey].value}
/>
</Stack>
);
};
@@ -0,0 +1,8 @@
{
"extends": "../../../../tsconfig.json",
"compilerOptions": {
"outDir": "dist/"
},
"include": ["src/**/*"],
"exclude": ["node_modules"]
}
2 changes: 2 additions & 0 deletions packages/paste-core/core-bundle/.gitignore
Expand Up @@ -20,6 +20,7 @@
/checkbox
/clipboard-copy-library
/code-block
/code-editor-library
/color-contrast-utils
/combobox
/combobox-primitive
Expand All @@ -35,6 +36,7 @@
/display-heading
/display-pill-group
/dropdown-library
/editable-code-block
/file-picker
/file-uploader
/flex
Expand Down
8 changes: 8 additions & 0 deletions packages/paste-core/core-bundle/code-editor/package.json
@@ -0,0 +1,8 @@
{
"name": "@twilio-paste-core/code-editor",
"version": "0.0.0",
"private": true,
"sideEffects": false,
"main": "../dist/code-editor.js",
"types": "../dist/code-editor.d.ts"
}
2 changes: 2 additions & 0 deletions packages/paste-core/core-bundle/package.json
Expand Up @@ -91,6 +91,7 @@
"@twilio-paste/checkbox": "^12.0.0",
"@twilio-paste/clipboard-copy-library": "^2.0.0",
"@twilio-paste/code-block": "^3.1.1",
"@twilio-paste/code-editor-library": "^0.0.0",
"@twilio-paste/color-contrast-utils": "^4.0.0",
"@twilio-paste/combobox": "^15.0.3",
"@twilio-paste/combobox-primitive": "^1.0.1",
Expand All @@ -106,6 +107,7 @@
"@twilio-paste/display-heading": "^3.0.0",
"@twilio-paste/display-pill-group": "^7.0.0",
"@twilio-paste/dropdown-library": "^2.0.0",
"@twilio-paste/editable-code-block": "^0.0.0",
"@twilio-paste/file-picker": "^3.0.1",
"@twilio-paste/file-uploader": "^3.0.0",
"@twilio-paste/flex": "^7.0.0",
Expand Down
@@ -0,0 +1 @@
export * from '@twilio-paste/code-editor-library';
@@ -0,0 +1 @@
export * from '@twilio-paste/editable-code-block';
1 change: 1 addition & 0 deletions packages/paste-core/core-bundle/src/index.tsx
Expand Up @@ -26,6 +26,7 @@ export * from '@twilio-paste/disclosure';
export * from '@twilio-paste/disclosure-primitive';
export * from '@twilio-paste/display-heading';
export * from '@twilio-paste/display-pill-group';
export * from '@twilio-paste/editable-code-block';
export * from '@twilio-paste/file-picker';
export * from '@twilio-paste/file-uploader';
export * from '@twilio-paste/flex';
Expand Down
Empty file.
3 changes: 3 additions & 0 deletions packages/paste-libraries/code-editor/build.js
@@ -0,0 +1,3 @@
const {build} = require('../../../tools/build/esbuild');

build(require('./package.json'));

0 comments on commit 61ba09f

Please sign in to comment.