From 9920322aedb3467e9c0721156252b3b39e7685b8 Mon Sep 17 00:00:00 2001 From: Hetu Nandu Date: Tue, 6 Oct 2020 12:38:11 +0530 Subject: [PATCH 1/3] Fix CI checkouts for all pull_request_target steps --- .github/workflows/client.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/client.yml b/.github/workflows/client.yml index aa320ac996..ad1929ec49 100644 --- a/.github/workflows/client.yml +++ b/.github/workflows/client.yml @@ -104,6 +104,8 @@ jobs: steps: # Checkout the code - uses: actions/checkout@v2 + with: + ref: refs/pull/${{ github.event.pull_request.number }}/merge - name: Use Node.js 10.16.3 uses: actions/setup-node@v1 @@ -207,6 +209,8 @@ jobs: # Checkout the code - uses: actions/checkout@v2 + with: + ref: refs/pull/${{ github.event.pull_request.number }}/merge - name: Download the react build artifact uses: actions/download-artifact@v2 From 8b75e1c8f44dff05a07b67aa7bde09a31e9aeb8e Mon Sep 17 00:00:00 2001 From: Hetu Nandu Date: Tue, 6 Oct 2020 14:31:51 +0530 Subject: [PATCH 2/3] Local meta state in widgets (#851) --- app/client/src/widgets/BaseWidget.tsx | 10 -- app/client/src/widgets/CheckboxWidget.tsx | 9 +- app/client/src/widgets/DatePickerWidget.tsx | 9 +- app/client/src/widgets/DropdownWidget.tsx | 24 ++- app/client/src/widgets/FilepickerWidget.tsx | 13 +- app/client/src/widgets/FormWidget.tsx | 5 +- app/client/src/widgets/InputWidget.tsx | 49 ++---- app/client/src/widgets/MapWidget.tsx | 32 ++-- app/client/src/widgets/MetaHOC.tsx | 90 +++++++++++ app/client/src/widgets/ModalWidget.tsx | 7 +- app/client/src/widgets/RadioGroupWidget.tsx | 9 +- .../src/widgets/RichTextEditorWidget.tsx | 12 +- app/client/src/widgets/TableWidget.tsx | 145 +++++++++--------- app/client/src/widgets/TabsWidget.tsx | 17 +- app/client/src/widgets/VideoWidget.tsx | 11 +- 15 files changed, 261 insertions(+), 181 deletions(-) create mode 100644 app/client/src/widgets/MetaHOC.tsx diff --git a/app/client/src/widgets/BaseWidget.tsx b/app/client/src/widgets/BaseWidget.tsx index 387f3ce11e..afc8eb018c 100644 --- a/app/client/src/widgets/BaseWidget.tsx +++ b/app/client/src/widgets/BaseWidget.tsx @@ -33,7 +33,6 @@ import { DerivedPropertiesMap, TriggerPropertiesMap, } from "utils/WidgetFactory"; -import { clearPropertyCache } from "utils/DynamicBindingUtils"; /*** * BaseWidget @@ -115,15 +114,6 @@ abstract class BaseWidget< updateWidgetProperty(widgetId, propertyName, propertyValue); } - updateWidgetMetaProperty(propertyName: string, propertyValue: any): void { - const { updateWidgetMetaProperty } = this.context; - const { widgetId } = this.props; - // Whenever this value updates, we need to clear cache to handle correct evaluation - clearPropertyCache(`${this.props.widgetName}.${propertyName}`); - updateWidgetMetaProperty && - updateWidgetMetaProperty(widgetId, propertyName, propertyValue); - } - resetChildrenMetaProperty(widgetId: string) { const { resetChildrenMetaProperty } = this.context; resetChildrenMetaProperty(widgetId); diff --git a/app/client/src/widgets/CheckboxWidget.tsx b/app/client/src/widgets/CheckboxWidget.tsx index c330263428..cb5447753c 100644 --- a/app/client/src/widgets/CheckboxWidget.tsx +++ b/app/client/src/widgets/CheckboxWidget.tsx @@ -13,6 +13,7 @@ import { DerivedPropertiesMap, } from "utils/WidgetFactory"; import * as Sentry from "@sentry/react"; +import withMeta, { WithMeta } from "./MetaHOC"; class CheckboxWidget extends BaseWidget { static getPropertyValidationMap(): WidgetPropertyValidationType { @@ -63,7 +64,7 @@ class CheckboxWidget extends BaseWidget { } onCheckChange = (isChecked: boolean) => { - this.updateWidgetMetaProperty("isChecked", isChecked); + this.props.updateWidgetMetaProperty("isChecked", isChecked); if (this.props.onCheckChange) { super.executeAction({ dynamicString: this.props.onCheckChange, @@ -79,7 +80,7 @@ class CheckboxWidget extends BaseWidget { } } -export interface CheckboxWidgetProps extends WidgetProps { +export interface CheckboxWidgetProps extends WidgetProps, WithMeta { label: string; defaultCheckedState: boolean; isChecked?: boolean; @@ -88,4 +89,6 @@ export interface CheckboxWidgetProps extends WidgetProps { } export default CheckboxWidget; -export const ProfiledCheckboxWidget = Sentry.withProfiler(CheckboxWidget); +export const ProfiledCheckboxWidget = Sentry.withProfiler( + withMeta(CheckboxWidget), +); diff --git a/app/client/src/widgets/DatePickerWidget.tsx b/app/client/src/widgets/DatePickerWidget.tsx index a01f358afd..51dc1ad412 100644 --- a/app/client/src/widgets/DatePickerWidget.tsx +++ b/app/client/src/widgets/DatePickerWidget.tsx @@ -13,6 +13,7 @@ import { TriggerPropertiesMap, } from "utils/WidgetFactory"; import * as Sentry from "@sentry/react"; +import withMeta, { WithMeta } from "./MetaHOC"; class DatePickerWidget extends BaseWidget { static getPropertyValidationMap(): WidgetPropertyValidationType { @@ -73,7 +74,7 @@ class DatePickerWidget extends BaseWidget { } onDateSelected = (selectedDate: string) => { - this.updateWidgetMetaProperty("selectedDate", selectedDate); + this.props.updateWidgetMetaProperty("selectedDate", selectedDate); if (this.props.onDateSelected) { super.executeAction({ dynamicString: this.props.onDateSelected, @@ -91,7 +92,7 @@ class DatePickerWidget extends BaseWidget { export type DatePickerType = "DATE_PICKER" | "DATE_RANGE_PICKER"; -export interface DatePickerWidgetProps extends WidgetProps { +export interface DatePickerWidgetProps extends WidgetProps, WithMeta { defaultDate: string; selectedDate: string; isDisabled: boolean; @@ -106,4 +107,6 @@ export interface DatePickerWidgetProps extends WidgetProps { } export default DatePickerWidget; -export const ProfiledDatePickerWidget = Sentry.withProfiler(DatePickerWidget); +export const ProfiledDatePickerWidget = Sentry.withProfiler( + withMeta(DatePickerWidget), +); diff --git a/app/client/src/widgets/DropdownWidget.tsx b/app/client/src/widgets/DropdownWidget.tsx index 12eecfaa98..8f6b929d8c 100644 --- a/app/client/src/widgets/DropdownWidget.tsx +++ b/app/client/src/widgets/DropdownWidget.tsx @@ -14,6 +14,7 @@ import { VALIDATORS } from "utils/Validators"; import { DataTree } from "entities/DataTree/dataTreeFactory"; import { Intent as BlueprintIntent } from "@blueprintjs/core"; import * as Sentry from "@sentry/react"; +import withMeta, { WithMeta } from "./MetaHOC"; class DropdownWidget extends BaseWidget { static getPropertyValidationMap(): WidgetPropertyValidationType { @@ -95,7 +96,6 @@ class DropdownWidget extends BaseWidget { return { selectedOptionValue: undefined, selectedOptionValueArr: undefined, - selectedOptionValues: undefined, }; } @@ -138,9 +138,9 @@ class DropdownWidget extends BaseWidget { onOptionSelected = (selectedOption: DropdownOption) => { let isChanged = true; if (this.props.selectionType === "SINGLE_SELECT") { - isChanged = !(this.props.selectedOption.value == selectedOption.value); + isChanged = !(this.props.selectedOption.value === selectedOption.value); if (isChanged) { - this.updateWidgetMetaProperty( + this.props.updateWidgetMetaProperty( "selectedOptionValue", selectedOption.value, ); @@ -158,7 +158,10 @@ class DropdownWidget extends BaseWidget { } else { newSelectedValue.push(selectedOption.value); } - this.updateWidgetMetaProperty("selectedOptionValueArr", newSelectedValue); + this.props.updateWidgetMetaProperty( + "selectedOptionValueArr", + newSelectedValue, + ); } if (this.props.onOptionChange && isChanged) { @@ -176,7 +179,10 @@ class DropdownWidget extends BaseWidget { (v: string) => _.findIndex(this.props.options, { value: v }) !== removedIndex, ); - this.updateWidgetMetaProperty("selectedOptionValueArr", newSelectedValue); + this.props.updateWidgetMetaProperty( + "selectedOptionValueArr", + newSelectedValue, + ); if (this.props.onOptionChange) { super.executeAction({ dynamicString: this.props.onOptionChange, @@ -202,7 +208,7 @@ export interface DropdownOption { intent?: BlueprintIntent; } -export interface DropdownWidgetProps extends WidgetProps { +export interface DropdownWidgetProps extends WidgetProps, WithMeta { placeholderText?: string; label?: string; selectedIndex?: number; @@ -213,7 +219,11 @@ export interface DropdownWidgetProps extends WidgetProps { onOptionChange?: string; defaultOptionValue?: string | string[]; isRequired: boolean; + selectedOptionValue: string; + selectedOptionValueArr: string[]; } export default DropdownWidget; -export const ProfiledDropDownWidget = Sentry.withProfiler(DropdownWidget); +export const ProfiledDropDownWidget = Sentry.withProfiler( + withMeta(DropdownWidget), +); diff --git a/app/client/src/widgets/FilepickerWidget.tsx b/app/client/src/widgets/FilepickerWidget.tsx index 104a888111..a217ca48e4 100644 --- a/app/client/src/widgets/FilepickerWidget.tsx +++ b/app/client/src/widgets/FilepickerWidget.tsx @@ -21,6 +21,7 @@ import Dashboard from "@uppy/dashboard"; import shallowequal from "shallowequal"; import _ from "lodash"; import * as Sentry from "@sentry/react"; +import withMeta, { WithMeta } from "./MetaHOC"; class FilePickerWidget extends BaseWidget< FilePickerWidgetProps, @@ -121,7 +122,7 @@ class FilePickerWidget extends BaseWidget< return file.id !== dslFile.id; }) : []; - this.updateWidgetMetaProperty("files", updatedFiles); + this.props.updateWidgetMetaProperty("files", updatedFiles); }); this.uppy.on("file-added", (file: any) => { const dslFiles = this.props.files || []; @@ -135,7 +136,7 @@ class FilePickerWidget extends BaseWidget< blob: file.data, }; dslFiles.push(newFile); - this.updateWidgetMetaProperty("files", dslFiles); + this.props.updateWidgetMetaProperty("files", dslFiles); }; }); this.uppy.on("upload", () => { @@ -164,7 +165,7 @@ class FilePickerWidget extends BaseWidget< handleFileUploaded = (result: ExecutionResult) => { if (result.success) { - this.updateWidgetMetaProperty( + this.props.updateWidgetMetaProperty( "uploadedFileUrls", this.props.uploadedFileUrlPaths, ); @@ -220,7 +221,7 @@ export interface FilePickerWidgetState extends WidgetState { version: number; } -export interface FilePickerWidgetProps extends WidgetProps { +export interface FilePickerWidgetProps extends WidgetProps, WithMeta { label: string; maxNumFiles?: number; maxFileSize?: number; @@ -232,4 +233,6 @@ export interface FilePickerWidgetProps extends WidgetProps { } export default FilePickerWidget; -export const ProfiledFilePickerWidget = Sentry.withProfiler(FilePickerWidget); +export const ProfiledFilePickerWidget = Sentry.withProfiler( + withMeta(FilePickerWidget), +); diff --git a/app/client/src/widgets/FormWidget.tsx b/app/client/src/widgets/FormWidget.tsx index a672212c6a..d1aaba1116 100644 --- a/app/client/src/widgets/FormWidget.tsx +++ b/app/client/src/widgets/FormWidget.tsx @@ -6,6 +6,7 @@ import ContainerWidget, { ContainerWidgetProps } from "widgets/ContainerWidget"; import { ContainerComponentProps } from "components/designSystems/appsmith/ContainerComponent"; import shallowEqual from "shallowequal"; import * as Sentry from "@sentry/react"; +import withMeta from "./MetaHOC"; class FormWidget extends ContainerWidget { checkInvalidChildren = (children: WidgetProps[]): boolean => { @@ -34,7 +35,7 @@ class FormWidget extends ContainerWidget { if (this.props.children) { const formData = this.getFormData(this.props.children[0]); if (!shallowEqual(formData, this.props.data)) { - this.updateWidgetMetaProperty("data", formData); + this.props.updateWidgetMetaProperty("data", formData); } } } @@ -73,4 +74,4 @@ export interface FormWidgetProps extends ContainerComponentProps { } export default FormWidget; -export const ProfiledFormWidget = Sentry.withProfiler(FormWidget); +export const ProfiledFormWidget = Sentry.withProfiler(withMeta(FormWidget)); diff --git a/app/client/src/widgets/InputWidget.tsx b/app/client/src/widgets/InputWidget.tsx index 9fbd074f33..f34a1b7280 100644 --- a/app/client/src/widgets/InputWidget.tsx +++ b/app/client/src/widgets/InputWidget.tsx @@ -15,20 +15,10 @@ import { DerivedPropertiesMap, TriggerPropertiesMap, } from "utils/WidgetFactory"; -import _ from "lodash"; import * as Sentry from "@sentry/react"; +import withMeta, { WithMeta } from "./MetaHOC"; -class InputWidget extends BaseWidget { - debouncedHandleTextChanged = _.debounce( - this.handleTextChanged.bind(this), - 200, - ); - constructor(props: InputWidgetProps) { - super(props); - this.state = { - text: props.text, - }; - } +class InputWidget extends BaseWidget { static getPropertyValidationMap(): WidgetPropertyValidationType { return { ...BASE_WIDGET_VALIDATION, @@ -103,28 +93,11 @@ class InputWidget extends BaseWidget { }; } - componentDidUpdate(prevProps: InputWidgetProps) { - super.componentDidUpdate(prevProps); - if ( - prevProps.text !== this.props.text && - this.props.defaultText === this.props.text - ) { - const text = this.props.text; - this.setState({ text }); - } - } - onValueChange = (value: string) => { - this.setState({ text: value }, () => { - this.updateWidgetMetaProperty("text", value); - }); + this.props.updateWidgetMetaProperty("text", value); if (!this.props.isDirty) { - this.updateWidgetMetaProperty("isDirty", true); + this.props.updateWidgetMetaProperty("isDirty", true); } - this.debouncedHandleTextChanged(); - }; - - handleTextChanged() { if (this.props.onTextChanged) { super.executeAction({ dynamicString: this.props.onTextChanged, @@ -133,14 +106,14 @@ class InputWidget extends BaseWidget { }, }); } - } + }; handleFocusChange = (focusState: boolean) => { - this.updateWidgetMetaProperty("isFocused", focusState); + this.props.updateWidgetMetaProperty("isFocused", focusState); }; getPageView() { - const value = this.state.text || ""; + const value = this.props.text || ""; const isInvalid = "isValid" in this.props && !this.props.isValid && !!this.props.isDirty; @@ -198,7 +171,7 @@ export interface InputValidator { validationRegex: string; errorMessage: string; } -export interface InputWidgetProps extends WidgetProps { +export interface InputWidgetProps extends WidgetProps, WithMeta { inputType: InputType; defaultText?: string; isDisabled?: boolean; @@ -220,9 +193,5 @@ export interface InputWidgetProps extends WidgetProps { isDirty?: boolean; } -interface InputWidgetState extends WidgetState { - text: string; -} - export default InputWidget; -export const ProfiledInputWidget = Sentry.withProfiler(InputWidget); +export const ProfiledInputWidget = Sentry.withProfiler(withMeta(InputWidget)); diff --git a/app/client/src/widgets/MapWidget.tsx b/app/client/src/widgets/MapWidget.tsx index 7d81407e50..5861595a66 100644 --- a/app/client/src/widgets/MapWidget.tsx +++ b/app/client/src/widgets/MapWidget.tsx @@ -9,6 +9,7 @@ import { TriggerPropertiesMap } from "utils/WidgetFactory"; import { getAppsmithConfigs } from "configs"; import styled from "styled-components"; import * as Sentry from "@sentry/react"; +import withMeta, { WithMeta } from "./MetaHOC"; const { google } = getAppsmithConfigs(); @@ -58,33 +59,33 @@ class MapWidget extends BaseWidget { return { center: undefined, markers: undefined, + selectedMarker: undefined, }; } updateCenter = (lat: number, long: number) => { - this.updateWidgetMetaProperty("center", { lat, long }); + this.props.updateWidgetMetaProperty("center", { lat, long }); }; updateMarker = (lat: number, long: number, index: number) => { - const markers: Array = [...this.props.markers]; - this.disableDrag(false); - this.updateWidgetMetaProperty( - "markers", - markers.map((marker, i) => { + const markers: Array = [...this.props.markers].map( + (marker, i) => { if (index === i) { marker.lat = lat; marker.long = long; } return marker; - }), + }, ); + this.disableDrag(false); + this.props.updateWidgetMetaProperty("markers", markers); }; onCreateMarker = (lat: number, long: number) => { this.disableDrag(true); - this.updateWidgetMetaProperty("selectedMarker", { - lat: lat, - long: long, + this.props.updateWidgetMetaProperty("selectedMarker", { + lat, + long, }); if (this.props.onCreateMarker) { super.executeAction({ @@ -97,12 +98,13 @@ class MapWidget extends BaseWidget { }; onMarkerClick = (lat: number, long: number, title: string) => { - this.updateWidgetMetaProperty("selectedMarker", { + this.disableDrag(true); + const selectedMarker = { lat: lat, long: long, title: title, - }); - this.disableDrag(true); + }; + this.props.updateWidgetMetaProperty("selectedMarker", selectedMarker); if (this.props.onMarkerClick) { super.executeAction({ dynamicString: this.props.onMarkerClick, @@ -172,7 +174,7 @@ export interface MarkerProps { description?: string; } -export interface MapWidgetProps extends WidgetProps { +export interface MapWidgetProps extends WidgetProps, WithMeta { isDisabled?: boolean; isVisible?: boolean; enableSearch: boolean; @@ -199,4 +201,4 @@ export interface MapWidgetProps extends WidgetProps { } export default MapWidget; -export const ProfiledMapWidget = Sentry.withProfiler(MapWidget); +export const ProfiledMapWidget = Sentry.withProfiler(withMeta(MapWidget)); diff --git a/app/client/src/widgets/MetaHOC.tsx b/app/client/src/widgets/MetaHOC.tsx new file mode 100644 index 0000000000..70e96e199d --- /dev/null +++ b/app/client/src/widgets/MetaHOC.tsx @@ -0,0 +1,90 @@ +import React from "react"; +import BaseWidget, { WidgetProps } from "./BaseWidget"; +import _ from "lodash"; +import { EditorContext } from "../components/editorComponents/EditorContextProvider"; +import { clearPropertyCache } from "../utils/DynamicBindingUtils"; + +export interface WithMeta { + updateWidgetMetaProperty: (propertyName: string, propertyValue: any) => void; +} + +const withMeta = (WrappedWidget: typeof BaseWidget) => { + return class MetaHOC extends React.Component { + static contextType = EditorContext; + updatedProperties = new Map(); + + debouncedHandleUpdateWidgetMetaProperty = _.debounce( + this.handleUpdateWidgetMetaProperty.bind(this), + 200, + { + leading: true, + trailing: true, + }, + ); + + constructor(props: any) { + super(props); + const metaProperties = WrappedWidget.getMetaPropertiesMap(); + this.state = _.fromPairs( + Object.keys(metaProperties).map(metaProperty => { + return [metaProperty, this.props[metaProperty]]; + }), + ); + } + + componentDidUpdate(prevProps: WidgetProps) { + const metaProperties = WrappedWidget.getMetaPropertiesMap(); + const defaultProperties = WrappedWidget.getDefaultPropertiesMap(); + Object.keys(metaProperties).forEach(metaProperty => { + const defaultProperty = defaultProperties[metaProperty]; + if ( + prevProps[metaProperty] !== this.props[metaProperty] && + this.props[defaultProperty] === this.props[metaProperty] + ) { + this.setState({ [metaProperty]: this.props[metaProperty] }); + } + }); + } + + updateWidgetMetaProperty = ( + propertyName: string, + propertyValue: any, + ): void => { + this.setState({ + [propertyName]: propertyValue, + }); + this.updatedProperties.set(propertyName, true); + this.debouncedHandleUpdateWidgetMetaProperty(); + }; + + handleUpdateWidgetMetaProperty() { + const { updateWidgetMetaProperty } = this.context; + const { widgetId, widgetName } = this.props; + // We have kept a map of all updated properties. After debouncing we will + // go through these properties and update with the final value. This way + // we will only update a certain property once per debounce interval. + [...this.updatedProperties.keys()].forEach(propertyName => { + if (updateWidgetMetaProperty) { + const propertyValue = this.state[propertyName]; + clearPropertyCache(`${widgetName}.${propertyName}`); + updateWidgetMetaProperty(widgetId, propertyName, propertyValue); + this.updatedProperties.delete(propertyName); + } + }); + } + + updatedProps = () => { + return { + ...this.props, + ...this.state, + updateWidgetMetaProperty: this.updateWidgetMetaProperty, + }; + }; + + render() { + return ; + } + }; +}; + +export default withMeta; diff --git a/app/client/src/widgets/ModalWidget.tsx b/app/client/src/widgets/ModalWidget.tsx index a8258dd168..28828b0e80 100644 --- a/app/client/src/widgets/ModalWidget.tsx +++ b/app/client/src/widgets/ModalWidget.tsx @@ -12,6 +12,7 @@ import { } from "constants/WidgetConstants"; import { generateClassName } from "utils/generators"; import * as Sentry from "@sentry/react"; +import withMeta, { WithMeta } from "./MetaHOC"; const MODAL_SIZE: { [id: string]: { width: number; height: number } } = { MODAL_SMALL: { @@ -48,7 +49,7 @@ class ModalWidget extends BaseWidget { this.props.showPropertyPane(undefined); // TODO(abhinav): Create a static property with is a map of widget properties // Populate the map on widget load - super.updateWidgetMetaProperty("isVisible", false); + this.props.updateWidgetMetaProperty("isVisible", false); e.stopPropagation(); e.preventDefault(); }; @@ -97,7 +98,7 @@ class ModalWidget extends BaseWidget { } } -export interface ModalWidgetProps extends WidgetProps { +export interface ModalWidgetProps extends WidgetProps, WithMeta { renderMode: RenderMode; isOpen?: boolean; children?: WidgetProps[]; @@ -131,4 +132,4 @@ export default ModalWidget; export const ProfiledModalWidget = connect( null, mapDispatchToProps, -)(Sentry.withProfiler(ModalWidget)); +)(Sentry.withProfiler(withMeta(ModalWidget))); diff --git a/app/client/src/widgets/RadioGroupWidget.tsx b/app/client/src/widgets/RadioGroupWidget.tsx index ec73394437..f6b83f9c7b 100644 --- a/app/client/src/widgets/RadioGroupWidget.tsx +++ b/app/client/src/widgets/RadioGroupWidget.tsx @@ -10,6 +10,7 @@ import { import { VALIDATION_TYPES } from "constants/WidgetValidation"; import { TriggerPropertiesMap } from "utils/WidgetFactory"; import * as Sentry from "@sentry/react"; +import withMeta, { WithMeta } from "./MetaHOC"; class RadioGroupWidget extends BaseWidget { static getPropertyValidationMap(): WidgetPropertyValidationType { @@ -65,7 +66,7 @@ class RadioGroupWidget extends BaseWidget { } onRadioSelectionChange = (updatedValue: string) => { - super.updateWidgetMetaProperty("selectedOptionValue", updatedValue); + this.props.updateWidgetMetaProperty("selectedOptionValue", updatedValue); if (this.props.onSelectionChange) { super.executeAction({ dynamicString: this.props.onSelectionChange, @@ -86,7 +87,7 @@ export interface RadioOption { value: string; } -export interface RadioGroupWidgetProps extends WidgetProps { +export interface RadioGroupWidgetProps extends WidgetProps, WithMeta { label: string; options: RadioOption[]; selectedOptionValue: string; @@ -96,4 +97,6 @@ export interface RadioGroupWidgetProps extends WidgetProps { } export default RadioGroupWidget; -export const ProfiledRadioGroupWidget = Sentry.withProfiler(RadioGroupWidget); +export const ProfiledRadioGroupWidget = Sentry.withProfiler( + withMeta(RadioGroupWidget), +); diff --git a/app/client/src/widgets/RichTextEditorWidget.tsx b/app/client/src/widgets/RichTextEditorWidget.tsx index 208f33151d..2b35eb0b9a 100644 --- a/app/client/src/widgets/RichTextEditorWidget.tsx +++ b/app/client/src/widgets/RichTextEditorWidget.tsx @@ -11,6 +11,7 @@ import { import Skeleton from "components/utils/Skeleton"; import * as Sentry from "@sentry/react"; import { retryPromise } from "utils/AppsmithUtils"; +import withMeta, { WithMeta } from "./MetaHOC"; const RichTextEditorComponent = lazy(() => retryPromise(() => @@ -60,7 +61,7 @@ class RichTextEditorWidget extends BaseWidget< } onValueChange = (text: string) => { - this.updateWidgetMetaProperty("text", text); + this.props.updateWidgetMetaProperty("text", text); if (this.props.onTextChange) { super.executeAction({ dynamicString: this.props.onTextChange, @@ -92,12 +93,7 @@ class RichTextEditorWidget extends BaseWidget< } } -export interface InputValidator { - validationRegex: string; - errorMessage: string; -} - -export interface RichTextEditorWidgetProps extends WidgetProps { +export interface RichTextEditorWidgetProps extends WidgetProps, WithMeta { defaultText?: string; text?: string; placeholder?: string; @@ -108,5 +104,5 @@ export interface RichTextEditorWidgetProps extends WidgetProps { export default RichTextEditorWidget; export const ProfiledRichTextEditorWidget = Sentry.withProfiler( - RichTextEditorWidget, + withMeta(RichTextEditorWidget), ); diff --git a/app/client/src/widgets/TableWidget.tsx b/app/client/src/widgets/TableWidget.tsx index 28a03ae5cd..c02907aae8 100644 --- a/app/client/src/widgets/TableWidget.tsx +++ b/app/client/src/widgets/TableWidget.tsx @@ -1,29 +1,29 @@ -import React, { Suspense, lazy } from "react"; +import React, { lazy, Suspense } from "react"; import BaseWidget, { WidgetProps, WidgetState } from "./BaseWidget"; -import { WidgetType } from "constants/WidgetConstants"; +import { RenderModes, WidgetType } from "constants/WidgetConstants"; import { EventType } from "constants/ActionConstants"; import { compare, + ConditionFunctions, getAllTableColumnKeys, - renderCell, renderActions, + renderCell, reorderColumns, sortTableFunction, - ConditionFunctions, } from "components/designSystems/appsmith/TableUtilities"; import { VALIDATION_TYPES } from "constants/WidgetValidation"; -import { RenderModes } from "constants/WidgetConstants"; import { - WidgetPropertyValidationType, BASE_WIDGET_VALIDATION, + WidgetPropertyValidationType, } from "utils/ValidationFactory"; import { ColumnAction } from "components/propertyControls/ColumnActionSelectorControl"; import { TriggerPropertiesMap } from "utils/WidgetFactory"; import Skeleton from "components/utils/Skeleton"; import moment from "moment"; -import { isString, isNumber, isUndefined } from "lodash"; +import { isNumber, isString, isUndefined } from "lodash"; import * as Sentry from "@sentry/react"; import { retryPromise } from "utils/AppsmithUtils"; +import withMeta, { WithMeta } from "./MetaHOC"; const ReactTableComponent = lazy(() => retryPromise(() => @@ -297,7 +297,7 @@ class TableWidget extends BaseWidget { if (!tableData || !tableData.length) { return []; } - let sortedTableData = []; + let sortedTableData: any[]; const searchKey = searchText ? searchText.toUpperCase() : ""; if (sortedColumn) { const sortColumn = sortedColumn.column; @@ -311,35 +311,32 @@ class TableWidget extends BaseWidget { } else { sortedTableData = [...tableData]; } - const filteredTableData = sortedTableData.filter( - (item: { [key: string]: any }) => { - const searchFound = searchKey - ? Object.values(item) - .join(", ") - .toUpperCase() - .includes(searchKey) - : true; - if (!searchFound) return false; - if (!filters || filters.length === 0) return true; - const filterOperator: Operator = - filters.length >= 2 ? filters[1].operator : OperatorTypes.OR; - let filter = filterOperator === OperatorTypes.AND ? true : false; - for (let i = 0; i < filters.length; i++) { - const filterValue = compare( - item[filters[i].column], - filters[i].value, - filters[i].condition, - ); - if (filterOperator === OperatorTypes.AND) { - filter = filter && filterValue; - } else { - filter = filter || filterValue; - } + return sortedTableData.filter((item: { [key: string]: any }) => { + const searchFound = searchKey + ? Object.values(item) + .join(", ") + .toUpperCase() + .includes(searchKey) + : true; + if (!searchFound) return false; + if (!filters || filters.length === 0) return true; + const filterOperator: Operator = + filters.length >= 2 ? filters[1].operator : OperatorTypes.OR; + let filter = filterOperator === OperatorTypes.AND; + for (let i = 0; i < filters.length; i++) { + const filterValue = compare( + item[filters[i].column], + filters[i].value, + filters[i].condition, + ); + if (filterOperator === OperatorTypes.AND) { + filter = filter && filterValue; + } else { + filter = filter || filterValue; } - return filter; - }, - ); - return filteredTableData; + } + return filter; + }); }; getSelectedRow = (filteredTableData: object[], selectedRowIndex?: number) => { @@ -356,9 +353,9 @@ class TableWidget extends BaseWidget { componentDidMount() { const filteredTableData = this.filterTableData(); - super.updateWidgetMetaProperty("filteredTableData", filteredTableData); + this.props.updateWidgetMetaProperty("filteredTableData", filteredTableData); const { selectedRowIndex } = this.props; - super.updateWidgetMetaProperty( + this.props.updateWidgetMetaProperty( "selectedRow", this.getSelectedRow(filteredTableData, selectedRowIndex), ); @@ -377,14 +374,17 @@ class TableWidget extends BaseWidget { !this.props.filteredTableData ) { const filteredTableData = this.filterTableData(); - super.updateWidgetMetaProperty("filteredTableData", filteredTableData); + this.props.updateWidgetMetaProperty( + "filteredTableData", + filteredTableData, + ); if (!this.props.multiRowSelection) { - super.updateWidgetMetaProperty( + this.props.updateWidgetMetaProperty( "selectedRow", this.getSelectedRow(filteredTableData), ); } else { - super.updateWidgetMetaProperty( + this.props.updateWidgetMetaProperty( "selectedRows", filteredTableData.filter((item: object, i: number) => { return this.props.selectedRowIndices.includes(i); @@ -393,36 +393,36 @@ class TableWidget extends BaseWidget { } } if (tableDataUpdated) { - super.updateWidgetMetaProperty("selectedRowIndices", []); - super.updateWidgetMetaProperty("selectedRows", []); - super.updateWidgetMetaProperty("selectedRowIndex", -1); + this.props.updateWidgetMetaProperty("selectedRowIndices", []); + this.props.updateWidgetMetaProperty("selectedRows", []); + this.props.updateWidgetMetaProperty("selectedRowIndex", -1); } if (this.props.multiRowSelection !== prevProps.multiRowSelection) { if (this.props.multiRowSelection) { const selectedRowIndices = this.props.selectedRowIndex ? [this.props.selectedRowIndex] : []; - super.updateWidgetMetaProperty( + this.props.updateWidgetMetaProperty( "selectedRowIndices", selectedRowIndices, ); - super.updateWidgetMetaProperty("selectedRowIndex", -1); + this.props.updateWidgetMetaProperty("selectedRowIndex", -1); const filteredTableData = this.filterTableData(); - super.updateWidgetMetaProperty( + this.props.updateWidgetMetaProperty( "selectedRows", filteredTableData.filter((item: object, i: number) => { return selectedRowIndices.includes(i); }), ); - super.updateWidgetMetaProperty( + this.props.updateWidgetMetaProperty( "selectedRow", this.getSelectedRow(filteredTableData), ); } else { const filteredTableData = this.filterTableData(); - super.updateWidgetMetaProperty("selectedRowIndices", []); - super.updateWidgetMetaProperty("selectedRows", []); - super.updateWidgetMetaProperty( + this.props.updateWidgetMetaProperty("selectedRowIndices", []); + this.props.updateWidgetMetaProperty("selectedRows", []); + this.props.updateWidgetMetaProperty( "selectedRow", this.getSelectedRow(filteredTableData), ); @@ -456,7 +456,7 @@ class TableWidget extends BaseWidget { if (pageNo === undefined) { pageNo = 1; - super.updateWidgetMetaProperty("pageNo", pageNo); + this.props.updateWidgetMetaProperty("pageNo", pageNo); } const { componentWidth, componentHeight } = this.getComponentDimensions(); const tableSizes = @@ -477,7 +477,7 @@ class TableWidget extends BaseWidget { pageSize += 1; if (pageSize !== this.props.pageSize) { - super.updateWidgetMetaProperty("pageSize", pageSize); + this.props.updateWidgetMetaProperty("pageSize", pageSize); } return ( }> @@ -511,7 +511,7 @@ class TableWidget extends BaseWidget { nextPageClick={this.handleNextPageClick} prevPageClick={this.handlePrevPageClick} updatePageNo={(pageNo: number) => { - super.updateWidgetMetaProperty("pageNo", pageNo); + this.props.updateWidgetMetaProperty("pageNo", pageNo); }} updateHiddenColumns={(hiddenColumns?: string[]) => { super.updateWidgetProperty("hiddenColumns", hiddenColumns); @@ -538,14 +538,14 @@ class TableWidget extends BaseWidget { filters={this.props.filters} applyFilter={(filters: ReactTableFilter[]) => { this.resetSelectedRowIndex(); - super.updateWidgetMetaProperty("filters", filters); + this.props.updateWidgetMetaProperty("filters", filters); }} compactMode={this.props.compactMode || CompactModeTypes.DEFAULT} updateCompactMode={(compactMode: CompactMode) => { if (this.props.renderMode === RenderModes.CANVAS) { - super.updateWidgetProperty("compactMode", compactMode); + this.props.updateWidgetMetaProperty("compactMode", compactMode); } else { - super.updateWidgetMetaProperty("compactMode", compactMode); + this.props.updateWidgetMetaProperty("compactMode", compactMode); } }} sortTableColumn={this.handleColumnSorting} @@ -557,9 +557,9 @@ class TableWidget extends BaseWidget { handleColumnSorting = (column: string, asc: boolean) => { this.resetSelectedRowIndex(); if (column === "") { - super.updateWidgetMetaProperty("sortedColumn", undefined); + this.props.updateWidgetMetaProperty("sortedColumn", undefined); } else { - super.updateWidgetMetaProperty("sortedColumn", { + this.props.updateWidgetMetaProperty("sortedColumn", { column: column, asc: asc, }); @@ -569,8 +569,8 @@ class TableWidget extends BaseWidget { handleSearchTable = (searchKey: any) => { const { onSearchTextChanged } = this.props; this.resetSelectedRowIndex(); - this.updateWidgetMetaProperty("pageNo", 1); - super.updateWidgetMetaProperty("searchText", searchKey); + this.props.updateWidgetMetaProperty("pageNo", 1); + this.props.updateWidgetMetaProperty("searchText", searchKey); if (onSearchTextChanged) { super.executeAction({ dynamicString: onSearchTextChanged, @@ -604,16 +604,19 @@ class TableWidget extends BaseWidget { } else { selectedRowIndices.push(index); } - super.updateWidgetMetaProperty("selectedRowIndices", selectedRowIndices); - super.updateWidgetMetaProperty( + this.props.updateWidgetMetaProperty( + "selectedRowIndices", + selectedRowIndices, + ); + this.props.updateWidgetMetaProperty( "selectedRows", this.props.filteredTableData.filter((item: object, i: number) => { return selectedRowIndices.includes(i); }), ); } else { - super.updateWidgetMetaProperty("selectedRowIndex", index); - super.updateWidgetMetaProperty( + this.props.updateWidgetMetaProperty("selectedRowIndex", index); + this.props.updateWidgetMetaProperty( "selectedRow", this.props.filteredTableData[index], ); @@ -631,7 +634,7 @@ class TableWidget extends BaseWidget { handleNextPageClick = () => { let pageNo = this.props.pageNo || 1; pageNo = pageNo + 1; - super.updateWidgetMetaProperty("pageNo", pageNo); + this.props.updateWidgetMetaProperty("pageNo", pageNo); if (this.props.onPageChange) { this.resetSelectedRowIndex(); super.executeAction({ @@ -644,15 +647,15 @@ class TableWidget extends BaseWidget { }; resetSelectedRowIndex = () => { - super.updateWidgetMetaProperty("selectedRowIndex", -1); - super.updateWidgetMetaProperty("selectedRowIndices", []); + this.props.updateWidgetMetaProperty("selectedRowIndex", -1); + this.props.updateWidgetMetaProperty("selectedRowIndices", []); }; handlePrevPageClick = () => { let pageNo = this.props.pageNo || 1; pageNo = pageNo - 1; if (pageNo >= 1) { - super.updateWidgetMetaProperty("pageNo", pageNo); + this.props.updateWidgetMetaProperty("pageNo", pageNo); if (this.props.onPageChange) { this.resetSelectedRowIndex(); super.executeAction({ @@ -697,7 +700,7 @@ export interface ReactTableColumnProps { Cell: (props: any) => JSX.Element; } -export interface TableWidgetProps extends WidgetProps { +export interface TableWidgetProps extends WidgetProps, WithMeta { nextPageKey?: string; prevPageKey?: string; label: string; @@ -729,4 +732,4 @@ export interface TableWidgetProps extends WidgetProps { } export default TableWidget; -export const ProfiledTableWidget = Sentry.withProfiler(TableWidget); +export const ProfiledTableWidget = Sentry.withProfiler(withMeta(TableWidget)); diff --git a/app/client/src/widgets/TabsWidget.tsx b/app/client/src/widgets/TabsWidget.tsx index e610437d47..cdff94c742 100644 --- a/app/client/src/widgets/TabsWidget.tsx +++ b/app/client/src/widgets/TabsWidget.tsx @@ -10,6 +10,7 @@ import { EventType } from "constants/ActionConstants"; import { WidgetOperations } from "widgets/BaseWidget"; import * as Sentry from "@sentry/react"; import { generateReactKey } from "utils/generators"; +import withMeta, { WithMeta } from "./MetaHOC"; class TabsWidget extends BaseWidget< TabsWidgetProps, @@ -23,7 +24,7 @@ class TabsWidget extends BaseWidget< } onTabChange = (tabId: string) => { - this.updateWidgetMetaProperty("selectedTabId", tabId); + this.props.updateWidgetMetaProperty("selectedTabId", tabId); if (this.props.onTabSelected) { super.executeAction({ dynamicString: this.props.onTabSelected, @@ -174,7 +175,7 @@ class TabsWidget extends BaseWidget< label: this.props.defaultTab, }); const selectedTabId = selectedTab ? selectedTab.id : undefined; - this.updateWidgetMetaProperty("selectedTabId", selectedTabId); + this.props.updateWidgetMetaProperty("selectedTabId", selectedTabId); } } } @@ -227,12 +228,15 @@ class TabsWidget extends BaseWidget< // If we have a legitimate default tab Id and it is not already the selected Tab if (selectedTabId && selectedTabId !== this.props.selectedTabId) { // Select the default tab - this.updateWidgetMetaProperty("selectedTabId", selectedTabId); + this.props.updateWidgetMetaProperty("selectedTabId", selectedTabId); } } else if (!this.props.selectedTabId) { // If no tab is selected // Select the first tab in the tabs list. - this.updateWidgetMetaProperty("selectedTabId", this.props.tabs[0].id); + this.props.updateWidgetMetaProperty( + "selectedTabId", + this.props.tabs[0].id, + ); } this.generateTabContainers(); } @@ -243,7 +247,8 @@ export interface TabContainerWidgetProps extends WidgetProps { } export interface TabsWidgetProps - extends WidgetProps { + extends WidgetProps, + WithMeta { isVisible?: boolean; shouldScrollContents: boolean; tabs: Array<{ @@ -261,4 +266,4 @@ export interface TabsWidgetProps } export default TabsWidget; -export const ProfiledTabsWidget = Sentry.withProfiler(TabsWidget); +export const ProfiledTabsWidget = Sentry.withProfiler(withMeta(TabsWidget)); diff --git a/app/client/src/widgets/VideoWidget.tsx b/app/client/src/widgets/VideoWidget.tsx index 404956d4d5..7e8a9b3e54 100644 --- a/app/client/src/widgets/VideoWidget.tsx +++ b/app/client/src/widgets/VideoWidget.tsx @@ -12,6 +12,7 @@ import Skeleton from "components/utils/Skeleton"; import * as Sentry from "@sentry/react"; import { retryPromise } from "utils/AppsmithUtils"; import ReactPlayer from "react-player"; +import withMeta, { WithMeta } from "./MetaHOC"; const VideoComponent = lazy(() => retryPromise(() => @@ -67,7 +68,7 @@ class VideoWidget extends BaseWidget { autoplay={autoPlay} controls={true} onPlay={() => { - this.updateWidgetMetaProperty("playState", PlayState.PLAYING); + this.props.updateWidgetMetaProperty("playState", PlayState.PLAYING); if (onPlay) { super.executeAction({ dynamicString: onPlay, @@ -79,7 +80,7 @@ class VideoWidget extends BaseWidget { }} onPause={() => { //TODO: We do not want the pause event for onSeek or onEnd. - this.updateWidgetMetaProperty("playState", PlayState.PAUSED); + this.props.updateWidgetMetaProperty("playState", PlayState.PAUSED); if (onPause) { super.executeAction({ dynamicString: onPause, @@ -90,7 +91,7 @@ class VideoWidget extends BaseWidget { } }} onEnded={() => { - this.updateWidgetMetaProperty("playState", PlayState.ENDED); + this.props.updateWidgetMetaProperty("playState", PlayState.ENDED); if (onEnd) { super.executeAction({ dynamicString: onEnd, @@ -110,7 +111,7 @@ class VideoWidget extends BaseWidget { } } -export interface VideoWidgetProps extends WidgetProps { +export interface VideoWidgetProps extends WidgetProps, WithMeta { url: string; autoPlay: boolean; onPause?: string; @@ -119,4 +120,4 @@ export interface VideoWidgetProps extends WidgetProps { } export default VideoWidget; -export const ProfiledVideoWidget = Sentry.withProfiler(VideoWidget); +export const ProfiledVideoWidget = Sentry.withProfiler(withMeta(VideoWidget)); From 012c78a698cfeb11e33c518d58c4d2d20d8c320b Mon Sep 17 00:00:00 2001 From: areyabhishek Date: Tue, 6 Oct 2020 17:57:35 +0530 Subject: [PATCH 3/3] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7ae1c4a714..0e30dc2559 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ You can try our online sandbox or deploy a Docker image on a server. When we build internal tools today, we turn to admin panels, UI frameworks or use a bootstrap theme. We took inspirations from the best admin panels, bootstrap themes, and brought back the easy UI builder of Visual Basic. -Appsmith is a quicker way of building internal tools by visualising them as modular blocks (**Widgets, APIs, Queries, JS**) and giving developers a simple user interface to configure them. Building new features, creating UI, changing dataflows, and modifying business logic becomes a [piece of cake](https://i.kym-cdn.com/photos/images/newsfeed/001/355/125/5ca.png) because you no longer have to trudge through large undocumented code bases or wrestle with HTML/CSS. +Appsmith is a quicker way of building internal tools by visualising them as modular blocks (**Widgets, APIs, Queries, JS**) and giving developers a simple user interface to configure them. Building new features, creating UI, changing dataflows, and modifying business logic becomes simpler because you no longer have to trudge through large undocumented code bases or wrestle with HTML/CSS. Appsmith doesn't take the fun out of coding, because it treats every block as an object and exposes it via javascript so that you can read, transform and manipulate it. Whether it's a widget, API or query, you get to decide where you need to configure using UI and where you need to code. ## 🏭 Features