chore:remove space b/w form and CTA onboarding page (#35985)
## Description Following are the improvements made in this PR: - Remove the unnecessary space b/w form and CTA in Gsheet onboarding step - Made one new RadioButtonControl in form control and replaced the current dropdown by radio buttons. - Move the callout to after the permissions | scope property. - Limit the width of the white section Fixes #30523 output screenshot:  Desired design:  > > _Please also include relevant motivation and context. List any dependencies that are required for this change. Add links to Notion, Figma or any other documents that might be relevant to the PR._ *Fixes #`35950` _or_ Fixes `Issue URL` > [!WARNING] > _If no issue exists, please create an issue first, and check with the maintainers if the issue is valid._ ## Automation /ok-to-test tags="" ### 🔍 Cypress test results <!-- This is an auto-generated comment: Cypress test results --> > [!CAUTION] > If you modify the content in this section, you are likely to disrupt the CI result for your PR. <!-- end of auto-generated comment: Cypress test results --> ## Communication Should the DevRel and Marketing teams inform users about this change? - [ ] Yes - [ ] No <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Summary by CodeRabbit - **New Features** - Introduced a new `RadioButtonControl` component for improved form control options. - Enhanced the `FormControlRegistry` to support radio button controls. - Updated the Google Sheets plugin to use radio buttons for permission settings. - **UI Changes** - Corrected styling syntax in the `FormContainer` for proper rendering. - Reorganized the display order of information banners in the `DatasourceForm` for better clarity. - **Tests** - Added a comprehensive suite of unit tests for the `RadioButtonControl` component to ensure proper functionality and user interaction. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
parent
8463d02740
commit
ac91339d54
|
|
@ -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) =>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,118 @@
|
|||
import React from "react";
|
||||
import RadioButtonControl from "./RadioButtonControl";
|
||||
import { render, screen } from "test/testUtils";
|
||||
import { Provider } from "react-redux";
|
||||
import { reduxForm } from "redux-form";
|
||||
import configureStore from "redux-mock-store";
|
||||
import "@testing-library/jest-dom";
|
||||
|
||||
const mockStore = configureStore([]);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
function TestForm(props: any) {
|
||||
return <div>{props.children}</div>;
|
||||
}
|
||||
|
||||
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", () => {
|
||||
const mockStoreInstance = mockStore();
|
||||
let store: typeof mockStoreInstance;
|
||||
|
||||
beforeEach(() => {
|
||||
store = mockStore();
|
||||
});
|
||||
it("should render RadioButtonControl and options properly", async () => {
|
||||
render(
|
||||
<Provider store={store}>
|
||||
<ReduxFormDecorator>
|
||||
<RadioButtonControl {...radioButtonProps} />
|
||||
</ReduxFormDecorator>
|
||||
</Provider>,
|
||||
);
|
||||
const radioButton = (await screen.findByTestId(
|
||||
"actionConfiguration.testPath",
|
||||
)) as HTMLElement;
|
||||
|
||||
expect(radioButton).toBeInTheDocument();
|
||||
|
||||
const options = screen.getAllByRole("radio");
|
||||
|
||||
expect(options).toHaveLength(3);
|
||||
});
|
||||
|
||||
it("should show the default selected option", async () => {
|
||||
radioButtonProps = {
|
||||
...radioButtonProps,
|
||||
};
|
||||
|
||||
render(
|
||||
<Provider store={store}>
|
||||
<ReduxFormDecorator>
|
||||
<RadioButtonControl {...radioButtonProps} />
|
||||
</ReduxFormDecorator>
|
||||
</Provider>,
|
||||
);
|
||||
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();
|
||||
});
|
||||
|
||||
it("should select the option when clicked", async () => {
|
||||
radioButtonProps = {
|
||||
...radioButtonProps,
|
||||
};
|
||||
|
||||
render(
|
||||
<Provider store={store}>
|
||||
<ReduxFormDecorator>
|
||||
<RadioButtonControl {...radioButtonProps} />
|
||||
</ReduxFormDecorator>
|
||||
</Provider>,
|
||||
);
|
||||
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();
|
||||
|
||||
options[1].click();
|
||||
|
||||
expect(options[0]).not.toBeChecked();
|
||||
expect(options[1]).toBeChecked();
|
||||
expect(options[2]).not.toBeChecked();
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
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<RadioButtonControlProps> {
|
||||
getControlType(): ControlType {
|
||||
return "RADIO_BUTTON";
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<Field
|
||||
component={renderComponent}
|
||||
name={this.props.configProperty}
|
||||
props={{ ...this.props }}
|
||||
type="radiobutton"
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
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) => {
|
||||
if (typeof props.input?.onChange === "function") {
|
||||
props.input.onChange(value);
|
||||
}
|
||||
};
|
||||
|
||||
const options = props.options || [];
|
||||
const defaultValue = props.initialValue as string;
|
||||
|
||||
return (
|
||||
<StyledRadioGroup
|
||||
data-testid={props.input?.name}
|
||||
defaultValue={defaultValue}
|
||||
onChange={onChangeHandler}
|
||||
>
|
||||
{options.map((option) => {
|
||||
return (
|
||||
<Radio key={option.value} value={option.value}>
|
||||
{option.label}
|
||||
</Radio>
|
||||
);
|
||||
})}
|
||||
</StyledRadioGroup>
|
||||
);
|
||||
}
|
||||
|
||||
export interface RadioButtonControlProps extends ControlProps {
|
||||
options: SelectOptionProps[];
|
||||
}
|
||||
|
||||
export default RadioButtonControl;
|
||||
|
|
@ -662,17 +662,6 @@ class DatasourceSaaSEditor extends JSONtoForm<Props, State> {
|
|||
>
|
||||
{(!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 ? (
|
||||
<AuthMessage
|
||||
actionType={ActionType.DOCUMENTATION}
|
||||
calloutType="info"
|
||||
datasource={datasource}
|
||||
description={googleSheetsInfoMessage}
|
||||
pageId={pageId}
|
||||
/>
|
||||
) : null}
|
||||
{/* This adds error banner for google sheets datasource if the datasource is unauthorised */}
|
||||
{datasource &&
|
||||
isGoogleSheetPlugin &&
|
||||
|
|
@ -688,6 +677,17 @@ class DatasourceSaaSEditor extends JSONtoForm<Props, State> {
|
|||
? 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 ? (
|
||||
<AuthMessage
|
||||
actionType={ActionType.DOCUMENTATION}
|
||||
calloutType="info"
|
||||
datasource={datasource}
|
||||
description={googleSheetsInfoMessage}
|
||||
pageId={pageId}
|
||||
/>
|
||||
) : null}
|
||||
</>
|
||||
)}
|
||||
{viewMode &&
|
||||
|
|
|
|||
|
|
@ -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 <RadioButtonControl {...controlProps} />;
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,4 +18,5 @@ export default {
|
|||
PROJECTION: "PROJECTION",
|
||||
FORM_TEMPLATE: "FORM_TEMPLATE",
|
||||
MULTIPLE_FILE_PICKER: "MULTIPLE_FILE_PICKER",
|
||||
RADIO_BUTTON: "RADIO_BUTTON",
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user