diff --git a/.all-contributorsrc b/.all-contributorsrc
index d614d3c8f0..cdcc6091b5 100644
--- a/.all-contributorsrc
+++ b/.all-contributorsrc
@@ -205,7 +205,16 @@
"avatar_url": "https://avatars0.githubusercontent.com/u/25309929?v=4",
"profile": "https://github.com/A-Scratchy",
"contributions": [
- "code"
+ "code"
+ ]
+ },
+ {
+ "login": "sumanthyedoti",
+ "name": "Sumanth Yedoti",
+ "avatar_url": "https://avatars3.githubusercontent.com/u/30371888?v=4",
+ "profile": "https://github.com/sumanthyedoti",
+ "contributions": [
+ "code"
]
}
],
diff --git a/README.md b/README.md
index 6c2da1c1d3..0370fb76c7 100644
--- a/README.md
+++ b/README.md
@@ -121,6 +121,9 @@ The Appsmith platform is available under the [Apache License 2.0](https://www.ap
 Prashant Chaubey 💻 |
 Adam 💻 |
+
+  Sumanth Yedoti 🔧 💻 |
+
diff --git a/app/client/cypress/integration/Smoke_TestSuite/Binding/No_Binding_Prompt_spec.js b/app/client/cypress/integration/Smoke_TestSuite/Binding/No_Binding_Prompt_spec.js
new file mode 100644
index 0000000000..b8317bb468
--- /dev/null
+++ b/app/client/cypress/integration/Smoke_TestSuite/Binding/No_Binding_Prompt_spec.js
@@ -0,0 +1,18 @@
+const dsl = require("../../../fixtures/inputdsl.json");
+const widgetsPage = require("../../../locators/Widgets.json");
+const dynamicInput = require("../../../locators/DynamicInput.json");
+
+describe("Binding prompt", function() {
+ before(() => {
+ cy.addDsl(dsl);
+ });
+
+ it("Show binding prompt when there are no bindings in the editor", () => {
+ cy.openPropertyPane("inputwidget");
+ cy.get(widgetsPage.defaultInput).type(" ");
+ cy.get(dynamicInput.bindingPrompt).should("be.visible");
+
+ cy.get(widgetsPage.defaultInput).type("{{");
+ cy.get(dynamicInput.bindingPrompt).should("not.be.visible");
+ });
+});
diff --git a/app/client/cypress/integration/Smoke_TestSuite/DisplayWidgets/video_spec.js b/app/client/cypress/integration/Smoke_TestSuite/DisplayWidgets/video_spec.js
index d447fcb5f6..243a441c3b 100644
--- a/app/client/cypress/integration/Smoke_TestSuite/DisplayWidgets/video_spec.js
+++ b/app/client/cypress/integration/Smoke_TestSuite/DisplayWidgets/video_spec.js
@@ -48,6 +48,9 @@ describe("Video Widget Functionality", function() {
it("Update video url and check play and pause functionality validation", function() {
cy.testCodeMirror(testdata.videoUrl);
+ cy.get(".CodeMirror textarea")
+ .first()
+ .blur();
cy.get(widgetsPage.autoPlay).click();
cy.wait("@updateLayout").should(
"have.nested.property",
diff --git a/app/client/cypress/locators/DynamicInput.json b/app/client/cypress/locators/DynamicInput.json
index 5c3eb61356..8f34ce8219 100644
--- a/app/client/cypress/locators/DynamicInput.json
+++ b/app/client/cypress/locators/DynamicInput.json
@@ -1,5 +1,6 @@
{
"input": ".CodeMirror textarea",
"hints": "ul.CodeMirror-hints",
- "evaluatedValue": ".t--CodeEditor-evaluatedValue"
+ "evaluatedValue": ".t--CodeEditor-evaluatedValue",
+ "bindingPrompt": ".t--no-binding-prompt"
}
diff --git a/app/client/src/components/editorComponents/CodeEditor/BindingPrompt.tsx b/app/client/src/components/editorComponents/CodeEditor/BindingPrompt.tsx
new file mode 100644
index 0000000000..4efb6526f9
--- /dev/null
+++ b/app/client/src/components/editorComponents/CodeEditor/BindingPrompt.tsx
@@ -0,0 +1,48 @@
+import React, { useRef } from "react";
+import styled from "styled-components";
+import { Colors } from "constants/Colors";
+
+const Wrapper = styled.span<{ visible: boolean; bottomOffset: number }>`
+ padding: 8px;
+ font-size: 12px;
+ color: ${Colors.GRAY_CHATEAU};
+ border-radius: 2px;
+ background-color: ${Colors.BLUE_CHARCOAL};
+ position: absolute;
+ bottom: ${props => -props.bottomOffset}px;
+ width: 100%;
+ line-height: 13px;
+ visibility: ${props => (props.visible ? "visible" : "hidden")};
+ z-index: 1;
+`;
+
+const CurlyBraces = styled.span`
+ color: white;
+ background-color: #f3672a;
+ border-radius: 2px;
+ padding: 2px;
+ margin: 0px 2px;
+`;
+
+const BindingPrompt = (props: { isOpen: boolean }): JSX.Element => {
+ const promptRef = useRef(null);
+ let bottomOffset = 30;
+
+ if (promptRef.current) {
+ const boundingRect = promptRef.current.getBoundingClientRect();
+ bottomOffset = boundingRect.height;
+ }
+
+ return (
+
+ Type {"{{"} to see a list of variables
+
+ );
+};
+
+export default BindingPrompt;
diff --git a/app/client/src/components/editorComponents/CodeEditor/index.tsx b/app/client/src/components/editorComponents/CodeEditor/index.tsx
index 7c64d1dfe4..c3fb0a30f2 100644
--- a/app/client/src/components/editorComponents/CodeEditor/index.tsx
+++ b/app/client/src/components/editorComponents/CodeEditor/index.tsx
@@ -36,6 +36,7 @@ import {
import { bindingMarker } from "components/editorComponents/CodeEditor/markHelpers";
import { bindingHint } from "components/editorComponents/CodeEditor/hintHelpers";
import { retryPromise } from "utils/AppsmithUtils";
+import BindingPrompt from "./BindingPrompt";
const LightningMenu = lazy(() =>
retryPromise(() => import("components/editorComponents/LightningMenu")),
@@ -91,9 +92,7 @@ class CodeEditor extends Component {
};
textArea = React.createRef();
- // eslint-disable-next-line @typescript-eslint/ban-ts-ignore
- // @ts-ignore
- editor: CodeMirror.Editor;
+ editor!: CodeMirror.Editor;
hinters: Hinter[] = [];
constructor(props: Props) {
@@ -272,6 +271,7 @@ class CodeEditor extends Component {
theme,
disabled,
className,
+ placeholder,
showLightningMenu,
dataTreePath,
dynamicData,
@@ -291,6 +291,10 @@ class CodeEditor extends Component {
("evaluatedValue" in this.props ||
("dataTreePath" in this.props && !!this.props.dataTreePath));
+ const showBindingPrompt =
+ (!this.props.input.value?.includes("{{") || !this.props.input.value) &&
+ showEvaluatedValue;
+
return (
{
className="leftImageStyles"
/>
)}
-
{this.props.link && (
@@ -368,6 +371,7 @@ class CodeEditor extends Component {
{this.props.rightIcon && (
{this.props.rightIcon}
)}
+
diff --git a/app/client/src/components/editorComponents/CodeEditor/styledComponents.ts b/app/client/src/components/editorComponents/CodeEditor/styledComponents.ts
index 0b121afb60..5b8d195fca 100644
--- a/app/client/src/components/editorComponents/CodeEditor/styledComponents.ts
+++ b/app/client/src/components/editorComponents/CodeEditor/styledComponents.ts
@@ -124,7 +124,7 @@ export const EditorWrapper = styled.div<{
left: 0;
top: 0;
`
- : `z-index: 0; position: relative;`}
+ : `position: relative;`}
min-height: 32px;
height: ${props => props.height || "auto"};
background-color: ${props =>
diff --git a/app/client/src/components/editorComponents/FormRow.tsx b/app/client/src/components/editorComponents/FormRow.tsx
index d70384821f..195d19cd20 100644
--- a/app/client/src/components/editorComponents/FormRow.tsx
+++ b/app/client/src/components/editorComponents/FormRow.tsx
@@ -6,4 +6,5 @@ export default styled.div`
flex-direction: row;
justify-content: space-between;
align-items: flex-start;
+ min-height: 50px;
`;
diff --git a/app/client/src/components/editorComponents/form/fields/EmbeddedDatasourcePathField.tsx b/app/client/src/components/editorComponents/form/fields/EmbeddedDatasourcePathField.tsx
index 04fba4d05f..d04f13a09a 100644
--- a/app/client/src/components/editorComponents/form/fields/EmbeddedDatasourcePathField.tsx
+++ b/app/client/src/components/editorComponents/form/fields/EmbeddedDatasourcePathField.tsx
@@ -237,7 +237,7 @@ const mapStateToProps = (
};
};
-const mapDispatchToProps = (dispatch: Function): ReduxDispatchProps => ({
+const mapDispatchToProps = (dispatch: any): ReduxDispatchProps => ({
updateDatasource: datasource =>
dispatch(change(API_EDITOR_FORM_NAME, "datasource", datasource)),
});
diff --git a/app/client/src/widgets/DropdownWidget.tsx b/app/client/src/widgets/DropdownWidget.tsx
index c50828fd67..2bd9966ee3 100644
--- a/app/client/src/widgets/DropdownWidget.tsx
+++ b/app/client/src/widgets/DropdownWidget.tsx
@@ -24,7 +24,6 @@ class DropdownWidget extends BaseWidget {
selectionType: VALIDATION_TYPES.TEXT,
isRequired: VALIDATION_TYPES.BOOLEAN,
// onOptionChange: VALIDATION_TYPES.ACTION_SELECTOR,
- selectedOptionValueArr: VALIDATION_TYPES.ARRAY,
selectedOptionValues: VALIDATION_TYPES.ARRAY,
defaultOptionValue: VALIDATION_TYPES.DEFAULT_OPTION_VALUE,
};
diff --git a/app/client/src/widgets/MetaHOC.tsx b/app/client/src/widgets/MetaHOC.tsx
index d370cefa79..29ae322107 100644
--- a/app/client/src/widgets/MetaHOC.tsx
+++ b/app/client/src/widgets/MetaHOC.tsx
@@ -51,8 +51,8 @@ const withMeta = (WrappedWidget: typeof BaseWidget) => {
Object.keys(metaProperties).forEach(metaProperty => {
const defaultProperty = defaultProperties[metaProperty];
if (
- prevProps[metaProperty] !== this.props[metaProperty] &&
- this.props[defaultProperty] === this.props[metaProperty]
+ !_.isEqual(prevProps[metaProperty], this.props[metaProperty]) &&
+ _.isEqual(this.props[defaultProperty], this.props[metaProperty])
) {
this.setState({ [metaProperty]: this.props[metaProperty] });
}
diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/migrations/DatabaseChangelog.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/migrations/DatabaseChangelog.java
index e4802b9f51..d78ce1f328 100644
--- a/app/server/appsmith-server/src/main/java/com/appsmith/server/migrations/DatabaseChangelog.java
+++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/migrations/DatabaseChangelog.java
@@ -88,6 +88,9 @@ public class DatabaseChangelog {
* from an index with the fields `"organizationId", "name"`. If an index exists with the first ordering and we try
* to **ensure** an index with the same name but the second ordering of fields, errors will show up and bad things
* WILL happen.
+ *
+ * Also, please check out the following blog on how to best create indexes :
+ * https://emptysqua.re/blog/optimizing-mongodb-compound-indexes/
*/
private static Index makeIndex(String... fields) {
if (fields.length == 1) {
@@ -1193,4 +1196,23 @@ public class DatabaseChangelog {
}
+ @ChangeSet(order = "040", id = "new-page-new-action-add-indexes", author = "")
+ public void addNewPageAndNewActionNewIndexes(MongoTemplate mongoTemplate) {
+
+ dropIndexIfExists(mongoTemplate, NewAction.class, "createdAt");
+
+ ensureIndexes(mongoTemplate, NewAction.class,
+ makeIndex("applicationId", "deleted", "createdAt")
+ .named("applicationId_deleted_createdAt_compound_index")
+ );
+
+ dropIndexIfExists(mongoTemplate, NewPage.class, "createdAt");
+
+ ensureIndexes(mongoTemplate, NewPage.class,
+ makeIndex("applicationId", "deleted")
+ .named("applicationId_deleted_compound_index")
+ );
+
+ }
+
}
diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/NewActionServiceImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/NewActionServiceImpl.java
index 9ca3b80d95..b4ea078bde 100644
--- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/NewActionServiceImpl.java
+++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/NewActionServiceImpl.java
@@ -24,7 +24,6 @@ import com.appsmith.server.domains.NewPage;
import com.appsmith.server.domains.Page;
import com.appsmith.server.domains.Plugin;
import com.appsmith.server.domains.PluginType;
-import com.appsmith.server.domains.QNewAction;
import com.appsmith.server.dtos.ActionDTO;
import com.appsmith.server.dtos.ActionViewDTO;
import com.appsmith.server.dtos.ExecuteActionDTO;
@@ -66,7 +65,6 @@ import static com.appsmith.server.acl.AclPermission.MANAGE_DATASOURCES;
import static com.appsmith.server.acl.AclPermission.READ_ACTIONS;
import static com.appsmith.server.acl.AclPermission.READ_PAGES;
import static com.appsmith.server.helpers.BeanCopyUtils.copyNewFieldValuesIntoOldObject;
-import static com.appsmith.server.repositories.BaseAppsmithRepositoryImpl.fieldName;
import static java.lang.Boolean.TRUE;
@Service
@@ -727,14 +725,14 @@ public class NewActionServiceImpl extends BaseService getActionsForViewMode(String applicationId) {
- Sort sort = Sort.by(fieldName(QNewAction.newAction.publishedAction) + "." + fieldName(QNewAction.newAction.publishedAction.name));
if (applicationId == null || applicationId.isEmpty()) {
return Flux.error(new AppsmithException(AppsmithError.INVALID_PARAMETER, FieldName.APPLICATION_ID));
}
// fetch the published actions by applicationId
- return findAllByApplicationIdAndViewMode(applicationId, true, EXECUTE_ACTIONS, sort)
+ // No need to sort the results
+ return findAllByApplicationIdAndViewMode(applicationId, true, EXECUTE_ACTIONS, null)
.map(action -> {
ActionViewDTO actionViewDTO = new ActionViewDTO();
actionViewDTO.setId(action.getId());
@@ -783,7 +781,9 @@ public class NewActionServiceImpl extends BaseService getUnpublishedActions(MultiValueMap params) {
String name = null;
List pageIds = new ArrayList<>();
- Sort sort = Sort.by(FieldName.NAME);
+
+ // In the edit mode, the actions should be displayed in the order they were created.
+ Sort sort = Sort.by(FieldName.CREATED_AT);
if (params.getFirst(FieldName.NAME) != null) {
name = params.getFirst(FieldName.NAME);