@codecademy/codebytes 1.0.4-alpha.37044bcdd.0 → 1.0.4-alpha.41a141e2a.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.
Files changed (49) hide show
  1. package/.eslintrc.json +35 -0
  2. package/CHANGELOG.md +14 -0
  3. package/babel.config.js +22 -0
  4. package/jest.config.ts +21 -0
  5. package/package.json +3 -8
  6. package/project.json +52 -0
  7. package/src/MonacoEditor/colorsDark.ts +54 -0
  8. package/src/MonacoEditor/index.tsx +56 -0
  9. package/src/MonacoEditor/theme.ts +65 -0
  10. package/src/MonacoEditor/types.ts +1 -0
  11. package/src/__tests__/codebyte.test.tsx +186 -0
  12. package/src/__tests__/editor.test.tsx +108 -0
  13. package/src/__tests__/helpers.test.tsx +39 -0
  14. package/src/__tests__/language-selection.test.tsx +14 -0
  15. package/src/api.ts +28 -0
  16. package/src/codeByteEditor.tsx +115 -0
  17. package/src/consts.ts +64 -0
  18. package/src/drawers.tsx +133 -0
  19. package/src/editor.tsx +162 -0
  20. package/src/helpers/index.ts +8 -0
  21. package/src/helpers/useEverInView.test.ts +29 -0
  22. package/src/helpers/useEverInView.ts +28 -0
  23. package/src/helpers/useIntersection.test.ts +87 -0
  24. package/src/helpers/useIntersection.ts +35 -0
  25. package/{dist/index.d.ts → src/index.ts} +3 -3
  26. package/src/languageSelection.tsx +26 -0
  27. package/src/libs/eventTracking.ts +18 -0
  28. package/src/theme.d.ts +5 -0
  29. package/src/types.ts +30 -0
  30. package/tsconfig.json +27 -0
  31. package/tsconfig.spec.json +21 -0
  32. package/dist/MonacoEditor/colorsDark.d.ts +0 -32
  33. package/dist/MonacoEditor/index.d.ts +0 -8
  34. package/dist/MonacoEditor/theme.d.ts +0 -2
  35. package/dist/MonacoEditor/types.d.ts +0 -1
  36. package/dist/api.d.ts +0 -12
  37. package/dist/codeByteEditor.d.ts +0 -4
  38. package/dist/consts.d.ts +0 -23
  39. package/dist/drawers.d.ts +0 -6
  40. package/dist/editor.d.ts +0 -15
  41. package/dist/helpers/index.d.ts +0 -2
  42. package/dist/helpers/useEverInView.d.ts +0 -5
  43. package/dist/helpers/useIntersection.d.ts +0 -2
  44. package/dist/index.cjs +0 -829
  45. package/dist/index.js +0 -741
  46. package/dist/languageSelection.d.ts +0 -6
  47. package/dist/libs/eventTracking.d.ts +0 -1
  48. package/dist/package.json +0 -44
  49. package/dist/types.d.ts +0 -22
