merge conflicts resolved

This commit is contained in:
vicky_primathon.in 2020-06-12 14:16:42 +05:30
commit 3bf86936b1
34 changed files with 277 additions and 53 deletions

View File

@ -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");
});

View File

@ -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\\)]"
}

View File

@ -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"],
);
@ -1125,8 +1130,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")

View File

@ -3,7 +3,4 @@
<mask id="mask0" mask-type="alpha" maskUnits="userSpaceOnUse" x="1" y="1" width="18" height="18">
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.33341 13.3333C8.33341 13.7916 7.95841 14.1666 7.50008 14.1666C7.04175 14.1666 6.66675 13.7916 6.66675 13.3333V9.99996C6.66675 9.54163 7.04175 9.16663 7.50008 9.16663C7.95841 9.16663 8.33341 9.54163 8.33341 9.99996V13.3333ZM13.3334 13.3333C13.3334 13.7916 12.9584 14.1666 12.5001 14.1666C12.0417 14.1666 11.6667 13.7916 11.6667 13.3333V9.99996C11.6667 9.54163 12.0417 9.16663 12.5001 9.16663C12.9584 9.16663 13.3334 9.54163 13.3334 9.99996V13.3333ZM15.0001 15.8333C15.0001 16.2925 14.6267 16.6666 14.1667 16.6666H5.83341C5.37341 16.6666 5.00008 16.2925 5.00008 15.8333V6.66663H15.0001V15.8333ZM8.33341 3.60663C8.33341 3.47746 8.51175 3.33329 8.75008 3.33329H11.2501C11.4884 3.33329 11.6667 3.47746 11.6667 3.60663V4.99996H8.33341V3.60663ZM17.5001 4.99996H16.6667H13.3334V3.60663C13.3334 2.53663 12.3992 1.66663 11.2501 1.66663H8.75008C7.60091 1.66663 6.66675 2.53663 6.66675 3.60663V4.99996H3.33341H2.50008C2.04175 4.99996 1.66675 5.37496 1.66675 5.83329C1.66675 6.29163 2.04175 6.66663 2.50008 6.66663H3.33341V15.8333C3.33341 17.2116 4.45508 18.3333 5.83342 18.3333H14.1667C15.5451 18.3333 16.6667 17.2116 16.6667 15.8333V6.66663H17.5001C17.9584 6.66663 18.3334 6.29163 18.3334 5.83329C18.3334 5.37496 17.9584 4.99996 17.5001 4.99996Z" fill="white"/>
</mask>
<g mask="url(#mask0)">
<rect width="20" height="20" fill="#A3B3BF"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@ -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;

View File

@ -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: (
<CodeEditor
height={"100%"}
input={{
value: _.isObject(response.requestBody)
? JSON.stringify(response.requestBody, null, 2)
: response.requestBody || "",
}}
height={700}
/>
),
},
@ -203,12 +207,14 @@ const ApiResponseView = (props: Props) => {
</ResponseMetaInfo>
</React.Fragment>
</FormRow>
<BaseTabbedView
overflow
tabs={tabs}
selectedIndex={selectedIndex}
setSelectedIndex={setSelectedIndex}
/>
<TabbedViewWrapper>
<BaseTabbedView
overflow
tabs={tabs}
selectedIndex={selectedIndex}
setSelectedIndex={setSelectedIndex}
/>
</TabbedViewWrapper>
</ResponseWrapper>
);
};

View File

@ -25,7 +25,6 @@ export const StyledCheckbox = styled(BlueprintCheckbox)<CheckboxProps>`
&&&& {
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])};

View File

@ -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<HTMLTextAreaElement>) => void;
};
height: number;
height: number | string;
}
class CodeEditor extends React.Component<Props> {
@ -35,7 +39,6 @@ class CodeEditor extends React.Component<Props> {
lineNumbers: true,
lineWrapping: true,
});
this.editor.setSize(null, this.props.height);
}
}

View File

@ -288,6 +288,11 @@ const DynamicAutocompleteInputWrapper = styled.div<{
props.isActive && props.skin === Skin.DARK
? Colors.ALABASTER
: "transparent"};
.bp3-popover-wrapper:first-of-type {
position: absolute;
right: 0;
top: 0;
}
&:hover {
border: ${props =>
props.skin === Skin.DARK ? "1px solid " + Colors.ALABASTER : "none"};

View File

@ -224,7 +224,8 @@ const views = {
props.set(event);
}
}}
dataTreePath={""}
expected={"string"}
evaluatedValue={props.get(props.value, false) as string}
isValid={props.isValid}
errorMessage={props.validationMessage}
/>

View File

@ -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,

View File

@ -10,6 +10,7 @@ export function InputText(props: {
onChange: (event: React.ChangeEvent<HTMLTextAreaElement> | 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 (
<StyledDynamicInput>
@ -30,6 +32,7 @@ export function InputText(props: {
value: value,
onChange: onChange,
}}
evaluatedValue={evaluatedValue}
expected={expected}
dataTreePath={dataTreePath}
meta={{

View File

@ -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",

View File

@ -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;
`;

