diff --git a/app/client/cypress/integration/Smoke_TestSuite_Fat/ClientSideTests/EmbedSettings/EmbedSettings_spec.js b/app/client/cypress/integration/Smoke_TestSuite_Fat/ClientSideTests/EmbedSettings/EmbedSettings_spec.js new file mode 100644 index 00000000000..963f9b345f5 --- /dev/null +++ b/app/client/cypress/integration/Smoke_TestSuite_Fat/ClientSideTests/EmbedSettings/EmbedSettings_spec.js @@ -0,0 +1,143 @@ +import { ObjectsRegistry } from "../../../../support/Objects/Registry"; +const adminSettings = require("../../../../locators/AdminsSettings"); + +describe("Embed settings options", function() { + const { + AggregateHelper: agHelper, + DeployMode: deployMode, + EntityExplorer: ee, + HomePage: homePage, + } = ObjectsRegistry; + + const getIframeBody = () => { + // get the iframe > document > body + // and retry until the body element is not empty + return ( + cy + .get(".t--widget-iframewidget iframe") + .its("0.contentDocument.body") + .should("not.be.empty") + // wraps "body" DOM element to allow + // chaining more Cypress commands, like ".find(...)" + // https://on.cypress.io/wrap + .then(cy.wrap) + ); + }; + + before(() => { + ee.DragDropWidgetNVerify("buttonwidget", 100, 100); + deployMode.DeployApp(); + cy.get("[data-cy='viewmode-share']").click(); + cy.get(".t--deployed-url input") + .invoke("attr", "value") + .as("embeddedAppUrl"); + cy.enablePublicAccess(); + cy.get(".t--back-to-home").click(); + homePage.CreateNewApplication(); + ee.DragDropWidgetNVerify("iframewidget", 100, 100); + cy.get("@embeddedAppUrl").then((url) => { + cy.testJsontext("url", url); + }); + // cy.testJsontext("url", this.embeddedAppUrl); + deployMode.DeployApp(); + cy.get("[data-cy='viewmode-share']").click(); + cy.get(".t--deployed-url input") + .invoke("attr", "value") + .as("deployUrl"); + cy.enablePublicAccess(); + cy.wait(6000); + getIframeBody() + .contains("Submit") + .should("exist"); + }); + + beforeEach(() => { + agHelper.RestoreLocalStorageCache(); + }); + + afterEach(() => { + agHelper.SaveLocalStorageCache(); + }); + + describe("Wrapper to get access to the alias in all tests", () => { + it("1. Allow embedding everywhere", function() { + cy.log(this.deployUrl); + cy.get(".t--back-to-home").click(); + cy.get(".admin-settings-menu-option").click(); + cy.get(".t--admin-settings-APPSMITH_ALLOWED_FRAME_ANCESTORS").within( + () => { + cy.get("input") + .eq(0) + .click(); + }, + ); + cy.get(adminSettings.saveButton).click(); + cy.wait(60000); + cy.wait(["@getEnvVariables", "@getEnvVariables"]).then((interception) => { + const { + APPSMITH_ALLOWED_FRAME_ANCESTORS, + } = interception[1].response.body.data; + expect(APPSMITH_ALLOWED_FRAME_ANCESTORS).to.equal("*"); + }); + cy.get(adminSettings.restartNotice).should("not.exist"); + cy.visit(this.deployUrl); + getIframeBody() + .contains("Submit") + .should("exist"); + }); + + it("2. Limit embedding", function() { + cy.log(this.deployUrl); + cy.get(".t--back-to-home").click(); + cy.get(".admin-settings-menu-option").click(); + cy.get(".t--admin-settings-APPSMITH_ALLOWED_FRAME_ANCESTORS").within( + () => { + cy.get("input") + .eq(1) + .click(); + cy.get(".bp3-tag-remove") + .eq(1) + .click(); + cy.get(".bp3-tag-remove") + .eq(0) + .click(); + cy.get(".bp3-input-ghost") + .type(window.location.origin) + .blur(); + }, + ); + cy.get(adminSettings.saveButton).click(); + cy.wait(50000); + cy.get(adminSettings.restartNotice).should("not.exist"); + cy.visit(this.deployUrl); + getIframeBody() + .contains("Submit") + .should("exist"); + }); + it("3. Disable everywhere", function() { + cy.log(this.deployUrl); + cy.get(".t--back-to-home").click(); + cy.get(".admin-settings-menu-option").click(); + cy.get(".t--admin-settings-APPSMITH_ALLOWED_FRAME_ANCESTORS").within( + () => { + cy.get("input") + .last() + .click(); + }, + ); + cy.get(adminSettings.saveButton).click(); + cy.wait(60000); + cy.get(adminSettings.restartNotice).should("not.exist"); + cy.visit(this.deployUrl); + cy.wait(["@getEnvVariables", "@getEnvVariables"]).then((interception) => { + const { + APPSMITH_ALLOWED_FRAME_ANCESTORS, + } = interception[1].response.body.data; + expect(APPSMITH_ALLOWED_FRAME_ANCESTORS).to.equal("'none'"); + }); + getIframeBody() + .contains("Submit") + .should("not.exist"); + }); + }); +}); diff --git a/app/client/cypress/locators/AdminsSettings.js b/app/client/cypress/locators/AdminsSettings.js index ebfc82bd16d..b0bc515d877 100644 --- a/app/client/cypress/locators/AdminsSettings.js +++ b/app/client/cypress/locators/AdminsSettings.js @@ -25,4 +25,5 @@ export default { disconnectBtn: "[data-testid='disconnect-service-button']", formSignupDisabled: "[data-cy='APPSMITH_SIGNUP_DISABLED']", formLoginDisabled: "[data-cy='APPSMITH_FORM_LOGIN_DISABLED']", + embedSettings: ".t--admin-settings-APPSMITH_ALLOWED_FRAME_ANCESTORS", }; diff --git a/app/client/src/ce/pages/AdminSettings/config/general.tsx b/app/client/src/ce/pages/AdminSettings/config/general.tsx index ef7d2e1f8c5..58e5762d24a 100644 --- a/app/client/src/ce/pages/AdminSettings/config/general.tsx +++ b/app/client/src/ce/pages/AdminSettings/config/general.tsx @@ -10,6 +10,10 @@ import { Setting, } from "@appsmith/pages/AdminSettings/config/types"; import BrandingBadge from "pages/AppViewer/BrandingBadge"; +import { TagInput } from "design-system"; +import QuestionFillIcon from "remixicon-react/QuestionFillIcon"; +import localStorage from "utils/localStorage"; +import isUndefined from "lodash/isUndefined"; export const APPSMITH_INSTANCE_NAME_SETTING_SETTING: Setting = { id: "APPSMITH_INSTANCE_NAME", @@ -81,6 +85,83 @@ export const APPSMITH_HIDE_WATERMARK_SETTING: Setting = { "Hello, I would like to upgrade and remove the watermark.", }; +export const APPSMITH_ALLOWED_FRAME_ANCESTORS_SETTING: Setting = { + id: "APPSMITH_ALLOWED_FRAME_ANCESTORS", + name: "APPSMITH_ALLOWED_FRAME_ANCESTORS", + category: SettingCategories.GENERAL, + controlType: SettingTypes.RADIO, + label: "Embed Settings", + controlTypeProps: { + options: [ + { + badge: "NOT RECOMMENDED", + tooltip: { + icon: , + text: + "Lets all domains, including malicious ones, embed your Appsmith apps. ", + linkText: "SEE WHY THIS IS RISKY", + link: + "https://docs.appsmith.com/getting-started/setup/instance-configuration/frame-ancestors#why-should-i-control-this", + }, + label: "Allow embedding everywhere", + value: "ALLOW_EMBEDDING_EVERYWHERE", + }, + { + label: "Limit embedding to certain URLs", + value: "LIMIT_EMBEDDING", + nodeLabel: "You can add one or more URLs", + node: , + nodeInputPath: "input", + nodeParentClass: "tag-input", + }, + { + label: "Disable embedding everywhere", + value: "DISABLE_EMBEDDING_EVERYWHERE", + }, + ], + }, + format: (value: string) => { + if (value === "*") { + return { + value: "ALLOW_EMBEDDING_EVERYWHERE", + }; + } else if (value === "'none'") { + return { + value: "DISABLE_EMBEDDING_EVERYWHERE", + }; + } else { + return { + value: "LIMIT_EMBEDDING", + additionalData: value ? value.replaceAll(" ", ",") : "", + }; + } + }, + parse: (value: { value: string; additionalData?: any }) => { + // Retrieve values from local storage while switching to limit by url option + const sources = isUndefined(value.additionalData) + ? localStorage.getItem("ALLOWED_FRAME_ANCESTORS") ?? "" + : value.additionalData.replaceAll(",", " "); + // If they are one of the other options we don't store it in storage since it will + // set in the env variable on save + if (sources !== "*" && sources !== "'none'") { + localStorage.setItem("ALLOWED_FRAME_ANCESTORS", sources); + } + + if (value.value === "ALLOW_EMBEDDING_EVERYWHERE") { + return "*"; + } else if (value.value === "DISABLE_EMBEDDING_EVERYWHERE") { + return "'none'"; + } else { + return sources; + } + }, + validate: (value: string) => { + if (!value) { + return "This field cannot be empty"; + } + }, +}; + export const config: AdminConfigType = { icon: "settings-2-line", type: SettingCategories.GENERAL, @@ -93,5 +174,6 @@ export const config: AdminConfigType = { APPSMITH_DOWNLOAD_DOCKER_COMPOSE_FILE_SETTING, APPSMITH_DISABLE_TELEMETRY_SETTING, APPSMITH_HIDE_WATERMARK_SETTING, + APPSMITH_ALLOWED_FRAME_ANCESTORS_SETTING, ], } as AdminConfigType; diff --git a/app/client/src/ce/pages/AdminSettings/config/types.ts b/app/client/src/ce/pages/AdminSettings/config/types.ts index f144a2c1a12..49bb965fa83 100644 --- a/app/client/src/ce/pages/AdminSettings/config/types.ts +++ b/app/client/src/ce/pages/AdminSettings/config/types.ts @@ -2,8 +2,32 @@ import React from "react"; import { ReduxAction } from "@appsmith/constants/ReduxActionConstants"; import { Dispatch } from "react"; import { EventName } from "utils/AnalyticsUtil"; +import { RadioProps } from "pages/Settings/FormGroup/Radio"; + +type ControlType = { + [K in keyof ControlPropsType]: { + controlType: K; + controlTypeProps?: ControlPropsType[K]; + }; +}[keyof ControlPropsType]; + +type ControlPropsType = { + [SettingTypes.RADIO]: RadioProps; + [SettingTypes.TEXTINPUT]: unknown; + [SettingTypes.TOGGLE]: unknown; + [SettingTypes.LINK]: unknown; + [SettingTypes.BUTTON]: unknown; + [SettingTypes.GROUP]: unknown; + [SettingTypes.TEXT]: unknown; + [SettingTypes.UNEDITABLEFIELD]: unknown; + [SettingTypes.ACCORDION]: unknown; + [SettingTypes.TAGINPUT]: unknown; + [SettingTypes.DROPDOWN]: unknown; + [SettingTypes.CHECKBOX]: unknown; +}; export enum SettingTypes { + RADIO = "RADIO", TEXTINPUT = "TEXTINPUT", TOGGLE = "TOGGLE", LINK = "LINK", @@ -25,11 +49,12 @@ export enum SettingSubtype { PASSWORD = "password", } -export interface Setting { +export type Setting = ControlType & { id: string; category?: string; - controlType: SettingTypes; controlSubType?: SettingSubtype; + format?: (value: string) => any; + parse?: (value: any) => any; helpText?: string; label?: string; name?: string; @@ -60,7 +85,7 @@ export interface Setting { needsUpgrade?: boolean; upgradeLogEventName?: EventName; upgradeIntercomMessage?: string; -} +}; export interface Category { title: string; diff --git a/app/client/src/pages/AppViewer/AppViewerHeader.tsx b/app/client/src/pages/AppViewer/AppViewerHeader.tsx index 758793717cc..9f1ead7d595 100644 --- a/app/client/src/pages/AppViewer/AppViewerHeader.tsx +++ b/app/client/src/pages/AppViewer/AppViewerHeader.tsx @@ -148,6 +148,7 @@ export function AppViewerHeader(props: AppViewerHeaderProps) { } buttonVariant="SECONDARY" className="h-8" + data-cy="viewmode-share" text="Share" /> } diff --git a/app/client/src/pages/Settings/FormGroup/Accordion.test.tsx b/app/client/src/pages/Settings/FormGroup/Accordion.test.tsx index b6841c4131a..a8831781cb4 100644 --- a/app/client/src/pages/Settings/FormGroup/Accordion.test.tsx +++ b/app/client/src/pages/Settings/FormGroup/Accordion.test.tsx @@ -3,13 +3,14 @@ import React from "react"; import { SettingTypes, SettingSubtype, + Setting, } from "@appsmith/pages/AdminSettings/config/types"; import Accordion from "./Accordion"; import { SETTINGS_FORM_NAME } from "@appsmith/constants/forms"; import { reduxForm } from "redux-form"; let container: any = null; -const setting = { +const setting: Setting = { id: "SETTING_TOGGLE_ID", name: "SETTING_TOGGLE_ID", category: "test category", @@ -53,7 +54,8 @@ function renderComponent() { form: { [SETTINGS_FORM_NAME]: { values: { - [setting.advanced[0].id]: false, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + [setting.advanced![0].id]: false, }, }, }, diff --git a/app/client/src/pages/Settings/FormGroup/Button.test.tsx b/app/client/src/pages/Settings/FormGroup/Button.test.tsx index fc4048b78ad..3281a331d62 100644 --- a/app/client/src/pages/Settings/FormGroup/Button.test.tsx +++ b/app/client/src/pages/Settings/FormGroup/Button.test.tsx @@ -1,12 +1,15 @@ import { render, screen } from "test/testUtils"; import React from "react"; -import { SettingTypes } from "@appsmith/pages/AdminSettings/config/types"; +import { + Setting, + SettingTypes, +} from "@appsmith/pages/AdminSettings/config/types"; import ButtonComponent from "./Button"; let container: any = null; const buttonClickHandler = jest.fn(); const buttonIsDisabled = jest.fn(); -const setting = { +const setting: Setting = { id: "SETTING_ID", text: "download", action: buttonClickHandler, diff --git a/app/client/src/pages/Settings/FormGroup/Group.test.tsx b/app/client/src/pages/Settings/FormGroup/Group.test.tsx index de322ca2f1d..4db64f57368 100644 --- a/app/client/src/pages/Settings/FormGroup/Group.test.tsx +++ b/app/client/src/pages/Settings/FormGroup/Group.test.tsx @@ -1,12 +1,15 @@ import { render, screen } from "test/testUtils"; import React from "react"; -import { SettingTypes } from "@appsmith/pages/AdminSettings/config/types"; +import { + Setting, + SettingTypes, +} from "@appsmith/pages/AdminSettings/config/types"; import Group from "./group"; import { SETTINGS_FORM_NAME } from "@appsmith/constants/forms"; import { reduxForm } from "redux-form"; let container: any = null; -const settings = [ +const settings: Setting[] = [ { id: "test", name: "test", diff --git a/app/client/src/pages/Settings/FormGroup/Link.test.tsx b/app/client/src/pages/Settings/FormGroup/Link.test.tsx index d63b2b20185..c1cf740dc83 100644 --- a/app/client/src/pages/Settings/FormGroup/Link.test.tsx +++ b/app/client/src/pages/Settings/FormGroup/Link.test.tsx @@ -1,11 +1,14 @@ import { render, screen } from "test/testUtils"; import React from "react"; -import { SettingTypes } from "@appsmith/pages/AdminSettings/config/types"; +import { + Setting, + SettingTypes, +} from "@appsmith/pages/AdminSettings/config/types"; import Link from "./Link"; let container: any = null; const linkClickHandler = jest.fn(); -const setting = { +const setting: Setting = { id: "SETTING_ID", isHidden: false, label: "setting label", diff --git a/app/client/src/pages/Settings/FormGroup/Radio.test.tsx b/app/client/src/pages/Settings/FormGroup/Radio.test.tsx new file mode 100644 index 00000000000..08eee1dba76 --- /dev/null +++ b/app/client/src/pages/Settings/FormGroup/Radio.test.tsx @@ -0,0 +1,80 @@ +import { render } from "test/testUtils"; +import React from "react"; +import { + Setting, + SettingTypes, +} from "@appsmith/pages/AdminSettings/config/types"; +import Radio from "./Radio"; +import { SETTINGS_FORM_NAME } from "@appsmith/constants/forms"; +import { reduxForm } from "redux-form"; + +let container: any = null; +const setting: Setting = { + id: "SETTING_RADIO", + name: "SETTING_RADIO", + category: "test category", + controlType: SettingTypes.RADIO, + label: "test label", + controlTypeProps: { + options: [ + { + label: "Label one", + value: "ONE", + }, + { + label: "Label two", + value: "TWO", + }, + ], + }, + format: (value) => { + return { value }; + }, + parse: (value) => { + return value.value; + }, +}; + +function renderComponent() { + function RadioFieldComponent() { + return ; + } + const Parent = reduxForm({ + validate: () => { + return {}; + }, + form: SETTINGS_FORM_NAME, + touchOnBlur: true, + })(RadioFieldComponent); + + render(, { + initialState: { + form: { + [SETTINGS_FORM_NAME]: { + values: { + [setting.id]: "TWO", + }, + }, + }, + }, + }); +} + +describe("Radio", () => { + beforeEach(() => { + container = document.createElement("div"); + document.body.appendChild(container); + }); + + it("is rendered", () => { + renderComponent(); + const radioOptions: NodeListOf = document.querySelectorAll( + "input[type=radio]", + ); + const numberOfCheckboxes = radioOptions.length; + expect(numberOfCheckboxes).toEqual( + setting.controlTypeProps?.options.length, + ); + expect(radioOptions[1].checked).toBeTruthy(); + }); +}); diff --git a/app/client/src/pages/Settings/FormGroup/Radio.tsx b/app/client/src/pages/Settings/FormGroup/Radio.tsx new file mode 100644 index 00000000000..ebfc31f8ec0 --- /dev/null +++ b/app/client/src/pages/Settings/FormGroup/Radio.tsx @@ -0,0 +1,216 @@ +import React, { ReactElement } from "react"; +import { + IconWrapper, + OptionProps, + Radio, + Text, + TextType, + Button, + Size, + IconSize, +} from "design-system"; +import { Popover2 } from "@blueprintjs/popover2"; +import { FormGroup, SettingComponentProps } from "./Common"; +import { + Field, + WrappedFieldInputProps, + WrappedFieldMetaProps, +} from "redux-form"; +import { FieldError } from "design-system"; +import { Colors } from "constants/Colors"; +import styled from "styled-components"; +import { Position } from "@blueprintjs/core"; + +type RadioOption = { + node?: ReactElement; + nodeLabel?: string; + nodeInputPath?: string; + nodeParentClass?: string; + badge?: string; + tooltip?: { + icon: any; + text: string; + linkText: string; + link: string; + }; +} & OptionProps; +export type RadioProps = { + options: RadioOption[]; +}; + +const Badge = styled(Text)<{ selected?: boolean }>` + background-color: ${(props) => + props.selected ? Colors.WARNING_ORANGE : Colors.SEA_SHELL}; + padding: 1.5px 5px; + margin-left: 4px; +`; + +const TooltipContent = styled.div` + width: 300px; + padding: 12px; + + a { + justify-content: flex-start; + padding: 0; + margin-top: 4px; + text-decoration: underline; + } + + .tooltip-text { + line-height: 1.17; + } +`; + +const RadioWrapper = styled.div<{ index: number }>` + ${(props) => + props.index > 0 && + ` + margin-top: 12.5px; + `} + + .icon { + margin-left: 4px; + } +`; + +const SuffixWrapper = styled.div` + display: inline-flex; + align-items: center; +`; + +const NodeWrapper = styled.div` + margin-left: 27px; + margin-top: 8px; +`; + +type RadioGroupProps = SettingComponentProps; + +function RadioFieldWrapper( + componentProps: { + meta: Partial; + input: Partial; + } & RadioProps, +) { + function onChangeHandler(e?: any) { + componentProps.input.onChange && + componentProps.input.onChange({ + value: e.target.value, + additionalData: componentProps.input.value.additionalData, + }); + } + + function onInputNodeChangeHandler(value?: any) { + componentProps.input.onChange && + componentProps.input.onChange({ + value: componentProps.input.value.value, + additionalData: value, + }); + componentProps.input.onBlur && + componentProps.input.onBlur({ + value: componentProps.input.value.value, + additionalData: value, + }); + } + + return ( +
+ {componentProps.options.map((item, index) => { + const isSelected = componentProps.input.value.value === item.value; + + return ( + + + {item.label} + + + + {item.badge && ( + + {item.badge} + + )} + {item.tooltip && ( + + + {item.tooltip.text} + +
+ ); +} + +export default function RadioField({ setting }: RadioGroupProps) { + const controlTypeProps = setting.controlTypeProps as RadioProps; + + return ( + + + + ); +} diff --git a/app/client/src/pages/Settings/FormGroup/TagInputField.test.tsx b/app/client/src/pages/Settings/FormGroup/TagInputField.test.tsx index 37daf2d70b7..b8a134bcce4 100644 --- a/app/client/src/pages/Settings/FormGroup/TagInputField.test.tsx +++ b/app/client/src/pages/Settings/FormGroup/TagInputField.test.tsx @@ -1,12 +1,15 @@ import { render, screen } from "test/testUtils"; import React from "react"; -import { SettingTypes } from "@appsmith/pages/AdminSettings/config/types"; +import { + Setting, + SettingTypes, +} from "@appsmith/pages/AdminSettings/config/types"; import TagInputField from "./TagInputField"; import { SETTINGS_FORM_NAME } from "@appsmith/constants/forms"; import { reduxForm } from "redux-form"; let container: any = null; -const setting = { +const setting: Setting = { id: "SETTING_TAG_INPUT_ID", name: "SETTING_TAG_INPUT_ID", category: "test category", diff --git a/app/client/src/pages/Settings/FormGroup/TagInputField.tsx b/app/client/src/pages/Settings/FormGroup/TagInputField.tsx index 4e4f61a916d..5e29c04b96e 100644 --- a/app/client/src/pages/Settings/FormGroup/TagInputField.tsx +++ b/app/client/src/pages/Settings/FormGroup/TagInputField.tsx @@ -18,7 +18,7 @@ const renderComponent = ( const setting = componentProps.setting; return ( diff --git a/app/client/src/pages/Settings/FormGroup/Text.test.tsx b/app/client/src/pages/Settings/FormGroup/Text.test.tsx index c747cb1b0c6..bf30469bd19 100644 --- a/app/client/src/pages/Settings/FormGroup/Text.test.tsx +++ b/app/client/src/pages/Settings/FormGroup/Text.test.tsx @@ -1,11 +1,14 @@ import { render, screen } from "test/testUtils"; import React from "react"; -import { SettingTypes } from "@appsmith/pages/AdminSettings/config/types"; +import { + Setting, + SettingTypes, +} from "@appsmith/pages/AdminSettings/config/types"; import TextComponent from "./Text"; let container: any = null; const buttonClickHandler = jest.fn(); -const setting = { +const setting: Setting = { id: "SETTING_ID", name: "textType", text: "download", diff --git a/app/client/src/pages/Settings/FormGroup/TextInput.test.tsx b/app/client/src/pages/Settings/FormGroup/TextInput.test.tsx index bb5bd5e5b87..df5730692ac 100644 --- a/app/client/src/pages/Settings/FormGroup/TextInput.test.tsx +++ b/app/client/src/pages/Settings/FormGroup/TextInput.test.tsx @@ -3,13 +3,14 @@ import React from "react"; import { SettingTypes, SettingSubtype, + Setting, } from "@appsmith/pages/AdminSettings/config/types"; import TextInput from "./TextInput"; import { SETTINGS_FORM_NAME } from "@appsmith/constants/forms"; import { reduxForm } from "redux-form"; let container: any = null; -const setting = { +const setting: Setting = { id: "SETTING_TEXT_INPUT_ID", name: "SETTING_TEXT_INPUT_ID", category: "test category", diff --git a/app/client/src/pages/Settings/FormGroup/Toggle.test.tsx b/app/client/src/pages/Settings/FormGroup/Toggle.test.tsx index 78df982e6f2..e096c845acb 100644 --- a/app/client/src/pages/Settings/FormGroup/Toggle.test.tsx +++ b/app/client/src/pages/Settings/FormGroup/Toggle.test.tsx @@ -1,12 +1,15 @@ import { render } from "test/testUtils"; import React from "react"; -import { SettingTypes } from "@appsmith/pages/AdminSettings/config/types"; +import { + Setting, + SettingTypes, +} from "@appsmith/pages/AdminSettings/config/types"; import Toggle from "./Toggle"; import { SETTINGS_FORM_NAME } from "@appsmith/constants/forms"; import { reduxForm } from "redux-form"; let container: any = null; -const setting = { +const setting: Setting = { id: "SETTING_TOGGLE_ID", name: "SETTING_TOGGLE_ID", category: "test category", diff --git a/app/client/src/pages/Settings/FormGroup/common.test.tsx b/app/client/src/pages/Settings/FormGroup/common.test.tsx index 424a99f0c83..bddfe2c1623 100644 --- a/app/client/src/pages/Settings/FormGroup/common.test.tsx +++ b/app/client/src/pages/Settings/FormGroup/common.test.tsx @@ -1,10 +1,13 @@ import { render, screen } from "test/testUtils"; import React from "react"; -import { SettingTypes } from "@appsmith/pages/AdminSettings/config/types"; +import { + Setting, + SettingTypes, +} from "@appsmith/pages/AdminSettings/config/types"; import { FormGroup } from "./Common"; let container: any = null; -const setting = { +const setting: Setting = { id: "SETTING_ID", label: "formGroup", helpText: "", diff --git a/app/client/src/pages/Settings/FormGroup/group.tsx b/app/client/src/pages/Settings/FormGroup/group.tsx index 2a8549e4328..8f211f39aa4 100644 --- a/app/client/src/pages/Settings/FormGroup/group.tsx +++ b/app/client/src/pages/Settings/FormGroup/group.tsx @@ -24,7 +24,9 @@ import Dropdown from "./Dropdown"; import { Classes } from "@blueprintjs/core"; import { Colors } from "constants/Colors"; import Checkbox from "./Checkbox"; +import Radio from "./Radio"; import { useDispatch } from "react-redux"; +import { getTypographyByKey } from "constants/DefaultTheme"; type GroupProps = { name?: string; @@ -58,29 +60,29 @@ const GroupBody = styled.div` margin-top: 0px; } } - & .tag-input { - .t--admin-settings-tag-input { - > div { - margin: 0; - .${Classes.TAG_INPUT}, .${Classes.TAG_INPUT}.${Classes.ACTIVE} { - border: 1.2px solid var(--appsmith-color-black-250); - box-shadow: none; - .bp3-tag { - background: var(--appsmith-color-black-50); - color: ${Colors.BLACK}; - svg:hover { - cursor: pointer; - path { - fill: currentColor; - } - } - } - } - .${Classes.TAG_INPUT}.${Classes.ACTIVE} { - border: 1.2px solid var(--appsmith-input-focus-border-color); - } + &&&& { + // TagInput in design system has a right margin + .tag-input > div { + margin: 0; + } + + .tag-input .${Classes.TAG_INPUT} { + box-shadow: none; + } + + .tag-input .${Classes.TAG} { + color: ${Colors.GRAY_700}; + background-color: ${Colors.GRAY_200}; + ${(props) => getTypographyByKey(props, "h5")} + // Cursor on close icon need to be a pointer + svg:hover { + cursor: pointer; } } + + .tag-input .${Classes.TAG_INPUT}.${Classes.ACTIVE} { + border: 1.2px solid var(--appsmith-color-black-900); + } } `; @@ -110,6 +112,16 @@ export default function Group({ return null; } switch (setting.controlType) { + case SettingTypes.RADIO: + return ( +
+ +
+ ); case SettingTypes.TEXTINPUT: return (