From 20ba6f021b12d1dc150220111c2947fd06d13db2 Mon Sep 17 00:00:00 2001 From: Shivam kumar Date: Thu, 29 Aug 2024 17:04:15 +0530 Subject: [PATCH 1/8] chore:remove space b/w form and CTA onboarding page --- .../formControls/RadioButtonControl.tsx | 59 +++++++++++++++++++ .../Editor/DataSourceEditor/JSONtoForm.tsx | 2 +- .../Editor/SaaSEditor/DatasourceForm.tsx | 22 +++---- .../utils/formControl/FormControlRegistry.tsx | 7 +++ .../src/utils/formControl/formControlTypes.ts | 1 + .../src/main/resources/form.json | 2 +- 6 files changed, 80 insertions(+), 13 deletions(-) create mode 100644 app/client/src/components/formControls/RadioButtonControl.tsx diff --git a/app/client/src/components/formControls/RadioButtonControl.tsx b/app/client/src/components/formControls/RadioButtonControl.tsx new file mode 100644 index 00000000000..7639c7ed9a6 --- /dev/null +++ b/app/client/src/components/formControls/RadioButtonControl.tsx @@ -0,0 +1,59 @@ +import React from "react"; +import type { ControlProps } from "./BaseControl"; +import BaseControl from "./BaseControl"; +import type { ControlType } from "constants/PropertyControlConstants"; +import type { WrappedFieldInputProps, WrappedFieldMetaProps } from "redux-form"; +import { Field } from "redux-form"; +import { Radio, RadioGroup, type SelectOptionProps } from "@appsmith/ads"; + +class RadioButtonControl extends BaseControl { + getControlType(): ControlType { + return "RADIO_BUTTON"; + } + render() { + return ( + + ); + } +} + +type renderComponentProps = RadioButtonControlProps & { + input?: WrappedFieldInputProps; + meta?: WrappedFieldMetaProps; + options?: Array<{ label: string; value: string }>; +}; + +function renderComponent(props: renderComponentProps) { + const onChangeHandler = (value: string) => { + props.input && props.input.onChange && props.input.onChange(value); + }; + + const options = props.options || []; + const defaultValue = props.initialValue as string; + + return ( + + {options.map((option) => { + return ( + + {option.label} + + ); + })} + + ); +} +export interface RadioButtonControlProps extends ControlProps { + options: SelectOptionProps[]; +} + +export default RadioButtonControl; diff --git a/app/client/src/pages/Editor/DataSourceEditor/JSONtoForm.tsx b/app/client/src/pages/Editor/DataSourceEditor/JSONtoForm.tsx index 5acada00fd0..04858755091 100644 --- a/app/client/src/pages/Editor/DataSourceEditor/JSONtoForm.tsx +++ b/app/client/src/pages/Editor/DataSourceEditor/JSONtoForm.tsx @@ -12,7 +12,7 @@ import type { FeatureFlags } from "ee/entities/FeatureFlag"; export const FormContainer = styled.div` display: flex; position: relative; - height: 100%; + height: 55vh; overflow: hidden; flex: 1; flex-direction: column; diff --git a/app/client/src/pages/Editor/SaaSEditor/DatasourceForm.tsx b/app/client/src/pages/Editor/SaaSEditor/DatasourceForm.tsx index 3acf9dc6945..0399075364e 100644 --- a/app/client/src/pages/Editor/SaaSEditor/DatasourceForm.tsx +++ b/app/client/src/pages/Editor/SaaSEditor/DatasourceForm.tsx @@ -649,17 +649,6 @@ class DatasourceSaaSEditor extends JSONtoForm { > {(!viewMode || createFlow || isInsideReconnectModal) && ( <> - {/* This adds information banner when creating google sheets datasource, - this info banner explains why appsmith requires permissions from users google account */} - {datasource && isGoogleSheetPlugin && createFlow ? ( - - ) : null} {/* This adds error banner for google sheets datasource if the datasource is unauthorised */} {datasource && isGoogleSheetPlugin && @@ -675,6 +664,17 @@ class DatasourceSaaSEditor extends JSONtoForm { ? map(sections, this.renderMainSection) : null} {""} + {/* This adds information banner when creating google sheets datasource, + this info banner explains why appsmith requires permissions from users google account */} + {datasource && isGoogleSheetPlugin && createFlow ? ( + + ) : null} )} {viewMode && diff --git a/app/client/src/utils/formControl/FormControlRegistry.tsx b/app/client/src/utils/formControl/FormControlRegistry.tsx index 90fe1623630..0779695fbdd 100644 --- a/app/client/src/utils/formControl/FormControlRegistry.tsx +++ b/app/client/src/utils/formControl/FormControlRegistry.tsx @@ -35,6 +35,8 @@ import FormTemplateControl from "components/formControls/FormTemplateControl"; import type { FormTemplateControlProps } from "components/formControls/FormTemplateControl"; import MultiFilePickerControl from "components/formControls/MultiFilePickerControl"; import type { MultipleFilePickerControlProps } from "components/formControls/MultiFilePickerControl"; +import type { RadioButtonControlProps } from "components/formControls/RadioButtonControl"; +import RadioButtonControl from "components/formControls/RadioButtonControl"; /** * NOTE: If you are adding a component that uses FormControl @@ -183,6 +185,11 @@ class FormControlRegistry { }, }, ); + FormControlFactory.registerControlBuilder(formControlTypes.RADIO_BUTTON, { + buildPropertyControl(controlProps: RadioButtonControlProps): JSX.Element { + return ; + }, + }); } } diff --git a/app/client/src/utils/formControl/formControlTypes.ts b/app/client/src/utils/formControl/formControlTypes.ts index 053f0d6f855..86242a50bdf 100644 --- a/app/client/src/utils/formControl/formControlTypes.ts +++ b/app/client/src/utils/formControl/formControlTypes.ts @@ -18,4 +18,5 @@ export default { PROJECTION: "PROJECTION", FORM_TEMPLATE: "FORM_TEMPLATE", MULTIPLE_FILE_PICKER: "MULTIPLE_FILE_PICKER", + RADIO_BUTTON: "RADIO_BUTTON", }; diff --git a/app/server/appsmith-plugins/googleSheetsPlugin/src/main/resources/form.json b/app/server/appsmith-plugins/googleSheetsPlugin/src/main/resources/form.json index 66447b9f563..f3df69e17bf 100644 --- a/app/server/appsmith-plugins/googleSheetsPlugin/src/main/resources/form.json +++ b/app/server/appsmith-plugins/googleSheetsPlugin/src/main/resources/form.json @@ -33,7 +33,7 @@ { "label": "Permissions | Scope", "configProperty": "datasourceConfiguration.authentication.scopeString", - "controlType": "DROP_DOWN", + "controlType": "RADIO_BUTTON", "options": [ { "label": "Read / Write / Delete | Selected google sheets", From f3d043a04301877c6c1f7bf188c72929a46a7adf Mon Sep 17 00:00:00 2001 From: Shivam kumar Date: Thu, 5 Sep 2024 09:45:19 +0530 Subject: [PATCH 2/8] chore:added RadioButtonControl unit test case --- .../formControls/RadioButtonControl.test.tsx | 110 ++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 app/client/src/components/formControls/RadioButtonControl.test.tsx diff --git a/app/client/src/components/formControls/RadioButtonControl.test.tsx b/app/client/src/components/formControls/RadioButtonControl.test.tsx new file mode 100644 index 00000000000..cf1df437192 --- /dev/null +++ b/app/client/src/components/formControls/RadioButtonControl.test.tsx @@ -0,0 +1,110 @@ +import React from "react"; +import RadioButtonControl from "./RadioButtonControl"; +import { render, waitFor, screen } from "test/testUtils"; +import { Provider } from "react-redux"; +import { reduxForm } from "redux-form"; +import configureStore from "redux-mock-store"; +import store from "store"; +import "@testing-library/jest-dom"; + +const mockStore = configureStore([]); + +function TestForm(props: any) { + return
{props.children}
; +} + +const ReduxFormDecorator = reduxForm({ + form: "TestForm", +})(TestForm); + +const mockOptions = [ + { label: "Option 1", value: "option1", children: "Option 1" }, + { label: "Option 2", value: "option2", children: "Option 2" }, + { label: "Option 3", value: "option3", children: "Option 3" }, +]; + +let radioButtonProps = { + options: mockOptions, + configProperty: "actionConfiguration.testPath", + controlType: "PROJECTION", + label: "Columns", + id: "column", + formName: "", + isValid: true, + initialValue: "option1", +}; + +describe("RadioButtonControl", () => { + beforeEach(() => { + let store: any; + store = mockStore(); + }); + it("should render RadioButtonControl and options properly", async () => { + render( + + + + + , + ); + const radioButton = await waitFor(async () => + screen.getByTestId("actionConfiguration.testPath"), + ); + expect(radioButton).toBeInTheDocument(); + + const options = screen.getAllByRole("radio"); + expect(options).toHaveLength(3); + }); + + it("should show the default selected option", async () => { + radioButtonProps = { + ...radioButtonProps, + }; + + render( + + + + + , + ); + const radioButton = await waitFor(async () => + screen.getByTestId("actionConfiguration.testPath"), + ); + expect(radioButton).toBeInTheDocument(); + + const options = screen.getAllByRole("radio"); + expect(options[0]).toBeChecked(); + expect(options[1]).not.toBeChecked(); + expect(options[2]).not.toBeChecked(); + }); + + it("should select the option when clicked", async () => { + radioButtonProps = { + ...radioButtonProps, + }; + + render( + + + + + , + ); + const radioButton = await waitFor(async () => + screen.getByTestId("actionConfiguration.testPath"), + ); + expect(radioButton).toBeInTheDocument(); + + const options = screen.getAllByRole("radio"); + expect(options[0]).toBeChecked(); + expect(options[1]).not.toBeChecked(); + expect(options[2]).not.toBeChecked(); + + options[1].click(); + + expect(options[0]).not.toBeChecked(); + expect(options[1]).toBeChecked(); + expect(options[2]).not.toBeChecked(); + }); +}); From 230a8a7aa4d2c65fcc4f9c971d7d6cd890c2f06c Mon Sep 17 00:00:00 2001 From: Shivam kumar Date: Thu, 19 Sep 2024 17:23:38 +0530 Subject: [PATCH 3/8] fix/removed hardcoded height value and adjusted spacing --- .../formControls/RadioButtonControl.tsx | 20 ++++++++++++++----- .../Editor/DataSourceEditor/JSONtoForm.tsx | 2 +- .../pages/Editor/DataSourceEditor/index.tsx | 1 - 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/app/client/src/components/formControls/RadioButtonControl.tsx b/app/client/src/components/formControls/RadioButtonControl.tsx index 7639c7ed9a6..d6e21dac24c 100644 --- a/app/client/src/components/formControls/RadioButtonControl.tsx +++ b/app/client/src/components/formControls/RadioButtonControl.tsx @@ -5,6 +5,7 @@ import type { ControlType } from "constants/PropertyControlConstants"; import type { WrappedFieldInputProps, WrappedFieldMetaProps } from "redux-form"; import { Field } from "redux-form"; import { Radio, RadioGroup, type SelectOptionProps } from "@appsmith/ads"; +import styled from "styled-components"; class RadioButtonControl extends BaseControl { getControlType(): ControlType { @@ -28,17 +29,26 @@ type renderComponentProps = RadioButtonControlProps & { options?: Array<{ label: string; value: string }>; }; +const StyledRadioGroup = styled(RadioGroup)({ + display: "flex", + flexDirection: "column", + gap: "16px", + marginTop: "16px", +}); + function renderComponent(props: renderComponentProps) { - const onChangeHandler = (value: string) => { - props.input && props.input.onChange && props.input.onChange(value); + const onChangeHandler = (value: string): any => { + if (typeof props.input?.onChange === "function") { + props.input.onChange(value); + } }; const options = props.options || []; const defaultValue = props.initialValue as string; return ( - @@ -49,7 +59,7 @@ function renderComponent(props: renderComponentProps) { ); })} - + ); } export interface RadioButtonControlProps extends ControlProps { diff --git a/app/client/src/pages/Editor/DataSourceEditor/JSONtoForm.tsx b/app/client/src/pages/Editor/DataSourceEditor/JSONtoForm.tsx index 04858755091..8f026590aae 100644 --- a/app/client/src/pages/Editor/DataSourceEditor/JSONtoForm.tsx +++ b/app/client/src/pages/Editor/DataSourceEditor/JSONtoForm.tsx @@ -12,7 +12,7 @@ import type { FeatureFlags } from "ee/entities/FeatureFlag"; export const FormContainer = styled.div` display: flex; position: relative; - height: 55vh; + height 100%; overflow: hidden; flex: 1; flex-direction: column; diff --git a/app/client/src/pages/Editor/DataSourceEditor/index.tsx b/app/client/src/pages/Editor/DataSourceEditor/index.tsx index ea66049160e..067dc64255f 100644 --- a/app/client/src/pages/Editor/DataSourceEditor/index.tsx +++ b/app/client/src/pages/Editor/DataSourceEditor/index.tsx @@ -162,7 +162,6 @@ type Props = ReduxStateProps & }>; export const DSEditorWrapper = styled.div` - height: calc(100vh - ${(props) => props.theme.headerHeight}); overflow: hidden; display: flex; flex-direction: row; From 61fcaa26786f76d3b281f1526dd1c5fc2a9129f2 Mon Sep 17 00:00:00 2001 From: Shivam kumar Date: Fri, 20 Sep 2024 15:22:56 +0530 Subject: [PATCH 4/8] Update JSONtoForm.tsx chore/refactored missed colone in FormContainer height property --- app/client/src/pages/Editor/DataSourceEditor/JSONtoForm.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/client/src/pages/Editor/DataSourceEditor/JSONtoForm.tsx b/app/client/src/pages/Editor/DataSourceEditor/JSONtoForm.tsx index 8f026590aae..5acada00fd0 100644 --- a/app/client/src/pages/Editor/DataSourceEditor/JSONtoForm.tsx +++ b/app/client/src/pages/Editor/DataSourceEditor/JSONtoForm.tsx @@ -12,7 +12,7 @@ import type { FeatureFlags } from "ee/entities/FeatureFlag"; export const FormContainer = styled.div` display: flex; position: relative; - height 100%; + height: 100%; overflow: hidden; flex: 1; flex-direction: column; From e5f4baba6b317f43eb54a61a89548b6b19230310 Mon Sep 17 00:00:00 2001 From: Shivam kumar Date: Mon, 30 Sep 2024 09:00:49 +0530 Subject: [PATCH 5/8] chore: revert back the height value for DSEditorWrapper, which was leading for cypress failures. --- app/client/src/pages/Editor/DataSourceEditor/index.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/app/client/src/pages/Editor/DataSourceEditor/index.tsx b/app/client/src/pages/Editor/DataSourceEditor/index.tsx index 8d0d603fc4f..abb3af29531 100644 --- a/app/client/src/pages/Editor/DataSourceEditor/index.tsx +++ b/app/client/src/pages/Editor/DataSourceEditor/index.tsx @@ -162,6 +162,7 @@ type Props = ReduxStateProps & }>; export const DSEditorWrapper = styled.div` + height: calc(100vh - ${(props) => props.theme.headerHeight}); overflow: hidden; display: flex; flex-direction: row; From 4e7ef9c543e1477cb66d3f6dfd9cb6ef465fce98 Mon Sep 17 00:00:00 2001 From: Shivam kumar Date: Tue, 1 Oct 2024 22:53:25 +0530 Subject: [PATCH 6/8] fix:replaced dropdown class with radio group in gsScopeOptions --- app/client/cypress/support/Pages/DataSources.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/client/cypress/support/Pages/DataSources.ts b/app/client/cypress/support/Pages/DataSources.ts index 35777482759..a961030d59f 100644 --- a/app/client/cypress/support/Pages/DataSources.ts +++ b/app/client/cypress/support/Pages/DataSources.ts @@ -194,7 +194,7 @@ export class DataSources { _globalSearchInput = ".t--global-search-input"; _gsScopeDropdown = "[data-testid^='datasourceStorages.'][data-testid$='.datasourceConfiguration.authentication.scopeString']"; - _gsScopeOptions = ".ads-v2-select__dropdown .rc-select-item-option"; + _gsScopeOptions = ".ads-v2-radio-group"; _queryTimeout = "//input[@name='actionConfiguration.timeoutInMillisecond']"; _getStructureReq = "/api/v1/datasources/*/structure?ignoreCache=true"; _editDatasourceFromActiveTab = (dsName: string) => From af42b63c033eae744690e9e180c0f0b5ac11b8e9 Mon Sep 17 00:00:00 2001 From: Shivam kumar Date: Mon, 7 Oct 2024 09:56:40 +0530 Subject: [PATCH 7/8] fix:client linting errors --- .../formControls/RadioButtonControl.test.tsx | 24 ++++++++++++------- .../formControls/RadioButtonControl.tsx | 3 ++- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/app/client/src/components/formControls/RadioButtonControl.test.tsx b/app/client/src/components/formControls/RadioButtonControl.test.tsx index cf1df437192..e67e89551d0 100644 --- a/app/client/src/components/formControls/RadioButtonControl.test.tsx +++ b/app/client/src/components/formControls/RadioButtonControl.test.tsx @@ -4,11 +4,11 @@ import { render, waitFor, screen } from "test/testUtils"; import { Provider } from "react-redux"; import { reduxForm } from "redux-form"; import configureStore from "redux-mock-store"; -import store from "store"; import "@testing-library/jest-dom"; const mockStore = configureStore([]); +// eslint-disable-next-line @typescript-eslint/no-explicit-any function TestForm(props: any) { return
{props.children}
; } @@ -35,8 +35,10 @@ let radioButtonProps = { }; describe("RadioButtonControl", () => { + const mockStoreInstance = mockStore(); + let store: typeof mockStoreInstance; + beforeEach(() => { - let store: any; store = mockStore(); }); it("should render RadioButtonControl and options properly", async () => { @@ -50,9 +52,11 @@ describe("RadioButtonControl", () => { const radioButton = await waitFor(async () => screen.getByTestId("actionConfiguration.testPath"), ); + expect(radioButton).toBeInTheDocument(); const options = screen.getAllByRole("radio"); + expect(options).toHaveLength(3); }); @@ -68,12 +72,14 @@ describe("RadioButtonControl", () => { , ); - const radioButton = await waitFor(async () => - screen.getByTestId("actionConfiguration.testPath"), - ); + const radioButton = (await screen.findByTestId( + "actionConfiguration.testPath", + )) as HTMLElement; + expect(radioButton).toBeInTheDocument(); const options = screen.getAllByRole("radio"); + expect(options[0]).toBeChecked(); expect(options[1]).not.toBeChecked(); expect(options[2]).not.toBeChecked(); @@ -91,12 +97,14 @@ describe("RadioButtonControl", () => { , ); - const radioButton = await waitFor(async () => - screen.getByTestId("actionConfiguration.testPath"), - ); + const radioButton = (await screen.findByTestId( + "actionConfiguration.testPath", + )) as HTMLElement; + expect(radioButton).toBeInTheDocument(); const options = screen.getAllByRole("radio"); + expect(options[0]).toBeChecked(); expect(options[1]).not.toBeChecked(); expect(options[2]).not.toBeChecked(); diff --git a/app/client/src/components/formControls/RadioButtonControl.tsx b/app/client/src/components/formControls/RadioButtonControl.tsx index d6e21dac24c..ecee054f58c 100644 --- a/app/client/src/components/formControls/RadioButtonControl.tsx +++ b/app/client/src/components/formControls/RadioButtonControl.tsx @@ -37,7 +37,7 @@ const StyledRadioGroup = styled(RadioGroup)({ }); function renderComponent(props: renderComponentProps) { - const onChangeHandler = (value: string): any => { + const onChangeHandler = (value: string) => { if (typeof props.input?.onChange === "function") { props.input.onChange(value); } @@ -62,6 +62,7 @@ function renderComponent(props: renderComponentProps) { ); } + export interface RadioButtonControlProps extends ControlProps { options: SelectOptionProps[]; } From 2b47472554a56efaf8602fb1d1891d5f7806fa04 Mon Sep 17 00:00:00 2001 From: Shivam kumar Date: Tue, 8 Oct 2024 09:52:35 +0530 Subject: [PATCH 8/8] fix: linting error in radioButtonControl test --- .../components/formControls/RadioButtonControl.test.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/client/src/components/formControls/RadioButtonControl.test.tsx b/app/client/src/components/formControls/RadioButtonControl.test.tsx index e67e89551d0..03077c379b1 100644 --- a/app/client/src/components/formControls/RadioButtonControl.test.tsx +++ b/app/client/src/components/formControls/RadioButtonControl.test.tsx @@ -1,6 +1,6 @@ import React from "react"; import RadioButtonControl from "./RadioButtonControl"; -import { render, waitFor, screen } from "test/testUtils"; +import { render, screen } from "test/testUtils"; import { Provider } from "react-redux"; import { reduxForm } from "redux-form"; import configureStore from "redux-mock-store"; @@ -49,9 +49,9 @@ describe("RadioButtonControl", () => { , ); - const radioButton = await waitFor(async () => - screen.getByTestId("actionConfiguration.testPath"), - ); + const radioButton = (await screen.findByTestId( + "actionConfiguration.testPath", + )) as HTMLElement; expect(radioButton).toBeInTheDocument();