View File

@ -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) => {
<DynamicTextField
name="actionConfiguration.body"
expected={FIELD_VALUES.API_ACTION.body}
height={300}
showLineNumbers
allowTabIndent
singleLine={false}

View File

@ -55,7 +55,7 @@ const MainConfiguration = styled.div`
const SecondaryWrapper = styled.div`
display: flex;
height: 100%;
height: calc(100% - 120px);
border-top: 1px solid #d0d7dd;
margin-top: 10px;
`;
@ -163,7 +163,12 @@ const RapidApiEditorForm: React.FC<Props> = (props: Props) => {
// }
return (
<Form onSubmit={handleSubmit}>
<Form
onSubmit={handleSubmit}
style={{
height: "100%",
}}
>
<MainConfiguration>
<FormRow>
<DynamicTextField

View File

@ -188,6 +188,7 @@ class ApiEditor extends React.Component<Props> {
<div
style={{
position: "relative",
height: "100%",
}}
>
{apiId ? (

View File

@ -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<Props, ComponentState> {
super(props);
this.state = {
isOpen: true,
isOpen: props.defaultIsOpen || false,
};
}
@ -54,6 +55,7 @@ class Collapsible extends React.Component<Props, ComponentState> {
}}
/>
<SectionContainer
data-cy={`section-${title}`}
onClick={() => this.setState({ isOpen: !this.state.isOpen })}
>
<SectionLabel>{title}</SectionLabel>

View File

@ -332,9 +332,7 @@ class DatasourceDBEditor extends React.Component<
/>
</FormTitleContainer>
{!_.isNil(sections)
? _.map(sections, section => {
return this.renderMainSection(section);
})
? _.map(sections, this.renderMainSection)
: undefined}
<SaveButtonContainer>
<ActionButton
@ -367,9 +365,9 @@ class DatasourceDBEditor extends React.Component<
);
};
renderMainSection = (section: any) => {
renderMainSection = (section: any, index: number) => {
return (
<Collapsible title={section.sectionName}>
<Collapsible title={section.sectionName} defaultIsOpen={index === 0}>
{this.renderEachConfig(section)}
</Collapsible>
);
@ -413,7 +411,7 @@ class DatasourceDBEditor extends React.Component<
}
return (
<div style={{ marginTop: "16px" }}>
<div key={configProperty} style={{ marginTop: "16px" }}>
{controlType !== "KEYVALUE_ARRAY" &&
controlType !== "SWITCH" && (
<FormLabel>

View File

@ -11,9 +11,16 @@ 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";
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";
@ -93,7 +100,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 +182,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 +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;
@ -299,6 +331,40 @@ const QueryEditorForm: React.FC<Props> = (props: Props) => {
);
};
const SingleValue = (props: SingleValueProps<OptionTypeBase>) => {
return (
<>
<components.SingleValue {...props}>
<Container>
<img
className="plugin-image"
src={props.data.image}
alt="Datasource"
/>
<div className="selected-value">{props.children}</div>
</Container>
</components.SingleValue>
</>
);
};
const CustomOption = (props: OptionProps<OptionTypeBase>) => {
return (
<>
<components.Option {...props}>
<Container>
<img
className="plugin-image"
src={props.data.image}
alt="Datasource"
/>
<div style={{ marginLeft: "6px" }}>{props.children}</div>
</Container>
</components.Option>
</>
);
};
return (
<QueryFormContainer>
<form onSubmit={handleSubmit}>
@ -314,9 +380,9 @@ const QueryEditorForm: React.FC<Props> = (props: Props) => {
placeholder="Datasource"
name="datasource.id"
options={DATASOURCES_OPTIONS}
width={200}
width={232}
maxMenuHeight={200}
components={{ MenuList }}
components={{ MenuList, Option: CustomOption, SingleValue }}
/>
</DropdownSelect>
<ActionButtons>
@ -430,6 +496,12 @@ const QueryEditorForm: React.FC<Props> = (props: Props) => {
mode="js-js"
/>
)}
<StyledCheckbox
intent="primary"
name="executeOnLoad"
align="left"
label="Run on Page Load"
/>
</form>
{dataSources.length === 0 && (

View File

@ -32,6 +32,7 @@ class JSONOutput extends React.Component<Props> {
style: {
fontSize: "14px",
},
collapsed: 1,
};
if (!src.length) {

View File

@ -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;
}
};

View File

@ -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<Props> {
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),

View File

@ -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<Props> {
renderItem = (query: RestAction) => {
return (
<ActionItem>
<StyledImage
src={getPluginImage(this.props.plugins, query.pluginId)}
className="pluginImage"
alt="Plugin Image"
/>
<ActionName>{query.name}</ActionName>
</ActionItem>
);
@ -191,6 +216,7 @@ class QuerySidebar extends React.Component<Props> {
}
const mapStateToProps = (state: AppState): ReduxStateProps => ({
plugins: getPlugins(state),
queries: getQueryActions(state),
apiPane: state.ui.apiPane,
actions: state.entities.actions,

View File

@ -157,12 +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: 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",

View File

@ -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<CheckboxWidgetProps, WidgetState> {
static getPropertyValidationMap(): WidgetPropertyValidationType {
@ -32,6 +35,12 @@ class CheckboxWidget extends BaseWidget<CheckboxWidgetProps, WidgetState> {
};
}
static getDerivedPropertiesMap(): DerivedPropertiesMap {
return {
value: `{{this.isChecked}}`,
};
}
static getMetaPropertiesMap(): Record<string, any> {
return {
isChecked: undefined,

View File

@ -34,6 +34,7 @@ class DatePickerWidget extends BaseWidget<DatePickerWidgetProps, WidgetState> {
static getDerivedPropertiesMap(): DerivedPropertiesMap {
return {
isValid: `{{ this.isRequired ? !!this.selectedDate : true }}`,
value: `{{ this.selectedDate }}`,
};
}

View File

@ -63,6 +63,7 @@ class DropdownWidget extends BaseWidget<DropdownWidgetProps, WidgetState> {
},
};
}
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<DropdownWidgetProps, WidgetState> {
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 }}`,
};
}

