feat: show additional info for autocomplete results (#28564)
## Description Adds another popover against a selected autocomplete result that shows relevant information beforehand to the user. Info includes a brief description, example if applicable and a link to docs. > #### PR fixes following issue(s) Fixes #23381 > #### Media <img width="580" alt="Screenshot 2023-11-07 at 13 00 25" src="https://github.com/appsmithorg/appsmith/assets/32433245/3a9d254d-29cf-4ad4-8033-c4763431a215"> #### Type of change - New feature (non-breaking change which adds functionality) > ## Testing > #### How Has This Been Tested? - [x] Manual - [x] Cypress > > #### Test Plan > Add Testsmith test cases links that relate to this PR > > #### Issues raised during DP testing > Link issues raised during DP testing for better visiblity and tracking (copy link from comments dropped on this PR) > ## Checklist: #### Dev activity - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my own code - [x] I have commented my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [x] I have added tests that prove my fix is effective or that my feature works - [x] New and existing unit tests pass locally with my changes - [ ] PR is being merged under a feature flag #### QA activity: - [ ] [Speedbreak features](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#speedbreakers-) have been covered - [ ] Test plan covers all impacted features and [areas of interest](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#areas-of-interest-) - [ ] Test plan has been peer reviewed by project stakeholders and other QA members - [ ] Manually tested functionality on DP - [ ] We had an implementation alignment call with stakeholders post QA Round 2 - [ ] Cypress test cases have been added and approved by SDET/manual QA - [ ] Added `Test Plan Approved` label after Cypress tests were reviewed - [ ] Added `Test Plan Approved` label after JUnit tests were reviewed --------- Co-authored-by: Aishwarya UR <aishwarya@appsmith.com>
This commit is contained in:
parent
f99ab85ee0
commit
863dbddb7f
|
|
@ -97,4 +97,12 @@ describe("Autocomplete tests for setters", () => {
|
||||||
);
|
);
|
||||||
agHelper.AssertElementAbsence(locators._evalValuePopover);
|
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);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -201,6 +201,7 @@ export class CommonLocators {
|
||||||
`//p[text()='${fieldName}']/parent::div//following-sibling::div//input[@type='checkbox']`;
|
`//p[text()='${fieldName}']/parent::div//following-sibling::div//input[@type='checkbox']`;
|
||||||
_deployedPage = `.t--page-switch-tab`;
|
_deployedPage = `.t--page-switch-tab`;
|
||||||
_hints = "ul.CodeMirror-hints li";
|
_hints = "ul.CodeMirror-hints li";
|
||||||
|
_tern_doc = ".t--tern-doc";
|
||||||
_argHintFnName = ".CodeMirror-Tern-tooltip .CodeMirror-Tern-fname";
|
_argHintFnName = ".CodeMirror-Tern-tooltip .CodeMirror-Tern-fname";
|
||||||
_cancelActionExecution = ".t--cancel-action-button";
|
_cancelActionExecution = ".t--cancel-action-button";
|
||||||
_widgetPane = "[data-testid='widget-sidebar-scrollable-wrapper']";
|
_widgetPane = "[data-testid='widget-sidebar-scrollable-wrapper']";
|
||||||
|
|
|
||||||
|
|
@ -19,16 +19,65 @@ export const entityDefinitions = {
|
||||||
) {
|
) {
|
||||||
return {
|
return {
|
||||||
...generatedTypeDef,
|
...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: {
|
geolocation: {
|
||||||
...generatedTypeDef.geolocation,
|
...generatedTypeDef.geolocation,
|
||||||
"!doc":
|
"!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":
|
"!url":
|
||||||
"https://docs.appsmith.com/reference/appsmith-framework/context-object#geolocation-object",
|
"https://docs.appsmith.com/reference/appsmith-framework/context-object#geolocation-object",
|
||||||
getCurrentPosition:
|
getCurrentPosition: {
|
||||||
"fn(onSuccess: fn() -> void, onError: fn() -> void, options: object) -> void",
|
"!type":
|
||||||
watchPosition: "fn(options: object) -> void",
|
"fn(onSuccess: fn() -> void, onError: fn() -> void, options: object) -> +Promise|void",
|
||||||
clearWatch: "fn() -> 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) => {
|
ACTION: (entity: ActionEntity, extraDefsToDefine: ExtraDef) => {
|
||||||
const dataDef = generateTypeDef(entity.data, extraDefsToDefine);
|
const dataDef = generateTypeDef(entity.data, extraDefsToDefine);
|
||||||
|
let responseMetaDef = generateTypeDef(
|
||||||
|
entity.responseMeta,
|
||||||
|
extraDefsToDefine,
|
||||||
|
);
|
||||||
|
|
||||||
let data: Def = {
|
if (_.isString(responseMetaDef)) {
|
||||||
"!doc": "The response of the action",
|
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)) {
|
if (_.isString(dataDef)) {
|
||||||
data["!type"] = dataDef;
|
dataCustomDef["!type"] = dataDef;
|
||||||
} else {
|
} else {
|
||||||
data = { ...data, ...dataDef };
|
dataCustomDef = { ...dataCustomDef, ...dataDef };
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
"!doc":
|
"!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":
|
"!url":
|
||||||
"https://docs.appsmith.com/reference/appsmith-framework/query-object",
|
"https://docs.appsmith.com/reference/appsmith-framework/query-object",
|
||||||
isLoading: "bool",
|
isLoading: {
|
||||||
data,
|
"!type": "bool",
|
||||||
responseMeta: {
|
"!doc":
|
||||||
"!doc": "The response meta of the action",
|
"Boolean that indicates whether the query is currently being executed.",
|
||||||
"!type": "?",
|
},
|
||||||
|
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 = {
|
export const GLOBAL_FUNCTIONS = {
|
||||||
"!name": "DATA_TREE.APPSMITH.FUNCTIONS",
|
"!name": "DATA_TREE.APPSMITH.FUNCTIONS",
|
||||||
navigateTo: {
|
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":
|
"!type":
|
||||||
"fn(pageNameOrUrl: string, params: {}, target?: string) -> +Promise",
|
"fn(pageNameOrUrl: string, params: {}, target?: string) -> +Promise",
|
||||||
},
|
},
|
||||||
showAlert: {
|
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",
|
"!type": "fn(message: string, style: string) -> +Promise",
|
||||||
},
|
},
|
||||||
showModal: {
|
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",
|
"!type": "fn(modalName: string) -> +Promise",
|
||||||
},
|
},
|
||||||
closeModal: {
|
closeModal: {
|
||||||
|
"!url":
|
||||||
|
"https://docs.appsmith.com/reference/appsmith-framework/widget-actions/close-modal",
|
||||||
"!doc": "Close a modal",
|
"!doc": "Close a modal",
|
||||||
"!type": "fn(modalName: string) -> +Promise",
|
"!type": "fn(modalName: string) -> +Promise",
|
||||||
},
|
},
|
||||||
storeValue: {
|
storeValue: {
|
||||||
"!doc": "Store key value data locally",
|
"!url":
|
||||||
"!type": "fn(key: string, value: any) -> +Promise",
|
"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 <code>appsmith.store</code>.",
|
||||||
|
"!type": "fn(key: string, value: any, persist?: bool) -> +Promise",
|
||||||
},
|
},
|
||||||
removeValue: {
|
removeValue: {
|
||||||
|
"!url":
|
||||||
|
"https://docs.appsmith.com/reference/appsmith-framework/widget-actions/remove-value",
|
||||||
"!doc": "Remove key value data locally",
|
"!doc": "Remove key value data locally",
|
||||||
"!type": "fn(key: string) -> +Promise",
|
"!type": "fn(key: string) -> +Promise",
|
||||||
},
|
},
|
||||||
clearStore: {
|
clearStore: {
|
||||||
|
"!url":
|
||||||
|
"https://docs.appsmith.com/reference/appsmith-framework/widget-actions/clear-store",
|
||||||
"!doc": "Clear all key value data locally",
|
"!doc": "Clear all key value data locally",
|
||||||
"!type": "fn() -> +Promise",
|
"!type": "fn() -> +Promise",
|
||||||
},
|
},
|
||||||
download: {
|
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":
|
"!type":
|
||||||
"fn(data: string|+Blob, fileName: string, fileType?: string) -> +Promise",
|
"fn(data: string|+Blob, fileName: string, fileType?: string) -> +Promise",
|
||||||
},
|
},
|
||||||
copyToClipboard: {
|
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",
|
"!type": "fn(data: string, options: object) -> +Promise",
|
||||||
},
|
},
|
||||||
resetWidget: {
|
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",
|
"!type": "fn(widgetName: string, resetChildren: bool) -> +Promise",
|
||||||
},
|
},
|
||||||
setInterval: {
|
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":
|
"!type":
|
||||||
"fn(callback: fn() -> void, interval: number, id?: string) -> number",
|
"fn(callback: fn() -> void, interval: number, id?: string) -> number",
|
||||||
},
|
},
|
||||||
clearInterval: {
|
clearInterval: {
|
||||||
|
"!url":
|
||||||
|
"https://docs.appsmith.com/reference/appsmith-framework/widget-actions/clear-interval",
|
||||||
"!doc": "Stop executing a setInterval with id",
|
"!doc": "Stop executing a setInterval with id",
|
||||||
"!type": "fn(id: string) -> void",
|
"!type": "fn(id: string) -> void",
|
||||||
},
|
},
|
||||||
postWindowMessage: {
|
postWindowMessage: {
|
||||||
|
"!url":
|
||||||
|
"https://docs.appsmith.com/reference/appsmith-framework/widget-actions/post-message",
|
||||||
"!doc":
|
"!doc":
|
||||||
"Establish cross-origin communication between Window objects/page and iframes",
|
"Establish cross-origin communication between Window objects/page and iframes",
|
||||||
"!type": "fn(message: unknown, source: string, targetOrigin: string)",
|
"!type": "fn(message: unknown, source: string, targetOrigin: string)",
|
||||||
|
|
@ -180,4 +291,55 @@ export const getPropsForJSActionEntity = ({
|
||||||
return properties;
|
return properties;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const ternDocsInfo: Record<string, any> = {
|
||||||
|
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;
|
export type EntityDefinitionsOptions = keyof typeof entityDefinitions;
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,7 @@ export const CodeEditorColors = {
|
||||||
NUMBER: "#555",
|
NUMBER: "#555",
|
||||||
COMMENT: "var(--ads-v2-color-gray-400)",
|
COMMENT: "var(--ads-v2-color-gray-400)",
|
||||||
FUNCTION_ARGS: "hsl(288, 44%, 44%)",
|
FUNCTION_ARGS: "hsl(288, 44%, 44%)",
|
||||||
|
TOOLTIP_FN_ARGS: "#DB6E33",
|
||||||
};
|
};
|
||||||
|
|
||||||
export const EditorWrapper = styled.div<{
|
export const EditorWrapper = styled.div<{
|
||||||
|
|
|
||||||
|
|
@ -245,12 +245,12 @@
|
||||||
"setTimeout": {
|
"setTimeout": {
|
||||||
"!type": "fn(f: fn(), ms: number) -> number",
|
"!type": "fn(f: fn(), ms: number) -> number",
|
||||||
"!url": "https://developer.mozilla.org/en/docs/DOM/window.setTimeout",
|
"!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 <code>clearTimeout()</code>."
|
||||||
},
|
},
|
||||||
"clearTimeout": {
|
"clearTimeout": {
|
||||||
"!type": "fn(timeout: number)",
|
"!type": "fn(timerId: number)",
|
||||||
"!url": "https://developer.mozilla.org/en/docs/DOM/window.clearTimeout",
|
"!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 <code>setTimeout()</code>."
|
||||||
},
|
},
|
||||||
"Response": {
|
"Response": {
|
||||||
"!type": "fn()",
|
"!type": "fn()",
|
||||||
|
|
|
||||||
|
|
@ -1461,8 +1461,7 @@
|
||||||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/JSON/stringify",
|
"!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."
|
"!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",
|
"!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."
|
|
||||||
},
|
},
|
||||||
"ArrayBuffer": {
|
"ArrayBuffer": {
|
||||||
"!type": "fn(length: number)",
|
"!type": "fn(length: number)",
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import { createGlobalStyle } from "styled-components";
|
||||||
import type { EditorTheme } from "components/editorComponents/CodeEditor/EditorConfig";
|
import type { EditorTheme } from "components/editorComponents/CodeEditor/EditorConfig";
|
||||||
import type { Theme } from "constants/DefaultTheme";
|
import type { Theme } from "constants/DefaultTheme";
|
||||||
import { LINT_TOOLTIP_JUSTIFIED_LEFT_CLASS } from "components/editorComponents/CodeEditor/constants";
|
import { LINT_TOOLTIP_JUSTIFIED_LEFT_CLASS } from "components/editorComponents/CodeEditor/constants";
|
||||||
|
import { CodeEditorColors } from "components/editorComponents/CodeEditor/styledComponents";
|
||||||
|
|
||||||
export const CodemirrorHintStyles = createGlobalStyle<{
|
export const CodemirrorHintStyles = createGlobalStyle<{
|
||||||
editorTheme: EditorTheme;
|
editorTheme: EditorTheme;
|
||||||
|
|
@ -14,15 +15,13 @@ export const CodemirrorHintStyles = createGlobalStyle<{
|
||||||
z-index: 20;
|
z-index: 20;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
list-style: none;
|
list-style: none;
|
||||||
margin-top: ${(props) => props.theme.spaces[3]}px;
|
padding: 0px;
|
||||||
padding: 0px 0px;
|
font-family: ${(props) => props.theme.fonts.code};
|
||||||
font-family: monospace;
|
|
||||||
max-height: 25em;
|
max-height: 25em;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
background: var(--ads-v2-color-bg);
|
background: var(--ads-v2-color-bg);
|
||||||
box-shadow: var(--ads-v2-shadow-popovers);
|
box-shadow: var(--ads-v2-shadow-popovers);
|
||||||
border: 1px solid var(--ads-v2-color-border);
|
border: 1px solid var(--ads-v2-color-border);
|
||||||
border-radius: var(--ads-v2-border-radius);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.CodeMirror-hint {
|
.CodeMirror-hint {
|
||||||
|
|
@ -38,7 +37,6 @@ export const CodemirrorHintStyles = createGlobalStyle<{
|
||||||
letter-spacing: -0.24px;
|
letter-spacing: -0.24px;
|
||||||
&:hover {
|
&:hover {
|
||||||
background: var(--ads-v2-color-bg-subtle);
|
background: var(--ads-v2-color-bg-subtle);
|
||||||
border-radius: 0px;
|
|
||||||
color: var(--ads-v2-color-fg);
|
color: var(--ads-v2-color-fg);
|
||||||
&:after {
|
&:after {
|
||||||
color: var(--ads-v2-color-fg);
|
color: var(--ads-v2-color-fg);
|
||||||
|
|
@ -82,9 +80,6 @@ export const CodemirrorHintStyles = createGlobalStyle<{
|
||||||
font-family: ${(props) => props.theme.fonts.text};
|
font-family: ${(props) => props.theme.fonts.text};
|
||||||
font-size:14px;
|
font-size:14px;
|
||||||
margin: 0 4px;
|
margin: 0 4px;
|
||||||
&:hover {
|
|
||||||
border-radius: var(--ads-v2-border-radius);
|
|
||||||
}
|
|
||||||
&.CodeMirror-hint-active {
|
&.CodeMirror-hint-active {
|
||||||
.magic {
|
.magic {
|
||||||
path {
|
path {
|
||||||
|
|
@ -172,7 +167,6 @@ export const CodemirrorHintStyles = createGlobalStyle<{
|
||||||
bottom: 6px;
|
bottom: 6px;
|
||||||
height: 12px;
|
height: 12px;
|
||||||
width: 12px;
|
width: 12px;
|
||||||
border-radius: var(--ads-v2-border-radius);
|
|
||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
line-height: 12px;
|
line-height: 12px;
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
|
|
@ -238,7 +232,6 @@ export const CodemirrorHintStyles = createGlobalStyle<{
|
||||||
}
|
}
|
||||||
li.CodeMirror-hint-active {
|
li.CodeMirror-hint-active {
|
||||||
background-color: var(--ads-v2-color-bg-muted);
|
background-color: var(--ads-v2-color-bg-muted);
|
||||||
border-radius: var(--ads-v2-border-radius);
|
|
||||||
color: var(--ads-v2-color-fg);
|
color: var(--ads-v2-color-fg);
|
||||||
&:after {
|
&:after {
|
||||||
color: var(--ads-v2-color-fg);
|
color: var(--ads-v2-color-fg);
|
||||||
|
|
@ -261,22 +254,43 @@ export const CodemirrorHintStyles = createGlobalStyle<{
|
||||||
background: var(--ads-v2-color-bg);
|
background: var(--ads-v2-color-bg);
|
||||||
box-shadow: var(--ads-v2-shadow-popovers);
|
box-shadow: var(--ads-v2-shadow-popovers);
|
||||||
border: 1px solid var(--ads-v2-color-border);
|
border: 1px solid var(--ads-v2-color-border);
|
||||||
border-radius: var(--ads-v2-border-radius);
|
z-index: 15px;
|
||||||
|
font-weight: 500;
|
||||||
max-width: none;
|
max-width: none;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
.CodeMirror-Tern-fname {
|
.CodeMirror-Tern-fname {
|
||||||
color: #304EAA;
|
color: ${CodeEditorColors.KEYWORD};
|
||||||
}
|
}
|
||||||
.CodeMirror-Tern-farg {
|
.CodeMirror-Tern-farg {
|
||||||
color: #DB6E33;
|
color: ${CodeEditorColors.TOOLTIP_FN_ARGS};
|
||||||
&.CodeMirror-Tern-farg-current {
|
&.CodeMirror-Tern-farg-current {
|
||||||
color: #DB6E33;
|
color: ${CodeEditorColors.TOOLTIP_FN_ARGS};
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.CodeMirror-Tern-type {
|
.CodeMirror-Tern-type {
|
||||||
color: #364252;
|
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;
|
bottom: 6px;
|
||||||
height: 12px;
|
height: 12px;
|
||||||
width: 12px;
|
width: 12px;
|
||||||
border-radius: var(--ads-v2-border-radius);
|
|
||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
line-height: 12px;
|
line-height: 12px;
|
||||||
font-weight: normal;
|
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 {
|
.CodeMirror-lint-tooltip {
|
||||||
&& {
|
&& {
|
||||||
border: 1px solid var(--ads-v2-color-border) !important;
|
border: 1px solid var(--ads-v2-color-border) !important;
|
||||||
border-radius: var(--ads-v2-border-radius);
|
|
||||||
background: var(--ads-v2-color-bg) !important;
|
background: var(--ads-v2-color-bg) !important;
|
||||||
box-shadow: 0px 12px 28px -6px rgba(0, 0, 0, 0.32);
|
box-shadow: 0px 12px 28px -6px rgba(0, 0, 0, 0.32);
|
||||||
padding: 4px;
|
padding: 4px;
|
||||||
|
|
|
||||||
|
|
@ -10,27 +10,25 @@ export const createObjectPeekData = (
|
||||||
parentKey: string,
|
parentKey: string,
|
||||||
) => {
|
) => {
|
||||||
Object.keys(defs).forEach((key: string) => {
|
Object.keys(defs).forEach((key: string) => {
|
||||||
if (key.indexOf("!") === -1) {
|
if (key.startsWith("!")) return;
|
||||||
const childKeyPathArray = [parentKey, key];
|
const childKeyPathArray = [parentKey, key];
|
||||||
if (isObject(defs[key])) {
|
if (
|
||||||
if (Object.keys(defs[key]).length > 0) {
|
isObject(defs[key]) &&
|
||||||
peekData[key] = {};
|
Object.keys(defs[key]).filter((k) => !k.startsWith("!")).length > 0
|
||||||
const result = createObjectPeekData(
|
) {
|
||||||
defs[key],
|
peekData[key] = {};
|
||||||
data[key],
|
const result = createObjectPeekData(
|
||||||
peekData[key],
|
defs[key],
|
||||||
key,
|
data[key],
|
||||||
);
|
peekData[key],
|
||||||
_.set(peekData, childKeyPathArray, result.peekData);
|
key,
|
||||||
} else {
|
);
|
||||||
peekData[key] = data[key];
|
_.set(peekData, childKeyPathArray, result.peekData);
|
||||||
}
|
} else {
|
||||||
} else {
|
peekData[key] = isTernFunctionDef(defs[key])
|
||||||
peekData[key] = isTernFunctionDef(defs[key])
|
? // eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||||
? // eslint-disable-next-line @typescript-eslint/no-empty-function
|
function () {} // tern inference required here
|
||||||
function () {} // tern inference required here
|
: data[key];
|
||||||
: data[key];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return { peekData };
|
return { peekData };
|
||||||
|
|
|
||||||
|
|
@ -313,6 +313,9 @@ class BlockAsyncFnsInDataFieldRule implements AutocompleteRule {
|
||||||
"clearTimeout",
|
"clearTimeout",
|
||||||
"setInterval",
|
"setInterval",
|
||||||
"clearInterval",
|
"clearInterval",
|
||||||
|
"postWindowMessage",
|
||||||
|
"windowMessageListener",
|
||||||
|
"watchPosition",
|
||||||
];
|
];
|
||||||
computeScore(
|
computeScore(
|
||||||
completion: Completion<TernCompletionResult>,
|
completion: Completion<TernCompletionResult>,
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ import {
|
||||||
} from "../getCodeMirrorNamespace";
|
} from "../getCodeMirrorNamespace";
|
||||||
import AnalyticsUtil from "utils/AnalyticsUtil";
|
import AnalyticsUtil from "utils/AnalyticsUtil";
|
||||||
import { findIndex, isString } from "lodash";
|
import { findIndex, isString } from "lodash";
|
||||||
|
import { renderTernTooltipContent } from "./ternDocTooltip";
|
||||||
|
|
||||||
const bigDoc = 250;
|
const bigDoc = 250;
|
||||||
const cls = "CodeMirror-Tern-";
|
const cls = "CodeMirror-Tern-";
|
||||||
|
|
@ -558,32 +559,33 @@ class CodeMirrorTernService {
|
||||||
CodeMirror.on(
|
CodeMirror.on(
|
||||||
obj,
|
obj,
|
||||||
"select",
|
"select",
|
||||||
(cur: { data: { doc: string } }, node: any) => {
|
(cur: Completion<TernCompletionResult>, node: any) => {
|
||||||
this.active = cur;
|
this.active = cur;
|
||||||
this.remove(tooltip);
|
this.remove(tooltip);
|
||||||
const content = cur.data.doc;
|
const content = cur.data.doc;
|
||||||
if (content) {
|
if (!content) return;
|
||||||
tooltip = this.makeTooltip(
|
const docTooltipContainer = this.elt("div", "flex flex-col pb-1");
|
||||||
node.parentNode.getBoundingClientRect().right + window.pageXOffset,
|
renderTernTooltipContent(docTooltipContainer, cur);
|
||||||
node.getBoundingClientRect().top + window.pageYOffset,
|
tooltip = this.makeTooltip(
|
||||||
content,
|
node.parentNode.getBoundingClientRect().right + window.pageXOffset,
|
||||||
cm,
|
node.getBoundingClientRect().top + window.pageYOffset + 2,
|
||||||
);
|
docTooltipContainer,
|
||||||
tooltip.className += " " + cls + "hint-doc";
|
cm,
|
||||||
CodeMirror.on(
|
cls + "hint-doc",
|
||||||
cm,
|
);
|
||||||
"keyup",
|
CodeMirror.on(
|
||||||
(cm: CodeMirror.Editor, keyboardEvent: KeyboardEvent) => {
|
cm,
|
||||||
if (
|
"keyup",
|
||||||
keyboardEvent.code === "Space" &&
|
(cm: CodeMirror.Editor, keyboardEvent: KeyboardEvent) => {
|
||||||
keyboardEvent.ctrlKey &&
|
if (
|
||||||
tooltip
|
keyboardEvent.code === "Space" &&
|
||||||
) {
|
keyboardEvent.ctrlKey &&
|
||||||
tooltip.className += " visible";
|
tooltip
|
||||||
}
|
) {
|
||||||
},
|
tooltip.className += " visible";
|
||||||
);
|
}
|
||||||
}
|
},
|
||||||
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
resolve(obj);
|
resolve(obj);
|
||||||
|
|
|
||||||
74
app/client/src/utils/autocomplete/ternDocTooltip.tsx
Normal file
74
app/client/src/utils/autocomplete/ternDocTooltip.tsx
Normal file
|
|
@ -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<TernCompletionResult>,
|
||||||
|
) {
|
||||||
|
ReactDOM.render(<TernDocToolTip completion={completion} />, element);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function TernDocToolTip(props: {
|
||||||
|
completion: Completion<TernCompletionResult>;
|
||||||
|
}) {
|
||||||
|
const { completion } = props;
|
||||||
|
const {
|
||||||
|
data: { doc, url },
|
||||||
|
displayText,
|
||||||
|
} = completion;
|
||||||
|
|
||||||
|
if (!doc || !displayText) return null;
|
||||||
|
|
||||||
|
const examples =
|
||||||
|
displayText in ternDocsInfo ? ternDocsInfo[displayText].exampleArgs : null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col pb-1 t--tern-doc">
|
||||||
|
<div className="flex items-center justify-between px-2 p-1 border-b border-mercury text-sm font-semibold">
|
||||||
|
{displayText}
|
||||||
|
{url && (
|
||||||
|
<Link
|
||||||
|
className="text-xs doc-link"
|
||||||
|
kind="primary"
|
||||||
|
target="_blank"
|
||||||
|
to={url}
|
||||||
|
>
|
||||||
|
[docs]
|
||||||
|
</Link>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<pre
|
||||||
|
className="px-2 p-1 text-xs whitespace-normal"
|
||||||
|
dangerouslySetInnerHTML={{ __html: doc }}
|
||||||
|
/>
|
||||||
|
{examples && (
|
||||||
|
<div className="flex px-2 py-[2px] text-xs font-semibold">Example</div>
|
||||||
|
)}
|
||||||
|
{examples && (
|
||||||
|
<div className="px-2">
|
||||||
|
{examples.map((example: string) => {
|
||||||
|
const fnName = displayText;
|
||||||
|
const args = example;
|
||||||
|
return (
|
||||||
|
<span
|
||||||
|
className="flex items-center justify-start py-[2px] text-xs whitespace-nowrap"
|
||||||
|
key={example}
|
||||||
|
style={{ color: CodeEditorColors.KEYWORD }}
|
||||||
|
>
|
||||||
|
{`${fnName}(`}
|
||||||
|
<span style={{ color: CodeEditorColors.TOOLTIP_FN_ARGS }}>
|
||||||
|
{args}
|
||||||
|
</span>
|
||||||
|
{")"}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user