Merge branch 'feature/canvas-fixes-1' into 'release'

Fixes: Saving notification, global error handling, show controls on hover, fixed row height

See merge request theappsmith/internal-tools-client!36
This commit is contained in:
Nikhil Nandagopal 2019-10-03 06:50:30 +00:00
commit a308337172
27 changed files with 382 additions and 291 deletions

View File

@ -1,10 +1,3 @@
## GIT Commit Hooks
This project has scripts to ESLint fix and Prettier write the code on the git commit hook.
It is recommended to install ESLint and Prettier global for successful git commits
`yarn global add eslint`
`yarn global add prettier`
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).

View File

@ -1,5 +1,4 @@
import { FetchPageRequest } from "../api/PageApi";
import { ResponseMeta } from "../api/ApiResponses";
import { RenderMode } from "../constants/WidgetConstants";
import { WidgetProps, WidgetOperation } from "../widgets/BaseWidget";
import { WidgetType } from "../constants/WidgetConstants";
@ -8,7 +7,6 @@ import {
ReduxAction,
UpdateCanvasPayload,
SavePagePayload,
SavePageErrorPayload,
SavePageSuccessPayload,
} from "../constants/ReduxActionConstants";
import { ContainerWidgetProps } from "../widgets/ContainerWidget";
@ -26,13 +24,6 @@ export const fetchPage = (
};
};
export const fetchPageError = (payload: ResponseMeta) => {
return {
type: ReduxActionTypes.FETCH_PAGE_ERROR,
payload,
};
};
export const addWidget = (
pageId: string,
widget: WidgetProps,
@ -86,13 +77,6 @@ export const savePageSuccess = (payload: SavePageSuccessPayload) => {
};
};
export const savePageError = (payload: SavePageErrorPayload) => {
return {
type: ReduxActionTypes.SAVE_PAGE_ERROR,
payload,
};
};
export type WidgetAddChild = {
widgetId: string;
type: WidgetType;

View File

@ -1,4 +1,7 @@
import { ReduxActionTypes } from "../constants/ReduxActionConstants";
import {
ReduxActionTypes,
ReduxActionErrorTypes,
} from "../constants/ReduxActionConstants";
import { WidgetCardProps } from "../widgets/BaseWidget";
export const fetchWidgetCards = () => {
@ -9,7 +12,7 @@ export const fetchWidgetCards = () => {
export const errorFetchingWidgetCards = (error: any) => {
return {
type: ReduxActionTypes.ERROR_FETCHING_WIDGET_CARDS,
type: ReduxActionErrorTypes.FETCH_WIDGET_CARDS_ERROR,
error,
};
};
@ -18,7 +21,7 @@ export const successFetchingWidgetCards = (cards: {
[id: string]: WidgetCardProps[];
}) => {
return {
type: ReduxActionTypes.SUCCESS_FETCHING_WIDGET_CARDS,
type: ReduxActionTypes.FETCH_WIDGET_CARDS_SUCCESS,
cards,
};
};

View File

@ -31,7 +31,7 @@ export type FetchPageResponse = ApiResponse & {
};
};
export interface SavePageResponse {
export interface SavePageResponse extends ApiResponse {
pageId: string;
}

View File

@ -35,13 +35,13 @@ export const getEditorConfigs = () => {
return {
currentPageId: "5d807e7f795dc6000482bc78",
currentLayoutId: "5d807e7f795dc6000482bc77",
currentPageName: "page1",
};
} else {
return {
currentPageId: "5d807e76795dc6000482bc76",
currentLayoutId: "5d807e76795dc6000482bc75",
currentPageName: "page1",
};
}
};
console.log("here", process.env.NODE_ENV);

View File

@ -1,6 +1,8 @@
import { WidgetProps, WidgetCardProps } from "../widgets/BaseWidget";
export const ReduxActionTypes: { [key: string]: string } = {
REPORT_ERROR: "REPORT_ERROR",
FLUSH_ERRORS: "FLUSH_ERRORS",
UPDATE_CANVAS: "UPDATE_CANVAS",
FETCH_CANVAS: "FETCH_CANVAS",
CLEAR_CANVAS: "CLEAR_CANVAS",
@ -16,8 +18,7 @@ export const ReduxActionTypes: { [key: string]: string } = {
LOAD_PROPERTY_CONFIG: "LOAD_PROPERTY_CONFIG",
PUBLISH: "PUBLISH",
FETCH_WIDGET_CARDS: "FETCH_WIDGET_CARDS",
SUCCESS_FETCHING_WIDGET_CARDS: "SUCCESS_FETCHING_WIDGET_CARDS",
ERROR_FETCHING_WIDGET_CARDS: "ERROR_FETCHING_WIDGET_CARDS",
FETCH_WIDGET_CARDS_SUCCESS: "FETCH_WIDGET_CARDS_SUCCESS",
ADD_PAGE_WIDGET: "ADD_PAGE_WIDGET",
REMOVE_PAGE_WIDGET: "REMOVE_PAGE_WIDGET",
LOAD_API_RESPONSE: "LOAD_API_RESPONSE",
@ -26,8 +27,6 @@ export const ReduxActionTypes: { [key: string]: string } = {
LOAD_CANVAS_ACTIONS: "LOAD_CANVAS_ACTIONS",
SAVE_PAGE_INIT: "SAVE_PAGE_INIT",
SAVE_PAGE_SUCCESS: "SAVE_PAGE_SUCCESS",
SAVE_PAGE_ERROR: "SAVE_PAGE_ERROR",
FETCH_PAGE_ERROR: "FETCH_PAGE_ERROR",
UPDATE_LAYOUT: "UPDATE_LAYOUT",
WIDGET_ADD_CHILD: "WIDGET_ADD_CHILD",
WIDGET_REMOVE_CHILD: "WIDGET_REMOVE_CHILD",
@ -37,18 +36,37 @@ export const ReduxActionTypes: { [key: string]: string } = {
SHOW_PROPERTY_PANE: "SHOW_PROPERTY_PANE",
UPDATE_WIDGET_PROPERTY: "UPDATE_WIDGET_PROPERTY",
};
export type ReduxActionType = (typeof ReduxActionTypes)[keyof typeof ReduxActionTypes];
export const ReduxActionErrorTypes: { [key: string]: string } = {
API_ERROR: "API_ERROR",
WIDGET_DELETE_ERROR: "WIDGET_DELETE_ERROR",
WIDGET_MOVE_ERROR: "WIDGET_MOVE_ERROR",
WIDGET_RESIZE_ERROR: "WIDGET_RESIZE_ERROR",
WIDGET_REMOVE_CHILD_ERROR: "WIDGET_REMOVE_CHILD_ERROR",
WIDGET_ADD_CHILD_ERROR: "WIDGET_ADD_CHILD_ERROR",
FETCH_PAGE_ERROR: "FETCH_PAGE_ERROR",
SAVE_PAGE_ERROR: "SAVE_PAGE_ERROR",
FETCH_WIDGET_CARDS_ERROR: "FETCH_WIDGET_CARDS_ERROR",
};
export type ReduxActionErrorType = (typeof ReduxActionErrorTypes)[keyof typeof ReduxActionErrorTypes];
export interface ReduxAction<T> {
type: ReduxActionType;
type: ReduxActionType | ReduxActionErrorType;
payload: T;
}
export interface ReduxActionErrorPayload {
message: string;
source?: string;
}
export interface UpdateCanvasPayload {
pageWidgetId: string;
widgets: { [widgetId: string]: WidgetProps };
layoutId: string;
currentLayoutId: string;
currentPageId: string;
currentPageName: string;
}
export interface ShowPropertyPanePayload {

View File

@ -71,4 +71,5 @@ export const GridDefaults = {
DEFAULT_WIDGET_HEIGHT: 100,
DEFAULT_GRID_COLUMNS: 16,
DEFAULT_GRID_ROWS: 32,
DEFAULT_GRID_ROW_HEIGHT: 40,
};

View File

@ -6,17 +6,25 @@ import { useDrag, DragPreviewImage, DragSourceMonitor } from "react-dnd";
import blankImage from "../assets/images/blank.png";
import { ContainerProps } from "./ContainerComponent";
const DraggableWrapper = styled.div`
&:hover > div {
display: block;
}
`;
const DragHandle = styled.div`
position: absolute;
left: ${props => props.theme.spaces[2]}px;
top: -${props => props.theme.spaces[8]}px;
cursor: move;
display: none;
`;
const DeleteControl = styled.div`
position: absolute;
right: ${props => props.theme.spaces[2]}px;
top: -${props => props.theme.spaces[8]}px;
display: none;
`;
type DraggableComponentProps = WidgetProps & ContainerProps;
@ -35,7 +43,7 @@ const DraggableComponent = (props: DraggableComponentProps) => {
return (
<React.Fragment>
<DragPreviewImage src={blankImage} connect={preview} />
<div
<DraggableWrapper
ref={preview}
style={{
display: isDragging ? "none" : "flex",
@ -56,7 +64,7 @@ const DraggableComponent = (props: DraggableComponentProps) => {
<Icon icon="trash" iconSize={20} />
</DeleteControl>
{props.children}
</div>
</DraggableWrapper>
</React.Fragment>
);
};

View File

@ -13,7 +13,13 @@ const ResizableContainer = styled(Resizable)`
}};
`;
const CustomHandle = (props: any) => <div {...props} />;
const DisplayHandleWrapper = styled.div`
display: none;
${ResizableContainer}:hover & {
display: block;
}
`;
const CustomHandle = (props: any) => <DisplayHandleWrapper {...props} />;
const BottomRightHandle = () => (
<CustomHandle>
<Icon iconSize={15} icon="arrow-bottom-right" />

View File

@ -1,99 +1,99 @@
import { WidgetCardsPaneReduxState } from "../reducers/uiReducers/widgetCardsPaneReducer";
import { WidgetCardProps } from "../widgets/BaseWidget";
import { generateReactKey } from "../utils/generators";
const WidgetCardsPaneResponse: WidgetCardsPaneReduxState = {
cards: {
common: [
{
type: "TEXT_WIDGET",
icon: "icon-text",
widgetCardName: "Text",
key: generateReactKey(),
},
{
type: "BUTTON_WIDGET",
icon: "icon-button",
widgetCardName: "Button",
key: generateReactKey(),
},
{
type: "SPINNER_WIDGET",
icon: "icon-switch",
widgetCardName: "Spinner",
key: generateReactKey(),
},
{
type: "CONTAINER_WIDGET",
icon: "icon-container",
widgetCardName: "Container",
key: generateReactKey(),
},
],
form: [
{
type: "BUTTON_WIDGET",
icon: "icon-button",
widgetCardName: "Button",
key: generateReactKey(),
},
{
type: "BUTTON_WIDGET",
icon: "icon-button",
widgetCardName: "Button",
key: generateReactKey(),
},
{
type: "DROP_DOWN_WIDGET",
icon: "icon-dropdown",
widgetCardName: "Dropdown",
key: generateReactKey(),
},
{
type: "DATE_PICKER_WIDGET",
icon: "icon-datepicker",
widgetCardName: "DatePicker",
key: generateReactKey(),
},
{
type: "RADIO_GROUP_WIDGET",
icon: "icon-radio",
widgetCardName: "Radio Button",
key: generateReactKey(),
},
{
type: "SWITCH_WIDGET",
icon: "icon-switch",
widgetCardName: "Toggle",
key: generateReactKey(),
},
],
view: [
{
type: "TEXT_WIDGET",
icon: "icon-text",
widgetCardName: "Text",
key: generateReactKey(),
},
{
type: "CONTAINER_WIDGET",
icon: "icon-container",
widgetCardName: "Container",
key: generateReactKey(),
},
{
type: "SPINNER_WIDGET",
icon: "icon-spinner",
widgetCardName: "Spinner",
key: generateReactKey(),
},
{
type: "TABLE_WIDGET",
icon: "icon-table",
widgetCardName: "Table",
key: generateReactKey(),
},
],
},
const WidgetCardsPaneResponse: {
[id: string]: WidgetCardProps[];
} = {
common: [
{
type: "TEXT_WIDGET",
icon: "icon-text",
widgetCardName: "Text",
key: generateReactKey(),
},
{
type: "BUTTON_WIDGET",
icon: "icon-button",
widgetCardName: "Button",
key: generateReactKey(),
},
{
type: "SPINNER_WIDGET",
icon: "icon-switch",
widgetCardName: "Spinner",
key: generateReactKey(),
},
{
type: "CONTAINER_WIDGET",
icon: "icon-container",
widgetCardName: "Container",
key: generateReactKey(),
},
],
form: [
{
type: "BUTTON_WIDGET",
icon: "icon-button",
widgetCardName: "Button",
key: generateReactKey(),
},
{
type: "BUTTON_WIDGET",
icon: "icon-button",
widgetCardName: "Button",
key: generateReactKey(),
},
{
type: "DROP_DOWN_WIDGET",
icon: "icon-dropdown",
widgetCardName: "Dropdown",
key: generateReactKey(),
},
{
type: "DATE_PICKER_WIDGET",
icon: "icon-datepicker",
widgetCardName: "DatePicker",
key: generateReactKey(),
},
{
type: "RADIO_GROUP_WIDGET",
icon: "icon-radio",
widgetCardName: "Radio Button",
key: generateReactKey(),
},
{
type: "SWITCH_WIDGET",
icon: "icon-switch",
widgetCardName: "Toggle",
key: generateReactKey(),
},
],
view: [
{
type: "TEXT_WIDGET",
icon: "icon-text",
widgetCardName: "Text",
key: generateReactKey(),
},
{
type: "CONTAINER_WIDGET",
icon: "icon-container",
widgetCardName: "Container",
key: generateReactKey(),
},
{
type: "SPINNER_WIDGET",
icon: "icon-spinner",
widgetCardName: "Spinner",
key: generateReactKey(),
},
{
type: "TABLE_WIDGET",
icon: "icon-table",
widgetCardName: "Table",
key: generateReactKey(),
},
],
};
export default WidgetCardsPaneResponse;

View File

@ -1,35 +1,57 @@
import React, { Component } from "react";
import React from "react";
import styled from "styled-components";
// import { connect } from "react-redux";
// import { AppState } from "../../reducers";
// import { EditorHeaderReduxState } from "../../reducers/uiReducers/editorHeaderReducer";
import { Breadcrumbs, IBreadcrumbProps, Spinner } from "@blueprintjs/core";
const Header = styled.header`
display: flex;
justify-content: space-around;
align-items: center;
height: 50px;
padding: 0px 30px;
box-shadow: 0px 0px 3px #ccc;
background: #fff;
font-size: ${props => props.theme.fontSizes[1]}px;
`;
class EditorHeader extends Component {
render() {
return <Header></Header>;
const NotificationText = styled.div`
display: flex;
justify-content: space-evenly;
align-items: center;
flex-grow: 1;
`;
const StretchedBreadCrumb = styled(Breadcrumbs)`
flex-grow: 10;
* {
font-family: ${props => props.theme.fonts[0]};
font-size: ${props => props.theme.fontSizes[2]}px;
}
}
li:after {
background: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M10.71 7.29l-4-4a1.003 1.003 0 0 0-1.42 1.42L8.59 8 5.3 11.29c-.19.18-.3.43-.3.71a1.003 1.003 0 0 0 1.71.71l4-4c.18-.18.29-.43.29-.71 0-.28-.11-.53-.29-.71z' fill='rgba(92,112,128,1)'/%3E%3C/svg%3E");
}
`;
type EditorHeaderProps = {
notificationText?: string;
pageName: string;
};
export const EditorHeader = (props: EditorHeaderProps) => {
const navigation: IBreadcrumbProps[] = [
{ href: "#", icon: "folder-close", text: "appsmith-dev" },
{ href: "#", icon: "folder-close", text: "application" },
{ icon: "page-layout", text: props.pageName, current: true },
];
return (
<Header>
<StretchedBreadCrumb items={navigation} />
<NotificationText>
{props.notificationText && <Spinner size={Spinner.SIZE_SMALL} />}
<span>{props.notificationText}</span>
</NotificationText>
</Header>
);
};
export default EditorHeader;
// const mapStateToProps = (
// state: AppState,
// props: any,
// ): EditorHeaderReduxState => {
// return state;
// };
// const mapDispatchToProps = (dispatch: any) => {
// return {};
// };
// export default connect(
// mapStateToProps,
// mapDispatchToProps,
// )(EditorHeader);

View File

@ -29,6 +29,9 @@ export const Wrapper = styled.div`
path {
fill: ${props => props.theme.colors.textDefault};
}
rect {
stroke: ${props => props.theme.colors.textDefault};
}
}
}
& i {

View File

@ -1,5 +1,4 @@
import React, { Component } from "react";
import { Position, Toaster } from "@blueprintjs/core";
import { connect } from "react-redux";
import styled from "styled-components";
import Canvas from "./Canvas";
@ -20,10 +19,6 @@ import { executeAction } from "../../actions/widgetActions";
import { ActionPayload } from "../../constants/ActionConstants";
import PropertyPane from "./PropertyPane";
const SaveToast = Toaster.create({
position: Position.TOP,
});
const CanvasContainer = styled.section`
height: 100%;
width: 100%;
@ -60,7 +55,7 @@ type EditorProps = {
updateWidget: Function;
cards: { [id: string]: WidgetCardProps[] } | any;
savePageLayout: Function;
page: string;
currentPageName: string;
currentPageId: string;
currentLayoutId: string;
isSaving: boolean;
@ -71,23 +66,13 @@ class Editor extends Component<EditorProps> {
this.props.fetchCanvasWidgets(this.props.currentPageId);
}
componentDidUpdate(prevProps: EditorProps) {
if (this.props.isSaving && prevProps.isSaving !== this.props.isSaving) {
SaveToast.clear();
SaveToast.show({ message: "Saving Page..." });
} else if (
!this.props.isSaving &&
prevProps.isSaving !== this.props.isSaving
) {
SaveToast.clear();
SaveToast.show({ message: "Page Saved" });
}
}
public render() {
return (
<React.Fragment>
<EditorHeader></EditorHeader>
<EditorHeader
notificationText={this.props.isSaving ? "Saving page..." : undefined}
pageName={this.props.currentPageName}
/>
<EditorWrapper>
<WidgetCardsPane cards={this.props.cards} />
<CanvasContainer>
@ -122,8 +107,9 @@ const mapStateToProps = (state: AppState): EditorReduxState => {
);
const configs = state.entities.widgetConfig.config;
const cards = state.ui.widgetCardsPane.cards;
Object.keys(cards).forEach((group: string) => {
const cards = state.ui.editor.cards;
const groups: string[] = Object.keys(cards);
groups.forEach((group: string) => {
cards[group] = cards[group].map((widget: WidgetCardProps) => ({
...widget,
...configs[widget.type],
@ -136,6 +122,7 @@ const mapStateToProps = (state: AppState): EditorReduxState => {
pageWidgetId: state.ui.editor.pageWidgetId,
currentPageId: state.ui.editor.currentPageId,
currentLayoutId: state.ui.editor.currentLayoutId,
currentPageName: state.ui.editor.currentPageName,
isSaving: state.ui.editor.isSaving,
};
};

View File

@ -2,8 +2,8 @@ import { combineReducers } from "redux";
import entityReducer from "./entityReducers";
import uiReducer from "./uiReducers";
import { CanvasWidgetsReduxState } from "./entityReducers/canvasWidgetsReducer";
import { WidgetCardsPaneReduxState } from "./uiReducers/widgetCardsPaneReducer";
import { EditorReduxState } from "./uiReducers/editorReducer";
import { ErrorReduxState } from "./uiReducers/errorReducer";
import { APIDataState } from "./entityReducers/apiDataReducer";
import { QueryDataState } from "./entityReducers/queryDataReducer";
import { ActionDataState } from "./entityReducers/actionsReducer";
@ -20,9 +20,9 @@ export default appReducer;
export interface AppState {
ui: {
widgetCardsPane: WidgetCardsPaneReduxState;
editor: EditorReduxState;
propertyPane: PropertyPaneReduxState;
errors: ErrorReduxState;
};
entities: {
canvasWidgets: CanvasWidgetsReduxState;

View File

@ -1,9 +0,0 @@
import { createReducer } from "../../utils/AppsmithUtils";
const initialState = {};
const editorHeaderReducer = createReducer(initialState, {});
// export interface EditorHeaderReduxState {}
export default editorHeaderReducer;

View File

@ -7,15 +7,18 @@ import {
} from "../../constants/ReduxActionConstants";
import { WidgetCardProps, WidgetProps } from "../../widgets/BaseWidget";
import { ContainerWidgetProps } from "../../widgets/ContainerWidget";
import WidgetCardsPaneResponse from "../../mockResponses/WidgetCardsPaneResponse";
const editorConfigs = getEditorConfigs();
const initialState: EditorReduxState = {
pageWidgetId: "0",
...editorConfigs,
isSaving: false,
cards: WidgetCardsPaneResponse,
};
const editorReducer = createReducer(initialState, {
[ReduxActionTypes.SUCCESS_FETCHING_WIDGET_CARDS]: (
[ReduxActionTypes.FETCH_WIDGET_CARDS_SUCCESS]: (
state: EditorReduxState,
action: ReduxAction<LoadWidgetCardsPanePayload>,
) => {
@ -27,19 +30,17 @@ const editorReducer = createReducer(initialState, {
[ReduxActionTypes.SAVE_PAGE_SUCCESS]: (state: EditorReduxState) => {
return { ...state, isSaving: false };
},
[ReduxActionTypes.SAVE_PAGE_ERROR]: (state: EditorReduxState) => {
return { ...state, isSaving: false };
},
});
export interface EditorReduxState {
dsl?: ContainerWidgetProps<WidgetProps>;
cards?: {
cards: {
[id: string]: WidgetCardProps[];
};
pageWidgetId: string;
currentPageId: string;
currentLayoutId: string;
currentPageName: string;
isSaving: boolean;
}

View File

@ -0,0 +1,31 @@
import { createReducer } from "../../utils/AppsmithUtils";
import {
ReduxAction,
ReduxActionTypes,
ReduxActionErrorPayload,
} from "../../constants/ReduxActionConstants";
const initialState: ErrorReduxState = { sourceAction: "", message: "" };
const errorReducer = createReducer(initialState, {
[ReduxActionTypes.REPORT_ERROR]: (
state: ErrorReduxState,
action: ReduxAction<ReduxActionErrorPayload>,
) => {
return {
sourceAction: action.payload.source,
message: action.payload.message,
};
},
[ReduxActionTypes.FLUSH_ERRORS]: () => {
return {};
},
});
export interface ErrorReduxState {
// Expiration?
sourceAction?: string;
message?: string;
}
export default errorReducer;

View File

@ -1,13 +1,11 @@
import { combineReducers } from "redux";
import widgetCardsPaneReducer from "./widgetCardsPaneReducer";
import editorHeaderReducer from "./editorHeaderReducer";
import editorReducer from "./editorReducer";
import errorReducer from "./errorReducer";
import propertyPaneReducer from "./propertyPaneReducer";
const uiReducer = combineReducers({
widgetCardsPane: widgetCardsPaneReducer,
editorHeader: editorHeaderReducer,
editor: editorReducer,
errors: errorReducer,
propertyPane: propertyPaneReducer,
});
export default uiReducer;

View File

@ -1,27 +0,0 @@
import { createReducer } from "../../utils/AppsmithUtils";
import {
ReduxActionTypes,
ReduxAction,
LoadWidgetCardsPanePayload,
} from "../../constants/ReduxActionConstants";
import { WidgetCardProps } from "../../widgets/BaseWidget";
import WidgetCardsPaneResponse from "../../mockResponses/WidgetCardsPaneResponse";
const initialState: WidgetCardsPaneReduxState = WidgetCardsPaneResponse;
const widgetCardsPaneReducer = createReducer(initialState, {
[ReduxActionTypes.ERROR_FETCHING_WIDGET_CARDS]: (
state: WidgetCardsPaneReduxState,
action: ReduxAction<LoadWidgetCardsPanePayload>,
) => {
return { cards: action.payload.cards };
},
});
export interface WidgetCardsPaneReduxState {
cards: {
[id: string]: WidgetCardProps[];
};
}
export default widgetCardsPaneReducer;

View File

@ -0,0 +1,38 @@
import {
ReduxActionTypes,
ReduxActionErrorTypes,
ReduxAction,
} from "../constants/ReduxActionConstants";
import { ApiResponse } from "../api/ApiResponses";
import { put, takeLatest } from "redux-saga/effects";
export function* validateResponse(response: ApiResponse) {
if (response.responseMeta.success) {
return true;
} else {
yield put({
type: ReduxActionErrorTypes.API_ERROR,
payload: {
error: response.responseMeta.error,
},
});
return false;
}
}
export function* errorSaga(errorAction: ReduxAction<{ error: any }>) {
// Just a pass through for now.
// Add procedures to customize errors here
yield put({
type: ReduxActionTypes.REPORT_ERROR,
payload: {
message: errorAction.payload.error,
source: errorAction.type,
},
});
}
export default function* errorSagas() {
yield takeLatest(Object.values(ReduxActionErrorTypes), errorSaga);
}

View File

@ -1,15 +1,11 @@
import CanvasWidgetsNormalizer from "../normalizers/CanvasWidgetsNormalizer";
import {
ReduxActionTypes,
ReduxActionErrorTypes,
ReduxAction,
UpdateCanvasPayload,
} from "../constants/ReduxActionConstants";
import {
updateCanvas,
savePageError,
savePageSuccess,
fetchPageError,
} from "../actions/pageActions";
import { updateCanvas, savePageSuccess } from "../actions/pageActions";
import PageApi, {
FetchPageResponse,
SavePageResponse,
@ -25,27 +21,31 @@ import {
takeEvery,
all,
} from "redux-saga/effects";
import { extractCurrentDSL } from "../utils/WidgetPropsUtils";
import { getEditorConfigs } from "./selectors";
import { validateResponse } from "./ErrorSagas";
export function* fetchPageSaga(
pageRequestAction: ReduxAction<FetchPageRequest>,
) {
const pageRequest = pageRequestAction.payload;
try {
const pageRequest = pageRequestAction.payload;
const fetchPageResponse: FetchPageResponse = yield call(
PageApi.fetchPage,
pageRequest,
);
if (fetchPageResponse.responseMeta.success) {
const isValidResponse = yield validateResponse(fetchPageResponse);
if (isValidResponse) {
const normalizedResponse = CanvasWidgetsNormalizer.normalize(
extractCurrentDSL(fetchPageResponse),
);
const canvasWidgetsPayload: UpdateCanvasPayload = {
pageWidgetId: normalizedResponse.result,
currentPageName: fetchPageResponse.data.name,
currentPageId: fetchPageResponse.data.id,
widgets: normalizedResponse.entities.canvasWidgets,
layoutId: fetchPageResponse.data.layouts[0].id, // TODO(abhinav): Handle for multiple layouts
currentLayoutId: fetchPageResponse.data.layouts[0].id, // TODO(abhinav): Handle for multiple layouts
};
yield all([
put(updateCanvas(canvasWidgetsPayload)),
@ -56,8 +56,12 @@ export function* fetchPageSaga(
]);
}
} catch (error) {
console.log(error);
yield put(fetchPageError(error));
yield put({
type: ReduxActionErrorTypes.FETCH_PAGE_ERROR,
payload: {
error,
},
});
}
}
@ -68,10 +72,17 @@ export function* savePageSaga(savePageAction: ReduxAction<SavePageRequest>) {
PageApi.savePage,
savePageRequest,
);
yield put(savePageSuccess(savePageResponse));
} catch (err) {
console.log(err);
yield put(savePageError(err));
const isValidResponse = validateResponse(savePageResponse);
if (isValidResponse) {
yield put(savePageSuccess(savePageResponse));
}
} catch (error) {
yield put({
type: ReduxActionErrorTypes.SAVE_PAGE_ERROR,
payload: {
error,
},
});
}
}
@ -87,7 +98,6 @@ export function* saveLayoutSaga(
{ canvasWidgets: widgets },
);
const editorConfigs = yield select(getEditorConfigs) as any;
console.log(editorConfigs);
yield put({
type: ReduxActionTypes.SAVE_PAGE_INIT,
payload: {

View File

@ -1,5 +1,7 @@
// import CanvasWidgetsNormalizer from "../normalizers/CanvasWidgetsNormalizer"
import { ReduxActionTypes } from "../constants/ReduxActionConstants";
import {
ReduxActionTypes,
ReduxActionErrorTypes,
} from "../constants/ReduxActionConstants";
import WidgetCardsPaneApi, {
WidgetCardsPaneResponse,
} from "../api/WidgetCardsPaneApi";
@ -13,7 +15,7 @@ export function* fetchWidgetCards() {
]);
yield put(successFetchingWidgetCards(widgetCards.cards));
} catch (err) {
yield put({ type: ReduxActionTypes.ERROR_FETCHING_WIDGET_CARDS, err });
yield put({ type: ReduxActionErrorTypes.FETCH_WIDGET_CARDS_ERROR, err });
}
}

View File

@ -3,6 +3,7 @@ import pageSagas from "../sagas/PageSagas";
import { fetchWidgetCardsSaga } from "./WidgetCardsPaneSagas";
import { watchExecuteActionSaga } from "./ActionSagas";
import widgetOperationSagas from "./WidgetOperationSagas";
import errorSagas from "./ErrorSagas";
export function* rootSaga() {
yield all([
@ -10,5 +11,6 @@ export function* rootSaga() {
spawn(fetchWidgetCardsSaga),
spawn(watchExecuteActionSaga),
spawn(widgetOperationSagas),
spawn(errorSagas),
]);
}

View File

@ -0,0 +1,27 @@
import {
call as ReduxSagaCall,
select as ReduxSagaSelect,
put as ReduxSagaPut,
takeLatest as ReduxSagaTakeLatest,
takeEvery as ReduxSagaTakeEvery,
all as ReduxSagaAll,
} from "redux-saga/effects";
function* safe(effect: any) {
try {
return yield effect;
} catch (error) {
console.log(error);
}
}
export const call = (fn: any, ...args: unknown[]) =>
safe(ReduxSagaCall(fn, ...args));
export const select = (state: any, ...args: any[]) =>
safe(ReduxSagaSelect(state, ...args));
export const put = (action: any) => safe(ReduxSagaPut(action));
export const takeLatest = (pattern: any, worker: any) =>
safe(ReduxSagaTakeLatest(pattern, worker));
export const takeEvery = (pattern: any, worker: any) =>
safe(ReduxSagaTakeEvery(pattern, worker));
export const all = (args: unknown[]) => safe(ReduxSagaAll(args));

View File

@ -8,15 +8,3 @@ export const snapToGrid = (
const snappedY = Math.floor(y / rowHeight);
return [snappedX, snappedY];
};
export const getRowColSizes = (
rowCount: number,
columnCount: number,
width: number,
height: number,
): { rowHeight: number; columnWidth: number } => {
return {
columnWidth: width / columnCount,
rowHeight: height / rowCount,
};
};

View File

@ -94,17 +94,21 @@ abstract class BaseWidget<
getCanvasView(): JSX.Element {
const style = this.getPositionStyle();
return (
<DraggableComponent
{...this.props}
style={{ ...style }}
orientation={"VERTICAL"}
>
<ResizableComponent style={{ ...style }} {...this.props}>
{this.getPageView()}
</ResizableComponent>
</DraggableComponent>
);
if (!this.props.parentId) {
return this.getPageView();
} else {
return (
<DraggableComponent
{...this.props}
style={{ ...style }}
orientation={"VERTICAL"}
>
<ResizableComponent style={{ ...style }} {...this.props}>
{this.getPageView()}
</ResizableComponent>
</DraggableComponent>
);
}
}
abstract getWidgetType(): WidgetType;

View File

@ -14,7 +14,7 @@ import { GridDefaults } from "../constants/WidgetConstants";
import DraggableComponent from "../editorComponents/DraggableComponent";
import ResizableComponent from "../editorComponents/ResizableComponent";
const { DEFAULT_GRID_COLUMNS, DEFAULT_GRID_ROWS } = GridDefaults;
const { DEFAULT_GRID_COLUMNS, DEFAULT_GRID_ROW_HEIGHT } = GridDefaults;
class ContainerWidget extends BaseWidget<
ContainerWidgetProps<WidgetProps>,
@ -34,21 +34,14 @@ class ContainerWidget extends BaseWidget<
componentDidUpdate(previousProps: ContainerWidgetProps<WidgetProps>) {
super.componentDidUpdate(previousProps);
let snapColumnSpace = this.state.snapColumnSpace;
let snapRowSpace = this.state.snapRowSpace;
if (this.state.componentWidth)
snapColumnSpace =
this.state.componentWidth /
(this.props.snapColumns || DEFAULT_GRID_COLUMNS);
if (this.state.componentHeight)
snapRowSpace =
this.state.componentHeight / (this.props.snapRows || DEFAULT_GRID_ROWS);
if (
this.state.snapColumnSpace !== snapColumnSpace ||
this.state.snapRowSpace !== snapRowSpace
) {
if (this.state.snapColumnSpace !== snapColumnSpace) {
this.setState({
snapColumnSpace,
snapRowSpace,
snapRowSpace: DEFAULT_GRID_ROW_HEIGHT,
});
}
}
@ -79,9 +72,8 @@ class ContainerWidget extends BaseWidget<
);
}
getCanvasView() {
const style = this.getPositionStyle();
const occupiedSpaces: OccupiedSpace[] | null = this.props.children
getOccupiedSpaces(): OccupiedSpace[] | null {
return this.props.children
? this.props.children.map(child => ({
id: child.widgetId,
left: child.leftColumn,
@ -90,6 +82,23 @@ class ContainerWidget extends BaseWidget<
right: child.rightColumn,
}))
: null;
}
getCanvasView() {
const style = this.getPositionStyle();
const occupiedSpaces = this.getOccupiedSpaces();
const renderDraggableComponent = (
<DraggableComponent
style={{ ...style, xPosition: 0, yPosition: 0 }}
{...this.props}
orientation={"VERTICAL"}
>
<ResizableComponent style={{ ...style }} {...this.props}>
{this.getPageView()}
</ResizableComponent>
</DraggableComponent>
);
return (
<DropTargetComponent
{...this.props}
@ -99,15 +108,7 @@ class ContainerWidget extends BaseWidget<
...style,
}}
>
<DraggableComponent
style={{ ...style, xPosition: 0, yPosition: 0 }}
{...this.props}
orientation={"VERTICAL"}
>
<ResizableComponent style={{ ...style }} {...this.props}>
{this.getPageView()}
</ResizableComponent>
</DraggableComponent>
{this.props.parentId ? renderDraggableComponent : this.getPageView()}
</DropTargetComponent>
);
}