View File

@ -49,6 +49,7 @@ class FilePickerWidget extends BaseWidget<
static getDerivedPropertiesMap(): DerivedPropertiesMap {
return {
isValid: `{{ this.isRequired ? this.files.length > 0 : true }}`,
value: `{{this.files}}`,
};
}

View File

@ -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,36 @@ class FormWidget extends ContainerWidget {
super.resetChildrenMetaProperty(this.props.widgetId);
};
componentDidMount() {
super.componentDidMount();
this.updateFormData();
}
componentDidUpdate(prevProps: ContainerWidgetProps<any>) {
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<WidgetProps>) {
const formData: any = {};
if (formWidget.children)
formWidget.children.map(widgetData => {
if (widgetData.value) {
formData[widgetData.widgetName] = widgetData.value;
}
});
return formData;
}
renderChildWidget(childWidgetData: WidgetProps): React.ReactNode {
if (childWidgetData.children) {
const isInvalid = this.checkInvalidChildren(childWidgetData.children);
@ -37,6 +68,7 @@ class FormWidget extends ContainerWidget {
export interface FormWidgetProps extends ContainerComponentProps {
name: string;
data: object;
}
export default FormWidget;

View File

@ -59,6 +59,7 @@ class InputWidget extends BaseWidget<InputWidgetProps, InputWidgetState> {
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}}`,
};
}

View File

@ -27,6 +27,7 @@ class RadioGroupWidget extends BaseWidget<RadioGroupWidgetProps, WidgetState> {
selectedOption:
"{{_.find(this.options, { value: this.selectedOptionValue })}}",
isValid: `{{ this.isRequired ? !!this.selectedOptionValue : true }}`,
value: `{{this.selectedOptionValue}}`,
};
}
static getTriggerPropertyMap(): TriggerPropertiesMap {

View File

@ -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) {

View File

@ -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<TextWidgetProps, WidgetState> {
);
}
static getDerivedPropertiesMap(): DerivedPropertiesMap {
return {
value: `{{ this.text }}`,
};
}
getWidgetType(): WidgetType {
return "TEXT_WIDGET";
}