From 739ba35dc1ab02f5ff17bf284dedfa0a6085d97f Mon Sep 17 00:00:00 2001 From: Nikhil Nandagopal Date: Wed, 15 Apr 2020 11:42:11 +0000 Subject: [PATCH] Feature/widgets --- app/client/package.json | 1 + app/client/src/actions/pageActions.tsx | 1 + .../src/assets/icons/control/decrease.svg | 3 + .../src/assets/icons/control/draggable.svg | 4 + .../src/assets/icons/control/increase.svg | 3 + .../assets/icons/control/pick-my-location.svg | 1 + app/client/src/assets/icons/widget/map.svg | 3 + .../designSystems/appsmith/ChartComponent.tsx | 184 ++++++++++++--- .../appsmith/DraggableListComponent.tsx | 91 ++++++++ .../designSystems/appsmith/MapComponent.tsx | 159 +++++++++++++ .../appsmith/RichTextEditorComponent.tsx | 2 +- .../designSystems/appsmith/StepComponent.tsx | 95 ++++++++ .../designSystems/appsmith/TabsComponent.tsx | 80 +++++++ .../blueprint/DatePickerComponent.tsx | 62 ++--- .../propertyControls/ChartDataControl.tsx | 210 +++++++++++++++++ .../propertyControls/DatePickerControl.tsx | 194 ++++++++++++++-- .../LocationSearchControl.tsx | 95 ++++++++ .../propertyControls/StepControl.tsx | 55 +++++ .../propertyControls/StyledControls.tsx | 12 +- .../propertyControls/TabControl.tsx | 180 +++++++++++++++ .../src/components/propertyControls/index.ts | 16 +- app/client/src/constants/ActionConstants.tsx | 2 + app/client/src/constants/DefaultTheme.tsx | 3 + app/client/src/constants/WidgetConstants.tsx | 4 + app/client/src/constants/WidgetValidation.ts | 1 + app/client/src/constants/messages.ts | 3 + app/client/src/icons/ControlIcons.tsx | 25 +++ app/client/src/icons/WidgetIcons.tsx | 12 + .../PropertyPaneConfigResponse.tsx | 211 ++++++++++-------- .../mockResponses/WidgetConfigResponse.tsx | 120 +++++++--- .../mockResponses/WidgetSidebarResponse.tsx | 10 + .../entityReducers/widgetConfigReducer.tsx | 8 + app/client/src/sagas/ModalSagas.ts | 1 + app/client/src/sagas/WidgetOperationSagas.tsx | 2 - app/client/src/utils/Validators.ts | 17 ++ app/client/src/utils/WidgetPropsUtils.tsx | 1 - app/client/src/utils/WidgetRegistry.tsx | 30 +++ app/client/src/widgets/BaseWidget.tsx | 9 + app/client/src/widgets/ChartWidget.tsx | 10 +- app/client/src/widgets/DatePickerWidget.tsx | 54 +++-- app/client/src/widgets/MapWidget.tsx | 151 +++++++++++++ .../src/widgets/RichTextEditorWidget.tsx | 18 +- app/client/src/widgets/TabsWidget.tsx | 196 ++++++++++++++++ app/client/yarn.lock | 72 ++++-- 44 files changed, 2145 insertions(+), 266 deletions(-) create mode 100644 app/client/src/assets/icons/control/decrease.svg create mode 100644 app/client/src/assets/icons/control/draggable.svg create mode 100644 app/client/src/assets/icons/control/increase.svg create mode 100644 app/client/src/assets/icons/control/pick-my-location.svg create mode 100755 app/client/src/assets/icons/widget/map.svg create mode 100644 app/client/src/components/designSystems/appsmith/DraggableListComponent.tsx create mode 100644 app/client/src/components/designSystems/appsmith/MapComponent.tsx create mode 100644 app/client/src/components/designSystems/appsmith/StepComponent.tsx create mode 100644 app/client/src/components/designSystems/appsmith/TabsComponent.tsx create mode 100644 app/client/src/components/propertyControls/ChartDataControl.tsx create mode 100644 app/client/src/components/propertyControls/LocationSearchControl.tsx create mode 100644 app/client/src/components/propertyControls/StepControl.tsx create mode 100644 app/client/src/components/propertyControls/TabControl.tsx create mode 100644 app/client/src/widgets/MapWidget.tsx create mode 100644 app/client/src/widgets/TabsWidget.tsx diff --git a/app/client/package.json b/app/client/package.json index f183c7a20c..66352f93f3 100644 --- a/app/client/package.json +++ b/app/client/package.json @@ -73,6 +73,7 @@ "react-dnd-html5-backend": "^9.3.4", "react-dnd-touch-backend": "^9.4.0", "react-dom": "^16.7.0", + "react-google-maps": "^9.4.5", "react-helmet": "^5.2.1", "react-infinite-scroller": "^1.2.4", "react-json-view": "^1.19.1", diff --git a/app/client/src/actions/pageActions.tsx b/app/client/src/actions/pageActions.tsx index 1310b45c07..ac3fa64f50 100644 --- a/app/client/src/actions/pageActions.tsx +++ b/app/client/src/actions/pageActions.tsx @@ -101,6 +101,7 @@ export type WidgetAddChild = { parentRowSpace: number; parentColumnSpace: number; newWidgetId: string; + tabId: string; props?: Record; }; diff --git a/app/client/src/assets/icons/control/decrease.svg b/app/client/src/assets/icons/control/decrease.svg new file mode 100644 index 0000000000..f8ffdccb6e --- /dev/null +++ b/app/client/src/assets/icons/control/decrease.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/client/src/assets/icons/control/draggable.svg b/app/client/src/assets/icons/control/draggable.svg new file mode 100644 index 0000000000..4be5a7b6d6 --- /dev/null +++ b/app/client/src/assets/icons/control/draggable.svg @@ -0,0 +1,4 @@ + + + + diff --git a/app/client/src/assets/icons/control/increase.svg b/app/client/src/assets/icons/control/increase.svg new file mode 100644 index 0000000000..e20986e3e8 --- /dev/null +++ b/app/client/src/assets/icons/control/increase.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/client/src/assets/icons/control/pick-my-location.svg b/app/client/src/assets/icons/control/pick-my-location.svg new file mode 100644 index 0000000000..e519a17dd3 --- /dev/null +++ b/app/client/src/assets/icons/control/pick-my-location.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/client/src/assets/icons/widget/map.svg b/app/client/src/assets/icons/widget/map.svg new file mode 100755 index 0000000000..61c7b73472 --- /dev/null +++ b/app/client/src/assets/icons/widget/map.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/client/src/components/designSystems/appsmith/ChartComponent.tsx b/app/client/src/components/designSystems/appsmith/ChartComponent.tsx index db0bb4c45a..c476318a62 100644 --- a/app/client/src/components/designSystems/appsmith/ChartComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/ChartComponent.tsx @@ -1,5 +1,5 @@ import React from "react"; -import { ChartType, ChartData } from "widgets/ChartWidget"; +import { ChartType, ChartData, ChartDataPoint } from "widgets/ChartWidget"; import styled from "styled-components"; import { invisible } from "constants/DefaultTheme"; import _ from "lodash"; @@ -18,6 +18,7 @@ export interface ChartComponentProps { chartName: string; widgetId: string; isVisible?: boolean; + allowHorizontalScroll: boolean; } const CanvasContainer = styled.div` @@ -29,29 +30,49 @@ const CanvasContainer = styled.div` box-shadow: 0 1px 1px 0 rgba(60,75,100,.14),0 2px 1px -1px rgba(60,75,100,.12),0 1px 3px 0 rgba(60,75,100,.2); position: relative; ${props => (!props.isVisible ? invisible : "")}; + padding: 10px 0 0 0; }`; class ChartComponent extends React.Component { chartInstance = new FusionCharts(); - getChartType = (chartType: ChartType) => { + getChartType = () => { + const { chartType, allowHorizontalScroll, chartData } = this.props; + const isMSChart = chartData.length > 1; switch (chartType) { - case "LINE_CHART": - return "line"; - case "BAR_CHART": - return "bar2d"; case "PIE_CHART": return "pie2d"; + case "LINE_CHART": + return allowHorizontalScroll + ? "scrollline2d" + : isMSChart + ? "msline" + : "line"; + case "BAR_CHART": + return allowHorizontalScroll + ? "scrollBar2D" + : isMSChart + ? "msbar2d" + : "bar2d"; case "COLUMN_CHART": - return "column2d"; + return allowHorizontalScroll + ? "scrollColumn2D" + : isMSChart + ? "mscolumn2d" + : "column2d"; case "AREA_CHART": - return "area2d"; + return allowHorizontalScroll + ? "scrollarea2d" + : isMSChart + ? "msarea" + : "area2d"; default: - return "column2d"; + return allowHorizontalScroll ? "scrollColumn2D" : "mscolumn2d"; } }; getChartData = (chartData: ChartData[]) => { - return chartData.map(item => { + const data: ChartDataPoint[] = chartData[0].data; + return data.map(item => { return { label: item.x, value: item.y, @@ -59,26 +80,123 @@ class ChartComponent extends React.Component { }); }; + getChartCategoriesMutliSeries = (chartData: ChartData[]) => { + const categories: string[] = []; + for (let index = 0; index < chartData.length; index++) { + const data: ChartDataPoint[] = chartData[index].data; + for (let dataIndex = 0; dataIndex < data.length; dataIndex++) { + const category = data[dataIndex].x; + if (!categories.includes(category)) { + categories.push(category); + } + } + } + return categories; + }; + + getChartCategories = (chartData: ChartData[]) => { + const categories: string[] = this.getChartCategoriesMutliSeries(chartData); + return categories.map(item => { + return { + label: item, + }; + }); + }; + + getSeriesChartData = (data: ChartDataPoint[], categories: string[]) => { + const dataMap: { [key: string]: string } = {}; + for (let index = 0; index < data.length; index++) { + const item: ChartDataPoint = data[index]; + dataMap[item.x] = item.y; + } + return categories.map((category: string) => { + return { + value: dataMap[category] ? dataMap[category] : null, + }; + }); + }; + + getChartDataset = (chartData: ChartData[]) => { + const categories: string[] = this.getChartCategoriesMutliSeries(chartData); + return chartData.map((item: ChartData) => { + const seriesChartData: object[] = this.getSeriesChartData( + item.data, + categories, + ); + return { + seriesName: item.seriesName, + data: seriesChartData, + }; + }); + }; + + getChartConfig = () => { + return { + caption: this.props.chartName, + xAxisName: this.props.xAxisName, + yAxisName: this.props.yAxisName, + theme: "fusion", + captionAlignment: "left", + captionHorizontalPadding: 10, + alignCaptionWithCanvas: 0, + }; + }; + + getChartDataSource = () => { + if ( + this.props.chartData.length === 1 || + this.props.chartType === "PIE_CHART" + ) { + return { + chart: this.getChartConfig(), + data: this.getChartData(this.props.chartData), + }; + } else { + return { + chart: this.getChartConfig(), + categories: [ + { + category: this.getChartCategories(this.props.chartData), + }, + ], + dataset: this.getChartDataset(this.props.chartData), + }; + } + }; + + getScrollChartDataSource = () => { + const chartConfig = this.getChartConfig(); + return { + chart: { + ...chartConfig, + scrollheight: "10", + showvalues: "1", + numVisiblePlot: "5", + flatScrollBars: "1", + }, + categories: [ + { + category: this.getChartCategories(this.props.chartData), + }, + ], + dataset: this.getChartDataset(this.props.chartData), + }; + }; + createGraph = () => { + const dataSource = + this.props.allowHorizontalScroll && this.props.chartType !== "PIE_CHART" + ? this.getScrollChartDataSource() + : this.getChartDataSource(); const chartConfig = { - type: this.getChartType(this.props.chartType), + type: this.getChartType(), renderAt: this.props.widgetId + "chart-container", width: "100%", height: "100%", dataFormat: "json", - dataSource: { - chart: { - caption: this.props.chartName, - xAxisName: this.props.xAxisName, - yAxisName: this.props.yAxisName, - theme: "fusion", - captionAlignment: "left", - captionHorizontalPadding: 10, - alignCaptionWithCanvas: 0, - }, - data: this.getChartData(this.props.chartData), - }, + dataSource: dataSource, }; + console.log("chartConfig", chartConfig); this.chartInstance = new FusionCharts(chartConfig); }; @@ -91,19 +209,15 @@ class ChartComponent extends React.Component { componentDidUpdate(prevProps: ChartComponentProps) { if (!_.isEqual(prevProps, this.props)) { - if (prevProps.chartType !== this.props.chartType) { - const chartType = this.getChartType(this.props.chartType); - this.chartInstance.chartType(chartType); + const chartType = this.getChartType(); + this.chartInstance.chartType(chartType); + if ( + this.props.allowHorizontalScroll && + this.props.chartType !== "PIE_CHART" + ) { + this.chartInstance.setChartData(this.getScrollChartDataSource()); } else { - this.chartInstance.setChartData({ - chart: { - caption: this.props.chartName, - xAxisName: this.props.xAxisName, - yAxisName: this.props.yAxisName, - theme: "fusion", - }, - data: this.getChartData(this.props.chartData), - }); + this.chartInstance.setChartData(this.getChartDataSource()); } } } diff --git a/app/client/src/components/designSystems/appsmith/DraggableListComponent.tsx b/app/client/src/components/designSystems/appsmith/DraggableListComponent.tsx new file mode 100644 index 0000000000..2a3036bcc5 --- /dev/null +++ b/app/client/src/components/designSystems/appsmith/DraggableListComponent.tsx @@ -0,0 +1,91 @@ +import React from "react"; +import { ControlWrapper } from "../../propertyControls/StyledControls"; +import styled from "constants/DefaultTheme"; +import { Droppable, Draggable } from "react-beautiful-dnd"; + +const StyledListWrapper = styled(ControlWrapper)` + display: flex; + justify-content: flex-start; + padding-right: 16px; + margin: 8px 0 0 0; +`; + +type RenderComponentProps = { + index: number; + item: { + label: string; + }; + deleteOption: (index: number) => void; + 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 ( + + {({ innerRef, draggableProps, dragHandleProps }) => ( + } + style={{ + ...draggableProps.style, + userSelect: "none", + position: "static", + }} + > + {renderComponent({ deleteOption, updateOption, item, index })} + + )} + + ); +}; + +type DroppableComponentProps = { + items: object[]; + renderComponent: (props: RenderComponentProps) => JSX.Element; + deleteOption: (index: number) => void; + updateOption: (index: number, value: string) => void; +}; + +export const DroppableComponent = (props: DroppableComponentProps) => { + const { items } = props; + return ( + + {({ innerRef, droppableProps, placeholder }) => ( +
} {...droppableProps}> + {items.map((item: { id: string } & any, index: number) => { + return ( + + ); + })} +
+ )} +
+ ); +}; diff --git a/app/client/src/components/designSystems/appsmith/MapComponent.tsx b/app/client/src/components/designSystems/appsmith/MapComponent.tsx new file mode 100644 index 0000000000..34df7e16a5 --- /dev/null +++ b/app/client/src/components/designSystems/appsmith/MapComponent.tsx @@ -0,0 +1,159 @@ +import React from "react"; +import { + withScriptjs, + withGoogleMap, + GoogleMap, + Marker, +} from "react-google-maps"; +import SearchBox from "react-google-maps/lib/components/places/SearchBox"; +import { MarkerProps } from "widgets/MapWidget"; +import { ControlIcons } from "icons/ControlIcons"; +import styled, { AnyStyledComponent } from "styled-components"; + +interface MapComponentProps { + widgetId: string; + isDisabled?: boolean; + isVisible?: boolean; + enableSearch: boolean; + zoomLevel: number; + enablePickLocation: boolean; + allowZoom: boolean; + center: { + lat: number; + lng: number; + }; + markers?: Array; + enableCreateMarker: boolean; + updateCenter: (lat: number, lng: number) => void; + saveMarker: (lat: number, lng: number) => void; + selectMarker: (lat: number, lng: number, title: string) => void; +} + +const StyledInput = styled.input` + box-sizing: border-box; + border: 1px solid transparent; + width: 240px; + height: 32px; + margin-top: 27px; + padding: 0 12px; + border-radius: 3px; + box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3); + font-size: 14px; + outline: none; + text-overflow: ellipses; +`; +type PickMyLocationProps = { + allowZoom: boolean; +}; + +const PickMyLocationWrapper = styled.div` + position: absolute; + bottom: ${props => (props.allowZoom ? 110 : 20)}px; + right: -95px; + width: 140px; +`; + +const StyledPickMyLocationIcon = styled( + ControlIcons.PICK_MY_LOCATION_CONTROL as AnyStyledComponent, +)` + position: relative; + cursor: pointer; +`; + +const MyMapComponent = withScriptjs( + withGoogleMap((props: any) => ( + { + if (props.enableCreateMarker) { + props.saveMarker(e.latLng.lat(), e.latLng.lng()); + } + }} + > + {props.enableSearch && ( + + + + )} + {props.markers.map((marker: any, index: number) => ( + { + props.selectMarker(marker.lat, marker.lng, marker.title); + }} + /> + ))} + {props.enablePickLocation && ( + + + + )} + + )), +); + +class MapComponent extends React.Component { + private searchBox = React.createRef(); + + onSearchBoxMounted = (ref: any) => { + this.searchBox = ref; + }; + onPlacesChanged = () => { + const node: any = this.searchBox; + if (node) { + const places: any = node.getPlaces(); + const location = places[0].geometry.location; + const lat = location.lat(); + const lng = location.lng(); + this.props.updateCenter(lat, lng); + } + }; + + getUserLocation = () => { + if ("geolocation" in navigator) { + return navigator.geolocation.getCurrentPosition(data => { + const { + coords: { latitude: lat, longitude: lng }, + } = data; + this.props.saveMarker(lat, lng); + }); + } + }; + + render() { + const zoom = Math.floor(this.props.zoomLevel / 5); + return ( + } + containerElement={
} + mapElement={
} + {...this.props} + zoom={zoom} + onPlacesChanged={this.onPlacesChanged} + onSearchBoxMounted={this.onSearchBoxMounted} + getUserLocation={this.getUserLocation} + /> + ); + } +} + +export default MapComponent; diff --git a/app/client/src/components/designSystems/appsmith/RichTextEditorComponent.tsx b/app/client/src/components/designSystems/appsmith/RichTextEditorComponent.tsx index f4aa3ede2a..0eb86a0f75 100644 --- a/app/client/src/components/designSystems/appsmith/RichTextEditorComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/RichTextEditorComponent.tsx @@ -41,7 +41,7 @@ export const RichtextEditorComponent = ( "insertdatetime media table paste code help", ], toolbar: - "undo redo | formatselect | bold italic backcolor | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | removeformat | help", + "undo redo | formatselect | bold italic backcolor forecolor | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | removeformat | help", }} onEditorChange={(content: any) => props.onValueChange(content)} /> diff --git a/app/client/src/components/designSystems/appsmith/StepComponent.tsx b/app/client/src/components/designSystems/appsmith/StepComponent.tsx new file mode 100644 index 0000000000..5c5b1237ba --- /dev/null +++ b/app/client/src/components/designSystems/appsmith/StepComponent.tsx @@ -0,0 +1,95 @@ +import React from "react"; +import { ControlIcons } from "icons/ControlIcons"; +import { AnyStyledComponent } from "styled-components"; +import styled from "constants/DefaultTheme"; + +const StyledIncreaseIcon = styled( + ControlIcons.INCREASE_CONTROL as AnyStyledComponent, +)` + display: flex; + justify-content: center; + align-items: center; + position: relative; + cursor: pointer; + width: 40px; + height: 32px; + svg { + path { + fill: ${props => props.theme.colors.paneSectionLabel}; + } + } +`; + +const StyledDecreaseIcon = styled( + ControlIcons.DECREASE_CONTROL as AnyStyledComponent, +)` + display: flex; + justify-content: center; + align-items: center; + position: relative; + cursor: pointer; + width: 40px; + height: 32px; + svg { + path { + fill: ${props => props.theme.colors.paneSectionLabel}; + } + } +`; + +const StepWrapper = styled.div` + display: flex; + align-items: center; + justify-content: center; + width: 100%; + background: #121518; + border-radius: 4px; + height: 32px; + line-height: 32px; +`; + +const InputWrapper = styled.div` + width: calc(100% - 80px); + height: 32px; + line-height: 32px; + background: #23292e; + font-size: 14px; + color: ${props => props.theme.colors.textOnDarkBG}; + text-align: center; + letter-spacing: 1.44px; +`; + +interface StepComponentProps { + value: number; + min: number; + max: number; + steps: number; + displayFormat: (value: number) => string; + onChange: (value: number) => void; +} + +export const StepComponent = (props: StepComponentProps) => { + function decrease() { + if (props.value < props.min) { + return; + } + const value = props.value - props.steps; + props.onChange(value); + } + function increase() { + if (props.value > props.max) { + return; + } + const value = props.value + props.steps; + props.onChange(value); + } + return ( + + + {props.displayFormat(props.value)} + + + ); +}; + +export default StepComponent; diff --git a/app/client/src/components/designSystems/appsmith/TabsComponent.tsx b/app/client/src/components/designSystems/appsmith/TabsComponent.tsx new file mode 100644 index 0000000000..e37e235c85 --- /dev/null +++ b/app/client/src/components/designSystems/appsmith/TabsComponent.tsx @@ -0,0 +1,80 @@ +import React from "react"; +import styled from "styled-components"; + +interface TabsComponentProps { + isVisible?: boolean; + tabs?: Array<{ + id: string; + label: string; + }>; + selectedTabId?: string; + onTabChange: (tabId: string) => void; +} + +const TabsContainer = styled.div` + && { + height: 32px; + width: 100%; + display: flex; + justify-content: flex-start; + align-items: flex-start; + } +`; + +type TabProps = { + selected?: boolean; +}; + +const StyledTab = styled.div` + height: 32px; + border-bottom: 1px solid; + border-color: ${props => props.theme.colors.bodyBG}; + width: 100%; +`; + +const StyledText = styled.div` + white-space: nowrap; + background: ${props => props.theme.colors.builderBodyBG}; + color: ${props => props.theme.colors.menuIconColorInactive}; + font-size: ${props => props.theme.fontSizes[3]}px; + line-height: 32px; + height: 32px; + padding: 0 16px; + cursor: pointer; + box-shadow: ${props => (props.selected ? props.theme.shadows[2] : "")}; + border-bottom: ${props => (props.selected ? "none" : "1px solid")}; + border-color: ${props => props.theme.colors.bodyBG}; + &:hover { + background: ${props => + props.selected + ? props.theme.colors.textOnDarkBG + : props.theme.colors.hover}; + box-shadow: ${props => (props.selected ? "" : props.theme.shadows[3])}; + } +`; + +class TabsComponent extends React.Component { + selectTab = (tab: { id: string; label: string }) => { + this.props.onTabChange(tab.id); + }; + + render() { + return ( + + {this.props.tabs && + this.props.tabs.map((tab, index) => ( + + {tab.label} + + ))} + + + ); + } +} + +export default TabsComponent; diff --git a/app/client/src/components/designSystems/blueprint/DatePickerComponent.tsx b/app/client/src/components/designSystems/blueprint/DatePickerComponent.tsx index 40d4f1ceaf..a788138587 100644 --- a/app/client/src/components/designSystems/blueprint/DatePickerComponent.tsx +++ b/app/client/src/components/designSystems/blueprint/DatePickerComponent.tsx @@ -8,6 +8,7 @@ import moment from "moment-timezone"; import "../../../../node_modules/@blueprintjs/datetime/lib/css/blueprint-datetime.css"; import { DatePickerType } from "widgets/DatePickerWidget"; import { WIDGET_PADDING } from "constants/WidgetConstants"; +import { TimePrecision } from "@blueprintjs/datetime"; import { Colors } from "constants/Colors"; const StyledControlGroup = styled(ControlGroup)` @@ -45,19 +46,34 @@ const StyledControlGroup = styled(ControlGroup)` } &&& { input { - border: 1px solid #a1acb3; - border-radius: 4px; + border: 1px solid ${Colors.HIT_GRAY}; + border-radius: ${props => props.theme.radii[1]}px; box-shadow: none; - color: #2e3d49; - font-size: 14px; + color: ${Colors.OXFORD_BLUE}; + font-size: ${props => props.theme.fontSizes[3]}px; } } `; class DatePickerComponent extends React.Component { render() { + const now = moment(); + const year = now.get("year"); + const month = now.get("month"); + const date = now.get("date"); + const minDate = now.clone().set({ month, date: date - 1, year: year - 20 }); + const maxDate = now.clone().set({ month, date: date + 1, year: year + 20 }); + const selectedDate = new Date( + new Date(this.props.selectedDate || "").getTime() + + this.getOffset(new Date(this.props.selectedDate || "")), + ); return ( - + { + e.stopPropagation(); + }} + > {this.props.label && (