Property validations

This commit is contained in:
Hetu Nandu 2019-11-14 09:28:51 +00:00
parent fe91dd4441
commit 750d69a6c6
23 changed files with 342 additions and 74 deletions

View File

@ -95,6 +95,7 @@
"not op_mini all"
],
"devDependencies": {
"@types/jest": "^24.0.22",
"@types/react-select": "^3.0.5",
"@types/react-tabs": "^2.3.1",
"@types/redux-form": "^8.1.9",
@ -106,6 +107,7 @@
"eslint-config-react": "^1.1.7",
"eslint-plugin-prettier": "^3.1.0",
"eslint-plugin-react": "^7.14.3",
"react-test-renderer": "^16.11.0",
"redux-devtools": "^3.5.0",
"redux-devtools-extension": "^2.13.8"
},

View File

@ -3,6 +3,7 @@ import {
ReduxAction,
} from "../constants/ReduxActionConstants";
import { RenderMode } from "../constants/WidgetConstants";
import { ErrorCode } from "../constants/validationErrorCodes";
export const updateWidgetProperty = (
widgetId: string,
@ -21,9 +22,28 @@ export const updateWidgetProperty = (
};
};
export const updateWidgetPropertyValidation = (
widgetId: string,
propertyName: string,
errorCode: ErrorCode,
): ReduxAction<UpdateWidgetPropertyValidation> => ({
type: ReduxActionTypes.UPDATE_WIDGET_PROPERTY_VALIDATION,
payload: {
widgetId,
propertyName,
errorCode,
},
});
export interface UpdateWidgetPropertyPayload {
widgetId: string;
propertyName: string;
propertyValue: any;
renderMode: RenderMode;
}
export interface UpdateWidgetPropertyValidation {
widgetId: string;
propertyName: string;
errorCode: ErrorCode;
}

View File

