Fix/map issues

This commit is contained in:
Vicky Bansal 2020-05-07 10:51:37 +00:00 committed by Abhinav Jha
parent a3ddf1250a
commit fc5f417923
16 changed files with 307 additions and 182 deletions

View File

@ -34,12 +34,12 @@ export const executePageLoadActions = (
});
export const disableDragAction = (
disable: boolean,
): ReduxAction<{ disable: boolean }> => {
isDraggingDisabled: boolean,
): ReduxAction<{ isDraggingDisabled: boolean }> => {
return {
type: ReduxActionTypes.DISABLE_WIDGET_DRAG,
payload: {
disable,
isDraggingDisabled,
},
};
};

View File

@ -70,13 +70,24 @@ class ChartComponent extends React.Component<ChartComponentProps> {
}
};
getChartData = (chartData: ChartData[]) => {
getChartData = () => {
const chartData: ChartData[] = this.props.chartData;
if (chartData.length === 0) {
return [
{
label: "",
value: "",
},
];
}
const data: ChartDataPoint[] = chartData[0].data;
if (data.length === 0) {
return {
label: "",
value: "",
};
return [
{
label: "",
value: "",
},
];
}
return data.map(item => {
return {
@ -162,12 +173,12 @@ class ChartComponent extends React.Component<ChartComponentProps> {
getChartDataSource = () => {
if (
this.props.chartData.length === 1 ||
this.props.chartData.length <= 1 ||
this.props.chartType === "PIE_CHART"
) {
return {
chart: this.getChartConfig(),
data: this.getChartData(this.props.chartData),
data: this.getChartData(),
};
} else {
return {

View File

@ -1,15 +1,16 @@
import React from "react";
import { ControlWrapper } from "../../propertyControls/StyledControls";
import styled from "constants/DefaultTheme";
import { Droppable, Draggable } from "react-beautiful-dnd";
import { DragDropContext, Droppable, Draggable } from "react-beautiful-dnd";
const StyledListWrapper = styled(ControlWrapper)`
display: flex;
justify-content: flex-start;
const ItemWrapper = styled.div`
padding-right: 16px;
margin: 8px 0 0 0;
`;
const DroppableWrapper = styled.div`
width: 250px;
`;
type RenderComponentProps = {
index: number;
item: {
@ -19,73 +20,100 @@ type RenderComponentProps = {
updateOption: (index: number, value: string) => void;
};
type DraggableComponentProps = {
index: number;
draggableId: string;
item: {
label: string;
};
deleteOption: (index: number) => void;
updateOption: (index: number, value: string) => void;
renderComponent: (props: RenderComponentProps) => JSX.Element;
};
export const DraggableComponent = (props: DraggableComponentProps) => {
const {
deleteOption,
updateOption,
item,
index,
draggableId,
renderComponent,
} = props;
return (
<Draggable draggableId={draggableId} key={draggableId} index={index}>
{({ innerRef, draggableProps, dragHandleProps }) => (
<StyledListWrapper
orientation={"HORIZONTAL"}
{...draggableProps}
{...dragHandleProps}
ref={innerRef as React.Ref<HTMLDivElement>}
style={{
...draggableProps.style,
userSelect: "none",
position: "static",
}}
>
{renderComponent({ deleteOption, updateOption, item, index })}
</StyledListWrapper>
)}
</Draggable>
);
};
type DroppableComponentProps = {
interface DroppableComponentProps {
items: object[];
renderComponent: (props: RenderComponentProps) => JSX.Element;
deleteOption: (index: number) => void;
updateOption: (index: number, value: string) => void;
};
updateItems: (items: object[]) => void;
}
export const DroppableComponent = (props: DroppableComponentProps) => {
const { items } = props;
return (
<Droppable droppableId="0">
{({ innerRef, droppableProps, placeholder }) => (
<div ref={innerRef as React.Ref<HTMLDivElement>} {...droppableProps}>
{items.map((item: { id: string } & any, index: number) => {
return (
<DraggableComponent
key={index}
index={index}
item={item}
draggableId={item.id}
{...props}
/>
);
})}
</div>
)}
</Droppable>
);
};
interface DroppableComponentState {
items: object[];
}
export class DroppableComponent extends React.Component<
DroppableComponentProps,
DroppableComponentState
> {
constructor(props: DroppableComponentProps) {
super(props);
this.state = {
items: props.items,
};
}
onDragEnd = (result: any) => {
const { destination, source } = result;
if (!destination) {
return;
}
if (
destination.droppableId === source.droppableId &&
destination.index === source.index
) {
return;
}
const tabs: object[] = this.state.items;
const sourceTab = tabs[source.index];
const destinationTab = tabs[destination.index];
const updatedTabs = tabs.map((tab, index) => {
if (index === source.index) {
return destinationTab;
} else if (index === destination.index) {
return sourceTab;
}
return tab;
});
this.setState({ items: updatedTabs });
this.props.updateItems(updatedTabs);
};
render() {
const { renderComponent, deleteOption, updateOption } = this.props;
return (
<DragDropContext onDragEnd={this.onDragEnd}>
<Droppable droppableId="droppable">
{({ innerRef, droppableProps, placeholder }) => (
<DroppableWrapper
ref={innerRef as React.Ref<HTMLDivElement>}
{...droppableProps}
>
{this.state.items.map(
(item: { id: string } & any, index: number) => {
return (
<Draggable
draggableId={item.id}
key={item.id}
index={index}
>
{({ innerRef, draggableProps, dragHandleProps }) => (
<ItemWrapper
ref={innerRef as React.Ref<HTMLDivElement>}
{...draggableProps}
{...dragHandleProps}
style={{
...draggableProps.style,
userSelect: "none",
position: "static",
}}
>
{renderComponent({
deleteOption,
updateOption,
item,
index,
})}
</ItemWrapper>
)}
</Draggable>
);
},
)}
</DroppableWrapper>
)}
</Droppable>
</DragDropContext>
);
}
}

View File

@ -20,7 +20,7 @@ interface MapComponentProps {
allowZoom: boolean;
center: {
lat: number;
lng: number;
long: number;
};
markers?: Array<MarkerProps>;
selectedMarker?: {
@ -29,12 +29,23 @@ interface MapComponentProps {
title?: string;
};
enableCreateMarker: boolean;
updateCenter: (lat: number, lng: number) => void;
updateMarker: (lat: number, lng: number, index: number) => void;
saveMarker: (lat: number, lng: number) => void;
selectMarker: (lat: number, lng: number, title: string) => void;
updateCenter: (lat: number, long: number) => void;
updateMarker: (lat: number, long: number, index: number) => void;
saveMarker: (lat: number, long: number) => void;
selectMarker: (lat: number, long: number, title: string) => void;
disableDrag: (e: any) => void;
}
const MapWrapper = styled.div`
position: relative;
width: 100%;
height: 100%;
`;
const MapContainerWrapper = styled.div`
width: 100%;
`;
const StyledInput = styled.input`
box-sizing: border-box;
border: 1px solid transparent;
@ -78,7 +89,7 @@ const MyMapComponent = withScriptjs(
streetViewControl: false,
}}
zoom={props.zoom}
center={props.center}
center={{ ...props.center, lng: props.center.long }}
onClick={e => {
if (props.enableCreateMarker) {
props.saveMarker(e.latLng.lat(), e.latLng.lng());
@ -98,15 +109,15 @@ const MyMapComponent = withScriptjs(
<Marker
key={index}
title={marker.title}
position={{ lat: marker.lat, lng: marker.lng }}
position={{ lat: marker.lat, lng: marker.long }}
clickable
draggable={
props.selectedMarker &&
props.selectedMarker.lat === marker.lat &&
props.selectedMarker.long === marker.lng
props.selectedMarker.long === marker.long
}
onClick={e => {
props.selectMarker(marker.lat, marker.lng, marker.title);
props.selectMarker(marker.lat, marker.long, marker.title);
}}
onDragEnd={de => {
props.updateMarker(de.latLng.lat(), de.latLng.lng(), index);
@ -147,8 +158,8 @@ class MapComponent extends React.Component<MapComponentProps> {
) {
const location = places[0].geometry.location;
const lat = location.lat();
const lng = location.lng();
this.props.updateCenter(lat, lng);
const long = location.lng();
this.props.updateCenter(lat, long);
}
}
};
@ -157,9 +168,9 @@ class MapComponent extends React.Component<MapComponentProps> {
if ("geolocation" in navigator) {
return navigator.geolocation.getCurrentPosition(data => {
const {
coords: { latitude: lat, longitude: lng },
coords: { latitude: lat, longitude: long },
} = data;
this.props.saveMarker(lat, lng);
this.props.updateCenter(lat, long);
});
}
};
@ -167,17 +178,19 @@ class MapComponent extends React.Component<MapComponentProps> {
render() {
const zoom = Math.floor(this.props.zoomLevel / 5);
return (
<MyMapComponent
googleMapURL="https://maps.googleapis.com/maps/api/js?key=AIzaSyC2H_twoNbEKMm9Q0nYAh7715Dplg2asCI&v=3.exp&libraries=geometry,drawing,places"
loadingElement={<div style={{ height: `100%` }} />}
containerElement={<div style={{ height: `100%` }} />}
mapElement={<div style={{ height: `100%` }} />}
{...this.props}
zoom={zoom}
onPlacesChanged={this.onPlacesChanged}
onSearchBoxMounted={this.onSearchBoxMounted}
getUserLocation={this.getUserLocation}
/>
<MapWrapper onMouseLeave={this.props.disableDrag}>
<MyMapComponent
googleMapURL="https://maps.googleapis.com/maps/api/js?key=AIzaSyC2H_twoNbEKMm9Q0nYAh7715Dplg2asCI&v=3.exp&libraries=geometry,drawing,places"
loadingElement={<MapContainerWrapper />}
containerElement={<MapContainerWrapper />}
mapElement={<MapContainerWrapper />}
{...this.props}
zoom={zoom}
onPlacesChanged={this.onPlacesChanged}
onSearchBoxMounted={this.onSearchBoxMounted}
getUserLocation={this.getUserLocation}
/>
</MapWrapper>
);
}
}

View File

@ -87,7 +87,6 @@ const DraggableComponent = (props: DraggableComponentProps) => {
const isDraggingDisabled: boolean = useSelector(
(state: AppState) => state.ui.widgetDragResize.isDraggingDisabled,
);
// If this widget has not been selected, and the props tell us to
// disable the default click on the wiget;
// We add a mask on the widget and stop "first" click event from propagating to the

View File

@ -62,10 +62,7 @@ type RenderComponentProps = {
index: number;
item: {
seriesName: string;
data: Array<{
x: string;
y: string;
}>;
data: Array<{ x: string; y: string }> | any;
};
length: number;
deleteOption: Function;
@ -126,10 +123,7 @@ class ChartDataControl extends BaseControl<ControlProps> {
render() {
const chartData: Array<{
seriesName: string;
data: Array<{
x: string;
y: string;
}>;
data: Array<{ x: string; y: string }> | any;
}> = this.props.propertyValue || [];
const dataLength = chartData.length;
return (
@ -170,10 +164,7 @@ class ChartDataControl extends BaseControl<ControlProps> {
) => {
const chartData: Array<{
seriesName: string;
data: Array<{
x: string;
y: string;
}>;
data: Array<{ x: string; y: string }> | any;
}> = this.props.propertyValue;
this.updateProperty(
this.props.propertyName,
@ -182,7 +173,11 @@ class ChartDataControl extends BaseControl<ControlProps> {
if (propertyName === "seriesName") {
item.seriesName = updatedValue;
} else {
item.data = JSON.parse(updatedValue);
try {
item.data = JSON.parse(updatedValue);
} catch (err) {
item.data = updatedValue;
}
}
}
return item;
@ -193,10 +188,7 @@ class ChartDataControl extends BaseControl<ControlProps> {
addOption = () => {
const chartData: Array<{
seriesName: string;
data: Array<{
x: string;
y: string;
}>;
data: Array<{ x: string; y: string }> | any;
}> = this.props.propertyValue ? this.props.propertyValue.slice() : [];
chartData.push({ seriesName: "", data: [{ x: "", y: "" }] });
this.updateProperty(this.props.propertyName, chartData);

View File

@ -32,7 +32,16 @@ const DatePickerControlWrapper = styled.div`
}
`;
class DatePickerControl extends BaseControl<DatePickerControlProps> {
class DatePickerControl extends BaseControl<
DatePickerControlProps,
DatePickerControlState
> {
constructor(props: DatePickerControlProps) {
super(props);
this.state = {
selectedDate: props.propertyValue,
};
}
render() {
const now = moment();
const year = now.get("year");
@ -61,6 +70,7 @@ class DatePickerControl extends BaseControl<DatePickerControlProps> {
}
onDateSelected = (date: Date): void => {
this.setState({ selectedDate: moment(date).toISOString(true) });
this.updateProperty(
this.props.propertyName,
moment(date).toISOString(true),
@ -85,4 +95,8 @@ export interface DatePickerControlProps extends ControlProps {
propertyValue: string;
}
interface DatePickerControlState {
selectedDate: string;
}
export default DatePickerControl;

View File

@ -72,8 +72,8 @@ class LocationSearchControl extends BaseControl<ControlProps> {
) => {
const location = places[0].geometry.location;
const lat = location.lat();
const lng = location.lng();
const value = { lat, lng };
const long = location.lng();
const value = { lat, long };
this.updateProperty(this.props.propertyName, value);
};
render() {

View File

@ -5,7 +5,6 @@ import styled from "constants/DefaultTheme";
import { FormIcons } from "icons/FormIcons";
import { ControlIcons } from "icons/ControlIcons";
import { AnyStyledComponent } from "styled-components";
import { DragDropContext } from "react-beautiful-dnd";
import { generateReactKey } from "utils/generators";
import { DroppableComponent } from "../designSystems/appsmith/DraggableListComponent";
@ -37,6 +36,17 @@ const StyledPropertyPaneButtonWrapper = styled.div`
margin-top: 10px;
`;
const ItemWrapper = styled.div`
display: flex;
justify-content: flex-start;
`;
const TabsWrapper = styled.div`
width: 100%;
display: flex;
flex-direction: column;
`;
const StyledOptionControlInputGroup = styled(StyledInputGroup)`
margin-right: 2px;
&&& {
@ -65,7 +75,7 @@ type RenderComponentProps = {
function TabControlComponent(props: RenderComponentProps) {
const { deleteOption, updateOption, item, index } = props;
return (
<React.Fragment>
<ItemWrapper>
<StyledOptionControlInputGroup
type="text"
placeholder="Tab Title"
@ -82,36 +92,13 @@ function TabControlComponent(props: RenderComponentProps) {
}}
/>
<StyledDragIcon height={20} width={20} />
</React.Fragment>
</ItemWrapper>
);
}
class TabControl extends BaseControl<ControlProps> {
onDragEnd = (result: any) => {
const { destination, source } = result;
if (!destination) {
return;
}
if (
destination.droppableId === source.droppableId &&
destination.index === source.index
) {
return;
}
const tabs: string[] = this.props.propertyValue || [""];
const sourceTab = tabs[source.index];
const destinationTab = tabs[destination.index];
this.updateProperty(
this.props.propertyName,
tabs.map((tab, index) => {
if (index === source.index) {
return destinationTab;
} else if (index === destination.index) {
return sourceTab;
}
return tab;
}),
);
updateItems = (items: object[]) => {
this.updateProperty(this.props.propertyName, items);
};
render() {
@ -120,12 +107,13 @@ class TabControl extends BaseControl<ControlProps> {
label: string;
}> = this.props.propertyValue || [{ id: "" }];
return (
<DragDropContext onDragEnd={this.onDragEnd}>
<TabsWrapper>
<DroppableComponent
items={tabs}
renderComponent={TabControlComponent}
deleteOption={this.deleteOption}
updateOption={this.updateOption}
updateItems={this.updateItems}
/>
<StyledPropertyPaneButtonWrapper>
<StyledPropertyPaneButton
@ -135,7 +123,7 @@ class TabControl extends BaseControl<ControlProps> {
onClick={this.addOption}
/>
</StyledPropertyPaneButtonWrapper>
</DragDropContext>
</TabsWrapper>
);
}

View File

@ -1206,12 +1206,6 @@ const PropertyPaneConfigResponse = {
controlType: "INPUT_TEXT",
inputType: "ARRAY",
},
{
id: "25.5",
propertyName: "enableCreateMarker",
label: "Create new marker",
controlType: "SWITCH",
},
],
},
{

View File

@ -436,12 +436,11 @@ const WidgetConfigResponse: WidgetConfigReducerState = {
isVisible: true,
widgetName: "Map",
enableSearch: true,
enableCreateMarker: true,
zoomLevel: 50,
enablePickLocation: true,
allowZoom: true,
mapCenter: { lat: -34.397, lng: 150.644 },
defaultMarkers: [{ lat: -34.397, lng: 150.644, title: "Test A" }],
mapCenter: { lat: -34.397, long: 150.644 },
defaultMarkers: [{ lat: -34.397, long: 150.644, title: "Test A" }],
},
},
configVersion: 1,

View File

@ -10,7 +10,7 @@ const initialState: WidgetDragResizeState = {
export const widgetDraggingReducer = createReducer(initialState, {
[ReduxActionTypes.DISABLE_WIDGET_DRAG]: (
state: WidgetDragResizeState,
action: ReduxAction<{ disable: boolean }>,
action: ReduxAction<{ isDraggingDisabled: boolean }>,
) => {
return { ...state, ...action.payload };
},

View File

@ -248,7 +248,24 @@ export const VALIDATORS: Record<ValidationType, Validator> = {
parsed,
message: `${WIDGET_TYPE_VALIDATION_ERROR}: Chart Data`,
};
} else if (!_.every(parsed, datum => _.isObject(datum))) {
}
const hasChartData = _.every(
parsed,
(datum: { seriesName: any; data: any }) => {
if (_.isObject(datum)) {
return (
_.isString(datum.seriesName) &&
_.isArray(datum.data) &&
_.every(datum.data, (item: { x: any; y: any }) => {
return _.isString(item.x) && !_.isUndefined(item.y);
})
);
} else {
return false;
}
},
);
if (!hasChartData) {
return {
isValid: false,
parsed: [],

View File

@ -100,6 +100,66 @@ const chartDataMigration = (currentDSL: ContainerWidgetProps<WidgetProps>) => {
return currentDSL;
};
const mapDataMigration = (currentDSL: ContainerWidgetProps<WidgetProps>) => {
currentDSL.children = currentDSL.children?.map((children: WidgetProps) => {
if (children.type === WidgetTypes.MAP_WIDGET) {
if (children.markers) {
children.markers = children.markers.map(
(marker: { lat: any; lng: any; long: any; title: any }) => {
return {
lat: marker.lat,
long: marker.lng || marker.long,
title: marker.title,
};
},
);
}
if (children.defaultMarkers) {
const defaultMarkers = JSON.parse(children.defaultMarkers);
children.defaultMarkers = defaultMarkers.map(
(marker: {
lat: number;
lng: number;
long: number;
title: string;
}) => {
return {
lat: marker.lat,
long: marker.lng || marker.long,
title: marker.title,
};
},
);
}
if (children.selectedMarker) {
children.selectedMarker = {
lat: children.selectedMarker.lat,
long: children.selectedMarker.lng || children.selectedMarker.long,
title: children.selectedMarker.title,
};
}
if (children.mapCenter) {
children.mapCenter = {
lat: children.mapCenter.lat,
long: children.mapCenter.lng || children.mapCenter.long,
title: children.mapCenter.title,
};
}
if (children.center) {
children.center = {
lat: children.center.lat,
long: children.center.lng || children.center.long,
title: children.center.title,
};
}
} else if (children.children && children.children.length > 0) {
children = mapDataMigration(children);
}
return children;
});
return currentDSL;
};
// A rudimentary transform function which updates the DSL based on its version.
// A more modular approach needs to be designed.
const transformDSL = (currentDSL: ContainerWidgetProps<WidgetProps>) => {
@ -135,6 +195,10 @@ const transformDSL = (currentDSL: ContainerWidgetProps<WidgetProps>) => {
currentDSL = chartDataMigration(currentDSL);
currentDSL.version = 3;
}
if (currentDSL.version === 3) {
currentDSL = mapDataMigration(currentDSL);
currentDSL.version = 4;
}
return currentDSL;
};

View File

@ -19,6 +19,7 @@ class ChartWidget extends BaseWidget<ChartWidgetProps, WidgetState> {
yAxisName: VALIDATION_TYPES.TEXT,
chartName: VALIDATION_TYPES.TEXT,
isVisible: VALIDATION_TYPES.BOOLEAN,
chartData: VALIDATION_TYPES.CHART_DATA,
};
}

View File

@ -16,7 +16,6 @@ class MapWidget extends BaseWidget<MapWidgetProps, WidgetState> {
enableSearch: VALIDATION_TYPES.BOOLEAN,
enablePickLocation: VALIDATION_TYPES.BOOLEAN,
allowZoom: VALIDATION_TYPES.BOOLEAN,
enableCreateMarker: VALIDATION_TYPES.BOOLEAN,
zoomLevel: VALIDATION_TYPES.NUMBER,
onMarkerClick: VALIDATION_TYPES.ACTION_SELECTOR,
onCreateMarker: VALIDATION_TYPES.ACTION_SELECTOR,
@ -44,28 +43,31 @@ class MapWidget extends BaseWidget<MapWidgetProps, WidgetState> {
};
}
updateCenter = (lat: number, lng: number) => {
this.updateWidgetMetaProperty("center", { lat, lng });
updateCenter = (lat: number, long: number) => {
this.updateWidgetMetaProperty("center", { lat, long });
};
updateMarker = (lat: number, lng: number, index: number) => {
updateMarker = (lat: number, long: number, index: number) => {
const markers: Array<MarkerProps> = [...this.props.markers];
this.disableDrag(false);
this.updateWidgetMetaProperty(
"markers",
markers.map((marker, i) => {
if (index === i) {
marker.lat = lat;
marker.lng = lng;
marker.long = long;
}
return marker;
}),
);
};
onCreateMarker = (lat: number, lng: number) => {
const markers: Array<MarkerProps> = [...this.props.markers];
markers.push({ lat: lat, lng: lng });
this.updateWidgetMetaProperty("markers", markers);
onCreateMarker = (lat: number, long: number) => {
this.disableDrag(true);
this.updateWidgetMetaProperty("selectedMarker", {
lat: lat,
long: long,
});
if (this.props.onCreateMarker) {
super.executeAction({
dynamicString: this.props.onCreateMarker,
@ -76,12 +78,13 @@ class MapWidget extends BaseWidget<MapWidgetProps, WidgetState> {
}
};
onMarkerClick = (lat: number, lng: number, title: string) => {
onMarkerClick = (lat: number, long: number, title: string) => {
this.updateWidgetMetaProperty("selectedMarker", {
lat: lat,
long: lng,
long: long,
title: title,
});
this.disableDrag(true);
if (this.props.onMarkerClick) {
super.executeAction({
dynamicString: this.props.onMarkerClick,
@ -119,7 +122,7 @@ class MapWidget extends BaseWidget<MapWidgetProps, WidgetState> {
zoomLevel={this.props.zoomLevel}
allowZoom={this.props.allowZoom}
center={this.props.center || this.props.mapCenter}
enableCreateMarker={this.props.enableCreateMarker}
enableCreateMarker
selectedMarker={this.props.selectedMarker}
updateCenter={this.updateCenter}
isDisabled={this.props.isDisabled}
@ -129,6 +132,9 @@ class MapWidget extends BaseWidget<MapWidgetProps, WidgetState> {
updateMarker={this.updateMarker}
selectMarker={this.onMarkerClick}
markers={this.props.markers || []}
disableDrag={() => {
this.disableDrag(false);
}}
/>
);
}
@ -140,7 +146,7 @@ class MapWidget extends BaseWidget<MapWidgetProps, WidgetState> {
export interface MarkerProps {
lat: number;
lng: number;
long: number;
title?: string;
description?: string;
}
@ -152,14 +158,13 @@ export interface MapWidgetProps extends WidgetProps {
zoomLevel: number;
allowZoom: boolean;
enablePickLocation: boolean;
enableCreateMarker: boolean;
mapCenter: {
lat: number;
lng: number;
long: number;
};
center?: {
lat: number;
lng: number;
long: number;
};
defaultMarkers?: Array<MarkerProps>;
markers?: Array<MarkerProps>;