Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions app/client/jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,8 @@ module.exports = {
moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node", "css"],
moduleDirectories: ["node_modules", "src"],
transformIgnorePatterns: ["<rootDir>/node_modules/(?!codemirror)"],
moduleNameMapper: {
"\\.(css|less)$": "<rootDir>/test/__mocks__/styleMock.js",
"\\.svg$": "<rootDir>/test/__mocks__/svgMock.js",
},
};
4 changes: 2 additions & 2 deletions app/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
"algoliasearch": "^4.2.0",
"axios": "^0.18.0",
"chance": "^1.1.3",
"codemirror": "^5.50.0",
"codemirror": "^5.55.0",
"eslint": "^6.4.0",
"fast-deep-equal": "^3.1.1",
"flow-bin": "^0.91.0",
Expand Down Expand Up @@ -149,7 +149,7 @@
"@storybook/addons": "^5.2.6",
"@storybook/preset-create-react-app": "^1.3.1",
"@storybook/react": "^5.2.6",
"@types/codemirror": "^0.0.82",
"@types/codemirror": "^0.0.96",
"@types/jest": "^24.0.22",
"@types/react-beautiful-dnd": "^11.0.4",
"@types/react-select": "^3.0.5",
Expand Down
4 changes: 2 additions & 2 deletions app/client/src/api/DatasourcesApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ export interface Datasource {
headers?: Record<string, string>;
databaseName?: string;
};
invalids: string[];
isValid: boolean;
invalids?: string[];
isValid?: boolean;
}

export interface CreateDatasourceConfig {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Tab, Tabs, TabList, TabPanel } from "react-tabs";
import "react-tabs/style/react-tabs.css";
import styled from "styled-components";