@ -5,6 +5,7 @@
import { Component } from "react";
import _ from "lodash";
import { ControlType } from "../../constants/PropertyControlConstants";
import { ErrorCode } from "../../constants/validationErrorCodes";
abstract class BaseControl<T extends ControlProps> extends Component<T> {
updateProperty(propertyName: string, propertyValue: any) {
@ -29,10 +30,13 @@ export interface ControlData {
propertyName: string;
controlType: ControlType;
propertyValue?: any;
propertyError?: ErrorCode;
}
export interface ControlFunctions {
onPropertyChange?: (propertyName: string, propertyValue: string) => void;
getDynamicValue: (dynamicBinding: string) => any;
setPropertyValidation: (propertyName: string, errorCode: ErrorCode) => void;
}
export default BaseControl;

View File

@ -1,8 +1,17 @@
import React from "react";
import _ from "lodash";
import BaseControl, { ControlProps } from "./BaseControl";
import { ControlWrapper, StyledInputGroup } from "./StyledControls";
import { InputType } from "zlib";
import {
ControlWrapper,
StyledInputGroup,
StyledValidationError,
} from "./StyledControls";
import { InputType } from "../../widgets/InputWidget";
import { ControlType } from "../../constants/PropertyControlConstants";
import { isDynamicValue } from "../../utils/DynamicBindingUtils";
import { ERROR_CODES } from "../../constants/validationErrorCodes";
type InputTextControlType = InputType | "OBJECT" | "ARRAY" | "BOOLEAN";
class InputTextControl extends BaseControl<InputControlProps> {
render() {
@ -15,11 +24,16 @@ class InputTextControl extends BaseControl<InputControlProps> {
placeholder={this.props.placeholderText}
defaultValue={this.props.propertyValue}
/>
{this.props.propertyError && (
<StyledValidationError>
{this.props.propertyError}
</StyledValidationError>
)}
</ControlWrapper>
);
}
isNumberType(inputType: InputType): boolean {
isNumberType(inputType: InputTextControlType): boolean {
switch (inputType) {
case "CURRENCY":
case "INTEGER":
@ -31,18 +45,66 @@ class InputTextControl extends BaseControl<InputControlProps> {
}
}
isStringType(inputType: InputTextControlType): boolean {
switch (inputType) {
case "TEXT":
case "EMAIL":
case "PASSWORD":
case "SEARCH":
return true;
default:
return false;
}
}
onTextChange = (event: React.ChangeEvent<HTMLInputElement>) => {
this.updateProperty(this.props.propertyName, event.target.value);
let value: string | number = event.target.value;
if (this.isNumberType(this.props.inputType)) {
value = _.toNumber(value);
}
this.validateInput(value);
this.updateProperty(this.props.propertyName, value);
};
getControlType(): ControlType {
return "INPUT_TEXT";
}
validateInput(inputValue: any): boolean {
const {
getDynamicValue,
inputType,
setPropertyValidation,
propertyName,
} = this.props;
let value = inputValue;
if (isDynamicValue(inputValue)) {
value = getDynamicValue(inputValue);
}
if (this.isNumberType(inputType) && !_.isNumber(value)) {
setPropertyValidation(propertyName, ERROR_CODES.TYPE_ERROR);
return false;
}
if (this.isStringType(inputType) && !_.isString(value)) {
setPropertyValidation(propertyName, ERROR_CODES.TYPE_ERROR);
return false;
}
if (inputType === "ARRAY" && !Array.isArray(value)) {
setPropertyValidation(propertyName, ERROR_CODES.TYPE_ERROR);
return false;
}
if (inputType === "OBJECT" && !_.isObject(value)) {
setPropertyValidation(propertyName, ERROR_CODES.TYPE_ERROR);
return false;
}
setPropertyValidation(propertyName, ERROR_CODES.NO_ERROR);
return true;
}
}
export interface InputControlProps extends ControlProps {
placeholderText: string;
inputType: InputType;
inputType: InputTextControlType;
isDisabled?: boolean;
}

View File

@ -76,3 +76,7 @@ export const StyledTimeZonePicker = styled(TimezonePicker)`
box-shadow: none;
}
`;
export const StyledValidationError = styled.span`
color: ${props => props.theme.colors.error};
`;

View File

@ -74,6 +74,7 @@ export const ReduxActionTypes: { [key: string]: string } = {
CREATE_APPLICATION_SUCCESS: "CREATE_APPLICATION_SUCCESS",
CREATE_UPDATE_BINDINGS_MAP_INIT: "CREATE_UPDATE_BINDINGS_MAP_INIT",
CREATE_UPDATE_BINDINGS_MAP_SUCCESS: "CREATE_UPDATE_BINDINGS_MAP_SUCCESS",
UPDATE_WIDGET_PROPERTY_VALIDATION: "UPDATE_WIDGET_PROPERTY_VALIDATION",
HIDE_PROPERTY_PANE: "HIDE_PROPERTY_PANE",
};

View File

@ -0,0 +1,11 @@
export const ERROR_CODES = {
NO_ERROR: "NO_ERROR",
TYPE_ERROR: "TYPE_ERROR",
};
export type ErrorCode = (typeof ERROR_CODES)[keyof typeof ERROR_CODES];
export const ERROR_CODES_MESSAGES: Record<ErrorCode, string> = {
NO_ERROR: "",
TYPE_ERROR: "This input is not of a valid type",
};

View File

@ -1,6 +1,6 @@
import { PropertyPaneConfigState } from "../reducers/entityReducers/propertyPaneConfigReducer";
const PropertyPaneConfigResponse: PropertyPaneConfigState = {
const PropertyPaneConfigResponse = {
config: {
BUTTON_WIDGET: [
{
@ -384,6 +384,7 @@ const PropertyPaneConfigResponse: PropertyPaneConfigState = {
propertyName: "tableData",
label: "Table Data",
controlType: "INPUT_TEXT",
inputType: "ARRAY",
},
{
id: "11.3",

View File

@ -1,17 +1,21 @@
import React, { Component } from "react";
import styled from "styled-components";
import { connect } from "react-redux";
import { AppState } from "../../reducers";
import { AppState, DataTree } from "../../reducers";
import PropertyControlFactory from "../../utils/PropertyControlFactory";
import _ from "lodash";
import { PropertySection } from "../../reducers/entityReducers/propertyPaneConfigReducer";
import { updateWidgetProperty } from "../../actions/controlActions";
import {
updateWidgetProperty,
updateWidgetPropertyValidation,
} from "../../actions/controlActions";
import {
getCurrentWidgetId,
getCurrentReferenceNode,
getPropertyConfig,
getIsPropertyPaneVisible,
getCurrentWidgetProperties,
getPropertyErrors,
} from "../../selectors/propertyPaneSelectors";
import { Divider } from "@blueprintjs/core";
@ -19,6 +23,12 @@ import Popper from "./Popper";
import { ControlProps } from "../../components/propertyControls/BaseControl";
import { RenderModes } from "../../constants/WidgetConstants";
import { ReduxActionTypes } from "constants/ReduxActionConstants";
import { getDataTree } from "../../selectors/entitiesSelector";
import { getDynamicValue as extractDynamicValue } from "../../utils/DynamicBindingUtils";
import {
ErrorCode,
ERROR_CODES_MESSAGES,
} from "../../constants/validationErrorCodes";
import { CloseButton } from "components/designSystems/blueprint/CloseButton";
import { theme } from "../../constants/DefaultTheme";
@ -52,6 +62,8 @@ class PropertyPane extends Component<
constructor(props: PropertyPaneProps & PropertyPaneFunctions) {
super(props);
this.onPropertyChange = this.onPropertyChange.bind(this);
this.getDynamicValue = this.getDynamicValue.bind(this);
this.setPropertyValidation = this.setPropertyValidation.bind(this);
}
getPropertyValue = (propertyName: string) => {
@ -63,6 +75,17 @@ class PropertyPane extends Component<
return widgetProperties[propertyName];
};
getPropertyValidation = (propertyName: string): string | undefined => {
const { propertyErrors, widgetId } = this.props;
if (!widgetId) return undefined;
if (widgetId in propertyErrors) {
const errorCode: ErrorCode = propertyErrors[widgetId][propertyName];
return ERROR_CODES_MESSAGES[errorCode];
} else {
return undefined;
}
};
render() {
if (this.props.isVisible) {
const content = this.renderPropertyPane(this.props.propertySections);
@ -131,9 +154,16 @@ class PropertyPane extends Component<
propertyControlOrSection.propertyValue = this.getPropertyValue(
propertyControlOrSection.propertyName,
);
propertyControlOrSection.propertyError = this.getPropertyValidation(
propertyControlOrSection.propertyName,
);
return PropertyControlFactory.createControl(
propertyControlOrSection,
{ onPropertyChange: this.onPropertyChange },
{
onPropertyChange: this.onPropertyChange,
getDynamicValue: this.getDynamicValue,
setPropertyValidation: this.setPropertyValidation,
},
);
} catch (e) {
console.log(e);
@ -153,11 +183,28 @@ class PropertyPane extends Component<
propertyValue,
);
}
getDynamicValue(dynamicBinding: string) {
const { dataTree } = this.props;
return extractDynamicValue(dynamicBinding, dataTree);
}
setPropertyValidation(propertyName: string, errorCode: ErrorCode) {
if (this.props.widgetId) {
this.props.setPropertyValidation(
this.props.widgetId,
propertyName,
errorCode,
);
}
}
}
const mapStateToProps = (state: AppState): PropertyPaneProps => {
return {
propertySections: getPropertyConfig(state),
dataTree: getDataTree(state),
propertyErrors: getPropertyErrors(state),
widgetId: getCurrentWidgetId(state),
widgetProperties: getCurrentWidgetProperties(state),
isVisible: getIsPropertyPaneVisible(state),
@ -184,11 +231,21 @@ const mapDispatchToProps = (dispatch: any): PropertyPaneFunctions => {
dispatch({
type: ReduxActionTypes.HIDE_PROPERTY_PANE,
}),
setPropertyValidation: (
widgetId: string,
propertyName: string,
errorCode: ErrorCode,
) =>
dispatch(
updateWidgetPropertyValidation(widgetId, propertyName, errorCode),
),
};
};
export interface PropertyPaneProps {
propertySections?: PropertySection[];
propertyErrors: Record<string, Record<string, ErrorCode>>;
dataTree: DataTree;
widgetId?: string;
widgetProperties?: any; //TODO(abhinav): Secure type definition
isVisible: boolean;
@ -198,6 +255,11 @@ export interface PropertyPaneProps {
export interface PropertyPaneFunctions {
updateWidgetProperty: Function;
hidePropertyPane: () => void;
setPropertyValidation: (
widgetId: string,
propertyName: string,
errorCode: ErrorCode,
) => void;
}
export default connect(

View File

@ -8,9 +8,7 @@ import { ActionDataState } from "./actionsReducer";
const initialState: APIDataState = {};
export interface APIDataState {
[id: string]: ActionResponse;
}
export type APIDataState = Record<string, ActionResponse>;
const apiDataReducer = createReducer(initialState, {
[ReduxActionTypes.EXECUTE_ACTION_SUCCESS]: (

View File

@ -1,14 +1,18 @@
import _ from "lodash";
import { createReducer } from "../../utils/AppsmithUtils";
import {
ReduxActionTypes,
ReduxAction,
ShowPropertyPanePayload,
} from "../../constants/ReduxActionConstants";
import { ERROR_CODES, ErrorCode } from "../../constants/validationErrorCodes";
import { UpdateWidgetPropertyValidation } from "../../actions/controlActions";
const initialState: PropertyPaneReduxState = {
isVisible: false,
widgetId: undefined,
node: undefined,
errors: {},
};
const propertyPaneReducer = createReducer(initialState, {
@ -24,17 +28,32 @@ const propertyPaneReducer = createReducer(initialState, {
if (toggle) {
isVisible = !state.isVisible;
}
return { widgetId, node, isVisible };
return { ...state, widgetId, node, isVisible };
},
[ReduxActionTypes.HIDE_PROPERTY_PANE]: (state: PropertyPaneReduxState) => {
return { ...state, isVisible: false };
},
[ReduxActionTypes.UPDATE_WIDGET_PROPERTY_VALIDATION]: (
state: PropertyPaneReduxState,
action: ReduxAction<UpdateWidgetPropertyValidation>,
) => {
const { widgetId, propertyName, errorCode } = action.payload;
let widgetErrors = { ...state.errors[widgetId] };
if (action.payload.errorCode === ERROR_CODES.NO_ERROR) {
widgetErrors = _.omit(widgetErrors, propertyName);
} else {
widgetErrors[propertyName] = errorCode;
}
const errors = { ...state.errors, [widgetId]: widgetErrors };
return { ...state, errors };
},
});
export interface PropertyPaneReduxState {
widgetId?: string;
isVisible: boolean;
node?: HTMLDivElement;
errors: Record<string, Record<string, ErrorCode>>;
}
export default propertyPaneReducer;

View File

@ -20,7 +20,7 @@ import ActionAPI, {
ExecuteActionRequest,
RestAction,
} from "../api/ActionAPI";
import { AppState, DataTree } from "../reducers";
import { AppState } from "../reducers";
import _ from "lodash";
import { mapToPropList } from "../utils/AppsmithUtils";
import AppToaster from "../components/editorComponents/ToastComponent";
@ -31,18 +31,15 @@ import {
updateActionSuccess,
} from "../actions/actionActions";
import { API_EDITOR_ID_URL, API_EDITOR_URL } from "../constants/routes";
import { getDynamicBoundValue } from "../utils/DynamicBindingUtils";
import { extractDynamicBoundValue } from "../utils/DynamicBindingUtils";
import history from "../utils/history";
import { validateResponse } from "./ErrorSagas";
import { getDataTree } from "../selectors/entitiesSelector";
import {
ERROR_MESSAGE_SELECT_ACTION,
ERROR_MESSAGE_SELECT_ACTION_TYPE,
} from "constants/messages";
const getDataTree = (state: AppState): DataTree => {
return state.entities;
};
const getAction = (
state: AppState,
actionId: string,
@ -69,7 +66,7 @@ const createActionErrorResponse = (
export function* evaluateJSONPathSaga(path: string): any {
const dataTree = yield select(getDataTree);
return getDynamicBoundValue(dataTree, path);
return extractDynamicBoundValue(dataTree, path);
}
export function* executeAPIQueryActionSaga(apiAction: ActionPayload) {

View File

@ -18,7 +18,7 @@ import {
import { put, select, takeEvery, takeLatest, all } from "redux-saga/effects";
import { getNextWidgetName } from "../utils/AppsmithUtils";
import { UpdateWidgetPropertyPayload } from "../actions/controlActions";
import { DATA_BIND_REGEX } from "../constants/BindingsConstants";
import { isDynamicValue } from "../utils/DynamicBindingUtils";
export function* addChildSaga(addChildAction: ReduxAction<WidgetAddChild>) {
try {
@ -173,8 +173,7 @@ function* updateWidgetPropertySaga(
payload: { propertyValue },
} = updateAction;
const isDynamic = DATA_BIND_REGEX.test(propertyValue);
if (isDynamic) {
if (isDynamicValue(propertyValue)) {
yield put({
type: ReduxActionTypes.UPDATE_WIDGET_DYNAMIC_PROPERTY,
payload: updateAction.payload,

View File

@ -3,9 +3,9 @@ import { AppState, DataTree } from "../reducers";
import { AppViewReduxState } from "../reducers/uiReducers/appViewReducer";
import { AppViewerProps } from "../pages/AppViewer";
import { injectDataTreeIntoDsl } from "../utils/DynamicBindingUtils";
import { getDataTree } from "./entitiesSelector";
const getAppViewState = (state: AppState) => state.ui.appView;
const getDataTree = (state: AppState): DataTree => state.entities;
export const getCurrentLayoutId = (state: AppState, props: AppViewerProps) =>
state.ui.appView.currentLayoutId || props.match.params.layoutId;

View File

@ -8,6 +8,7 @@ import { WidgetCardProps } from "widgets/BaseWidget";
import { WidgetSidebarReduxState } from "reducers/uiReducers/widgetSidebarReducer";
import CanvasWidgetsNormalizer from "normalizers/CanvasWidgetsNormalizer";
import { injectDataTreeIntoDsl } from "utils/DynamicBindingUtils";
import { getDataTree } from "./entitiesSelector";
import {
FlattenedWidgetProps,
CanvasWidgetsReduxState,
@ -18,7 +19,6 @@ import { WidgetTypes } from "constants/WidgetConstants";
const getEditorState = (state: AppState) => state.ui.editor;
const getWidgetConfigs = (state: AppState) => state.entities.widgetConfig;
const getEntities = (state: AppState) => state.entities;
const getWidgetSideBar = (state: AppState) => state.ui.widgetSidebar;
const getWidgets = (state: AppState): CanvasWidgetsReduxState =>
@ -102,7 +102,7 @@ export const getWidgetCards = createSelector(
export const getDenormalizedDSL = createCachedSelector(
getPageWidgetId,
getEntities,
getDataTree,
(pageWidgetId: string, entities: DataTree) => {
const dsl = CanvasWidgetsNormalizer.denormalize(pageWidgetId, entities);
return injectDataTreeIntoDsl(entities, dsl);

View File

@ -0,0 +1,3 @@
import { AppState, DataTree } from "../reducers";
export const getDataTree = (state: AppState): DataTree => state.entities;

View File

@ -61,3 +61,8 @@ export const getIsPropertyPaneVisible = createSelector(
(pane: PropertyPaneReduxState, content?: PropertySection[]) =>
!!(pane.isVisible && pane.widgetId && pane.node && content),
);
export const getPropertyErrors = createSelector(
getPropertyPaneState,
(pane: PropertyPaneReduxState) => pane.errors || {},
);

View File

@ -8,11 +8,28 @@ import {
DATA_PATH_REGEX,
} from "../constants/BindingsConstants";
export const isDynamicValue = (value: string): boolean =>
DATA_BIND_REGEX.test(value);
export const getDynamicBindings = (
dynamicString: string,
): { bindings: string[]; paths: string[] } => {
// Get the {{binding}} bound values
const bindings = dynamicString.match(DATA_BIND_REGEX) || [];
// Get the "binding" path values
const paths = bindings.map(p => {
const matches = p.match(DATA_PATH_REGEX);
if (matches) return matches[0];
return "";
});
return { bindings, paths };
};
// Paths are expected to have "{name}.{path}" signature
export const getDynamicBoundValue = (
export const extractDynamicBoundValue = (
dataTree: DataTree,
path: string,
): Array<any> => {
): any => {
// Remove the name in the binding
const splitPath = path.split(".");
// Find the dataTree path of the name
@ -20,7 +37,42 @@ export const getDynamicBoundValue = (
// Create the full path
const fullPath = `${bindingPath}.${splitPath.slice(1).join(".")}`;
// Search with JSONPath
return JSONPath({ path: fullPath, json: dataTree });
return JSONPath({ path: fullPath, json: dataTree })[0];
};
// For creating a final value where bindings could be in a template format
export const createDynamicValueString = (
binding: string,
subBindings: string[],
subValues: string[],
): string => {
// Replace the string with the data tree values
let finalValue = binding;
subBindings.forEach((b, i) => {
let value = subValues[i];
if (Array.isArray(value) || _.isObject(value)) {
value = JSON.stringify(value);
}
finalValue = finalValue.replace(b, value);
});
return finalValue;
};
export const getDynamicValue = (
dynamicBinding: string,
dataTree: DataTree,
): any => {
// Get the {{binding}} bound values
const { bindings, paths } = getDynamicBindings(dynamicBinding);
if (bindings.length) {
// Get the Data Tree value of those "binding "paths
const values = paths.map(p => extractDynamicBoundValue(dataTree, p));
// if it is just one binding, no need to create template string
if (bindings.length === 1) return values[0];
// else return a string template with bindings
return createDynamicValueString(dynamicBinding, bindings, values);
}
return undefined;
};
export const injectDataTreeIntoDsl = (
@ -36,33 +88,7 @@ export const injectDataTreeIntoDsl = (
// Check for dynamic bindings
if (dynamicBindings && !_.isEmpty(dynamicBindings)) {
Object.keys(dynamicBindings).forEach((dKey: string) => {
// Get the {{binding}} bound values
const bindings = dynamicBindings[dKey].match(DATA_BIND_REGEX);
if (bindings && bindings.length) {
// Get the "binding" path values
const paths = bindings.map(p => {
const matches = p.match(DATA_PATH_REGEX);
if (matches) return matches[0];
return "";
});
// Get the Data Tree value of those "binding "paths
const values = paths.map(p => {
const value = getDynamicBoundValue(entities, p)[0];
if (value) return value;
return "undefined";
});
// Replace the string with the data tree values
let string = dynamicBindings[dKey];
bindings.forEach((b, i) => {
let value = values[i];
if (Array.isArray(value)) {
value = JSON.stringify(value);
}
string = string.replace(b, value);
});
// Overwrite the property with the evaluated data tree property
widget[dKey] = string;
}
widget[dKey] = getDynamicValue(dynamicBindings[dKey], entities);
});
}
if (tree.children) {

View File

@ -0,0 +1,34 @@
import { getDynamicValue } from "./DynamicBindingUtils";
import { DataTree } from "../reducers";
it("Gets the value from the data tree", () => {
const dynamicBinding = "{{GetUsers.data}}";
const dataTree: Partial<DataTree> = {
apiData: {
id: {
body: {
data: "correct data",
},
headers: {},
statusCode: "0",
duration: "0",
size: "0",
},
someOtherId: {
body: {
data: "wrong data",
},
headers: {},
statusCode: "0",
duration: "0",
size: "0",
},
},
nameBindings: {
GetUsers: "$.apiData.id.body",
},
};
const actualValue = "correct data";
const value = getDynamicValue(dynamicBinding, dataTree);
expect(value).toEqual(actualValue);
});

View File

@ -1,7 +1,7 @@
import React from "react";
import _ from "lodash";
import ContainerComponent from "components/designSystems/appsmith/ContainerComponent";
import ContainerComponent from "../components/designSystems/appsmith/ContainerComponent";
import { ContainerOrientation, WidgetType } from "constants/WidgetConstants";
import WidgetFactory from "utils/WidgetFactory";
import { Color } from "constants/Colors";

View File

@ -1,4 +1,5 @@
import React from "react";
import _ from "lodash";
import BaseWidget, { WidgetProps, WidgetState } from "./BaseWidget";
import { WidgetType } from "../constants/WidgetConstants";
import { ActionPayload } from "../constants/ActionConstants";
@ -26,27 +27,24 @@ function constructColumns(data: object[]): Column[] {
return cols;
}
function parseTableArray(parsable: string): object[] {
let data: object[] = [];
function getTableArrayData(tableData: string | object[] | undefined): object[] {
try {
const parsedData = JSON.parse(parsable);
if (!Array.isArray(parsedData)) {
throw new Error("Parsed Data is an object");
if (!tableData) return [];
if (_.isString(tableData)) {
return JSON.parse(tableData);
}
data = parsedData;
} catch (ex) {
console.log(ex);
return tableData;
} catch (error) {
console.error({ error });
return [];
}
return data;
}
class TableWidget extends BaseWidget<TableWidgetProps, WidgetState> {
getPageView() {
const tableData = parseTableArray(
this.props.tableData ? ((this.props.tableData as any) as string) : "",
);
const columns = constructColumns(tableData);
const { tableData } = this.props;
const data = getTableArrayData(tableData);
const columns = constructColumns(data);
return (
<AutoResizer>
{({ width, height }: { width: number; height: number }) => (
@ -54,7 +52,7 @@ class TableWidget extends BaseWidget<TableWidgetProps, WidgetState> {
width={width}
height={height}
columns={columns}
data={tableData}
data={data}
maxHeight={height}
selectedRowIndex={
this.props.selectedRow && this.props.selectedRow.rowIndex

View File

@ -26,7 +26,7 @@
"importHelpers": true,
"typeRoots" : ["./typings", "./node_modules/@types"],
"sourceMap": true,
"baseUrl": "./src",
"baseUrl": "./src"
},
"include": [
"./src/**/*"

View File

@ -1607,6 +1607,18 @@
"@types/istanbul-lib-coverage" "*"
"@types/istanbul-lib-report" "*"
"@types/jest-diff@*":
version "20.0.1"
resolved "https://registry.yarnpkg.com/@types/jest-diff/-/jest-diff-20.0.1.tgz#35cc15b9c4f30a18ef21852e255fdb02f6d59b89"
integrity sha512-yALhelO3i0hqZwhjtcr6dYyaLoCHbAMshwtj6cGxTvHZAKXHsYGdff6E8EPw3xLKY0ELUTQ69Q1rQiJENnccMA==
"@types/jest@^24.0.22":
version "24.0.22"
resolved "https://registry.yarnpkg.com/@types/jest/-/jest-24.0.22.tgz#08a50be08e78aba850a1185626e71d31e2336145"
integrity sha512-t2OvhNZnrNjlzi2i0/cxbLVM59WN15I2r1Qtb7wDv28PnV9IzrPtagFRey/S9ezdLD0zyh1XGMQIEQND2YEfrw==
dependencies:
"@types/jest-diff" "*"
"@types/json-schema@^7.0.3":
version "7.0.3"
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.3.tgz#bdfd69d61e464dcc81b25159c270d75a73c1a636"
@ -10086,7 +10098,7 @@ react-input-autosize@^2.2.2:
dependencies:
prop-types "^15.5.8"
react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.2, react-is@^16.8.4:
react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.2, react-is@^16.8.4, react-is@^16.8.6:
version "16.11.0"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.11.0.tgz#b85dfecd48ad1ce469ff558a882ca8e8313928fa"
integrity sha512-gbBVYR2p8mnriqAwWx9LbuUrShnAuSCNnuPGyc7GJrMVQtPDAh8iLpv7FRuMPFb56KkaVZIYSz1PrjI9q0QPCw==
@ -10249,6 +10261,16 @@ react-select@^3.0.8:
react-input-autosize "^2.2.2"
react-transition-group "^2.2.1"
react-test-renderer@^16.11.0:
version "16.11.0"
resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-16.11.0.tgz#72574566496462c808ac449b0287a4c0a1a7d8f8"
integrity sha512-nh9gDl8R4ut+ZNNb2EeKO5VMvTKxwzurbSMuGBoKtjpjbg8JK/u3eVPVNi1h1Ue+eYK9oSzJjb+K3lzLxyA4ag==
dependencies:
object-assign "^4.1.1"
prop-types "^15.6.2"
react-is "^16.8.6"
scheduler "^0.17.0"
react-transition-group@^2.2.1, react-transition-group@^2.9.0:
version "2.9.0"
resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-2.9.0.tgz#df9cdb025796211151a436c69a8f3b97b5b07c8d"