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(); + }); +}); diff --git a/app/client/src/components/formControls/RadioButtonControl.tsx b/app/client/src/components/formControls/RadioButtonControl.tsx new file mode 100644 index 00000000000..d6e21dac24c --- /dev/null +++ b/app/client/src/components/formControls/RadioButtonControl.tsx @@ -0,0 +1,69 @@ +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"; +import styled from "styled-components"; + +class RadioButtonControl extends BaseControl { + getControlType(): ControlType { + return "RADIO_BUTTON"; + } + render() { + return ( + + ); + } +} + +type renderComponentProps = RadioButtonControlProps & { + input?: WrappedFieldInputProps; + meta?: WrappedFieldMetaProps; + 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): any => { + if (typeof props.input?.onChange === "function") { + 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/index.tsx b/app/client/src/pages/Editor/DataSourceEditor/index.tsx index abb3af29531..8d0d603fc4f 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; diff --git a/app/client/src/pages/Editor/SaaSEditor/DatasourceForm.tsx b/app/client/src/pages/Editor/SaaSEditor/DatasourceForm.tsx index 414c920f5aa..94e34ba1c05 100644 --- a/app/client/src/pages/Editor/SaaSEditor/DatasourceForm.tsx +++ b/app/client/src/pages/Editor/SaaSEditor/DatasourceForm.tsx @@ -662,17 +662,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 && @@ -688,6 +677,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 d5f7709c310..8e9fe67783e 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",