diff --git a/app/client/cypress/e2e/Regression/ClientSide/Autocomplete/Autocomplete_setters_spec.ts b/app/client/cypress/e2e/Regression/ClientSide/Autocomplete/Autocomplete_setters_spec.ts index 69b58556b7..a9ab1ac4a7 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/Autocomplete/Autocomplete_setters_spec.ts +++ b/app/client/cypress/e2e/Regression/ClientSide/Autocomplete/Autocomplete_setters_spec.ts @@ -97,4 +97,12 @@ describe("Autocomplete tests for setters", () => { ); agHelper.AssertElementAbsence(locators._evalValuePopover); }); + + it("4. function description tooltip shows up", () => { + entityExplorer.DragDropWidgetNVerify(draggableWidgets.BUTTON, 100, 100); + entityExplorer.SelectEntityByName("Button1"); + propPane.EnterJSContext("onClick", "{{showAlert", true, false); + agHelper.GetElementsNAssertTextPresence(locators._hints, "showAlert"); + agHelper.AssertElementExist(locators._tern_doc); + }); }); diff --git a/app/client/cypress/support/Objects/CommonLocators.ts b/app/client/cypress/support/Objects/CommonLocators.ts index 2271aa9e8e..1469466849 100644 --- a/app/client/cypress/support/Objects/CommonLocators.ts +++ b/app/client/cypress/support/Objects/CommonLocators.ts @@ -201,6 +201,7 @@ export class CommonLocators { `//p[text()='${fieldName}']/parent::div//following-sibling::div//input[@type='checkbox']`; _deployedPage = `.t--page-switch-tab`; _hints = "ul.CodeMirror-hints li"; + _tern_doc = ".t--tern-doc"; _argHintFnName = ".CodeMirror-Tern-tooltip .CodeMirror-Tern-fname"; _cancelActionExecution = ".t--cancel-action-button"; _widgetPane = "[data-testid='widget-sidebar-scrollable-wrapper']"; diff --git a/app/client/src/ce/utils/autocomplete/EntityDefinitions.ts b/app/client/src/ce/utils/autocomplete/EntityDefinitions.ts index 96efebcd3b..43f6cdc1bd 100644 --- a/app/client/src/ce/utils/autocomplete/EntityDefinitions.ts +++ b/app/client/src/ce/utils/autocomplete/EntityDefinitions.ts @@ -19,16 +19,65 @@ export const entityDefinitions = { ) { return { ...generatedTypeDef, + "!doc": + "A global object that provides access to information and functionalities within an application", + "!url": "https://docs.appsmith.com/reference/appsmith-framework", + store: { + ...(generatedTypeDef.store as Def), + "!doc": + "Object to access any app-level data or temporary state that is stored on the user's browser", + "!url": + "https://docs.appsmith.com/reference/appsmith-framework/context-object#store-object", + }, + user: { + ...(generatedTypeDef.user as Def), + "!doc": + "Object that contains the data of the currently authenticated user.", + "!url": + "https://docs.appsmith.com/reference/appsmith-framework/context-object#user-object", + }, + URL: { + ...(generatedTypeDef.URL as Def), + "!doc": "Object containing all the attributes of the current URL", + "!url": + "https://docs.appsmith.com/reference/appsmith-framework/context-object#url-object", + }, + theme: { + ...(generatedTypeDef.theme as Def), + "!doc": + "Object containing the details of the theme properties applied to the application", + "!url": + "https://docs.appsmith.com/reference/appsmith-framework/context-object#theme-object", + }, + mode: { + "!type": generatedTypeDef.mode as Def, + "!doc": + "An enum that contains whether the app runs in view or edit mode. It takes the values VIEW or EDIT", + "!url": + "https://docs.appsmith.com/reference/appsmith-framework/context-object#mode-enum", + }, geolocation: { ...generatedTypeDef.geolocation, "!doc": - "The user's geo location information. Only available when requested", + "Object containing functions that allow you to retrieve the current user's location and the coordinates received from the user's device using the Geolocation API.", "!url": "https://docs.appsmith.com/reference/appsmith-framework/context-object#geolocation-object", - getCurrentPosition: - "fn(onSuccess: fn() -> void, onError: fn() -> void, options: object) -> void", - watchPosition: "fn(options: object) -> void", - clearWatch: "fn() -> void", + getCurrentPosition: { + "!type": + "fn(onSuccess: fn() -> void, onError: fn() -> void, options: object) -> +Promise|void", + "!url": + "https://docs.appsmith.com/reference/appsmith-framework/context-object#geolocationgetcurrentposition", + }, + watchPosition: { + "!type": "fn(options: object) -> void", + "!url": + "https://docs.appsmith.com/reference/appsmith-framework/context-object#geolocationwatchposition", + }, + clearWatch: { + "!type": "fn() -> +Promise|void", + "!url": + "https://docs.appsmith.com/reference/appsmith-framework/context-object#geolocationclearwatch", + }, }, }; } @@ -36,29 +85,59 @@ export const entityDefinitions = { }, ACTION: (entity: ActionEntity, extraDefsToDefine: ExtraDef) => { const dataDef = generateTypeDef(entity.data, extraDefsToDefine); + let responseMetaDef = generateTypeDef( + entity.responseMeta, + extraDefsToDefine, + ); - let data: Def = { - "!doc": "The response of the action", + if (_.isString(responseMetaDef)) { + responseMetaDef = { + "!type": responseMetaDef, + }; + } + + let dataCustomDef: Def = { + "!doc": + "A read-only property that contains the response body from the last successful execution of this query.", + "!url": + "https://docs.appsmith.com/reference/appsmith-framework/query-object#data-array", }; if (_.isString(dataDef)) { - data["!type"] = dataDef; + dataCustomDef["!type"] = dataDef; } else { - data = { ...data, ...dataDef }; + dataCustomDef = { ...dataCustomDef, ...dataDef }; } return { "!doc": - "Actions allow you to connect your widgets to your backend data in a secure manner.", + "Object that contains the properties required to run queries and access the query data.", "!url": "https://docs.appsmith.com/reference/appsmith-framework/query-object", - isLoading: "bool", - data, - responseMeta: { - "!doc": "The response meta of the action", - "!type": "?", + isLoading: { + "!type": "bool", + "!doc": + "Boolean that indicates whether the query is currently being executed.", + }, + data: dataCustomDef, + responseMeta: { + "!doc": + "Object that contains details about the response, such as the status code, headers, and other relevant information related to the query's execution and the server's response.", + "!url": + "https://docs.appsmith.com/reference/appsmith-framework/query-object#responsemeta-object", + ...responseMetaDef, + }, + run: { + "!type": "fn(params: ?) -> +Promise", + "!url": + "https://docs.appsmith.com/reference/appsmith-framework/query-object#queryrun", + "!doc": "Executes the query with the given parameters.", + }, + clear: { + "!type": "fn() -> +Promise", + "!url": + "https://docs.appsmith.com/reference/appsmith-framework/query-object#queryclear", + "!doc": "Clears the query data.", }, - run: "fn(params: ?) -> +Promise", - clear: "fn() -> +Promise", }; }, }; @@ -99,57 +178,89 @@ export const GLOBAL_DEFS = { export const GLOBAL_FUNCTIONS = { "!name": "DATA_TREE.APPSMITH.FUNCTIONS", navigateTo: { - "!doc": "Action to navigate the user to another page or url", + "!url": + "https://docs.appsmith.com/reference/appsmith-framework/widget-actions/navigate-to", + "!doc": + "Enables navigation between the internal pages of the App or to an external URL.", "!type": "fn(pageNameOrUrl: string, params: {}, target?: string) -> +Promise", }, showAlert: { - "!doc": "Show a temporary notification style message to the user", + "!url": + "https://docs.appsmith.com/reference/appsmith-framework/widget-actions/show-alert", + "!doc": + "Displays a temporary toast-style alert message to the user for precisely 5 seconds. The duration of the alert message can't be modified.", "!type": "fn(message: string, style: string) -> +Promise", }, showModal: { - "!doc": "Open a modal", + "!url": + "https://docs.appsmith.com/reference/appsmith-framework/widget-actions/show-modal", + "!doc": + "Opens an existing Modal widget and bring it into focus on the page", "!type": "fn(modalName: string) -> +Promise", }, closeModal: { + "!url": + "https://docs.appsmith.com/reference/appsmith-framework/widget-actions/close-modal", "!doc": "Close a modal", "!type": "fn(modalName: string) -> +Promise", }, storeValue: { - "!doc": "Store key value data locally", - "!type": "fn(key: string, value: any) -> +Promise", + "!url": + "https://docs.appsmith.com/reference/appsmith-framework/widget-actions/store-value", + "!doc": + "Stores the data in the browser's local storage as key-value pairs that represent storage objects and can be later accessed anywhere in the application via appsmith.store.", + "!type": "fn(key: string, value: any, persist?: bool) -> +Promise", }, removeValue: { + "!url": + "https://docs.appsmith.com/reference/appsmith-framework/widget-actions/remove-value", "!doc": "Remove key value data locally", "!type": "fn(key: string) -> +Promise", }, clearStore: { + "!url": + "https://docs.appsmith.com/reference/appsmith-framework/widget-actions/clear-store", "!doc": "Clear all key value data locally", "!type": "fn() -> +Promise", }, download: { - "!doc": "Download anything as a file", + "!url": + "https://docs.appsmith.com/reference/appsmith-framework/widget-actions/download", + "!doc": + "Download any data as a file, leveraging the capabilities of the downloadjs library.", "!type": "fn(data: string|+Blob, fileName: string, fileType?: string) -> +Promise", }, copyToClipboard: { - "!doc": "Copy text to clipboard", + "!url": + "https://docs.appsmith.com/reference/appsmith-framework/widget-actions/copy-to-clipboard", + "!doc": "Copies the given text to clipboard", "!type": "fn(data: string, options: object) -> +Promise", }, resetWidget: { - "!doc": "Reset widget values", + "!url": + "https://docs.appsmith.com/reference/appsmith-framework/widget-actions/reset-widget", + "!doc": + "Resets a widget to its default state. All user input changes are reverted and its properties' default values are applied.", "!type": "fn(widgetName: string, resetChildren: bool) -> +Promise", }, setInterval: { - "!doc": "Execute triggers at a given interval", + "!url": + "https://docs.appsmith.com/reference/appsmith-framework/widget-actions/intervals-time-events", + "!doc": "Executes a function at a given interval", "!type": "fn(callback: fn() -> void, interval: number, id?: string) -> number", }, clearInterval: { + "!url": + "https://docs.appsmith.com/reference/appsmith-framework/widget-actions/clear-interval", "!doc": "Stop executing a setInterval with id", "!type": "fn(id: string) -> void", }, postWindowMessage: { + "!url": + "https://docs.appsmith.com/reference/appsmith-framework/widget-actions/post-message", "!doc": "Establish cross-origin communication between Window objects/page and iframes", "!type": "fn(message: unknown, source: string, targetOrigin: string)", @@ -180,4 +291,55 @@ export const getPropsForJSActionEntity = ({ return properties; }; +export const ternDocsInfo: Record = { + showAlert: { + exampleArgs: [ + "'This is a success message', 'success'", + "'This is an error message', 'error'", + ], + }, + showModal: { + exampleArgs: ["'Modal1'"], + }, + closeModal: { + exampleArgs: ["'Modal1'"], + }, + navigateTo: { + exampleArgs: [ + "'Page1', { id: 1 }", + "'https://appsmith.com', {}, 'NEW_WINDOW'", + ], + }, + copyToClipboard: { + exampleArgs: ["'Hello'"], + }, + download: { + exampleArgs: [ + "'Hello World', 'hello.txt', 'text/plain'", + "FilePicker1.files[0].data, 'data.json'", + ], + }, + storeValue: { + exampleArgs: ["'key', 'value'"], + }, + removeValue: { + exampleArgs: ["'key'"], + }, + clearStore: { + exampleArgs: [""], + }, + resetWidget: { + exampleArgs: ["'Table1', false"], + }, + setInterval: { + exampleArgs: ["() => showAlert('Hello'), 1000, 'id'"], + }, + clearInterval: { + exampleArgs: ["'id'"], + }, + postWindowMessage: { + exampleArgs: ["message, 'Iframe1', '*'"], + }, +}; + export type EntityDefinitionsOptions = keyof typeof entityDefinitions; diff --git a/app/client/src/components/editorComponents/CodeEditor/styledComponents.ts b/app/client/src/components/editorComponents/CodeEditor/styledComponents.ts index 7a5192f426..783921980d 100644 --- a/app/client/src/components/editorComponents/CodeEditor/styledComponents.ts +++ b/app/client/src/components/editorComponents/CodeEditor/styledComponents.ts @@ -37,6 +37,7 @@ export const CodeEditorColors = { NUMBER: "#555", COMMENT: "var(--ads-v2-color-gray-400)", FUNCTION_ARGS: "hsl(288, 44%, 44%)", + TOOLTIP_FN_ARGS: "#DB6E33", }; export const EditorWrapper = styled.div<{ diff --git a/app/client/src/constants/defs/browser.json b/app/client/src/constants/defs/browser.json index dd464f377c..66d3e55c49 100644 --- a/app/client/src/constants/defs/browser.json +++ b/app/client/src/constants/defs/browser.json @@ -245,12 +245,12 @@ "setTimeout": { "!type": "fn(f: fn(), ms: number) -> number", "!url": "https://developer.mozilla.org/en/docs/DOM/window.setTimeout", - "!doc": "Calls a function or executes a code snippet after specified delay." + "!doc": "Calls a function or executes a code snippet after specified delay. Returns a number that can be used to clear the timeout with clearTimeout()." }, "clearTimeout": { - "!type": "fn(timeout: number)", + "!type": "fn(timerId: number)", "!url": "https://developer.mozilla.org/en/docs/DOM/window.clearTimeout", - "!doc": "Clears the delay set by window.setTimeout()." + "!doc": "Clears the function scheduled to run by setTimeout()." }, "Response": { "!type": "fn()", diff --git a/app/client/src/constants/defs/ecmascript.json b/app/client/src/constants/defs/ecmascript.json index 228661efa0..eff7ec6a6b 100644 --- a/app/client/src/constants/defs/ecmascript.json +++ b/app/client/src/constants/defs/ecmascript.json @@ -1461,8 +1461,7 @@ "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/JSON/stringify", "!doc": "Convert a value to JSON, optionally replacing values if a replacer function is specified, or optionally including only the specified properties if a replacer array is specified." }, - "!url": "https://developer.mozilla.org/en-US/docs/JSON", - "!doc": "JSON (JavaScript Object Notation) is a data-interchange format. It closely resembles a subset of JavaScript syntax, although it is not a strict subset. (See JSON in the JavaScript Reference for full details.) It is useful when writing any kind of JavaScript-based application, including websites and browser extensions. For example, you might store user information in JSON format in a cookie, or you might store extension preferences in JSON in a string-valued browser preference." + "!url": "https://developer.mozilla.org/en-US/docs/JSON" }, "ArrayBuffer": { "!type": "fn(length: number)", diff --git a/app/client/src/globalStyles/CodemirrorHintStyles.ts b/app/client/src/globalStyles/CodemirrorHintStyles.ts index 39ace98f3b..658ad56fd5 100644 --- a/app/client/src/globalStyles/CodemirrorHintStyles.ts +++ b/app/client/src/globalStyles/CodemirrorHintStyles.ts @@ -2,6 +2,7 @@ import { createGlobalStyle } from "styled-components"; import type { EditorTheme } from "components/editorComponents/CodeEditor/EditorConfig"; import type { Theme } from "constants/DefaultTheme"; import { LINT_TOOLTIP_JUSTIFIED_LEFT_CLASS } from "components/editorComponents/CodeEditor/constants"; +import { CodeEditorColors } from "components/editorComponents/CodeEditor/styledComponents"; export const CodemirrorHintStyles = createGlobalStyle<{ editorTheme: EditorTheme; @@ -14,15 +15,13 @@ export const CodemirrorHintStyles = createGlobalStyle<{ z-index: 20; overflow: hidden; list-style: none; - margin-top: ${(props) => props.theme.spaces[3]}px; - padding: 0px 0px; - font-family: monospace; + padding: 0px; + font-family: ${(props) => props.theme.fonts.code}; max-height: 25em; overflow-y: auto; background: var(--ads-v2-color-bg); box-shadow: var(--ads-v2-shadow-popovers); border: 1px solid var(--ads-v2-color-border); - border-radius: var(--ads-v2-border-radius); } .CodeMirror-hint { @@ -38,7 +37,6 @@ export const CodemirrorHintStyles = createGlobalStyle<{ letter-spacing: -0.24px; &:hover { background: var(--ads-v2-color-bg-subtle); - border-radius: 0px; color: var(--ads-v2-color-fg); &:after { color: var(--ads-v2-color-fg); @@ -82,9 +80,6 @@ export const CodemirrorHintStyles = createGlobalStyle<{ font-family: ${(props) => props.theme.fonts.text}; font-size:14px; margin: 0 4px; - &:hover { - border-radius: var(--ads-v2-border-radius); - } &.CodeMirror-hint-active { .magic { path { @@ -172,7 +167,6 @@ export const CodemirrorHintStyles = createGlobalStyle<{ bottom: 6px; height: 12px; width: 12px; - border-radius: var(--ads-v2-border-radius); font-size: 10px; line-height: 12px; font-weight: normal; @@ -238,7 +232,6 @@ export const CodemirrorHintStyles = createGlobalStyle<{ } li.CodeMirror-hint-active { background-color: var(--ads-v2-color-bg-muted); - border-radius: var(--ads-v2-border-radius); color: var(--ads-v2-color-fg); &:after { color: var(--ads-v2-color-fg); @@ -261,22 +254,43 @@ export const CodemirrorHintStyles = createGlobalStyle<{ background: var(--ads-v2-color-bg); box-shadow: var(--ads-v2-shadow-popovers); border: 1px solid var(--ads-v2-color-border); - border-radius: var(--ads-v2-border-radius); + z-index: 15px; + font-weight: 500; max-width: none; white-space: nowrap; .CodeMirror-Tern-fname { - color: #304EAA; + color: ${CodeEditorColors.KEYWORD}; } .CodeMirror-Tern-farg { - color: #DB6E33; + color: ${CodeEditorColors.TOOLTIP_FN_ARGS}; &.CodeMirror-Tern-farg-current { - color: #DB6E33; + color: ${CodeEditorColors.TOOLTIP_FN_ARGS}; font-weight: 600; } } .CodeMirror-Tern-type { color: #364252; } + &.CodeMirror-Tern-hint-doc { + display: block; + background: var(--ads-v2-color-bg); + box-shadow: var(--ads-v2-shadow-popovers); + border: 1px solid var(--ads-v2-color-border); + color: var(--ads-v2-color-fg); + max-height: 150px; + max-width: 350px; + overflow: auto; + font-size: 11px; + padding: 0 !important; + .doc-link > span { + font-size: var(--ads-v2-font-size-2); + } + code { + background: var(--ads-v2-color-bg-subtle); + padding: 2px 4px; + border-radius: var(--ads-v2-border-radius); + } + } } } @@ -299,7 +313,6 @@ export const CodemirrorHintStyles = createGlobalStyle<{ bottom: 6px; height: 12px; width: 12px; - border-radius: var(--ads-v2-border-radius); font-size: 10px; line-height: 12px; font-weight: normal; @@ -339,26 +352,9 @@ export const CodemirrorHintStyles = createGlobalStyle<{ } } - .CodeMirror-Tern-hint-doc { - display: none; - &.visible { - display: block; - background-color: var(--ads-v2-color-bg) !important; - color: var(--ads-v2-color-fg) !important; - max-height: 150px; - width: 250px; - font-size: 12px; - padding: 5px !important; - border: 1px solid !important; - border: 1px solid var(--ads-v2-color-border) !important; - box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.12) !important; - overflow: scroll; - } - } .CodeMirror-lint-tooltip { && { border: 1px solid var(--ads-v2-color-border) !important; - border-radius: var(--ads-v2-border-radius); background: var(--ads-v2-color-bg) !important; box-shadow: 0px 12px 28px -6px rgba(0, 0, 0, 0.32); padding: 4px; diff --git a/app/client/src/utils/FilterInternalProperties/Common.ts b/app/client/src/utils/FilterInternalProperties/Common.ts index e57a878d99..ce8758a105 100644 --- a/app/client/src/utils/FilterInternalProperties/Common.ts +++ b/app/client/src/utils/FilterInternalProperties/Common.ts @@ -10,27 +10,25 @@ export const createObjectPeekData = ( parentKey: string, ) => { Object.keys(defs).forEach((key: string) => { - if (key.indexOf("!") === -1) { - const childKeyPathArray = [parentKey, key]; - if (isObject(defs[key])) { - if (Object.keys(defs[key]).length > 0) { - peekData[key] = {}; - const result = createObjectPeekData( - defs[key], - data[key], - peekData[key], - key, - ); - _.set(peekData, childKeyPathArray, result.peekData); - } else { - peekData[key] = data[key]; - } - } else { - peekData[key] = isTernFunctionDef(defs[key]) - ? // eslint-disable-next-line @typescript-eslint/no-empty-function - function () {} // tern inference required here - : data[key]; - } + if (key.startsWith("!")) return; + const childKeyPathArray = [parentKey, key]; + if ( + isObject(defs[key]) && + Object.keys(defs[key]).filter((k) => !k.startsWith("!")).length > 0 + ) { + peekData[key] = {}; + const result = createObjectPeekData( + defs[key], + data[key], + peekData[key], + key, + ); + _.set(peekData, childKeyPathArray, result.peekData); + } else { + peekData[key] = isTernFunctionDef(defs[key]) + ? // eslint-disable-next-line @typescript-eslint/no-empty-function + function () {} // tern inference required here + : data[key]; } }); return { peekData }; diff --git a/app/client/src/utils/autocomplete/AutocompleteSortRules.ts b/app/client/src/utils/autocomplete/AutocompleteSortRules.ts index a098a61264..eac6ae070a 100644 --- a/app/client/src/utils/autocomplete/AutocompleteSortRules.ts +++ b/app/client/src/utils/autocomplete/AutocompleteSortRules.ts @@ -313,6 +313,9 @@ class BlockAsyncFnsInDataFieldRule implements AutocompleteRule { "clearTimeout", "setInterval", "clearInterval", + "postWindowMessage", + "windowMessageListener", + "watchPosition", ]; computeScore( completion: Completion, diff --git a/app/client/src/utils/autocomplete/CodemirrorTernService.ts b/app/client/src/utils/autocomplete/CodemirrorTernService.ts index 405b04d665..72178e2e4a 100644 --- a/app/client/src/utils/autocomplete/CodemirrorTernService.ts +++ b/app/client/src/utils/autocomplete/CodemirrorTernService.ts @@ -20,6 +20,7 @@ import { } from "../getCodeMirrorNamespace"; import AnalyticsUtil from "utils/AnalyticsUtil"; import { findIndex, isString } from "lodash"; +import { renderTernTooltipContent } from "./ternDocTooltip"; const bigDoc = 250; const cls = "CodeMirror-Tern-"; @@ -558,32 +559,33 @@ class CodeMirrorTernService { CodeMirror.on( obj, "select", - (cur: { data: { doc: string } }, node: any) => { + (cur: Completion, node: any) => { this.active = cur; this.remove(tooltip); const content = cur.data.doc; - if (content) { - tooltip = this.makeTooltip( - node.parentNode.getBoundingClientRect().right + window.pageXOffset, - node.getBoundingClientRect().top + window.pageYOffset, - content, - cm, - ); - tooltip.className += " " + cls + "hint-doc"; - CodeMirror.on( - cm, - "keyup", - (cm: CodeMirror.Editor, keyboardEvent: KeyboardEvent) => { - if ( - keyboardEvent.code === "Space" && - keyboardEvent.ctrlKey && - tooltip - ) { - tooltip.className += " visible"; - } - }, - ); - } + if (!content) return; + const docTooltipContainer = this.elt("div", "flex flex-col pb-1"); + renderTernTooltipContent(docTooltipContainer, cur); + tooltip = this.makeTooltip( + node.parentNode.getBoundingClientRect().right + window.pageXOffset, + node.getBoundingClientRect().top + window.pageYOffset + 2, + docTooltipContainer, + cm, + cls + "hint-doc", + ); + CodeMirror.on( + cm, + "keyup", + (cm: CodeMirror.Editor, keyboardEvent: KeyboardEvent) => { + if ( + keyboardEvent.code === "Space" && + keyboardEvent.ctrlKey && + tooltip + ) { + tooltip.className += " visible"; + } + }, + ); }, ); resolve(obj); diff --git a/app/client/src/utils/autocomplete/ternDocTooltip.tsx b/app/client/src/utils/autocomplete/ternDocTooltip.tsx new file mode 100644 index 0000000000..d703190e01 --- /dev/null +++ b/app/client/src/utils/autocomplete/ternDocTooltip.tsx @@ -0,0 +1,74 @@ +import React from "react"; +import ReactDOM from "react-dom"; +import { ternDocsInfo } from "@appsmith/utils/autocomplete/EntityDefinitions"; +import type { Completion, TernCompletionResult } from "./CodemirrorTernService"; +import { CodeEditorColors } from "components/editorComponents/CodeEditor/styledComponents"; +import { Link } from "design-system"; + +export function renderTernTooltipContent( + element: HTMLElement, + completion: Completion, +) { + ReactDOM.render(, element); +} + +export function TernDocToolTip(props: { + completion: Completion; +}) { + const { completion } = props; + const { + data: { doc, url }, + displayText, + } = completion; + + if (!doc || !displayText) return null; + + const examples = + displayText in ternDocsInfo ? ternDocsInfo[displayText].exampleArgs : null; + + return ( +
+
+ {displayText} + {url && ( + + [docs] + + )} +
+
+      {examples && (
+        
Example
+ )} + {examples && ( +
+ {examples.map((example: string) => { + const fnName = displayText; + const args = example; + return ( + + {`${fnName}(`} + + {args} + + {")"} + + ); + })} +
+ )} +
+ ); +}