@codecademy/codebytes 0.6.1 → 0.6.3-alpha.e74dd5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -0
- package/dist/MonacoEditor/colorsDark.d.ts +32 -0
- package/{src/MonacoEditor/colorsDark.ts → dist/MonacoEditor/colorsDark.js} +8 -16
- package/dist/MonacoEditor/index.d.ts +7 -0
- package/dist/MonacoEditor/index.js +42 -0
- package/dist/MonacoEditor/theme.d.ts +2 -0
- package/dist/MonacoEditor/theme.js +122 -0
- package/dist/MonacoEditor/types.d.ts +1 -0
- package/dist/MonacoEditor/types.js +0 -0
- package/dist/__tests__/codebyte-test.d.ts +1 -0
- package/dist/__tests__/codebyte-test.js +195 -0
- package/dist/__tests__/editor-test.d.ts +1 -0
- package/dist/__tests__/editor-test.js +147 -0
- package/dist/__tests__/helpers-test.d.ts +1 -0
- package/dist/__tests__/helpers-test.js +117 -0
- package/dist/__tests__/language-selection-test.d.ts +1 -0
- package/dist/__tests__/language-selection-test.js +15 -0
- package/dist/__tests__/mocks.d.ts +0 -0
- package/dist/__tests__/mocks.js +13 -0
- package/dist/api.d.ts +12 -0
- package/dist/api.js +39 -0
- package/dist/codeByteEditor.d.ts +13 -0
- package/dist/codeByteEditor.js +134 -0
- package/dist/consts.d.ts +23 -0
- package/dist/consts.js +34 -0
- package/dist/drawers.d.ts +6 -0
- package/dist/drawers.js +148 -0
- package/dist/editor.d.ts +13 -0
- package/dist/editor.js +189 -0
- package/dist/helpers/index.d.ts +15 -0
- package/dist/helpers/index.js +29 -0
- package/{src/index.ts → dist/index.d.ts} +0 -0
- package/dist/index.js +2 -0
- package/dist/languageSelection.d.ts +6 -0
- package/dist/languageSelection.js +22 -0
- package/dist/libs/eventTracking.d.ts +1 -0
- package/dist/libs/eventTracking.js +11 -0
- package/{src → dist}/theme.d.ts +0 -0
- package/dist/types.d.ts +14 -0
- package/dist/types.js +0 -0
- package/package.json +7 -3
- package/babel.config.js +0 -15
- package/jest.config.js +0 -1
- package/src/MonacoEditor/index.tsx +0 -56
- package/src/MonacoEditor/theme.ts +0 -64
- package/src/MonacoEditor/types.ts +0 -1
- package/src/__tests__/codebyte-test.tsx +0 -197
- package/src/__tests__/editor-test.tsx +0 -113
- package/src/__tests__/helpers-test.tsx +0 -121
- package/src/__tests__/language-selection-test.tsx +0 -14
- package/src/__tests__/mocks.ts +0 -8
- package/src/api.ts +0 -28
- package/src/codeByteEditor.tsx +0 -118
- package/src/consts.ts +0 -64
- package/src/drawers.tsx +0 -132
- package/src/editor.tsx +0 -160
- package/src/helpers/index.ts +0 -41
- package/src/languageSelection.tsx +0 -26
- package/src/libs/eventTracking.ts +0 -18
- package/src/types.ts +0 -15
- package/tsconfig.json +0 -8
package/src/drawers.tsx
DELETED
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
import { FlexBox, IconButton } from '@codecademy/gamut';
|
|
2
|
-
import {
|
|
3
|
-
ArrowChevronLeftIcon,
|
|
4
|
-
ArrowChevronRightIcon,
|
|
5
|
-
} from '@codecademy/gamut-icons';
|
|
6
|
-
import styled from '@emotion/styled';
|
|
7
|
-
import React, { useState } from 'react';
|
|
8
|
-
|
|
9
|
-
const DrawerLabel = styled.span`
|
|
10
|
-
padding: 0.875rem 0.5rem;
|
|
11
|
-
`;
|
|
12
|
-
|
|
13
|
-
const LeftDrawerIcon = styled(ArrowChevronLeftIcon)<{ open?: boolean }>`
|
|
14
|
-
transition: transform 0.2s ease-in-out;
|
|
15
|
-
`;
|
|
16
|
-
const RightDrawerIcon = LeftDrawerIcon.withComponent(ArrowChevronRightIcon);
|
|
17
|
-
|
|
18
|
-
const Drawer = styled(FlexBox)<{ open?: boolean; hideOnClose?: boolean }>`
|
|
19
|
-
position: relative;
|
|
20
|
-
${({ open, hideOnClose }) => `
|
|
21
|
-
flex-basis: ${open ? '100%' : '0%'};
|
|
22
|
-
visibility: ${!open && hideOnClose ? 'hidden' : 'visible'};
|
|
23
|
-
transition: flex-basis 0.2s ${
|
|
24
|
-
open ? 'ease-out' : 'ease-in, visibility 0s 0.2s'
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
${LeftDrawerIcon}, ${RightDrawerIcon} {
|
|
28
|
-
transform: rotateZ(${open ? '0' : '180'}deg)};
|
|
29
|
-
}
|
|
30
|
-
`}
|
|
31
|
-
`;
|
|
32
|
-
|
|
33
|
-
export type DrawersProps = {
|
|
34
|
-
leftChild: React.ReactNode;
|
|
35
|
-
rightChild: React.ReactNode;
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
export const Drawers: React.FC<DrawersProps> = ({ leftChild, rightChild }) => {
|
|
39
|
-
const [open, setOpen] = useState<'left' | 'right' | 'both'>('both');
|
|
40
|
-
|
|
41
|
-
let ariaLabelCodeButton = 'hide code';
|
|
42
|
-
let ariaLabelOutputButton = 'hide output';
|
|
43
|
-
let isLeftOpen = false;
|
|
44
|
-
let isRightOpen = false;
|
|
45
|
-
|
|
46
|
-
if (open === 'left') {
|
|
47
|
-
ariaLabelCodeButton = ariaLabelOutputButton = 'show output';
|
|
48
|
-
isLeftOpen = true;
|
|
49
|
-
} else if (open === 'right') {
|
|
50
|
-
ariaLabelCodeButton = ariaLabelOutputButton = 'show code';
|
|
51
|
-
isRightOpen = true;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
return (
|
|
55
|
-
<>
|
|
56
|
-
<FlexBox>
|
|
57
|
-
<Drawer
|
|
58
|
-
open={!isRightOpen}
|
|
59
|
-
alignItems="center"
|
|
60
|
-
flexWrap="nowrap"
|
|
61
|
-
textAlign="left"
|
|
62
|
-
borderRight={1}
|
|
63
|
-
borderColor="gray-900"
|
|
64
|
-
px={8}
|
|
65
|
-
>
|
|
66
|
-
<IconButton
|
|
67
|
-
icon={LeftDrawerIcon}
|
|
68
|
-
variant="secondary"
|
|
69
|
-
size="small"
|
|
70
|
-
onClick={() =>
|
|
71
|
-
setOpen((state) => (state === 'both' ? 'right' : 'both'))
|
|
72
|
-
}
|
|
73
|
-
aria-label={ariaLabelCodeButton}
|
|
74
|
-
aria-controls="code-drawer"
|
|
75
|
-
aria-expanded={!isRightOpen}
|
|
76
|
-
/>
|
|
77
|
-
<DrawerLabel id="code-drawer-label">Code</DrawerLabel>
|
|
78
|
-
</Drawer>
|
|
79
|
-
<Drawer
|
|
80
|
-
open={!isLeftOpen}
|
|
81
|
-
alignItems="center"
|
|
82
|
-
flexWrap="nowrap"
|
|
83
|
-
justifyContent="flex-end"
|
|
84
|
-
px={8}
|
|
85
|
-
>
|
|
86
|
-
<DrawerLabel id="output-drawer-label">Output</DrawerLabel>
|
|
87
|
-
<IconButton
|
|
88
|
-
icon={RightDrawerIcon}
|
|
89
|
-
variant="secondary"
|
|
90
|
-
size="small"
|
|
91
|
-
onClick={() =>
|
|
92
|
-
setOpen((state) => (state === 'both' ? 'left' : 'both'))
|
|
93
|
-
}
|
|
94
|
-
aria-label={ariaLabelOutputButton}
|
|
95
|
-
aria-controls="output-drawer"
|
|
96
|
-
aria-expanded={!isLeftOpen}
|
|
97
|
-
/>
|
|
98
|
-
</Drawer>
|
|
99
|
-
</FlexBox>
|
|
100
|
-
<FlexBox
|
|
101
|
-
flexGrow={1}
|
|
102
|
-
borderY={1}
|
|
103
|
-
borderColor="gray-900"
|
|
104
|
-
alignItems="stretch"
|
|
105
|
-
overflow="hidden"
|
|
106
|
-
>
|
|
107
|
-
<Drawer
|
|
108
|
-
hideOnClose
|
|
109
|
-
id="code-drawer"
|
|
110
|
-
aria-labelledby="code-drawer-label"
|
|
111
|
-
open={!isRightOpen}
|
|
112
|
-
flexGrow={0}
|
|
113
|
-
overflow="hidden"
|
|
114
|
-
borderColor="gray-900"
|
|
115
|
-
borderStyleRight="solid"
|
|
116
|
-
borderWidthRight="thin"
|
|
117
|
-
>
|
|
118
|
-
{leftChild}
|
|
119
|
-
</Drawer>
|
|
120
|
-
<Drawer
|
|
121
|
-
hideOnClose
|
|
122
|
-
id="output-drawer"
|
|
123
|
-
aria-labelledby="output-drawer-label"
|
|
124
|
-
open={!isLeftOpen}
|
|
125
|
-
overflow="hidden"
|
|
126
|
-
>
|
|
127
|
-
{rightChild}
|
|
128
|
-
</Drawer>
|
|
129
|
-
</FlexBox>
|
|
130
|
-
</>
|
|
131
|
-
);
|
|
132
|
-
};
|
package/src/editor.tsx
DELETED
|
@@ -1,160 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
FillButton,
|
|
3
|
-
FlexBox,
|
|
4
|
-
Spinner,
|
|
5
|
-
TextButton,
|
|
6
|
-
ToolTip,
|
|
7
|
-
} from '@codecademy/gamut';
|
|
8
|
-
import { CopyIcon } from '@codecademy/gamut-icons';
|
|
9
|
-
import styled from '@emotion/styled';
|
|
10
|
-
import React, { useState } from 'react';
|
|
11
|
-
|
|
12
|
-
import { postSnippet } from './api';
|
|
13
|
-
import type { LanguageOption } from './consts';
|
|
14
|
-
import { Drawers } from './drawers';
|
|
15
|
-
import { trackClick } from './helpers';
|
|
16
|
-
import { SimpleMonacoEditor } from './MonacoEditor';
|
|
17
|
-
import { CodebytesChangeHandler } from './types';
|
|
18
|
-
|
|
19
|
-
const Output = styled.pre<{ hasError: boolean }>`
|
|
20
|
-
width: 100%;
|
|
21
|
-
height: 100%;
|
|
22
|
-
margin: 0;
|
|
23
|
-
padding: 0 1rem;
|
|
24
|
-
font-family: Monaco;
|
|
25
|
-
font-size: 0.875rem;
|
|
26
|
-
overflow: auto;
|
|
27
|
-
${({ hasError, theme }) => `
|
|
28
|
-
color: ${hasError ? theme.colors.orange : theme.colors.white};
|
|
29
|
-
background-color: ${theme.colors['navy-900']};
|
|
30
|
-
`}
|
|
31
|
-
`;
|
|
32
|
-
|
|
33
|
-
const CopyIconStyled = styled(CopyIcon)`
|
|
34
|
-
margin-right: 0.5rem;
|
|
35
|
-
`;
|
|
36
|
-
|
|
37
|
-
const DOCKER_SIGTERM = 143;
|
|
38
|
-
|
|
39
|
-
type EditorProps = {
|
|
40
|
-
hideCopyButton: boolean;
|
|
41
|
-
language: LanguageOption;
|
|
42
|
-
text: string;
|
|
43
|
-
onChange: (text: string) => void;
|
|
44
|
-
snippetsBaseUrl?: string;
|
|
45
|
-
onCopy?: CodebytesChangeHandler;
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
export const Editor: React.FC<EditorProps> = ({
|
|
49
|
-
language,
|
|
50
|
-
text,
|
|
51
|
-
hideCopyButton,
|
|
52
|
-
onChange,
|
|
53
|
-
onCopy,
|
|
54
|
-
snippetsBaseUrl,
|
|
55
|
-
}) => {
|
|
56
|
-
const [output, setOutput] = useState('');
|
|
57
|
-
const [status, setStatus] = useState<'ready' | 'waiting' | 'error'>('ready');
|
|
58
|
-
const [isCodeByteCopied, setIsCodeByteCopied] = useState(false);
|
|
59
|
-
const onCopyClick = () => {
|
|
60
|
-
if (!isCodeByteCopied) {
|
|
61
|
-
navigator.clipboard
|
|
62
|
-
.writeText(text)
|
|
63
|
-
// eslint-disable-next-line no-console
|
|
64
|
-
.catch(() => console.error('Failed to copy'));
|
|
65
|
-
setIsCodeByteCopied(true);
|
|
66
|
-
onCopy?.(text, language);
|
|
67
|
-
trackClick('copy');
|
|
68
|
-
}
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
const setErrorStatusAndOutput = (message: string) => {
|
|
72
|
-
setOutput(message);
|
|
73
|
-
setStatus('error');
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
const handleSubmit = async () => {
|
|
77
|
-
if (text.trim().length === 0) {
|
|
78
|
-
return;
|
|
79
|
-
}
|
|
80
|
-
const data = {
|
|
81
|
-
language,
|
|
82
|
-
code: text,
|
|
83
|
-
};
|
|
84
|
-
setStatus('waiting');
|
|
85
|
-
setOutput('');
|
|
86
|
-
trackClick('run');
|
|
87
|
-
|
|
88
|
-
try {
|
|
89
|
-
const response = await postSnippet(data, snippetsBaseUrl);
|
|
90
|
-
if (response.stderr.length > 0) {
|
|
91
|
-
setErrorStatusAndOutput(response.stderr);
|
|
92
|
-
} else if (response.exit_code === DOCKER_SIGTERM) {
|
|
93
|
-
setErrorStatusAndOutput(
|
|
94
|
-
'Your code took too long to return a result. Double check your code for any issues and try again!'
|
|
95
|
-
);
|
|
96
|
-
} else if (response.exit_code !== 0) {
|
|
97
|
-
setErrorStatusAndOutput('An unknown error occured.');
|
|
98
|
-
} else {
|
|
99
|
-
setOutput(response.stdout);
|
|
100
|
-
setStatus('ready');
|
|
101
|
-
}
|
|
102
|
-
} catch (error) {
|
|
103
|
-
setErrorStatusAndOutput('Error: ' + error);
|
|
104
|
-
}
|
|
105
|
-
};
|
|
106
|
-
|
|
107
|
-
return (
|
|
108
|
-
<>
|
|
109
|
-
<Drawers
|
|
110
|
-
leftChild={
|
|
111
|
-
<SimpleMonacoEditor
|
|
112
|
-
value={text}
|
|
113
|
-
language={language}
|
|
114
|
-
onChange={onChange}
|
|
115
|
-
/>
|
|
116
|
-
}
|
|
117
|
-
rightChild={
|
|
118
|
-
<Output hasError={status === 'error'} aria-live="polite">
|
|
119
|
-
{output}
|
|
120
|
-
</Output>
|
|
121
|
-
}
|
|
122
|
-
/>
|
|
123
|
-
<FlexBox
|
|
124
|
-
justifyContent={hideCopyButton ? 'flex-end' : 'space-between'}
|
|
125
|
-
pl={8}
|
|
126
|
-
>
|
|
127
|
-
{!hideCopyButton ? (
|
|
128
|
-
<ToolTip
|
|
129
|
-
id="codebyte-copied"
|
|
130
|
-
alignment="top-right"
|
|
131
|
-
mode="dark"
|
|
132
|
-
target={
|
|
133
|
-
<TextButton
|
|
134
|
-
variant="secondary"
|
|
135
|
-
onClick={onCopyClick}
|
|
136
|
-
onBlur={() => setIsCodeByteCopied(false)}
|
|
137
|
-
data-testid="copy-codebyte-btn"
|
|
138
|
-
>
|
|
139
|
-
<CopyIconStyled aria-hidden="true" /> Copy Codebyte
|
|
140
|
-
</TextButton>
|
|
141
|
-
}
|
|
142
|
-
>
|
|
143
|
-
{isCodeByteCopied ? (
|
|
144
|
-
<span data-testid="copy-confirmation-tooltip" role="alert">
|
|
145
|
-
Copied!
|
|
146
|
-
</span>
|
|
147
|
-
) : (
|
|
148
|
-
<span data-testid="copy-prompt-tooltip">
|
|
149
|
-
Copy to your clipboard
|
|
150
|
-
</span>
|
|
151
|
-
)}
|
|
152
|
-
</ToolTip>
|
|
153
|
-
) : null}
|
|
154
|
-
<FillButton onClick={handleSubmit}>
|
|
155
|
-
{status === 'waiting' ? <Spinner /> : 'Run'}
|
|
156
|
-
</FillButton>
|
|
157
|
-
</FlexBox>
|
|
158
|
-
</>
|
|
159
|
-
);
|
|
160
|
-
};
|
package/src/helpers/index.ts
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import { trackUserClick } from '../libs/eventTracking';
|
|
2
|
-
|
|
3
|
-
export type CodebyteOptions = {
|
|
4
|
-
clientName: string;
|
|
5
|
-
parentPage: string;
|
|
6
|
-
renderMode: string;
|
|
7
|
-
};
|
|
8
|
-
|
|
9
|
-
export enum CodebytesParams {
|
|
10
|
-
Language = 'lang',
|
|
11
|
-
Text = 'text',
|
|
12
|
-
CopyMode = 'copy-mode',
|
|
13
|
-
ClientName = 'client-name',
|
|
14
|
-
Page = 'page',
|
|
15
|
-
Mode = 'mode',
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export const getOptions = (): CodebyteOptions => {
|
|
19
|
-
const currentUri = new URL(window.location.href);
|
|
20
|
-
|
|
21
|
-
return {
|
|
22
|
-
clientName:
|
|
23
|
-
currentUri.searchParams.get(CodebytesParams.ClientName) || 'Unknown',
|
|
24
|
-
parentPage:
|
|
25
|
-
currentUri.searchParams.get(CodebytesParams.Page) || document.referrer,
|
|
26
|
-
renderMode: currentUri.searchParams.get(CodebytesParams.Mode) || '',
|
|
27
|
-
};
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
export const trackClick = (target: string) => {
|
|
31
|
-
const options = getOptions();
|
|
32
|
-
const page_name = options.renderMode
|
|
33
|
-
? `${options.clientName}_${options.renderMode}`
|
|
34
|
-
: options.clientName;
|
|
35
|
-
|
|
36
|
-
trackUserClick({
|
|
37
|
-
page_name,
|
|
38
|
-
context: options.parentPage,
|
|
39
|
-
target,
|
|
40
|
-
});
|
|
41
|
-
};
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { Select, Text } from '@codecademy/gamut';
|
|
2
|
-
import { ColorMode } from '@codecademy/gamut-styles';
|
|
3
|
-
import React from 'react';
|
|
4
|
-
|
|
5
|
-
import type { LanguageOption } from './consts';
|
|
6
|
-
import { LanguageOptions } from './consts';
|
|
7
|
-
|
|
8
|
-
export type LanguageSelectionProps = {
|
|
9
|
-
onChange: (newLanguage: LanguageOption) => void;
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
export const LanguageSelection: React.FC<LanguageSelectionProps> = ({
|
|
13
|
-
onChange,
|
|
14
|
-
}) => {
|
|
15
|
-
return (
|
|
16
|
-
<ColorMode mode="dark" flex={1} px={16} pt={48}>
|
|
17
|
-
<Text mb={16}>Which language do you want to code in?</Text>
|
|
18
|
-
<Select
|
|
19
|
-
id="language-select"
|
|
20
|
-
aria-label="Select a language"
|
|
21
|
-
options={LanguageOptions}
|
|
22
|
-
onChange={(e) => onChange(e.target.value as LanguageOption)}
|
|
23
|
-
/>
|
|
24
|
-
</ColorMode>
|
|
25
|
-
);
|
|
26
|
-
};
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { createTracker } from '@codecademy/tracking';
|
|
2
|
-
|
|
3
|
-
const IS_DEV = process.env.NODE_ENV === 'development';
|
|
4
|
-
|
|
5
|
-
// TODO: confirm tracking details and implementation DISC-447
|
|
6
|
-
const tracker = createTracker({
|
|
7
|
-
apiBaseUrl:
|
|
8
|
-
typeof window === undefined
|
|
9
|
-
? 'https://www.codecademy.com'
|
|
10
|
-
: window.location.origin,
|
|
11
|
-
verbose: IS_DEV,
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
export const {
|
|
15
|
-
click: trackUserClick,
|
|
16
|
-
visit: trackUserVisit,
|
|
17
|
-
impression: trackUserImpression,
|
|
18
|
-
} = tracker;
|
package/src/types.ts
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { LanguageOption } from './consts';
|
|
2
|
-
|
|
3
|
-
export interface CodebytesChangeHandler {
|
|
4
|
-
(text: string, language: LanguageOption): void;
|
|
5
|
-
}
|
|
6
|
-
export interface CodeByteEditorProps {
|
|
7
|
-
text?: string;
|
|
8
|
-
language?: LanguageOption;
|
|
9
|
-
hideCopyButton?: boolean;
|
|
10
|
-
onCopy?: CodebytesChangeHandler;
|
|
11
|
-
isIFrame?: boolean;
|
|
12
|
-
snippetsBaseUrl?: string;
|
|
13
|
-
onEdit?: CodebytesChangeHandler;
|
|
14
|
-
onLanguageChange?: CodebytesChangeHandler;
|
|
15
|
-
}
|