const TabsWrapper = styled.div<{ overflow?: boolean }>`
const TabsWrapper = styled.div<{ shouldOverflow?: boolean }>`
height: 100%;
.react-tabs {
height: 100%;
Expand All @@ -16,7 +16,7 @@ const TabsWrapper = styled.div<{ overflow?: boolean }>`
border-bottom-color: #d0d7dd;
color: #a3b3bf;
${props =>
props.overflow &&
props.shouldOverflow &&
`
overflow-y: hidden;
overflow-x: auto;
Expand Down Expand Up @@ -51,7 +51,7 @@ type TabbedViewComponentType = {

export const BaseTabbedView = (props: TabbedViewComponentType) => {
return (
<TabsWrapper overflow={props.overflow}>
<TabsWrapper shouldOverflow={props.overflow}>
<Tabs
selectedIndex={props.selectedIndex}
onSelect={(index: number) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { ActionResponse } from "api/ActionAPI";
import { formatBytes } from "utils/helpers";
import { APIEditorRouteParams } from "constants/routes";
import LoadingOverlayScreen from "components/editorComponents/LoadingOverlayScreen";
import CodeEditor from "components/editorComponents/CodeEditor";
import ReadOnlyEditor from "components/editorComponents/ReadOnlyEditor";
import { getActionResponses } from "selectors/entitiesSelector";
import { Colors } from "constants/Colors";
import _ from "lodash";
Expand Down Expand Up @@ -201,7 +201,7 @@ const ApiResponseView = (props: Props) => {
</div>
</FailedMessageContainer>
)}
<CodeEditor
<ReadOnlyEditor
input={{
value: response.body
? JSON.stringify(response.body, null, 2)
Expand Down
66 changes: 0 additions & 66 deletions app/client/src/components/editorComponents/CodeEditor.tsx

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import CodeMirror from "codemirror";
import { DataTree } from "entities/DataTree/dataTreeFactory";

export enum EditorModes {
TEXT = "text/plain",
SQL = "sql",
TEXT_WITH_BINDING = "text-js",
JSON = "application/json",
JSON_WITH_BINDING = "json-js",
SQL_WITH_BINDING = "sql-js",
}

export enum EditorTheme {
LIGHT = "LIGHT",
DARK = "DARK",
}
export enum TabBehaviour {
INPUT = "INPUT",
INDENT = "INDENT",
}

export enum EditorSize {
COMPACT = "COMPACT",
EXTENDED = "EXTENDED",
}

export type EditorConfig = {
theme: EditorTheme;
mode: EditorModes;
tabBehaviour: TabBehaviour;
size: EditorSize;
hinting: Array<HintHelper>;
marking: Array<MarkHelper>;
};

export const EditorThemes: Record<EditorTheme, string> = {
[EditorTheme.LIGHT]: "neat",
[EditorTheme.DARK]: "monokai",
};

export type HintHelper = (editor: CodeMirror.Editor, data: DataTree) => Hinter;
export type Hinter = {
showHint: (editor: CodeMirror.Editor) => void;
update?: (data: DataTree) => void;
};

export type MarkHelper = (editor: CodeMirror.Editor) => void;
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import styled from "styled-components";
import _ from "lodash";
import Popper from "pages/Editor/Popper";
import ReactJson from "react-json-view";
import { EditorTheme } from "components/editorComponents/DynamicAutocompleteInput";
import { EditorTheme } from "components/editorComponents/CodeEditor/EditorConfig";
import { theme } from "constants/DefaultTheme";
import { Placement } from "popper.js";

Expand All @@ -23,13 +23,13 @@ type ThemeConfig = {
type PopupTheme = Record<EditorTheme, ThemeConfig>;

const THEMES: PopupTheme = {
LIGHT: {
[EditorTheme.LIGHT]: {
backgroundColor: "#fff",
textColor: "#1E242B",
editorBackground: "#F4F4F4",
editorColor: "#1E242B",
},
DARK: {
[EditorTheme.DARK]: {
backgroundColor: "#23292e",
textColor: "#F4F4F4",
editorBackground: "#090a0f",
Expand Down Expand Up @@ -123,7 +123,7 @@ const CurrentValueViewer = (props: {
Array.isArray(props.evaluatedValue)
) {
const reactJsonProps = {
theme: props.theme === "DARK" ? "monokai" : "rjv-default",
theme: props.theme === EditorTheme.DARK ? "monokai" : "rjv-default",
name: null,
enableClipboard: false,
displayObjectSize: false,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { bindingHint } from "components/editorComponents/CodeEditor/hintHelpers";
import { MockCodemirrorEditor } from "../../../../test/__mocks__/CodeMirrorEditorMock";
import RealmExecutor from "jsExecution/RealmExecutor";
jest.mock("jsExecution/RealmExecutor");

describe("hint helpers", () => {
beforeAll(() => {
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
// @ts-ignore
RealmExecutor.mockClear();
});
describe("binding hint helper", () => {
it("is initialized correctly", () => {
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
// @ts-ignore
const helper = bindingHint(MockCodemirrorEditor, {});
expect(MockCodemirrorEditor.setOption).toBeCalled();
expect(helper).toHaveProperty("showHint");
expect(helper).toHaveProperty("update");
});

it("opens hint correctly", () => {
// Setup
type Case = {
value: string;
cursor: { ch: number; line: number };
toCall: "closeHint" | "showHint";
getLine?: string[];
};
const cases: Case[] = [
{ value: "ABC", cursor: { ch: 3, line: 0 }, toCall: "closeHint" },
{ value: "{{ }}", cursor: { ch: 3, line: 0 }, toCall: "showHint" },
{
value: '{ name: "{{}}" }',
cursor: { ch: 11, line: 0 },
toCall: "showHint",
},
{
value: '{ name: "{{}}" }',
cursor: { ch: 12, line: 0 },
toCall: "closeHint",
},
{
value: "{somethingIsHere }}",
cursor: { ch: 18, line: 0 },
toCall: "closeHint",
},
{
value: `{\n\tname: "{{}}"\n}`,
getLine: ["{", `\tname: "{{}}`, "}"],
cursor: { ch: 10, line: 1 },
toCall: "showHint",
},
];

cases.forEach(testCase => {
MockCodemirrorEditor.getValue.mockReturnValueOnce(testCase.value);
MockCodemirrorEditor.getCursor.mockReturnValueOnce(testCase.cursor);
if (testCase.getLine) {
testCase.getLine.forEach(line => {
MockCodemirrorEditor.getLine.mockReturnValueOnce(line);
});
}
});

// Test
cases.forEach(() => {
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
// @ts-ignore
const helper = bindingHint(MockCodemirrorEditor, {});
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
// @ts-ignore
helper.showHint(MockCodemirrorEditor);
});

// Assert
const showHintCount = cases.filter(c => c.toCall === "showHint").length;
expect(MockCodemirrorEditor.showHint).toHaveBeenCalledTimes(
showHintCount,
);
const closeHintCount = cases.filter(c => c.toCall === "closeHint").length;
expect(MockCodemirrorEditor.closeHint).toHaveBeenCalledTimes(
closeHintCount,
);
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import CodeMirror from "codemirror";
import TernServer from "utils/autocomplete/TernServer";
import KeyboardShortcuts from "constants/KeyboardShortcuts";
import { dataTreeTypeDefCreator } from "utils/autocomplete/dataTreeTypeDefCreator";
import { DataTree } from "entities/DataTree/dataTreeFactory";
import { getDynamicStringSegments } from "utils/DynamicBindingUtils";
import { HintHelper } from "components/editorComponents/CodeEditor/EditorConfig";
import AnalyticsUtil from "utils/AnalyticsUtil";

export const bindingHint: HintHelper = (editor, data) => {
const ternServer = new TernServer(data);
editor.setOption("extraKeys", {
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
// @ts-ignore
...editor.options.extraKeys,
[KeyboardShortcuts.CodeEditor.OpenAutocomplete]: (cm: CodeMirror.Editor) =>
ternServer.complete(cm),
[KeyboardShortcuts.CodeEditor.ShowTypeAndInfo]: (cm: CodeMirror.Editor) => {
ternServer.showType(cm);
},
[KeyboardShortcuts.CodeEditor.OpenDocsLink]: (cm: CodeMirror.Editor) => {
ternServer.showDocs(cm);
},
});
return {
update: (data: DataTree) => {
const dataTreeDef = dataTreeTypeDefCreator(data);
ternServer.updateDef("dataTree", dataTreeDef);
},
showHint: (editor: CodeMirror.Editor) => {
let cursorBetweenBinding = false;
const cursor = editor.getCursor();
const value = editor.getValue();
let cursorIndex = cursor.ch;
if (cursor.line > 0) {
for (let lineIndex = 0; lineIndex < cursor.line; lineIndex++) {
const line = editor.getLine(lineIndex);
// Add line length + 1 for new line character
cursorIndex = cursorIndex + line.length + 1;
}
}
const stringSegments = getDynamicStringSegments(value);
// count of chars processed
let cumulativeCharCount = 0;
stringSegments.forEach((segment: string) => {
const start = cumulativeCharCount;
const dynamicStart = segment.indexOf("{{");
const dynamicDoesStart = dynamicStart > -1;
const dynamicEnd = segment.indexOf("}}");
const dynamicDoesEnd = dynamicEnd > -1;
const dynamicStartIndex = dynamicStart + start + 2;
const dynamicEndIndex = dynamicEnd + start;
if (
dynamicDoesStart &&
cursorIndex >= dynamicStartIndex &&
((dynamicDoesEnd && cursorIndex <= dynamicEndIndex) ||
(!dynamicDoesEnd && cursorIndex >= dynamicStartIndex))
) {
cursorBetweenBinding = true;
}
cumulativeCharCount = start + segment.length;
});

const shouldShow = cursorBetweenBinding;
if (shouldShow) {
AnalyticsUtil.logEvent("AUTO_COMPELTE_SHOW", {});
ternServer.complete(editor);
} else {
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
// @ts-ignore
editor.closeHint();
}
},
};
};
Loading