diff --git a/app/client/.gitignore b/app/client/.gitignore index 33be47b5f7..e019741285 100755 --- a/app/client/.gitignore +++ b/app/client/.gitignore @@ -29,4 +29,5 @@ yarn-error.log* .idea .storybook-out/ cypress/videos -results/ \ No newline at end of file +cypress/screenshots +results/ diff --git a/app/client/.gitlab-ci.yml b/app/client/.gitlab-ci.yml index 7d38185c39..ef873328b5 100644 --- a/app/client/.gitlab-ci.yml +++ b/app/client/.gitlab-ci.yml @@ -48,6 +48,7 @@ cypress-test: - echo "127.0.0.1 dev.appsmith.com" >> /etc/hosts - yarn test artifacts: + when: on_failure expire_in: 1 week paths: - build/ @@ -61,7 +62,7 @@ cypress-test: docker-package-release: image: docker:dind services: - - docker:dind + - docker:dind stage: package script: - docker build --build-arg REACT_APP_ENVIRONMENT=STAGING --build-arg GIT_SHA=$CI_COMMIT_SHORT_SHA -t appsmith/appsmith-editor:release . @@ -75,7 +76,7 @@ docker-package-prod: services: - docker:dind stage: package - script: + script: - docker build --build-arg REACT_APP_ENVIRONMENT=PRODUCTION --build-arg GIT_SHA=$CI_COMMIT_SHORT_SHA -t appsmith/appsmith-editor:latest . - docker login -u $DOCKER_HUB_USERNAME -p $DOCKER_HUB_ACCESS_TOKEN - docker push appsmith/appsmith-editor:latest diff --git a/app/client/package.json b/app/client/package.json index c2e6f53719..8873b5b082 100644 --- a/app/client/package.json +++ b/app/client/package.json @@ -44,6 +44,7 @@ "flow-bin": "^0.91.0", "fontfaceobserver": "^2.1.0", "fuse.js": "^3.4.5", + "fusioncharts": "^3.15.0-sr.1", "history": "^4.10.1", "husky": "^3.0.5", "interweave": "^12.1.1", @@ -67,6 +68,7 @@ "react-dnd-html5-backend": "^9.3.4", "react-dnd-touch-backend": "^9.4.0", "react-dom": "^16.7.0", + "react-fusioncharts": "^3.1.2", "react-helmet": "^5.2.1", "react-redux": "^7.1.3", "react-router": "^5.1.2", @@ -157,8 +159,7 @@ }, "husky": { "hooks": { - "pre-commit": "lint-staged", - "pre-push": "yarn run test" + "pre-commit": "lint-staged" } } } diff --git a/app/client/src/api/ActionAPI.tsx b/app/client/src/api/ActionAPI.tsx index 3e22563a25..bbbba58866 100644 --- a/app/client/src/api/ActionAPI.tsx +++ b/app/client/src/api/ActionAPI.tsx @@ -62,12 +62,12 @@ export interface RestAction { cacheResponse?: string; } -export type PaginationField = "PREV" | "NEXT" | undefined; +export type PaginationField = "PREV" | "NEXT"; export interface ExecuteActionRequest extends APIRequest { action: Pick | Omit; params?: Property[]; - paginationField: PaginationField; + paginationField?: PaginationField; } export interface ExecuteActionResponse extends ApiResponse { @@ -80,7 +80,7 @@ export interface ActionApiResponse { data: { body: object; headers: Record; - statusCode: string | number; + statusCode: string; }; clientMeta: { duration: string; @@ -91,7 +91,7 @@ export interface ActionApiResponse { export interface ActionResponse { body: object; headers: Record; - statusCode: string | number; + statusCode: string; duration: string; size: string; } diff --git a/app/client/src/assets/icons/widget/chart.svg b/app/client/src/assets/icons/widget/chart.svg new file mode 100644 index 0000000000..98e609cb6c --- /dev/null +++ b/app/client/src/assets/icons/widget/chart.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 new file mode 100644 index 0000000000..61038eed9c --- /dev/null +++ b/app/client/src/components/designSystems/appsmith/ChartComponent.tsx @@ -0,0 +1,83 @@ +import React from "react"; +import ReactFC from "react-fusioncharts"; +import FusionCharts from "fusioncharts"; +import Column2D from "fusioncharts/fusioncharts.charts"; +import FusionTheme from "fusioncharts/themes/fusioncharts.theme.fusion"; +import { ChartType, ChartData } from "widgets/ChartWidget"; +import styled from "styled-components"; +import { invisible } from "constants/DefaultTheme"; + +ReactFC.fcRoot(FusionCharts, Column2D, FusionTheme); + +export interface ChartComponentProps { + chartType: ChartType; + chartData: ChartData[]; + xAxisName: string; + yAxisName: string; + chartName: string; + componentWidth: number; + componentHeight: number; + isVisible?: boolean; +} + +const CanvasContainer = styled.div` + border: none; + border-radius: ${props => `${props.theme.radii[1]}px`}; + height: 100%; + width: 100%; + background: white; + 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 : "")}; +}`; + +/* eslint-disable react/display-name */ +const ChartComponent = (props: ChartComponentProps) => { + const getChartType = (chartType: ChartType) => { + switch (chartType) { + case "LINE_CHART": + return "line"; + case "BAR_CHART": + return "bar2d"; + case "PIE_CHART": + return "pie2d"; + case "COLUMN_CHART": + return "column2d"; + case "AREA_CHART": + return "area2d"; + default: + return "column2d"; + } + }; + + const getChartData = (chartData: ChartData[]) => { + return chartData.map(item => { + return { + label: item.x, + value: item.y, + }; + }); + }; + + return ( + + + + ); +}; + +export default ChartComponent; diff --git a/app/client/src/components/designSystems/appsmith/FilePickerComponent.tsx b/app/client/src/components/designSystems/appsmith/FilePickerComponent.tsx index 0665f0d672..f46d59f5c2 100644 --- a/app/client/src/components/designSystems/appsmith/FilePickerComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/FilePickerComponent.tsx @@ -32,7 +32,7 @@ class FilePickerComponent extends React.Component< diff --git a/app/client/src/components/designSystems/blueprint/CheckboxComponent.tsx b/app/client/src/components/designSystems/blueprint/CheckboxComponent.tsx index 44c7e7613c..d6238dc53a 100644 --- a/app/client/src/components/designSystems/blueprint/CheckboxComponent.tsx +++ b/app/client/src/components/designSystems/blueprint/CheckboxComponent.tsx @@ -26,22 +26,22 @@ class CheckboxComponent extends React.Component { className={ this.props.isLoading ? "bp3-skeleton" : Classes.RUNNING_TEXT } - defaultChecked={this.props.defaultCheckedState} onChange={this.onCheckChange} disabled={this.props.isDisabled} + checked={this.props.isChecked} /> ); } - onCheckChange = (event: React.ChangeEvent) => { - this.props.onCheckChange(event.target.value === "true"); + onCheckChange = () => { + this.props.onCheckChange(!this.props.isChecked); }; } export interface CheckboxComponentProps extends ComponentProps { label: string; - defaultCheckedState: boolean; + isChecked: boolean; onCheckChange: (isChecked: boolean) => void; isLoading: boolean; } diff --git a/app/client/src/components/designSystems/blueprint/DatePickerComponent.tsx b/app/client/src/components/designSystems/blueprint/DatePickerComponent.tsx index 1b983e774f..0646480710 100644 --- a/app/client/src/components/designSystems/blueprint/DatePickerComponent.tsx +++ b/app/client/src/components/designSystems/blueprint/DatePickerComponent.tsx @@ -8,15 +8,39 @@ 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 { Colors } from "constants/Colors"; const StyledControlGroup = styled(ControlGroup)` &&& { + .${Classes.INPUT} { + box-shadow: none; + border: 1px solid; + border-color: ${Colors.GEYSER_LIGHT}; + border-radius: ${props => props.theme.radii[1]}px; + width: 100%; + height: inherit; + align-items: center; + &:active { + border-color: ${Colors.HIT_GRAY}; + } + &:focus { + border-color: ${Colors.MYSTIC}; + } + } + .${Classes.INPUT_GROUP} { + display: block; + margin: 0; + } + .${Classes.CONTROL_GROUP} { + justify-content: flex-start; + } label { ${labelStyle} flex: 0 1 30%; + margin: 7px ${WIDGET_PADDING * 2}px 0 0; text-align: right; - margin: 0 ${WIDGET_PADDING * 2}px 0 0; - align-self: center; + align-self: flex-start; + max-width: calc(30% - ${WIDGET_PADDING}px); } } `; @@ -47,14 +71,14 @@ class DatePickerComponent extends React.Component { this.props.enableTimePicker ? { useAmPm: true, - value: this.props.selectedDate || this.props.defaultDate, + value: this.props.selectedDate, showArrowButtons: true, } : undefined } closeOnSelection={true} onChange={this.onDateSelected} - value={this.props.selectedDate || this.props.defaultDate} + value={this.props.selectedDate} /> ) : ( { export interface DatePickerComponentProps extends ComponentProps { label: string; - defaultDate?: Date; dateFormat: string; enableTimePicker?: boolean; selectedDate?: Date; diff --git a/app/client/src/components/designSystems/blueprint/DropdownComponent.tsx b/app/client/src/components/designSystems/blueprint/DropdownComponent.tsx index 6a18effe4f..a3072d6113 100644 --- a/app/client/src/components/designSystems/blueprint/DropdownComponent.tsx +++ b/app/client/src/components/designSystems/blueprint/DropdownComponent.tsx @@ -21,6 +21,7 @@ import _ from "lodash"; import { WIDGET_PADDING } from "constants/WidgetConstants"; import "../../../../node_modules/@blueprintjs/select/lib/css/blueprint-select.css"; import styled, { + createGlobalStyle, labelStyle, BlueprintCSSTransform, BlueprintInputTransform, @@ -79,53 +80,61 @@ const StyledControlGroup = styled(ControlGroup)<{ haslabel: string }>` } `; -const DropdownContainer = styled.div` - ${BlueprintCSSTransform} - &&&& .${Classes.MENU_ITEM} { - border-radius: ${props => props.theme.radii[1]}px; - &:hover{ - background: ${Colors.POLAR}; - } - &.${Classes.ACTIVE} { - background: ${Colors.POLAR}; - color: ${props => props.theme.colors.textDefault}; - position:relative; - &.single-select{ - &:before{ - left: 0; - top: -2px; - position: absolute; - content: ""; - background: ${props => props.theme.colors.primary}; - border-radius: 4px 0 0 4px; - width: 4px; - height:100%; - } - } - } -} - && .${Classes.POPOVER} { +const DropdownStyles = createGlobalStyle` + .select-popover-wrapper { width: 100%; border-radius: ${props => props.theme.radii[1]}px; box-shadow: 0px 2px 4px rgba(67, 70, 74, 0.14); padding: ${props => props.theme.spaces[3]}px; background: white; - } - - && .${Classes.POPOVER_WRAPPER} { - position:relative; - .${Classes.OVERLAY} { - position: absolute; - .${Classes.TRANSITION_CONTAINER} { - width: 100%; + && .${Classes.MENU} { + max-width: 100%; + max-height: auto; + } + &&&& .${Classes.MENU_ITEM} { + border-radius: ${props => props.theme.radii[1]}px; + &:hover{ + background: ${Colors.POLAR}; + } + &.${Classes.ACTIVE} { + background: ${Colors.POLAR}; + color: ${props => props.theme.colors.textDefault}; + position:relative; + &.single-select{ + &:before{ + left: 0; + top: -2px; + position: absolute; + content: ""; + background: ${props => props.theme.colors.primary}; + border-radius: 4px 0 0 4px; + width: 4px; + height:100%; + } + } + } + .${Classes.CONTROL} .${Classes.CONTROL_INDICATOR} { + background: white; + box-shadow: none; + border-width: 2px; + border-style: solid; + border-color: ${Colors.GEYSER}; + &::before { + width: auto; + height: 1em; + }& + } + .${Classes.CONTROL} input:checked ~ .${Classes.CONTROL_INDICATOR} { + background: ${props => props.theme.colors.primary}; + color: ${props => props.theme.colors.textOnDarkBG}; + border-color: ${props => props.theme.colors.primary}; } } } - && .${Classes.MENU} { - max-width: 100%; - max-height: auto; - } - width: 100%; +`; + +const DropdownContainer = styled.div` + ${BlueprintCSSTransform} `; const StyledMultiDropDown = styled(MultiDropDown)` @@ -169,24 +178,6 @@ const StyledMultiDropDown = styled(MultiDropDown)` } } } - &&&& { - .${Classes.CONTROL} .${Classes.CONTROL_INDICATOR} { - background: white; - box-shadow: none; - border-width: 2px; - border-style: solid; - border-color: ${Colors.GEYSER}; - &::before { - width: auto; - height: 1em; - } - } - .${Classes.CONTROL} input:checked ~ .${Classes.CONTROL_INDICATOR} { - background: ${props => props.theme.colors.primary}; - color: ${props => props.theme.colors.textOnDarkBG}; - border-color: ${props => props.theme.colors.primary}; - } - } `; const StyledCheckbox = styled(Checkbox)` @@ -204,6 +195,7 @@ class DropDownComponent extends React.Component { : []; return ( + { onItemSelect={this.onItemSelect} popoverProps={{ minimal: true, - usePortal: false, + usePortal: true, + popoverClassName: "select-popover-wrapper", }} >