From 9becaa9a9f343ee7dd6ba7a20c5302584acb2944 Mon Sep 17 00:00:00 2001 From: Nikhil Nandagopal Date: Tue, 9 Jun 2020 17:34:38 +0530 Subject: [PATCH 1/7] made form data visible at the top level --- .../mockResponses/WidgetSidebarResponse.tsx | 10 +++--- .../utils/autocomplete/EntityDefinitions.ts | 1 + app/client/src/widgets/CheckboxWidget.tsx | 11 ++++++- app/client/src/widgets/DatePickerWidget.tsx | 1 + app/client/src/widgets/DropdownWidget.tsx | 2 ++ app/client/src/widgets/FilepickerWidget.tsx | 1 + app/client/src/widgets/FormWidget.tsx | 32 ++++++++++++++++++- app/client/src/widgets/InputWidget.tsx | 1 + app/client/src/widgets/RadioGroupWidget.tsx | 1 + .../src/widgets/RichTextEditorWidget.tsx | 11 ++++++- app/client/src/widgets/TextWidget.tsx | 7 ++++ 11 files changed, 70 insertions(+), 8 deletions(-) diff --git a/app/client/src/mockResponses/WidgetSidebarResponse.tsx b/app/client/src/mockResponses/WidgetSidebarResponse.tsx index bff7d5e3a8..6558de5b71 100644 --- a/app/client/src/mockResponses/WidgetSidebarResponse.tsx +++ b/app/client/src/mockResponses/WidgetSidebarResponse.tsx @@ -6,11 +6,11 @@ const WidgetSidebarResponse: { [id: string]: WidgetCardProps[]; } = { ["Form Widgets"]: [ - // { - // type: "FORM_WIDGET", - // widgetCardName: "Form", - // key: generateReactKey(), - // }, + { + type: "FORM_WIDGET", + widgetCardName: "Form", + key: generateReactKey(), + }, { type: "INPUT_WIDGET", widgetCardName: "Input", diff --git a/app/client/src/utils/autocomplete/EntityDefinitions.ts b/app/client/src/utils/autocomplete/EntityDefinitions.ts index 9f983e4922..d552ed326b 100644 --- a/app/client/src/utils/autocomplete/EntityDefinitions.ts +++ b/app/client/src/utils/autocomplete/EntityDefinitions.ts @@ -162,6 +162,7 @@ export const entityDefinitions = { "Form is used to capture a set of data inputs from a user. Forms are used specifically because they reset the data inputs when a form is submitted and disable submission for invalid data inputs", "!url": "https://docs.appsmith.com/widget-reference/form", isVisible: isVisible, + data: "object", }, FORM_BUTTON_WIDGET: { "!doc": diff --git a/app/client/src/widgets/CheckboxWidget.tsx b/app/client/src/widgets/CheckboxWidget.tsx index 1df4d69bbf..dbad707205 100644 --- a/app/client/src/widgets/CheckboxWidget.tsx +++ b/app/client/src/widgets/CheckboxWidget.tsx @@ -8,7 +8,10 @@ import { WidgetPropertyValidationType, BASE_WIDGET_VALIDATION, } from "utils/ValidationFactory"; -import { TriggerPropertiesMap } from "utils/WidgetFactory"; +import { + TriggerPropertiesMap, + DerivedPropertiesMap, +} from "utils/WidgetFactory"; class CheckboxWidget extends BaseWidget { static getPropertyValidationMap(): WidgetPropertyValidationType { @@ -32,6 +35,12 @@ class CheckboxWidget extends BaseWidget { }; } + static getDerivedPropertiesMap(): DerivedPropertiesMap { + return { + value: `{{this.isChecked}}`, + }; + } + static getMetaPropertiesMap(): Record { return { isChecked: undefined, diff --git a/app/client/src/widgets/DatePickerWidget.tsx b/app/client/src/widgets/DatePickerWidget.tsx index 5ff07eee17..4889a6bc9b 100644 --- a/app/client/src/widgets/DatePickerWidget.tsx +++ b/app/client/src/widgets/DatePickerWidget.tsx @@ -34,6 +34,7 @@ class DatePickerWidget extends BaseWidget { static getDerivedPropertiesMap(): DerivedPropertiesMap { return { isValid: `{{ this.isRequired ? !!this.selectedDate : true }}`, + value: `{{ this.selectedDate }}`, }; } diff --git a/app/client/src/widgets/DropdownWidget.tsx b/app/client/src/widgets/DropdownWidget.tsx index b3cc7b0a80..7e2f29a603 100644 --- a/app/client/src/widgets/DropdownWidget.tsx +++ b/app/client/src/widgets/DropdownWidget.tsx @@ -63,6 +63,7 @@ class DropdownWidget extends BaseWidget { }, }; } + static getDerivedPropertiesMap() { return { isValid: `{{this.isRequired ? this.selectionType === 'SINGLE_SELECT' ? !!this.selectedOption : !!this.selectedIndexArr && this.selectedIndexArr.length > 0 : true}}`, @@ -70,6 +71,7 @@ class DropdownWidget extends BaseWidget { selectedOptionArr: `{{this.selectionType === "MULTI_SELECT" ? this.options.filter(opt => _.includes(this.selectedOptionValueArr, opt.value)) : undefined}}`, selectedIndex: `{{ _.findIndex(this.options, { value: this.selectedOption.value } ) }}`, selectedIndexArr: `{{ this.selectedOptionValueArr.map(o => _.findIndex(this.options, { value: o })) }}`, + value: `{{ this.selectionType === 'SINGLE_SELECT' ? this.selectedOptionValue : this.selectedOptionValueArr }}`, }; } diff --git a/app/client/src/widgets/FilepickerWidget.tsx b/app/client/src/widgets/FilepickerWidget.tsx index 5846c85fcf..239e56edbb 100644 --- a/app/client/src/widgets/FilepickerWidget.tsx +++ b/app/client/src/widgets/FilepickerWidget.tsx @@ -44,6 +44,7 @@ class FilePickerWidget extends BaseWidget { static getDerivedPropertiesMap(): DerivedPropertiesMap { return { isValid: `{{ this.isRequired ? this.files.length > 0 : true }}`, + value: `this.files`, }; } diff --git a/app/client/src/widgets/FormWidget.tsx b/app/client/src/widgets/FormWidget.tsx index 5330f3f2e3..c38fa11f66 100644 --- a/app/client/src/widgets/FormWidget.tsx +++ b/app/client/src/widgets/FormWidget.tsx @@ -2,8 +2,9 @@ import React from "react"; import _ from "lodash"; import { WidgetProps } from "./BaseWidget"; import { WidgetType } from "constants/WidgetConstants"; -import ContainerWidget from "widgets/ContainerWidget"; +import ContainerWidget, { ContainerWidgetProps } from "widgets/ContainerWidget"; import { ContainerComponentProps } from "components/designSystems/appsmith/ContainerComponent"; +import shallowEqual from "shallowequal"; class FormWidget extends ContainerWidget { checkInvalidChildren = (children: WidgetProps[]): boolean => { @@ -18,6 +19,34 @@ class FormWidget extends ContainerWidget { super.resetChildrenMetaProperty(this.props.widgetId); }; + componentDidMount() { + super.componentDidMount(); + this.updateFormData(); + } + + componentDidUpdate(prevProps: ContainerWidgetProps) { + super.componentDidUpdate(prevProps); + this.updateFormData(); + } + + updateFormData() { + if (this.props.children) { + const formData = this.getFormData(this.props.children[0]); + if (!shallowEqual(formData, this.props.data)) { + this.updateWidgetMetaProperty("data", formData); + } + } + } + + getFormData(formWidget: ContainerWidgetProps) { + const formData: any = {}; + if (formWidget.children) + formWidget.children.map(widgetData => { + formData[widgetData.widgetName] = widgetData.value; + }); + return formData; + } + renderChildWidget(childWidgetData: WidgetProps): React.ReactNode { if (childWidgetData.children) { const isInvalid = this.checkInvalidChildren(childWidgetData.children); @@ -37,6 +66,7 @@ class FormWidget extends ContainerWidget { export interface FormWidgetProps extends ContainerComponentProps { name: string; + data: object; } export default FormWidget; diff --git a/app/client/src/widgets/InputWidget.tsx b/app/client/src/widgets/InputWidget.tsx index 097694bf53..38ff82c101 100644 --- a/app/client/src/widgets/InputWidget.tsx +++ b/app/client/src/widgets/InputWidget.tsx @@ -59,6 +59,7 @@ class InputWidget extends BaseWidget { static getDerivedPropertiesMap(): DerivedPropertiesMap { return { isValid: `{{!!(this.isRequired ? this.text && this.text.length > 0 ? this.regex ? new RegExp(this.regex).test(this.text) : true : false : this.regex ? new RegExp(this.regex).test(this.text) : true)}}`, + value: `{{this.text}}`, }; } diff --git a/app/client/src/widgets/RadioGroupWidget.tsx b/app/client/src/widgets/RadioGroupWidget.tsx index 76becbf687..f666cdac51 100644 --- a/app/client/src/widgets/RadioGroupWidget.tsx +++ b/app/client/src/widgets/RadioGroupWidget.tsx @@ -27,6 +27,7 @@ class RadioGroupWidget extends BaseWidget { selectedOption: "{{_.find(this.options, { value: this.selectedOptionValue })}}", isValid: `{{ this.isRequired ? !!this.selectedOptionValue : true }}`, + value: `{{this.selectedOptionValue}}`, }; } static getTriggerPropertyMap(): TriggerPropertiesMap { diff --git a/app/client/src/widgets/RichTextEditorWidget.tsx b/app/client/src/widgets/RichTextEditorWidget.tsx index 2c0e85bbf3..d179098a5b 100644 --- a/app/client/src/widgets/RichTextEditorWidget.tsx +++ b/app/client/src/widgets/RichTextEditorWidget.tsx @@ -4,7 +4,10 @@ import { WidgetType } from "constants/WidgetConstants"; import { EventType } from "constants/ActionConstants"; import { WidgetPropertyValidationType } from "utils/ValidationFactory"; import { VALIDATION_TYPES } from "constants/WidgetValidation"; -import { TriggerPropertiesMap } from "utils/WidgetFactory"; +import { + TriggerPropertiesMap, + DerivedPropertiesMap, +} from "utils/WidgetFactory"; import Skeleton from "components/utils/Skeleton"; const RichtextEditorComponent = lazy(() => @@ -45,6 +48,12 @@ class RichTextEditorWidget extends BaseWidget< }; } + static getDerivedPropertiesMap(): DerivedPropertiesMap { + return { + value: `this.text`, + }; + } + onValueChange = (text: string) => { this.updateWidgetMetaProperty("text", text); if (this.props.onTextChange) { diff --git a/app/client/src/widgets/TextWidget.tsx b/app/client/src/widgets/TextWidget.tsx index 8fed6bf5e3..a683a7c92e 100644 --- a/app/client/src/widgets/TextWidget.tsx +++ b/app/client/src/widgets/TextWidget.tsx @@ -7,6 +7,7 @@ import { WidgetPropertyValidationType, BASE_WIDGET_VALIDATION, } from "utils/ValidationFactory"; +import { DerivedPropertiesMap } from "utils/WidgetFactory"; const LINE_HEIGHTS: { [key in TextStyle]: number } = { // The following values are arrived at by multiplying line-height with font-size @@ -48,6 +49,12 @@ class TextWidget extends BaseWidget { ); } + static getDerivedPropertiesMap(): DerivedPropertiesMap { + return { + value: `{{ this.text }}`, + }; + } + getWidgetType(): WidgetType { return "TEXT_WIDGET"; } From 2df86b79e918c75910c97fa696b481023e9baccc Mon Sep 17 00:00:00 2001 From: Nikhil Nandagopal Date: Tue, 9 Jun 2020 18:40:42 +0530 Subject: [PATCH 2/7] fixed type def resolved mr comments --- app/client/src/utils/autocomplete/EntityDefinitions.ts | 6 +++--- app/client/src/widgets/FilepickerWidget.tsx | 2 +- app/client/src/widgets/FormWidget.tsx | 4 +++- app/client/src/widgets/RichTextEditorWidget.tsx | 2 +- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/app/client/src/utils/autocomplete/EntityDefinitions.ts b/app/client/src/utils/autocomplete/EntityDefinitions.ts index d552ed326b..0a64fd3c06 100644 --- a/app/client/src/utils/autocomplete/EntityDefinitions.ts +++ b/app/client/src/utils/autocomplete/EntityDefinitions.ts @@ -157,13 +157,13 @@ export const entityDefinitions = { xAxisName: "string", yAxisName: "string", }, - FORM_WIDGET: { + FORM_WIDGET: (widget: any) => ({ "!doc": "Form is used to capture a set of data inputs from a user. Forms are used specifically because they reset the data inputs when a form is submitted and disable submission for invalid data inputs", "!url": "https://docs.appsmith.com/widget-reference/form", isVisible: isVisible, - data: "object", - }, + data: generateTypeDef(widget.data), + }), FORM_BUTTON_WIDGET: { "!doc": "Form button is provided by default to every form. It is used for form submission and resetting form inputs", diff --git a/app/client/src/widgets/FilepickerWidget.tsx b/app/client/src/widgets/FilepickerWidget.tsx index 239e56edbb..51e9c19edd 100644 --- a/app/client/src/widgets/FilepickerWidget.tsx +++ b/app/client/src/widgets/FilepickerWidget.tsx @@ -44,7 +44,7 @@ class FilePickerWidget extends BaseWidget { static getDerivedPropertiesMap(): DerivedPropertiesMap { return { isValid: `{{ this.isRequired ? this.files.length > 0 : true }}`, - value: `this.files`, + value: `{{this.files}}`, }; } diff --git a/app/client/src/widgets/FormWidget.tsx b/app/client/src/widgets/FormWidget.tsx index c38fa11f66..8349bc4d9e 100644 --- a/app/client/src/widgets/FormWidget.tsx +++ b/app/client/src/widgets/FormWidget.tsx @@ -42,7 +42,9 @@ class FormWidget extends ContainerWidget { const formData: any = {}; if (formWidget.children) formWidget.children.map(widgetData => { - formData[widgetData.widgetName] = widgetData.value; + if (widgetData.value) { + formData[widgetData.widgetName] = widgetData.value; + } }); return formData; } diff --git a/app/client/src/widgets/RichTextEditorWidget.tsx b/app/client/src/widgets/RichTextEditorWidget.tsx index d179098a5b..04af041977 100644 --- a/app/client/src/widgets/RichTextEditorWidget.tsx +++ b/app/client/src/widgets/RichTextEditorWidget.tsx @@ -50,7 +50,7 @@ class RichTextEditorWidget extends BaseWidget< static getDerivedPropertiesMap(): DerivedPropertiesMap { return { - value: `this.text`, + value: `{{this.text}}`, }; } From 8c0d010216d06135b8a197611ccf7fe03eb750ce Mon Sep 17 00:00:00 2001 From: rashmipn Date: Thu, 11 Jun 2020 10:43:18 +0530 Subject: [PATCH 3/7] fixed issue --- .../Smoke_TestSuite/Binding/Bind_TableTextPagination_spec.js | 5 ++++- app/client/cypress/support/commands.js | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/app/client/cypress/integration/Smoke_TestSuite/Binding/Bind_TableTextPagination_spec.js b/app/client/cypress/integration/Smoke_TestSuite/Binding/Bind_TableTextPagination_spec.js index 28ba0d87ed..c65c958612 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/Binding/Bind_TableTextPagination_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/Binding/Bind_TableTextPagination_spec.js @@ -14,6 +14,7 @@ describe("Test Create Api and Bind to Table widget", function() { cy.testCreateApiButton(); /**Create an Api1 of Paginate with Table Page No */ cy.createApi(this.data.paginationUrl, this.data.paginationParam); + cy.RunAPI(); }); it("Table-Text, Validate Server Side Pagination of Paginate with Table Page No", function() { @@ -59,6 +60,7 @@ describe("Test Create Api and Bind to Table widget", function() { cy.testCreateApiButton(); /** Create Api2 of Paginate with Response URL*/ cy.createApi(this.data.paginationUrl, "pokemon"); + cy.RunAPI(); cy.NavigateToPaginationTab(); cy.get(apiPage.apiPaginationNextText).type("{{Api2.data.next}}", { parseSpecialCharSequences: false, @@ -66,7 +68,7 @@ describe("Test Create Api and Bind to Table widget", function() { cy.get(apiPage.apiPaginationPrevText).type("{{Api2.data.previous}}", { parseSpecialCharSequences: false, }); - cy.SaveAPI(); + cy.WaitAutoSave(); cy.get(pages.pagesIcon).click({ force: true }); cy.openPropertyPane("textwidget"); @@ -74,6 +76,7 @@ describe("Test Create Api and Bind to Table widget", function() { cy.testJsontext("text", "{{Table1.selectedRow.url}}"); cy.get(commonlocators.editPropCrossButton).click(); cy.openPropertyPane("tablewidget"); + cy.testJsontext("tabledata", "{{Api2.data.results}}"); cy.callApi("Api2"); }); diff --git a/app/client/cypress/support/commands.js b/app/client/cypress/support/commands.js index 7f3440d0d9..1e8043a41d 100644 --- a/app/client/cypress/support/commands.js +++ b/app/client/cypress/support/commands.js @@ -1125,8 +1125,8 @@ Cypress.Commands.add("ValidatePaginateResponseUrlData", runTestCss => { localStorage.setItem("respBody", respBody); cy.log(respBody); cy.get(pages.pagesIcon).click({ force: true }); - cy.openPropertyPane("tablewidget"); - cy.testJsontext("tabledata", "{{Api2.data.results}}"); + // cy.openPropertyPane("tablewidget"); + // cy.testJsontext("tabledata", "{{Api2.data.results}}"); cy.isSelectRow(0); cy.get(commonlocators.labelTextStyle) .invoke("text") From 41e431ebb46cb8519eb255030f5d044910cc6383 Mon Sep 17 00:00:00 2001 From: Akash N Date: Thu, 11 Jun 2020 06:29:32 +0000 Subject: [PATCH 4/7] Add checkbox field for executeOnLoad in query form --- .../components/editorComponents/Checkbox.tsx | 1 - .../src/pages/Editor/QueryEditor/Form.tsx | 17 +++++++++++++++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/app/client/src/components/editorComponents/Checkbox.tsx b/app/client/src/components/editorComponents/Checkbox.tsx index f71f400c7f..f494e1e327 100644 --- a/app/client/src/components/editorComponents/Checkbox.tsx +++ b/app/client/src/components/editorComponents/Checkbox.tsx @@ -25,7 +25,6 @@ export const StyledCheckbox = styled(BlueprintCheckbox)` &&&& { span.bp3-control-indicator { outline: none; - background: white; box-shadow: none; border-radius: ${props => props.theme.radii[1]}px; border: ${props => getBorderCSSShorthand(props.theme.borders[3])}; diff --git a/app/client/src/pages/Editor/QueryEditor/Form.tsx b/app/client/src/pages/Editor/QueryEditor/Form.tsx index 4f0f44a2d5..6131e8918a 100644 --- a/app/client/src/pages/Editor/QueryEditor/Form.tsx +++ b/app/client/src/pages/Editor/QueryEditor/Form.tsx @@ -11,6 +11,7 @@ import { ColumnsDirective, ColumnDirective, } from "@syncfusion/ej2-react-grids"; +import CheckboxField from "components/editorComponents/form/fields/CheckboxField"; import styled, { createGlobalStyle } from "styled-components"; import { Popover, Icon } from "@blueprintjs/core"; import { components, MenuListComponentProps } from "react-select"; @@ -93,7 +94,7 @@ const ResponseContainer = styled.div` const ResponseContent = styled.div` height: calc( - 100vh - (100vh / 3) - 150px - ${props => props.theme.headerHeight} + 100vh - (100vh / 3) - 175px - ${props => props.theme.headerHeight} ); overflow: auto; `; @@ -175,7 +176,7 @@ const StyledGridComponent = styled(GridComponent)` } .e-gridcontent { max-height: calc( - 100vh - (100vh / 3) - 150px - 49px - + 100vh - (100vh / 3) - 175px - 49px - ${props => props.theme.headerHeight} ); overflow: auto; @@ -203,6 +204,12 @@ const CreateDatasource = styled.div` } `; +const StyledCheckbox = styled(CheckboxField)` + &&& { + font-size: 14px; + margin-top: 10px; + } +`; type QueryFormProps = { isCreating: boolean; onDeleteClick: () => void; @@ -430,6 +437,12 @@ const QueryEditorForm: React.FC = (props: Props) => { mode="js-js" /> )} + {dataSources.length === 0 && ( From c2213bd69623a0acacea29b983f48a011fcb8bfb Mon Sep 17 00:00:00 2001 From: Hetu Nandu Date: Thu, 11 Jun 2020 11:03:16 +0000 Subject: [PATCH 5/7] Evaluated value bug fixes --- .../editorComponents/actioncreator/ActionCreator.tsx | 3 ++- .../src/components/propertyControls/ChartDataControl.tsx | 4 ++-- .../src/components/propertyControls/InputTextControl.tsx | 3 +++ 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/app/client/src/components/editorComponents/actioncreator/ActionCreator.tsx b/app/client/src/components/editorComponents/actioncreator/ActionCreator.tsx index 9673601420..34554377fe 100644 --- a/app/client/src/components/editorComponents/actioncreator/ActionCreator.tsx +++ b/app/client/src/components/editorComponents/actioncreator/ActionCreator.tsx @@ -195,7 +195,8 @@ const views = { props.set(event); } }} - dataTreePath={""} + expected={"string"} + evaluatedValue={props.get(props.value, false) as string} isValid={props.isValid} errorMessage={props.validationMessage} /> diff --git a/app/client/src/components/propertyControls/ChartDataControl.tsx b/app/client/src/components/propertyControls/ChartDataControl.tsx index f64c22dc41..de4990e930 100644 --- a/app/client/src/components/propertyControls/ChartDataControl.tsx +++ b/app/client/src/components/propertyControls/ChartDataControl.tsx @@ -81,7 +81,7 @@ function DataControlComponent(props: RenderComponentProps) { updateOption(index, "seriesName", value); }, }} - evaluatedValue={evaluated.seriesName} + evaluatedValue={evaluated?.seriesName} theme={"DARK"} singleLine={false} placeholder="Series Name" @@ -113,7 +113,7 @@ function DataControlComponent(props: RenderComponentProps) { updateOption(index, "data", value); }, }} - evaluatedValue={evaluated.data} + evaluatedValue={evaluated?.data} meta={{ error: isValid ? "" : "There is an error", touched: true, diff --git a/app/client/src/components/propertyControls/InputTextControl.tsx b/app/client/src/components/propertyControls/InputTextControl.tsx index 0fcbebe18b..9faf5881b6 100644 --- a/app/client/src/components/propertyControls/InputTextControl.tsx +++ b/app/client/src/components/propertyControls/InputTextControl.tsx @@ -10,6 +10,7 @@ export function InputText(props: { onChange: (event: React.ChangeEvent | string) => void; isValid: boolean; errorMessage?: string; + evaluatedValue?: any; expected?: string; placeholder?: string; dataTreePath?: string; @@ -22,6 +23,7 @@ export function InputText(props: { onChange, placeholder, dataTreePath, + evaluatedValue, } = props; return ( @@ -30,6 +32,7 @@ export function InputText(props: { value: value, onChange: onChange, }} + evaluatedValue={evaluatedValue} expected={expected} dataTreePath={dataTreePath} meta={{ From 1538a70d1f54b0723f1ba5e11fd216c20b8bdaa6 Mon Sep 17 00:00:00 2001 From: Satbir Singh Date: Fri, 12 Jun 2020 03:54:12 +0000 Subject: [PATCH 6/7] Fixing api Scroll. --- .../designSystems/appsmith/TabbedView.tsx | 3 ++- .../editorComponents/ApiResponseView.tsx | 22 ++++++++++++------- .../editorComponents/CodeEditor.tsx | 11 ++++++---- .../src/pages/Editor/APIEditor/Form.tsx | 2 +- .../pages/Editor/APIEditor/PostBodyData.tsx | 5 ++++- .../Editor/APIEditor/RapidApiEditorForm.tsx | 9 ++++++-- .../src/pages/Editor/APIEditor/index.tsx | 1 + 7 files changed, 36 insertions(+), 17 deletions(-) diff --git a/app/client/src/components/designSystems/appsmith/TabbedView.tsx b/app/client/src/components/designSystems/appsmith/TabbedView.tsx index 9fa5f6c3a4..8eaae536d0 100644 --- a/app/client/src/components/designSystems/appsmith/TabbedView.tsx +++ b/app/client/src/components/designSystems/appsmith/TabbedView.tsx @@ -9,7 +9,8 @@ const TabsWrapper = styled.div<{ overflow?: boolean }>` height: 100%; } .react-tabs__tab-panel { - height: 100%; + height: calc(100% - 46px); + overflow: scroll; } .react-tabs__tab-list { border-bottom-color: #d0d7dd; diff --git a/app/client/src/components/editorComponents/ApiResponseView.tsx b/app/client/src/components/editorComponents/ApiResponseView.tsx index 3219c161fa..a7bf72dd78 100644 --- a/app/client/src/components/editorComponents/ApiResponseView.tsx +++ b/app/client/src/components/editorComponents/ApiResponseView.tsx @@ -103,6 +103,10 @@ const FailedMessageContainer = styled.div` align-items: center; `; +const TabbedViewWrapper = styled.div` + height: calc(100% - 30px); +`; + const ApiResponseView = (props: Props) => { const { match: { @@ -143,7 +147,7 @@ const ApiResponseView = (props: Props) => { ? JSON.stringify(response.body, null, 2) : "", }} - height={700} + height={"100%"} /> ), @@ -163,12 +167,12 @@ const ApiResponseView = (props: Props) => { title: "Request Body", panelComponent: ( ), }, @@ -203,12 +207,14 @@ const ApiResponseView = (props: Props) => { - + + + ); }; diff --git a/app/client/src/components/editorComponents/CodeEditor.tsx b/app/client/src/components/editorComponents/CodeEditor.tsx index df76370def..03c517a6a4 100644 --- a/app/client/src/components/editorComponents/CodeEditor.tsx +++ b/app/client/src/components/editorComponents/CodeEditor.tsx @@ -6,9 +6,13 @@ import "codemirror/theme/monokai.css"; require("codemirror/mode/javascript/javascript"); -const Wrapper = styled.div<{ height: number }>` - height: ${props => props.height}px; +const Wrapper = styled.div<{ height: number | string }>` + height: ${props => + typeof props.height === "number" ? props.height + "px" : props.height}; color: white; + .CodeMirror { + height: 100%; + } `; interface Props { @@ -16,7 +20,7 @@ interface Props { value: string; onChange?: (event: ChangeEvent) => void; }; - height: number; + height: number | string; } class CodeEditor extends React.Component { @@ -35,7 +39,6 @@ class CodeEditor extends React.Component { lineNumbers: true, lineWrapping: true, }); - this.editor.setSize(null, this.props.height); } } diff --git a/app/client/src/pages/Editor/APIEditor/Form.tsx b/app/client/src/pages/Editor/APIEditor/Form.tsx index 6a4142dc1d..27f06611d5 100644 --- a/app/client/src/pages/Editor/APIEditor/Form.tsx +++ b/app/client/src/pages/Editor/APIEditor/Form.tsx @@ -74,7 +74,7 @@ const DatasourceWrapper = styled.div` const SecondaryWrapper = styled.div` display: flex; - height: 100%; + height: calc(100% - 120px); border-top: 1px solid #d0d7dd; margin-top: 15px; `; diff --git a/app/client/src/pages/Editor/APIEditor/PostBodyData.tsx b/app/client/src/pages/Editor/APIEditor/PostBodyData.tsx index 487c49629a..e4c49ca04e 100644 --- a/app/client/src/pages/Editor/APIEditor/PostBodyData.tsx +++ b/app/client/src/pages/Editor/APIEditor/PostBodyData.tsx @@ -29,6 +29,10 @@ const PostbodyContainer = styled.div` const JSONEditorFieldWrapper = styled.div` margin: 5px; + .CodeMirror { + height: auto; + min-height: 300px; + } `; export interface RapidApiAction { editable: boolean; @@ -110,7 +114,6 @@ const PostBodyData = (props: Props) => { = (props: Props) => { // } return ( -
+ {
{apiId ? ( From e6ed4fd5bc42db0cc180a8e6459a75f3abaf9323 Mon Sep 17 00:00:00 2001 From: Akash N Date: Fri, 12 Jun 2020 05:09:49 +0000 Subject: [PATCH 7/7] Datasouces and query fixes - Show plugin icons in query sidebar and dropdown - Collapse all sections except the first one in datasources form - Collapse from second depth in query mongo response UI. --- .../cypress/locators/DatasourcesEditor.json | 4 +- app/client/cypress/support/commands.js | 5 ++ .../Editor/DataSourceEditor/Collapsible.tsx | 4 +- .../pages/Editor/DataSourceEditor/DBForm.tsx | 10 ++- .../src/pages/Editor/QueryEditor/Form.tsx | 65 ++++++++++++++++++- .../pages/Editor/QueryEditor/JSONViewer.tsx | 1 + .../src/pages/Editor/QueryEditor/helpers.ts | 21 ++++++ .../src/pages/Editor/QueryEditor/index.tsx | 6 ++ app/client/src/pages/Editor/QuerySidebar.tsx | 32 ++++++++- 9 files changed, 134 insertions(+), 14 deletions(-) create mode 100644 app/client/src/pages/Editor/QueryEditor/helpers.ts diff --git a/app/client/cypress/locators/DatasourcesEditor.json b/app/client/cypress/locators/DatasourcesEditor.json index 1857c50b78..8e89c48638 100644 --- a/app/client/cypress/locators/DatasourcesEditor.json +++ b/app/client/cypress/locators/DatasourcesEditor.json @@ -7,5 +7,7 @@ "password": "input[name='datasourceConfiguration.authentication.password']", "authenticationAuthtype": "[data-cy=datasourceConfiguration\\.authentication\\.authType]", "sslAuthtype": "[data-cy=datasourceConfiguration\\.connection\\.ssl\\.authType]", - "url": "input[name='datasourceConfiguration.url']" + "url": "input[name='datasourceConfiguration.url']", + "sectionAuthentication": "[data-cy=section-Authentication]", + "sectionSSL": "[data-cy=section-SSL\\ \\(optional\\)]" } diff --git a/app/client/cypress/support/commands.js b/app/client/cypress/support/commands.js index 1e8043a41d..14d8fb3daf 100644 --- a/app/client/cypress/support/commands.js +++ b/app/client/cypress/support/commands.js @@ -827,6 +827,8 @@ Cypress.Commands.add("testSaveDatasource", () => { Cypress.Commands.add("fillMongoDatasourceForm", () => { cy.get(datasourceEditor["host"]).type(datasourceFormData["mongo-host"]); cy.get(datasourceEditor["port"]).type(datasourceFormData["mongo-port"]); + + cy.get(datasourceEditor.sectionAuthentication).click(); cy.get(datasourceEditor["databaseName"]) .clear() .type(datasourceFormData["mongo-databaseName"]); @@ -837,6 +839,7 @@ Cypress.Commands.add("fillMongoDatasourceForm", () => { datasourceFormData["mongo-password"], ); + cy.get(datasourceEditor.sectionSSL).click(); cy.get(datasourceEditor["authenticationAuthtype"]).click(); cy.contains(datasourceFormData["mongo-authenticationAuthtype"]).click({ force: true, @@ -854,6 +857,8 @@ Cypress.Commands.add("fillPostgresDatasourceForm", () => { cy.get(datasourceEditor.databaseName) .clear() .type(datasourceFormData["postgres-databaseName"]); + + cy.get(datasourceEditor.sectionAuthentication).click(); cy.get(datasourceEditor.username).type( datasourceFormData["postgres-username"], ); diff --git a/app/client/src/pages/Editor/DataSourceEditor/Collapsible.tsx b/app/client/src/pages/Editor/DataSourceEditor/Collapsible.tsx index 156bbf0f29..7d67461003 100644 --- a/app/client/src/pages/Editor/DataSourceEditor/Collapsible.tsx +++ b/app/client/src/pages/Editor/DataSourceEditor/Collapsible.tsx @@ -27,6 +27,7 @@ interface ComponentState { interface ComponentProps { children: any; title: string; + defaultIsOpen: boolean; } type Props = ComponentProps; @@ -36,7 +37,7 @@ class Collapsible extends React.Component { super(props); this.state = { - isOpen: true, + isOpen: props.defaultIsOpen || false, }; } @@ -54,6 +55,7 @@ class Collapsible extends React.Component { }} /> this.setState({ isOpen: !this.state.isOpen })} > {title} diff --git a/app/client/src/pages/Editor/DataSourceEditor/DBForm.tsx b/app/client/src/pages/Editor/DataSourceEditor/DBForm.tsx index 8f2f3e9911..b6f19fe5a2 100644 --- a/app/client/src/pages/Editor/DataSourceEditor/DBForm.tsx +++ b/app/client/src/pages/Editor/DataSourceEditor/DBForm.tsx @@ -332,9 +332,7 @@ class DatasourceDBEditor extends React.Component< /> {!_.isNil(sections) - ? _.map(sections, section => { - return this.renderMainSection(section); - }) + ? _.map(sections, this.renderMainSection) : undefined} { + renderMainSection = (section: any, index: number) => { return ( - + {this.renderEachConfig(section)} ); @@ -413,7 +411,7 @@ class DatasourceDBEditor extends React.Component< } return ( -
+
{controlType !== "KEYVALUE_ARRAY" && controlType !== "SWITCH" && ( diff --git a/app/client/src/pages/Editor/QueryEditor/Form.tsx b/app/client/src/pages/Editor/QueryEditor/Form.tsx index 6131e8918a..4ea450b47d 100644 --- a/app/client/src/pages/Editor/QueryEditor/Form.tsx +++ b/app/client/src/pages/Editor/QueryEditor/Form.tsx @@ -14,7 +14,13 @@ import { import CheckboxField from "components/editorComponents/form/fields/CheckboxField"; import styled, { createGlobalStyle } from "styled-components"; import { Popover, Icon } from "@blueprintjs/core"; -import { components, MenuListComponentProps } from "react-select"; +import { + components, + MenuListComponentProps, + SingleValueProps, + OptionTypeBase, + OptionProps, +} from "react-select"; import history from "utils/history"; import DynamicAutocompleteInput from "components/editorComponents/DynamicAutocompleteInput"; import { DATA_SOURCES_EDITOR_URL } from "constants/routes"; @@ -204,12 +210,31 @@ const CreateDatasource = styled.div` } `; +const Container = styled.div` + display: flex; + flex-direction: row; + align-items: center; + + .plugin-image { + height: 20px; + width: auto; + } + + .selected-value { + overflow: hidden; + text-overflow: ellipsis; + white-space: no-wrap; + margin-left: 6px; + } +`; + const StyledCheckbox = styled(CheckboxField)` &&& { font-size: 14px; margin-top: 10px; } `; + type QueryFormProps = { isCreating: boolean; onDeleteClick: () => void; @@ -306,6 +331,40 @@ const QueryEditorForm: React.FC = (props: Props) => { ); }; + const SingleValue = (props: SingleValueProps) => { + return ( + <> + + + Datasource +
{props.children}
+
+
+ + ); + }; + + const CustomOption = (props: OptionProps) => { + return ( + <> + + + Datasource +
{props.children}
+
+
+ + ); + }; + return ( @@ -321,9 +380,9 @@ const QueryEditorForm: React.FC = (props: Props) => { placeholder="Datasource" name="datasource.id" options={DATASOURCES_OPTIONS} - width={200} + width={232} maxMenuHeight={200} - components={{ MenuList }} + components={{ MenuList, Option: CustomOption, SingleValue }} /> diff --git a/app/client/src/pages/Editor/QueryEditor/JSONViewer.tsx b/app/client/src/pages/Editor/QueryEditor/JSONViewer.tsx index 43306d43f5..c8ef723d20 100644 --- a/app/client/src/pages/Editor/QueryEditor/JSONViewer.tsx +++ b/app/client/src/pages/Editor/QueryEditor/JSONViewer.tsx @@ -32,6 +32,7 @@ class JSONOutput extends React.Component { style: { fontSize: "14px", }, + collapsed: 1, }; if (!src.length) { diff --git a/app/client/src/pages/Editor/QueryEditor/helpers.ts b/app/client/src/pages/Editor/QueryEditor/helpers.ts new file mode 100644 index 0000000000..5c2b7780a1 --- /dev/null +++ b/app/client/src/pages/Editor/QueryEditor/helpers.ts @@ -0,0 +1,21 @@ +import { Plugin } from "api/PluginApi"; +import { + PLUGIN_PACKAGE_MONGO, + PLUGIN_PACKAGE_POSTGRES, +} from "constants/QueryEditorConstants"; +import ImageAlt from "assets/images/placeholder-image.svg"; +import Postgres from "assets/images/Postgress.png"; +import MongoDB from "assets/images/MongoDB.png"; + +export const getPluginImage = (plugins: Plugin[], pluginId: string) => { + const plugin = plugins.find(plugin => plugin.id === pluginId); + + switch (plugin?.packageName) { + case PLUGIN_PACKAGE_MONGO: + return MongoDB; + case PLUGIN_PACKAGE_POSTGRES: + return Postgres; + default: + return ImageAlt; + } +}; diff --git a/app/client/src/pages/Editor/QueryEditor/index.tsx b/app/client/src/pages/Editor/QueryEditor/index.tsx index 1b39d7e42f..9249dd9c36 100644 --- a/app/client/src/pages/Editor/QueryEditor/index.tsx +++ b/app/client/src/pages/Editor/QueryEditor/index.tsx @@ -17,11 +17,13 @@ import { deleteQuery, executeQuery } from "actions/queryPaneActions"; import { AppState } from "reducers"; import { getDataSources } from "selectors/editorSelectors"; import { QUERY_EDITOR_FORM_NAME } from "constants/forms"; +import { Plugin } from "api/PluginApi"; import { Datasource } from "api/DatasourcesApi"; import { QueryPaneReduxState } from "reducers/uiReducers/queryPaneReducer"; import { getPluginIdsOfPackageNames, getPluginPackageFromDatasourceId, + getPlugins, } from "selectors/entitiesSelector"; import { PLUGIN_PACKAGE_DBS, @@ -30,6 +32,7 @@ import { import { getCurrentApplication } from "selectors/applicationSelectors"; import { ApiPaneReduxState } from "reducers/uiReducers/apiPaneReducer"; import { QueryAction, RestAction } from "entities/Action"; +import { getPluginImage } from "pages/Editor/QueryEditor/helpers"; const EmptyStateContainer = styled.div` display: flex; @@ -38,6 +41,7 @@ const EmptyStateContainer = styled.div` `; type QueryPageProps = { + plugins: Plugin[]; dataSources: Datasource[]; queryPane: QueryPaneReduxState; formData: RestAction; @@ -117,6 +121,7 @@ class QueryEditor extends React.Component { const DATASOURCES_OPTIONS = validDataSources.map(dataSource => ({ label: dataSource.name, value: dataSource.id, + image: getPluginImage(this.props.plugins, dataSource.pluginId), })); return ( @@ -170,6 +175,7 @@ const mapStateToProps = (state: AppState): any => { ); return { + plugins: getPlugins(state), runErrorMessage, apiPane: state.ui.apiPane, pluginIds: getPluginIdsOfPackageNames(state, PLUGIN_PACKAGE_DBS), diff --git a/app/client/src/pages/Editor/QuerySidebar.tsx b/app/client/src/pages/Editor/QuerySidebar.tsx index dfd0b1398d..57715f1c41 100644 --- a/app/client/src/pages/Editor/QuerySidebar.tsx +++ b/app/client/src/pages/Editor/QuerySidebar.tsx @@ -9,18 +9,24 @@ import EditorSidebar from "pages/Editor/EditorSidebar"; import { QUERY_CONSTANT } from "constants/QueryEditorConstants"; import { QueryEditorRouteParams } from "constants/routes"; import { Datasource } from "api/DatasourcesApi"; +import { getPluginImage } from "pages/Editor/QueryEditor/helpers"; +import { Plugin } from "api/PluginApi"; import { createActionRequest, moveActionRequest, copyActionRequest, } from "actions/actionActions"; -import { deleteQuery } from "actions/queryPaneActions"; -import { changeQuery, initQueryPane } from "actions/queryPaneActions"; -import { getQueryActions } from "selectors/entitiesSelector"; +import { + deleteQuery, + changeQuery, + initQueryPane, +} from "actions/queryPaneActions"; +import { getQueryActions, getPlugins } from "selectors/entitiesSelector"; import { getNextEntityName } from "utils/AppsmithUtils"; import { getDataSources } from "selectors/editorSelectors"; import { QUERY_EDITOR_URL_WITH_SELECTED_PAGE_ID } from "constants/routes"; import { RestAction } from "entities/Action"; +import { Colors } from "constants/Colors"; const ActionItem = styled.div` flex: 1; @@ -34,9 +40,23 @@ const ActionName = styled.span` white-space: nowrap; overflow: hidden; text-overflow: ellipsis; + margin-left: 10px; + max-width: 125px; +`; + +const StyledImage = styled.img` + height: 20px; + width: 20px; + + svg { + path { + fill: ${Colors.WHITE}; + } + } `; interface ReduxStateProps { + plugins: Plugin[]; queries: ActionDataState; apiPane: ApiPaneReduxState; actions: ActionDataState; @@ -148,6 +168,11 @@ class QuerySidebar extends React.Component { renderItem = (query: RestAction) => { return ( + {query.name} ); @@ -191,6 +216,7 @@ class QuerySidebar extends React.Component { } const mapStateToProps = (state: AppState): ReduxStateProps => ({ + plugins: getPlugins(state), queries: getQueryActions(state), apiPane: state.ui.apiPane, actions: state.entities.actions,