@@ -0,0 +1,87 @@
1
+ import { act, renderHook } from '@testing-library/react-hooks/dom';
2
+
3
+ import { useIntersection } from './useIntersection';
4
+
5
+ const originalIntersectionObserver = window.IntersectionObserver;
6
+ const mockObserve = jest.fn();
7
+ const mockDisconnect = jest.fn();
8
+ const mockIntersectionObserver = jest.fn();
9
+ const mockIntersectionObserverInstance = {
10
+ observe: mockObserve,
11
+ disconnect: mockDisconnect,
12
+ };
13
+ const ref = { current: document.createElement('div') };
14
+ const options = {
15
+ root: null,
16
+ rootMargin: '0px',
17
+ threshold: 0.2,
18
+ };
19
+
20
+ beforeEach(() => {
21
+ mockIntersectionObserver.mockReturnValue(mockIntersectionObserverInstance);
22
+ window.IntersectionObserver = mockIntersectionObserver;
23
+ });
24
+
25
+ afterEach(() => {
26
+ window.IntersectionObserver = originalIntersectionObserver;
27
+ });
28
+
29
+ describe('useIntersection', () => {
30
+ test('intersection observer is not called', async () => {
31
+ // We render useIntersection with ref.current = null,
32
+ // which should prevent our hook from calling intersectionObserver.
33
+ const hook = renderHook(() => useIntersection({ current: null }, options));
34
+ expect(mockObserve).not.toHaveBeenCalled();
35
+
36
+ hook.unmount();
37
+ expect(mockDisconnect).not.toHaveBeenCalled();
38
+ });
39
+
40
+ test('intersection observer is called', async () => {
41
+ // We render useIntersection with ref.current = HTMLdivElement,
42
+ // which should make our hook call intersectionObserver.
43
+ const hook = renderHook(() => useIntersection(ref, options));
44
+ expect(mockObserve).toHaveBeenCalledWith(ref.current);
45
+
46
+ hook.unmount();
47
+ expect(mockDisconnect).toHaveBeenCalled();
48
+ });
49
+
50
+ it('returns the IntersectionObserverEntry from the callback', async () => {
51
+ const fakeIntersectionObserverEntry = {};
52
+ let handler: Function = () => {
53
+ return null;
54
+ };
55
+
56
+ // We mock the insersectionObserver Implementation
57
+ // to grab the handler, which allows us to simulate
58
+ // the element getting scrolled into view.
59
+ mockIntersectionObserver.mockImplementationOnce((cb) => {
60
+ handler = cb;
61
+
62
+ return mockIntersectionObserverInstance;
63
+ });
64
+ const { result, rerender, unmount } = renderHook(() =>
65
+ useIntersection(ref, options)
66
+ );
67
+
68
+ // Element hasn't been scrolled into view and therefore
69
+ // results.current should be null.
70
+ expect(result.current).toBeNull();
71
+
72
+ // Simulate element being scrolled into view
73
+ act(() => {
74
+ handler([fakeIntersectionObserverEntry]);
75
+ });
76
+ expect(result.current).toEqual(fakeIntersectionObserverEntry);
77
+
78
+ // We expect the results to remain the same after a rerender
79
+ rerender();
80
+ expect(result.current).toEqual(fakeIntersectionObserverEntry);
81
+
82
+ // The results should be null after unmounting.
83
+ unmount();
84
+ rerender();
85
+ expect(result.current).toBeNull();
86
+ });
87
+ });
@@ -0,0 +1,35 @@
1
+ import { RefObject, useEffect, useState } from 'react';
2
+
3
+ export const useIntersection = (
4
+ ref: RefObject<HTMLElement>,
5
+ options: IntersectionObserverInit
6
+ ): IntersectionObserverEntry | null => {
7
+ const [
8
+ intersectionObserverEntry,
9
+ setIntersectionObserverEntry,
10
+ ] = useState<IntersectionObserverEntry | null>(null);
11
+
12
+ useEffect(() => {
13
+ if (ref.current && typeof IntersectionObserver === 'function') {
14
+ const handler = (entries: IntersectionObserverEntry[]) => {
15
+ setIntersectionObserverEntry(entries[0]);
16
+ };
17
+
18
+ const observer = new IntersectionObserver(handler, {
19
+ threshold: options.threshold,
20
+ root: options.root,
21
+ rootMargin: options.rootMargin,
22
+ });
23
+ observer.observe(ref.current);
24
+
25
+ return () => {
26
+ setIntersectionObserverEntry(null);
27
+ observer.disconnect();
28
+ };
29
+ }
30
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
31
+ return () => {};
32
+ }, [ref, options.threshold, options.root, options.rootMargin]);
33
+
34
+ return intersectionObserverEntry;
35
+ };
@@ -1,3 +1,3 @@
1
- export * from './codeByteEditor';
2
- export * from './consts';
3
- export * from './types';
1
+ export * from './codeByteEditor';
2
+ export * from './consts';
3
+ export * from './types';
@@ -0,0 +1,26 @@
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
+ };
@@ -0,0 +1,18 @@
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/theme.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ import { CoreTheme } from '@codecademy/gamut-styles';
2
+
3
+ declare module '@emotion/react' {
4
+ export interface Theme extends CoreTheme {}
5
+ }
package/src/types.ts ADDED
@@ -0,0 +1,30 @@
1
+ import { BackgroundProps } from '@codecademy/gamut-styles';
2
+ import { UserClickData } from '@codecademy/tracking';
3
+
4
+ import { LanguageOption } from './consts';
5
+
6
+ export interface CodebytesChangeHandler {
7
+ (text: string, language: LanguageOption): void;
8
+ }
9
+
10
+ export interface CodebytesCopyFormatterParams {
11
+ text: string;
12
+ language: LanguageOption;
13
+ }
14
+
15
+ export type CodebytesCopyFormatter = ({
16
+ text,
17
+ language,
18
+ }: CodebytesCopyFormatterParams) => string;
19
+
20
+ export interface CodeByteEditorProps extends Omit<BackgroundProps, 'bg'> {
21
+ text?: string;
22
+ language?: LanguageOption;
23
+ hideCopyButton?: boolean;
24
+ copyFormatter?: CodebytesCopyFormatter;
25
+ snippetsBaseUrl?: string;
26
+ onEdit?: CodebytesChangeHandler;
27
+ onLanguageChange?: CodebytesChangeHandler;
28
+ trackingData?: Omit<UserClickData, 'target'>;
29
+ trackFirstEdit?: boolean;
30
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "extends": "../../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "outDir": "./dist",
5
+ "esModuleInterop": true,
6
+ "jsx": "react",
7
+ "allowJs": true,
8
+ "declaration": true,
9
+ "noUnusedParameters": false,
10
+ "sourceMap": true,
11
+ "strictFunctionTypes": false,
12
+ "strictPropertyInitialization": false
13
+ },
14
+ "include": ["src/**/*.ts", "src/**/*.tsx"],
15
+ "exclude": [
16
+ "./dist",
17
+ "jest.config.ts",
18
+ "**/*.spec.ts",
19
+ "**/*.test.ts",
20
+ "**/*.spec.tsx",
21
+ "**/*.test.tsx",
22
+ "**/*.spec.js",
23
+ "**/*.test.js",
24
+ "**/*.spec.jsx",
25
+ "**/*.test.jsx"
26
+ ]
27
+ }
@@ -0,0 +1,21 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "compilerOptions": {
4
+ "outDir": "../../dist/out-tsc",
5
+ "module": "commonjs",
6
+ "types": ["jest", "node"]
7
+ },
8
+ "include": [
9
+ "jest.config.ts",
10
+ "**/*.test.ts",
11
+ "**/*.spec.ts",
12
+ "**/*.test.tsx",
13
+ "**/*.spec.tsx",
14
+ "**/*.test.js",
15
+ "**/*.spec.js",
16
+ "**/*.test.jsx",
17
+ "**/*.spec.jsx",
18
+ "**/*.d.ts",
19
+ "src/__tests__/mocks.ts"
20
+ ]
21
+ }
@@ -1,32 +0,0 @@
1
- export declare const syntax: {
2
- attribute: "#b4d353";
3
- annotation: "#ea6c8b";
4
- atom: "#cc7bc2";
5
- basic: "#ffffff";
6
- comment: "#939598";
7
- constant: "#ff8973";
8
- decoration: "#ea6c8b";
9
- invalid: "#ea6c8b";
10
- key: "#83fff5";
11
- keyword: "#b3ccff";
12
- number: "#ea6c8b";
13
- operator: "#ea6c8b";
14
- predefined: "#ffffff";
15
- property: "#ea6c8b";
16
- regexp: "#b4d353";
17
- string: "#ffe083";
18
- tag: "#ea6c8b";
19
- text: "#ff8973";
20
- value: "#ffe083";
21
- variable: "#b4d353";
22
- };
23
- export declare const ui: {
24
- background: string;
25
- text: "#ffffff";
26
- indent: {
27
- active: string;
28
- inactive: string;
29
- };
30
- };
31
- export declare type SyntaxColors = typeof syntax;
32
- export declare type UIColors = typeof ui;
@@ -1,8 +0,0 @@
1
- import { EditorProps } from '@monaco-editor/react';
2
- import React from 'react';
3
- export declare type SimpleMonacoEditorProps = {
4
- value: string;
5
- language: string;
6
- onChange?: EditorProps['onChange'];
7
- };
8
- export declare const SimpleMonacoEditor: React.FC<SimpleMonacoEditorProps>;
@@ -1,2 +0,0 @@
1
- import type * as monaco from 'monaco-editor';
2
- export declare const dark: monaco.editor.IStandaloneThemeData;
@@ -1 +0,0 @@
1
- export declare type Monaco = typeof import('monaco-editor');
package/dist/api.d.ts DELETED
@@ -1,12 +0,0 @@
1
- import type { LanguageOption } from './consts';
2
- interface Response {
3
- stderr: string;
4
- stdout: string;
5
- exit_code: number;
6
- }
7
- interface PostSnippetData {
8
- language: LanguageOption;
9
- code: string;
10
- }
11
- export declare const postSnippet: (data: PostSnippetData, snippetsBaseUrl?: string | undefined) => Promise<Response>;
12
- export {};
@@ -1,4 +0,0 @@
1
- import React from 'react';
2
- import { CodeByteEditorProps } from './types';
3
- export declare const CodeByteEditor: React.FC<CodeByteEditorProps>;
4
- export default CodeByteEditor;
package/dist/consts.d.ts DELETED
@@ -1,23 +0,0 @@
1
- export declare const LanguageOptions: {
2
- '': string;
3
- cpp: string;
4
- csharp: string;
5
- golang: string;
6
- javascript: string;
7
- php: string;
8
- python: string;
9
- ruby: string;
10
- scheme: string;
11
- };
12
- export declare type LanguageOption = keyof typeof LanguageOptions;
13
- export declare const validLanguages: ("cpp" | "csharp" | "golang" | "javascript" | "php" | "python" | "ruby" | "scheme")[];
14
- export declare const helloWorld: {
15
- readonly cpp: "#include <iostream>\nint main() {\n std::cout << \"Hello world!\";\n return 0;\n}";
16
- readonly csharp: "namespace HelloWorld {\n class Hello {\n static void Main(string[] args) {\n System.Console.WriteLine(\"Hello world!\");\n }\n }\n}";
17
- readonly golang: "package main\nimport \"fmt\"\nfunc main() {\n fmt.Println(\"Hello world!\")\n}";
18
- readonly javascript: "console.log('Hello world!');";
19
- readonly php: "<?php\n echo \"Hello world!\";\n?>";
20
- readonly python: "print('Hello world!')";
21
- readonly ruby: "puts \"Hello world!\"";
22
- readonly scheme: "(begin\n (display \"Hello world!\")\n (newline))";
23
- };
package/dist/drawers.d.ts DELETED
@@ -1,6 +0,0 @@
1
- import React from 'react';
2
- export declare type DrawersProps = {
3
- leftChild: React.ReactNode;
4
- rightChild: React.ReactNode;
5
- };
6
- export declare const Drawers: React.FC<DrawersProps>;
package/dist/editor.d.ts DELETED
@@ -1,15 +0,0 @@
1
- import { UserClickData } from '@codecademy/tracking';
2
- import React from 'react';
3
- import type { LanguageOption } from './consts';
4
- import { CodebytesCopyFormatter } from './types';
5
- declare type EditorProps = {
6
- hideCopyButton: boolean;
7
- language: LanguageOption;
8
- text: string;
9
- onChange: (text: string) => void;
10
- snippetsBaseUrl?: string;
11
- copyFormatter?: CodebytesCopyFormatter;
12
- trackingData?: Omit<UserClickData, 'target'>;
13
- };
14
- export declare const Editor: React.FC<EditorProps>;
15
- export {};
@@ -1,2 +0,0 @@
1
- import { UserClickData } from '@codecademy/tracking';
2
- export declare const trackClick: (target: string, trackingData?: Omit<UserClickData, "target"> | undefined) => void;
@@ -1,5 +0,0 @@
1
- /// <reference types="react" />
2
- export declare const useEverInView: (rootMargin?: string, threshold?: number) => {
3
- everInView: boolean;
4
- ref: import("react").MutableRefObject<null>;
5
- };
@@ -1,2 +0,0 @@
1
- import { RefObject } from 'react';
2
- export declare const useIntersection: (ref: RefObject<HTMLElement>, options: IntersectionObserverInit) => IntersectionObserverEntry | null;