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);
|
||||
});
|
||||
|
||||
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']`;
|
||||
_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']";
|
||||
|
|
|
|||
|
|
@ -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 <code>appsmith.store</code>.",
|
||||
"!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<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;
|
||||
|
|
|
|||
|
|
@ -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<{
|
||||
|
|
|
|||
|
|
@ -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 <code>clearTimeout()</code>."
|
||||
},
|
||||
"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 <code>setTimeout()</code>."
|
||||
},
|
||||
"Response": {
|
||||
"!type": "fn()",
|
||||
|
|
|
|||
|
|
@ -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)",
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 };
|
||||
|
|
|
|||
|
|
@ -313,6 +313,9 @@ class BlockAsyncFnsInDataFieldRule implements AutocompleteRule {
|
|||
"clearTimeout",
|
||||
"setInterval",
|
||||
"clearInterval",
|
||||
"postWindowMessage",
|
||||
"windowMessageListener",
|
||||
"watchPosition",
|
||||
];
|
||||
computeScore(
|
||||
completion: Completion<TernCompletionResult>,
|
||||
|
|
|
|||
|
|
@ -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<TernCompletionResult>, 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);
|
||||
|
|
|
|||
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