chore: misc updates to custom widget (#30114)

#### PR fixes following issue(s)
Fixes https://github.com/appsmithorg/appsmith/issues/29991
Fixes https://github.com/appsmithorg/appsmith/issues/30154
Fixes https://github.com/appsmithorg/appsmith/issues/30020
Fixes https://github.com/appsmithorg/appsmith/issues/30019
Fixes https://github.com/appsmithorg/appsmith/issues/30130
Fixes https://github.com/appsmithorg/appsmith/issues/30159
Fixes https://github.com/appsmithorg/appsmith/issues/30223

#### Media
> A video or a GIF is preferred. when using Loom, don’t embed because it
looks like it’s a GIF. instead, just link to the video
>
>
#### Type of change
> Please delete options that are not relevant.
- Bug fix (non-breaking change which fixes an issue)
- New feature (non-breaking change which adds functionality)
- Breaking change (fix or feature that would cause existing
functionality to not work as expected)
- Chore (housekeeping or task changes that don't impact user perception)
- This change requires a documentation update
>
>
>
## Testing
>
#### How Has This Been Tested?
> Please describe the tests that you ran to verify your changes. Also
list any relevant details for your test configuration.
> Delete anything that is not relevant
- [ ] Manual
- [ ] JUnit
- [ ] Jest
- [ ] Cypress
>
>
#### Test Plan
> Add Testsmith test cases links that relate to this PR
>
>
#### Issues raised during DP testing
> Link issues raised during DP testing for better visiblity and tracking
(copy link from comments dropped on this PR)
>
>
>
## Checklist:
#### Dev activity
- [ ] My code follows the style guidelines of this project
- [ ] I have performed a self-review of my own code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [ ] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [ ] New and existing unit tests pass locally with my changes
- [ ] PR is being merged under a feature flag


#### QA activity:
- [ ] [Speedbreak
features](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#speedbreakers-)
have been covered
- [ ] Test plan covers all impacted features and [areas of
interest](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#areas-of-interest-)
- [ ] Test plan has been peer reviewed by project stakeholders and other
QA members
- [ ] Manually tested functionality on DP
- [ ] We had an implementation alignment call with stakeholders post QA
Round 2
- [ ] Cypress test cases have been added and approved by SDET/manual QA
- [ ] Added `Test Plan Approved` label after Cypress tests were reviewed
- [ ] Added `Test Plan Approved` label after JUnit tests were reviewed


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **New Features**
- Custom widgets now support analytics events, enhancing visibility into
user interactions.
- Template selection, layout controls, and reference triggers in the
Custom Widget Builder are now integrated with analytics.
- Added new style options for custom widgets, including `primaryColor`,
`backgroundColor`, `borderRadius`, and `boxShadow`.

- **Bug Fixes**
  - Corrected a typo in the constant title for better clarity.
- Updated help text for the Container Widget to accurately describe the
widget's border edge.

- **Enhancements**
- Improved user interface with additional styling for reference names in
the Custom Widget Builder.
- Enhanced debugger functionality with `useCallback` optimization and
new analytics logging.

- **Refactor**
- Streamlined the property pane by introducing a new `LabelContainer`
styled component.
- Refined the handling of dynamic binding paths to ignore certain
properties efficiently.

- **Documentation**
- Added a new constant for the default model documentation URL in the
Custom Widget Builder.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
balajisoundar 2024-01-16 10:52:17 +05:30 committed by GitHub
parent 8bb61d996a
commit 5d44d4f2cf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 453 additions and 95 deletions

View File

@ -11,22 +11,18 @@ describe(
function () { function () {
before(() => { before(() => {
agHelper.AddDsl("customWidget"); agHelper.AddDsl("customWidget");
cy.wait(5000);
}); });
const getIframeBody = () => { const getIframeBody = () => {
// get the iframe > document > body // get the iframe > document > body
// and retry until the body element is not empty // and retry until the body element is not empty
return ( return cy
cy .get(".t--widget-customwidget iframe")
.get(".t--widget-customwidget iframe") .its("0.contentDocument")
.its("0.contentDocument.body") .should("exist")
.should("not.be.empty") .its("body")
// wraps "body" DOM element to allow .should("not.be.undefined")
// chaining more Cypress commands, like ".find(...)" .then(cy.wrap);
// https://on.cypress.io/wrap
.then(cy.wrap)
);
}; };
it("shoud check that default model changes are converyed to custom component", () => { it("shoud check that default model changes are converyed to custom component", () => {

View File

@ -2375,7 +2375,7 @@ export const CUSTOM_WIDGET_FEATURE = {
split: () => "Splits", split: () => "Splits",
}, },
referrences: { referrences: {
title: () => "Referrences", title: () => "References",
tooltip: { tooltip: {
open: () => "Open references", open: () => "Open references",
close: () => "Close references", close: () => "Close references",

View File

@ -350,7 +350,8 @@ export type EventName =
| "START_FROM_TEMPLATES_CLICK_SKIP_BUTTON" | "START_FROM_TEMPLATES_CLICK_SKIP_BUTTON"
| "SUPPORT_REQUEST_INITIATED" | "SUPPORT_REQUEST_INITIATED"
| ONBOARDING_FLOW_EVENTS | ONBOARDING_FLOW_EVENTS
| CANVAS_STARTER_BUILDING_BLOCK_EVENTS; | CANVAS_STARTER_BUILDING_BLOCK_EVENTS
| CUSTOM_WIDGET_EVENTS;
export type CANVAS_STARTER_BUILDING_BLOCK_EVENTS = export type CANVAS_STARTER_BUILDING_BLOCK_EVENTS =
| "STARTER_BUILDING_BLOCK_HOVER" | "STARTER_BUILDING_BLOCK_HOVER"
@ -429,3 +430,24 @@ export type VERSION_UPDATE_EVENTS =
| "VERSION_UPDATE_REQUESTED" | "VERSION_UPDATE_REQUESTED"
| "VERSION_UPDATE_SUCCESS" | "VERSION_UPDATE_SUCCESS"
| "VERSION_UPDATED_FAILED"; | "VERSION_UPDATED_FAILED";
export type CUSTOM_WIDGET_EVENTS =
| "CUSTOM_WIDGET_EDIT_SOURCE_CLICKED"
| "CUSTOM_WIDGET_ADD_EVENT_CLICKED"
| "CUSTOM_WIDGET_ADD_EVENT_CANCEL_CLICKED"
| "CUSTOM_WIDGET_ADD_EVENT_SAVE_CLICKED"
| "CUSTOM_WIDGET_EDIT_EVENT_CLICKED"
| "CUSTOM_WIDGET_DELETE_EVENT_CLICKED"
| "CUSTOM_WIDGET_EDIT_EVENT_SAVE_CLICKED"
| "CUSTOM_WIDGET_EDIT_EVENT_CANCEL_CLICKED"
| "CUSTOM_WIDGET_BUILDER_SRCDOC_UPDATE"
| "CUSTOM_WIDGET_BUILDER_TEMPLATE_OPENED"
| "CUSTOM_WIDGET_BUILDER_TEMPLATE_REVERT_TO_ORIGINAL_CLICKED"
| "CUSTOM_WIDGET_BUILDER_TEMPLATE_SELECT"
| "CUSTOM_WIDGET_BUILDER_TEMPLATE_SELECT_CANCELED"
| "CUSTOM_WIDGET_BUILDER_TEMPLATE_SELECT_CONFIRMED"
| "CUSTOM_WIDGET_BUILDER_LAYOUT_CHANGED"
| "CUSTOM_WIDGET_BUILDER_REFERENCE_VISIBILITY_CHANGED"
| "CUSTOM_WIDGET_BUILDER_REFERENCE_EVENT_OPENED"
| "CUSTOM_WIDGET_BUILDER_DEBUGGER_CLEARED"
| "CUSTOM_WIDGET_BUILDER_DEBUGGER_VISIBILITY_CHANGED";

View File

@ -9,6 +9,7 @@ import {
CUSTOM_WIDGET_FEATURE, CUSTOM_WIDGET_FEATURE,
createMessage, createMessage,
} from "@appsmith/constants/messages"; } from "@appsmith/constants/messages";
import AnalyticsUtil from "utils/AnalyticsUtil";
interface ButtonControlState { interface ButtonControlState {
showInput: boolean; showInput: boolean;
@ -72,8 +73,12 @@ class ButtonControl extends BaseControl<
reset = () => { reset = () => {
this.setState({ showInput: false, eventName: "", pristine: true }); this.setState({ showInput: false, eventName: "", pristine: true });
}; };
onCancel = () => { onCancel = () => {
this.reset(); this.reset();
AnalyticsUtil.logEvent("CUSTOM_WIDGET_ADD_EVENT_CANCEL_CLICKED", {
widgetId: this.props.widgetProperties.widgetId,
});
}; };
onSave = () => { onSave = () => {
@ -83,6 +88,9 @@ class ButtonControl extends BaseControl<
); );
this.batchUpdateProperties(updates); this.batchUpdateProperties(updates);
this.reset(); this.reset();
AnalyticsUtil.logEvent("CUSTOM_WIDGET_ADD_EVENT_SAVE_CLICKED", {
widgetId: this.props.widgetProperties.widgetId,
});
}; };
hasError = () => { hasError = () => {
@ -172,7 +180,12 @@ class ButtonControl extends BaseControl<
) : ( ) : (
<Button <Button
kind="tertiary" kind="tertiary"
onClick={() => this.setState({ showInput: true })} onClick={() => {
this.setState({ showInput: true });
AnalyticsUtil.logEvent("CUSTOM_WIDGET_ADD_EVENT_CLICKED", {
widgetId: this.props.widgetProperties.widgetId,
});
}}
size="sm" size="sm"
startIcon="plus" startIcon="plus"
> >

View File

@ -10,6 +10,7 @@ import {
} from "@appsmith/constants/messages"; } from "@appsmith/constants/messages";
import CustomWidgetBuilderService from "utils/CustomWidgetBuilderService"; import CustomWidgetBuilderService from "utils/CustomWidgetBuilderService";
import styled from "styled-components"; import styled from "styled-components";
import AnalyticsUtil from "utils/AnalyticsUtil";
interface ButtonControlState { interface ButtonControlState {
isSourceEditorOpen: boolean; isSourceEditorOpen: boolean;
@ -24,7 +25,32 @@ class ButtonControl extends BaseControl<ControlProps, ButtonControlState> {
isSourceEditorOpen: false, isSourceEditorOpen: false,
}; };
getPayload = () => {
return {
name: this.props.widgetProperties.widgetName,
widgetId: this.props.widgetProperties.widgetId,
srcDoc: this.props.widgetProperties.srcDoc,
uncompiledSrcDoc: this.props.widgetProperties.uncompiledSrcDoc,
model:
this.props.widgetProperties.__evaluation__?.evaluatedValues
?.defaultModel,
events: this.props.widgetProperties.events.reduce(
(prev: Record<string, string>, curr: string) => {
prev[curr] = this.props.widgetProperties[curr];
return prev;
},
{},
),
theme: this.props.widgetProperties.__evaluation__?.evaluatedValues?.theme,
};
};
onCTAClick = () => { onCTAClick = () => {
AnalyticsUtil.logEvent("CUSTOM_WIDGET_EDIT_SOURCE_CLICKED", {
widgetId: this.props.widgetProperties.widgetId,
});
if ( if (
CustomWidgetBuilderService.isConnected( CustomWidgetBuilderService.isConnected(
this.props.widgetProperties.widgetId, this.props.widgetProperties.widgetId,
@ -40,22 +66,7 @@ class ButtonControl extends BaseControl<ControlProps, ButtonControlState> {
onMessage(CUSTOM_WIDGET_BUILDER_EVENTS.READY, () => { onMessage(CUSTOM_WIDGET_BUILDER_EVENTS.READY, () => {
postMessage({ postMessage({
type: CUSTOM_WIDGET_BUILDER_EVENTS.READY_ACK, type: CUSTOM_WIDGET_BUILDER_EVENTS.READY_ACK,
name: this.props.widgetProperties.widgetName, ...this.getPayload(),
srcDoc: this.props.widgetProperties.srcDoc,
uncompiledSrcDoc: this.props.widgetProperties.uncompiledSrcDoc,
model:
this.props.widgetProperties.__evaluation__?.evaluatedValues
?.defaultModel,
events: this.props.widgetProperties.events.reduce(
(prev: Record<string, string>, curr: string) => {
prev[curr] = this.props.widgetProperties[curr];
return prev;
},
{},
),
theme:
this.props.widgetProperties.__evaluation__?.evaluatedValues?.theme,
}); });
}); });
@ -74,6 +85,7 @@ class ButtonControl extends BaseControl<ControlProps, ButtonControlState> {
onMessage(CUSTOM_WIDGET_BUILDER_EVENTS.DISCONNECTED, () => { onMessage(CUSTOM_WIDGET_BUILDER_EVENTS.DISCONNECTED, () => {
CustomWidgetBuilderService.closeConnection( CustomWidgetBuilderService.closeConnection(
this.props.widgetProperties.widgetId, this.props.widgetProperties.widgetId,
true,
); );
this.setState({ this.setState({
@ -105,6 +117,7 @@ class ButtonControl extends BaseControl<ControlProps, ButtonControlState> {
this.props.widgetProperties.widgetId, this.props.widgetProperties.widgetId,
)?.postMessage({ )?.postMessage({
type: CUSTOM_WIDGET_BUILDER_EVENTS.RESUME, type: CUSTOM_WIDGET_BUILDER_EVENTS.RESUME,
...this.getPayload(),
}); });
} }
} }

View File

@ -22,6 +22,7 @@ import {
CUSTOM_WIDGET_FEATURE, CUSTOM_WIDGET_FEATURE,
createMessage, createMessage,
} from "@appsmith/constants/messages"; } from "@appsmith/constants/messages";
import AnalyticsUtil from "utils/AnalyticsUtil";
const StyledButton = styled(Button)` const StyledButton = styled(Button)`
height: 32px !important; height: 32px !important;
@ -70,13 +71,14 @@ function ConfirmationModal(props: {
} }
export function CodeTemplates() { export function CodeTemplates() {
const { bulkUpdate, initialSrcDoc, lastSaved } = useContext( const { bulkUpdate, initialSrcDoc, lastSaved, widgetId } = useContext(
CustomWidgetBuilderContext, CustomWidgetBuilderContext,
); );
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const [selectedTemplate, setSelectedTemplate] = useState<SrcDoc | null>(null); const [selectedTemplate, setSelectedTemplate] = useState<SrcDoc | null>(null);
const [selectedTemplateName, setSelectedTemplateName] = useState("");
return ( return (
<div className={styles.templateMenu}> <div className={styles.templateMenu}>
@ -85,9 +87,13 @@ export function CodeTemplates() {
<StyledButton <StyledButton
className="t--custom-widget-template-trigger" className="t--custom-widget-template-trigger"
kind="secondary" kind="secondary"
onClick={() => {
AnalyticsUtil.logEvent("CUSTOM_WIDGET_BUILDER_TEMPLATE_OPENED", {
widgetId: widgetId,
});
}}
size="sm" size="sm"
startIcon="query" startIcon="query"
style={{}}
> >
{createMessage(CUSTOM_WIDGET_FEATURE.template.buttonCTA)} {createMessage(CUSTOM_WIDGET_FEATURE.template.buttonCTA)}
</StyledButton> </StyledButton>
@ -98,7 +104,17 @@ export function CodeTemplates() {
<MenuItem <MenuItem
onClick={() => { onClick={() => {
setSelectedTemplate(initialSrcDoc); setSelectedTemplate(initialSrcDoc);
setSelectedTemplateName(
CUSTOM_WIDGET_FEATURE.template.revert,
);
setOpen(true); setOpen(true);
AnalyticsUtil.logEvent(
"CUSTOM_WIDGET_BUILDER_TEMPLATE_SELECT",
{
widgetId: widgetId,
templateName: CUSTOM_WIDGET_FEATURE.template.revert,
},
);
}} }}
> >
{createMessage(CUSTOM_WIDGET_FEATURE.template.revert)} {createMessage(CUSTOM_WIDGET_FEATURE.template.revert)}
@ -111,7 +127,15 @@ export function CodeTemplates() {
key={template.key} key={template.key}
onClick={() => { onClick={() => {
setSelectedTemplate(template.uncompiledSrcDoc); setSelectedTemplate(template.uncompiledSrcDoc);
setSelectedTemplateName(template.key);
setOpen(true); setOpen(true);
AnalyticsUtil.logEvent(
"CUSTOM_WIDGET_BUILDER_TEMPLATE_SELECT",
{
widgetId: widgetId,
templateName: template.key,
},
);
}} }}
> >
{template.key} {template.key}
@ -122,18 +146,42 @@ export function CodeTemplates() {
<ConfirmationModal <ConfirmationModal
onCancel={() => { onCancel={() => {
setSelectedTemplate(null); setSelectedTemplate(null);
setSelectedTemplateName("");
setOpen(false); setOpen(false);
AnalyticsUtil.logEvent(
"CUSTOM_WIDGET_BUILDER_TEMPLATE_SELECT_CANCELED",
{
widgetId: widgetId,
templateName: selectedTemplateName,
},
);
}} }}
onOpenChange={(flag: boolean) => { onOpenChange={(flag: boolean) => {
if (!flag) { if (!flag) {
setSelectedTemplate(null); setSelectedTemplate(null);
setSelectedTemplateName("");
setOpen(false); setOpen(false);
AnalyticsUtil.logEvent(
"CUSTOM_WIDGET_BUILDER_TEMPLATE_SELECT_CANCELED",
{
widgetId: widgetId,
templateName: selectedTemplateName,
},
);
} }
}} }}
onReplace={() => { onReplace={() => {
selectedTemplate && bulkUpdate?.(selectedTemplate); selectedTemplate && bulkUpdate?.(selectedTemplate);
setSelectedTemplate(null); setSelectedTemplate(null);
setSelectedTemplateName("");
setOpen(false); setOpen(false);
AnalyticsUtil.logEvent(
"CUSTOM_WIDGET_BUILDER_TEMPLATE_SELECT_CONFIRMED",
{
widgetId: widgetId,
templateName: selectedTemplateName,
},
);
}} }}
open={open} open={open}
/> />

View File

@ -7,6 +7,7 @@ import {
CUSTOM_WIDGET_FEATURE, CUSTOM_WIDGET_FEATURE,
createMessage, createMessage,
} from "@appsmith/constants/messages"; } from "@appsmith/constants/messages";
import AnalyticsUtil from "utils/AnalyticsUtil";
const StyledSegmentedControl = styled(SegmentedControl)` const StyledSegmentedControl = styled(SegmentedControl)`
& .ads-v2-icon { & .ads-v2-icon {
@ -15,10 +16,17 @@ const StyledSegmentedControl = styled(SegmentedControl)`
`; `;
export default function LayoutControls() { export default function LayoutControls() {
const context = useContext(CustomWidgetBuilderContext); const { selectedLayout, selectLayout, widgetId } = useContext(
CustomWidgetBuilderContext,
);
const onChange = (value: string) => { const onChange = (value: string) => {
context.selectLayout?.(value); selectLayout?.(value);
AnalyticsUtil.logEvent("CUSTOM_WIDGET_BUILDER_LAYOUT_CHANGED", {
widgetId: widgetId,
layoutName: value,
});
}; };
return ( return (
@ -37,7 +45,7 @@ export default function LayoutControls() {
value: "tabs", value: "tabs",
}, },
]} ]}
value={context.selectedLayout} value={selectedLayout}
/> />
</div> </div>
); );

View File

@ -6,14 +6,23 @@ import {
CUSTOM_WIDGET_FEATURE, CUSTOM_WIDGET_FEATURE,
createMessage, createMessage,
} from "@appsmith/constants/messages"; } from "@appsmith/constants/messages";
import AnalyticsUtil from "utils/AnalyticsUtil";
export default function ReferenceTrigger() { export default function ReferenceTrigger() {
const { isReferenceOpen, toggleReference } = useContext( const { isReferenceOpen, toggleReference, widgetId } = useContext(
CustomWidgetBuilderContext, CustomWidgetBuilderContext,
); );
const onClick = () => { const onClick = () => {
toggleReference?.(); toggleReference?.();
AnalyticsUtil.logEvent(
"CUSTOM_WIDGET_BUILDER_REFERENCE_VISIBILITY_CHANGED",
{
widgetId: widgetId,
visible: !isReferenceOpen,
},
);
}; };
return ( return (

View File

@ -14,6 +14,7 @@ import {
CUSTOM_WIDGET_FEATURE, CUSTOM_WIDGET_FEATURE,
createMessage, createMessage,
} from "@appsmith/constants/messages"; } from "@appsmith/constants/messages";
import AnalyticsUtil from "utils/AnalyticsUtil";
const StyledLazyCodeEditorWrapper = styled.div` const StyledLazyCodeEditorWrapper = styled.div`
.CodeMirror-line.CodeMirror-line { .CodeMirror-line.CodeMirror-line {
@ -31,11 +32,16 @@ const StyledLazyCodeEditorWrapper = styled.div`
`; `;
export default function Events() { export default function Events() {
const { events } = useContext(CustomWidgetBuilderContext); const { events, widgetId } = useContext(CustomWidgetBuilderContext);
const [openState, setOpenState] = useState<Record<string, boolean>>({}); const [openState, setOpenState] = useState<Record<string, boolean>>({});
const toggleOpen = useCallback((event: string) => { const toggleOpen = useCallback((event: string) => {
AnalyticsUtil.logEvent("CUSTOM_WIDGET_BUILDER_REFERENCE_EVENT_OPENED", {
widgetId: widgetId,
eventName: event,
});
setOpenState((prev) => { setOpenState((prev) => {
return { return {
...prev, ...prev,

View File

@ -63,6 +63,9 @@
font-weight: 400; font-weight: 400;
margin-right: 2px; margin-right: 2px;
cursor: pointer; cursor: pointer;
overflow: hidden;
text-overflow: ellipsis;
width: calc(100% - 22px);
} }
.eventControl { .eventControl {

View File

@ -1,4 +1,4 @@
import React, { useContext, useEffect } from "react"; import React, { useCallback, useContext, useEffect } from "react";
import { Tabs, TabsList, Tab, TabPanel, Icon, Tooltip } from "design-system"; import { Tabs, TabsList, Tab, TabPanel, Icon, Tooltip } from "design-system";
import DebuggerItem from "./debuggerItem"; import DebuggerItem from "./debuggerItem";
import styles from "./styles.module.css"; import styles from "./styles.module.css";
@ -11,6 +11,7 @@ import {
CUSTOM_WIDGET_FEATURE, CUSTOM_WIDGET_FEATURE,
createMessage, createMessage,
} from "@appsmith/constants/messages"; } from "@appsmith/constants/messages";
import AnalyticsUtil from "utils/AnalyticsUtil";
const LOCAL_STORAGE_KEYS_IS_DEBUGGER_OPEN = const LOCAL_STORAGE_KEYS_IS_DEBUGGER_OPEN =
"custom-widget-builder-context-state-is-debugger-open"; "custom-widget-builder-context-state-is-debugger-open";
@ -23,7 +24,7 @@ export default function Debugger() {
false, false,
); );
const { clearDegbuggerLogs, debuggerLogs } = useContext( const { clearDegbuggerLogs, debuggerLogs, widgetId } = useContext(
CustomWidgetBuilderContext, CustomWidgetBuilderContext,
); );
@ -31,6 +32,17 @@ export default function Debugger() {
scrollToRef.current?.scrollIntoView({ behavior: "smooth" }); scrollToRef.current?.scrollIntoView({ behavior: "smooth" });
}, [debuggerLogs]); }, [debuggerLogs]);
const toggle = useCallback(() => {
setOpen(!open);
AnalyticsUtil.logEvent(
"CUSTOM_WIDGET_BUILDER_DEBUGGER_VISIBILITY_CHANGED",
{
widgetId: widgetId,
visible: !open,
},
);
}, [open]);
return ( return (
<div className={styles.wrapper}> <div className={styles.wrapper}>
<div className={styles.debuggerActions}> <div className={styles.debuggerActions}>
@ -43,31 +55,36 @@ export default function Debugger() {
debuggerLogs?.filter((d) => d.type === DebuggerLogType.LOG) debuggerLogs?.filter((d) => d.type === DebuggerLogType.LOG)
.length || 0 .length || 0
} }
onClick={() => setOpen(!open)} onClick={() => toggle()}
warn={ warn={
debuggerLogs?.filter((d) => d.type === DebuggerLogType.WARN) debuggerLogs?.filter((d) => d.type === DebuggerLogType.WARN)
.length || 0 .length || 0
} }
/> />
<Tooltip content="clear console"> <Tooltip content="Clear console">
<Icon <Icon
name="forbid-line" name="forbid-line"
onClick={() => clearDegbuggerLogs?.()} onClick={() => {
clearDegbuggerLogs?.();
AnalyticsUtil.logEvent("CUSTOM_WIDGET_BUILDER_DEBUGGER_CLEARED", {
widgetId: widgetId,
});
}}
size="md" size="md"
style={{ cursor: "pointer" }} style={{ cursor: "pointer" }}
/> />
</Tooltip> </Tooltip>
<Tooltip content={open ? "close console" : "open console"}> <Tooltip content={open ? "Close console" : "Open console"}>
<Icon <Icon
name={open ? "arrow-down-s-line" : "arrow-up-s-line"} name={open ? "arrow-down-s-line" : "arrow-up-s-line"}
onClick={() => setOpen(!open)} onClick={() => toggle()}
size="lg" size="lg"
style={{ cursor: "pointer" }} style={{ cursor: "pointer" }}
/> />
</Tooltip> </Tooltip>
</div> </div>
<Tabs value={"Debugger"}> <Tabs value={"Debugger"}>
<TabsList className={styles.debuggerTab} onClick={() => setOpen(!open)}> <TabsList className={styles.debuggerTab} onClick={() => toggle()}>
<Tab key="Debugger" value="Debugger"> <Tab key="Debugger" value="Debugger">
{createMessage(CUSTOM_WIDGET_FEATURE.debugger.title)} {createMessage(CUSTOM_WIDGET_FEATURE.debugger.title)}
</Tab> </Tab>

View File

@ -11,8 +11,15 @@ import {
import type { AppThemeProperties } from "entities/AppTheming"; import type { AppThemeProperties } from "entities/AppTheming";
export default function Preview() { export default function Preview() {
const { key, model, srcDoc, theme, updateDebuggerLogs, updateModel } = const {
useContext(CustomWidgetBuilderContext); key,
model,
srcDoc,
theme,
updateDebuggerLogs,
updateModel,
widgetId,
} = useContext(CustomWidgetBuilderContext);
const [dimensions, setDimensions] = useState({ const [dimensions, setDimensions] = useState({
width: 300, width: 300,
@ -100,6 +107,7 @@ export default function Preview() {
args: [{ message }, { message: data }], args: [{ message }, { message: data }],
}); });
}} }}
widgetId={widgetId || ""}
width={dimensions.width} width={dimensions.width}
/> />
<Debugger /> <Debugger />

View File

@ -19,6 +19,7 @@ export const LOCAL_STORAGE_KEYS_SELECTED_LAYOUT =
export const DEFAULT_CONTEXT_VALUE = { export const DEFAULT_CONTEXT_VALUE = {
name: "", name: "",
widgetId: "",
srcDoc: { srcDoc: {
html: "<div>Hello World</div>", html: "<div>Hello World</div>",
js: "function test() {console.log('Hello World');}", js: "function test() {console.log('Hello World');}",
@ -55,3 +56,6 @@ export const DEFAULT_CONTEXT_VALUE = {
export const CUSTOM_WIDGET_DOC_URL = export const CUSTOM_WIDGET_DOC_URL =
"https://docs.appsmith.com/reference/widgets/custom"; "https://docs.appsmith.com/reference/widgets/custom";
export const CUSTOM_WIDGET_DEFAULT_MODEL_DOC_URL =
"https://docs.appsmith.com/reference/widgets/custom#default-model";

View File

@ -22,6 +22,8 @@ import {
} from "./types"; } from "./types";
import { compileSrcDoc } from "./utility"; import { compileSrcDoc } from "./utility";
import ConnectionLost from "./connectionLost"; import ConnectionLost from "./connectionLost";
import Helmet from "react-helmet";
import AnalyticsUtil from "utils/AnalyticsUtil";
export const CustomWidgetBuilderContext = React.createContext< export const CustomWidgetBuilderContext = React.createContext<
Partial<CustomWidgetBuilderContextType> Partial<CustomWidgetBuilderContextType>
@ -56,7 +58,7 @@ export default function CustomWidgetBuilder() {
}); });
if (contextValue.lastSaved) { if (contextValue.lastSaved) {
window.opener.postMessage( window.opener?.postMessage(
{ {
type: CUSTOM_WIDGET_BUILDER_EVENTS.UPDATE_SRCDOC, type: CUSTOM_WIDGET_BUILDER_EVENTS.UPDATE_SRCDOC,
srcDoc: result.code, srcDoc: result.code,
@ -106,7 +108,7 @@ export default function CustomWidgetBuilder() {
setSelectedLayout(layout); setSelectedLayout(layout);
}, },
close: () => { close: () => {
window.opener.focus(); window.opener?.focus();
window.close(); window.close();
}, },
bulkUpdate: (uncompiledSrcDoc: SrcDoc) => { bulkUpdate: (uncompiledSrcDoc: SrcDoc) => {
@ -129,6 +131,11 @@ export default function CustomWidgetBuilder() {
lastSaved: Date.now(), lastSaved: Date.now(),
}; };
}); });
AnalyticsUtil.logEvent("CUSTOM_WIDGET_BUILDER_SRCDOC_UPDATE", {
widgetId: contextValue.widgetId,
srcDocFile: editor,
});
}, },
updateModel: (model: Record<string, unknown>) => { updateModel: (model: Record<string, unknown>) => {
setContextValue((prev) => { setContextValue((prev) => {
@ -187,6 +194,7 @@ export default function CustomWidgetBuilder() {
return { return {
...prev, ...prev,
name: event.data.name, name: event.data.name,
widgetId: event.data.widgetId,
srcDoc: event.data.srcDoc, srcDoc: event.data.srcDoc,
uncompiledSrcDoc: event.data.uncompiledSrcDoc, uncompiledSrcDoc: event.data.uncompiledSrcDoc,
initialSrcDoc: event.data.uncompiledSrcDoc, initialSrcDoc: event.data.uncompiledSrcDoc,
@ -222,13 +230,21 @@ export default function CustomWidgetBuilder() {
return { return {
...prev, ...prev,
showConnectionLostMessage: false, showConnectionLostMessage: false,
name: event.data.name,
widgetId: event.data.widgetId,
srcDoc: event.data.srcDoc,
uncompiledSrcDoc: event.data.uncompiledSrcDoc,
initialSrcDoc: event.data.uncompiledSrcDoc,
model: event.data.model,
events: event.data.events,
theme: event.data.theme,
}; };
}); });
break; break;
} }
}); });
window.opener.postMessage( window.opener?.postMessage(
{ {
type: CUSTOM_WIDGET_BUILDER_EVENTS.READY, type: CUSTOM_WIDGET_BUILDER_EVENTS.READY,
}, },
@ -236,7 +252,7 @@ export default function CustomWidgetBuilder() {
); );
window.addEventListener("beforeunload", () => { window.addEventListener("beforeunload", () => {
window.opener.postMessage( window.opener?.postMessage(
{ {
type: CUSTOM_WIDGET_BUILDER_EVENTS.DISCONNECTED, type: CUSTOM_WIDGET_BUILDER_EVENTS.DISCONNECTED,
}, },
@ -252,6 +268,10 @@ export default function CustomWidgetBuilder() {
return ( return (
<CustomWidgetBuilderContext.Provider value={context}> <CustomWidgetBuilderContext.Provider value={context}>
<Helmet>
<meta charSet="utf-8" />
<title>{`${contextValue.name} | Builder | Appsmith`}</title>
</Helmet>
<Header /> <Header />
{loading ? ( {loading ? (
<Spinner className={styles.loader} size="lg" /> <Spinner className={styles.loader} size="lg" />

View File

@ -27,6 +27,7 @@ export interface SrcDoc {
export interface CustomWidgetBuilderContextValueType { export interface CustomWidgetBuilderContextValueType {
//Custom widget name //Custom widget name
name: string; name: string;
widgetId: string;
isReferenceOpen: boolean; isReferenceOpen: boolean;
selectedLayout: string; selectedLayout: string;

View File

@ -70,6 +70,11 @@ const ResetIcon = importSvg(
const StyledDeviated = styled.div` const StyledDeviated = styled.div`
background-color: var(--ads-v2-color-bg-brand); background-color: var(--ads-v2-color-bg-brand);
`; `;
const LabelContainer = styled.div<{ hasEditIcon: boolean }>`
${(props) => props.hasEditIcon && "max-width: calc(100% - 110px);"}
`;
type Props = PropertyPaneControlConfig & { type Props = PropertyPaneControlConfig & {
panel: IPanelProps; panel: IPanelProps;
theme: EditorTheme; theme: EditorTheme;
@ -646,6 +651,10 @@ const PropertyControl = memo((props: Props) => {
onDeleteProperties([props.propertyName]); onDeleteProperties([props.propertyName]);
} }
resetEditing(); resetEditing();
AnalyticsUtil.logEvent("CUSTOM_WIDGET_EDIT_EVENT_SAVE_CLICKED", {
widgetId: widgetProperties.widgetId,
});
}, [ }, [
props, props,
onBatchUpdateProperties, onBatchUpdateProperties,
@ -657,6 +666,10 @@ const PropertyControl = memo((props: Props) => {
const resetEditing = useCallback(() => { const resetEditing = useCallback(() => {
setEditedName(props.propertyName); setEditedName(props.propertyName);
setIsRenaming(false); setIsRenaming(false);
AnalyticsUtil.logEvent("CUSTOM_WIDGET_EDIT_EVENT_CANCEL_CLICKED", {
widgetId: widgetProperties.widgetId,
});
}, [props.propertyName]); }, [props.propertyName]);
const { propertyName } = props; const { propertyName } = props;
@ -913,8 +926,15 @@ const PropertyControl = memo((props: Props) => {
</div> </div>
) : ( ) : (
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div className={clsx("flex items-center justify-right gap-1")}> <LabelContainer
className={clsx("flex items-center justify-right gap-1")}
hasEditIcon={
!!config.controlConfig?.allowEdit ||
!!config.controlConfig?.allowDelete
}
>
<PropertyHelpLabel <PropertyHelpLabel
className="w-full"
label={label} label={label}
theme={props.theme} theme={props.theme}
tooltip={helpText} tooltip={helpText}
@ -964,7 +984,7 @@ const PropertyControl = memo((props: Props) => {
</button> </button>
</> </>
)} )}
</div> </LabelContainer>
<div className={clsx("flex items-center justify-right")}> <div className={clsx("flex items-center justify-right")}>
{config.controlConfig?.allowEdit && ( {config.controlConfig?.allowEdit && (
<Button <Button
@ -975,7 +995,15 @@ const PropertyControl = memo((props: Props) => {
)} )}
isIconButton isIconButton
kind="tertiary" kind="tertiary"
onClick={() => setIsRenaming(true)} onClick={() => {
setIsRenaming(true);
AnalyticsUtil.logEvent(
"CUSTOM_WIDGET_EDIT_EVENT_CLICKED",
{
widgetId: widgetProperties.widgetId,
},
);
}}
size="small" size="small"
startIcon="pencil-line" startIcon="pencil-line"
/> />
@ -1000,6 +1028,13 @@ const PropertyControl = memo((props: Props) => {
onBatchUpdateProperties(updates); onBatchUpdateProperties(updates);
} }
onDeleteProperties([config.propertyName]); onDeleteProperties([config.propertyName]);
AnalyticsUtil.logEvent(
"CUSTOM_WIDGET_DELETE_EVENT_CLICKED",
{
widgetId: widgetProperties.widgetId,
},
);
}} }}
size="small" size="small"
startIcon="trash" startIcon="trash"

View File

@ -33,9 +33,9 @@ function PropertyHelpLabel(props: Props) {
content={props.tooltip || ""} content={props.tooltip || ""}
isDisabled={!toolTipDefined} isDisabled={!toolTipDefined}
> >
<div onClick={props.onClick}> <div className="w-full" onClick={props.onClick}>
<Label <Label
className={`t--property-control-label`} className={`t--property-control-label w-full block text-ellipsis overflow-hidden`}
style={{ style={{
cursor: toolTipDefined ? "help" : "default", cursor: toolTipDefined ? "help" : "default",
}} }}

View File

@ -389,6 +389,20 @@ function getDynamicTriggerPathListUpdate(
}; };
} }
const DYNAMIC_BINDING_IGNORED_LIST = [
/* Table widget */
"primaryColumns",
"derivedColumns",
/* custom widget */
"srcDoc.html",
"srcDoc.css",
"srcDoc.js",
"uncompiledSrcDoc.html",
"uncompiledSrcDoc.css",
"uncompiledSrcDoc.js",
];
function getDynamicBindingPathListUpdate( function getDynamicBindingPathListUpdate(
widget: WidgetProps, widget: WidgetProps,
propertyPath: string, propertyPath: string,
@ -400,9 +414,12 @@ function getDynamicBindingPathListUpdate(
stringProp = JSON.stringify(propertyValue); stringProp = JSON.stringify(propertyValue);
} }
//TODO(abhinav): This is not appropriate from the platform's archtecture's point of view. /*
* TODO(Balaji Soundararajan): This is not appropriate from the platform's archtecture's point of view.
* This setting should come from widget configuration
*/
// Figure out a holistic solutions where we donot have to stringify above. // Figure out a holistic solutions where we donot have to stringify above.
if (propertyPath === "primaryColumns" || propertyPath === "derivedColumns") { if (DYNAMIC_BINDING_IGNORED_LIST.includes(propertyPath)) {
return { return {
propertyPath, propertyPath,
effect: DynamicPathUpdateEffectEnum.NOOP, effect: DynamicPathUpdateEffectEnum.NOOP,

View File

@ -86,11 +86,13 @@ export default class CustomWidgetBuilderService {
} }
} }
static closeConnection(widgetId: string) { static closeConnection(widgetId: string, skipClosing?: boolean) {
if (this.builderWindowConnections.has(widgetId)) { if (this.builderWindowConnections.has(widgetId)) {
const connection = this.builderWindowConnections.get(widgetId); if (!skipClosing) {
const connection = this.builderWindowConnections.get(widgetId);
connection?.window?.close(); connection?.window?.close();
}
this.builderWindowConnections.delete(widgetId); this.builderWindowConnections.delete(widgetId);
} }

View File

@ -291,8 +291,7 @@ export class ContainerWidget extends BaseWidget<
{ {
propertyName: "borderRadius", propertyName: "borderRadius",
label: "Border radius", label: "Border radius",
helpText: helpText: "Rounds the corners of the widgets's outer border edge",
"Rounds the corners of the icon button's outer border edge",
controlType: "BORDER_RADIUS_OPTIONS", controlType: "BORDER_RADIUS_OPTIONS",
isJSConvertible: true, isJSConvertible: true,
isBindProperty: true, isBindProperty: true,

View File

@ -7,15 +7,22 @@ export const CUSTOM_WIDGET_LOAD_EVENTS = {
export const getAppsmithScriptSchema = (model: Record<string, unknown>) => ({ export const getAppsmithScriptSchema = (model: Record<string, unknown>) => ({
appsmith: { appsmith: {
mode: "", mode: "",
onUiChange: Function,
onModelChange: Function,
updateModel: Function,
triggerEvent: Function,
model: model, model: model,
ui: { ui: {
width: 1, width: 1,
height: 2, height: 2,
}, },
theme: {
primaryColor: "",
backgroundColor: "",
borderRadius: "",
boxShadow: "",
},
onUiChange: Function,
onModelChange: Function,
onThemeChange: Function,
updateModel: Function,
triggerEvent: Function,
onReady: Function, onReady: Function,
}, },
}); });

View File

@ -14,6 +14,15 @@ import appsmithConsole from "!!raw-loader!./appsmithConsole.js";
import css from "!!raw-loader!./reset.css"; import css from "!!raw-loader!./reset.css";
import clsx from "clsx"; import clsx from "clsx";
import type { AppThemeProperties } from "entities/AppTheming"; import type { AppThemeProperties } from "entities/AppTheming";
import WidgetStyleContainer from "components/designSystems/appsmith/WidgetStyleContainer";
import type { BoxShadow } from "components/designSystems/appsmith/WidgetStyleContainer";
import type { Color } from "constants/Colors";
import { connect } from "react-redux";
import type { AppState } from "@appsmith/reducers";
import { combinedPreviewModeSelector } from "selectors/editorSelectors";
import { getAppMode } from "@appsmith/selectors/applicationSelectors";
import { APP_MODE } from "entities/App";
import { getWidgetPropsForPropertyPane } from "selectors/propertyPaneSelectors";
const StyledIframe = styled.iframe<{ width: number; height: number }>` const StyledIframe = styled.iframe<{ width: number; height: number }>`
width: ${(props) => props.width - 8}px; width: ${(props) => props.width - 8}px;
@ -191,16 +200,26 @@ function CustomComponent(props: CustomComponentProps) {
})} })}
> >
{props.needsOverlay && <OverlayDiv data-testid="iframe-overlay" />} {props.needsOverlay && <OverlayDiv data-testid="iframe-overlay" />}
<StyledIframe <WidgetStyleContainer
height={props.height} backgroundColor={props.backgroundColor}
onLoad={() => { borderColor={props.borderColor}
setLoading(false); borderRadius={props.borderRadius}
}} borderWidth={props.borderWidth}
ref={iframe} boxShadow={props.boxShadow}
sandbox="allow-scripts allow-downloads" widgetId={props.widgetId}
srcDoc={srcDoc} >
width={props.width} <StyledIframe
/> height={props.height}
loading="lazy"
onLoad={() => {
setLoading(false);
}}
ref={iframe}
sandbox="allow-scripts allow-downloads"
srcDoc={srcDoc}
width={props.width}
/>
</WidgetStyleContainer>
</div> </div>
); );
} }
@ -221,6 +240,30 @@ export interface CustomComponentProps {
onConsole?: (type: string, message: string) => void; onConsole?: (type: string, message: string) => void;
renderMode: "EDITOR" | "DEPLOYED" | "BUILDER"; renderMode: "EDITOR" | "DEPLOYED" | "BUILDER";
theme: AppThemeProperties; theme: AppThemeProperties;
borderColor?: Color;
backgroundColor?: Color;
borderWidth?: number;
borderRadius?: number;
boxShadow?: BoxShadow;
widgetId: string;
} }
export default CustomComponent; /**
* TODO: Balaji soundararajan - to refactor code to move out selected widget details to platform
*/
export const mapStateToProps = (
state: AppState,
ownProps: CustomComponentProps,
) => {
const isPreviewMode = combinedPreviewModeSelector(state);
const appMode = getAppMode(state);
return {
needsOverlay:
appMode == APP_MODE.EDIT &&
!isPreviewMode &&
ownProps.widgetId !== getWidgetPropsForPropertyPane(state)?.widgetId,
};
};
export default connect(mapStateToProps)(CustomComponent);

View File

@ -7,8 +7,7 @@ export default {
height: calc(var(--appsmith-ui-height) * 1px); height: calc(var(--appsmith-ui-height) * 1px);
width: calc(var(--appsmith-ui-width) * 1px); width: calc(var(--appsmith-ui-width) * 1px);
justify-content: center; justify-content: center;
border-radius: var(--appsmith-theme-borderRadius); border-radius: 0px;
box-shadow: var(--appsmith-theme-boxShadow);
} }
.tip-container { .tip-container {
@ -37,13 +36,14 @@ export default {
.button-container button { .button-container button {
margin: 0 10px; margin: 0 10px;
border-radius: var(--appsmith-theme-borderRadius) !important;
} }
.button-container button.primary { .button-container button.primary {
background: var(--appsmith-theme-primaryColor) !important; background: var(--appsmith-theme-primaryColor) !important;
} }
.button-container button.reset { .button-container button.reset:not([disabled]) {
color: var(--appsmith-theme-primaryColor) !important; color: var(--appsmith-theme-primaryColor) !important;
border-color: var(--appsmith-theme-primaryColor) !important; border-color: var(--appsmith-theme-primaryColor) !important;
}`, }`,
@ -75,7 +75,7 @@ function App() {
</div> </div>
<div className="button-container"> <div className="button-container">
<Button className="primary" onClick={handleNext} type="primary">Next Tip</Button> <Button className="primary" onClick={handleNext} type="primary">Next Tip</Button>
<Button className="reset" onClick={handleReset}>Reset</Button> <Button className="reset" disabled={currentIndex === 0} onClick={handleReset}>Reset</Button>
</div> </div>
</Card> </Card>
); );
@ -93,8 +93,7 @@ appsmith.onReady(() => {
height: calc(var(--appsmith-ui-height) * 1px); height: calc(var(--appsmith-ui-height) * 1px);
width: calc(var(--appsmith-ui-width) * 1px); width: calc(var(--appsmith-ui-width) * 1px);
justify-content: center; justify-content: center;
border-radius: var(--appsmith-theme-borderRadius); border-radius: 0px;
box-shadow: var(--appsmith-theme-boxShadow);
} }
.tip-container { .tip-container {
@ -123,13 +122,14 @@ appsmith.onReady(() => {
.button-container button { .button-container button {
margin: 0 10px; margin: 0 10px;
border-radius: var(--appsmith-theme-borderRadius) !important;
} }
.button-container button.primary { .button-container button.primary {
background: var(--appsmith-theme-primaryColor) !important; background: var(--appsmith-theme-primaryColor) !important;
} }
.button-container button.reset { .button-container button.reset:not([disabled]) {
color: var(--appsmith-theme-primaryColor) !important; color: var(--appsmith-theme-primaryColor) !important;
border-color: var(--appsmith-theme-primaryColor) !important; border-color: var(--appsmith-theme-primaryColor) !important;
}`, }`,
@ -161,6 +161,7 @@ function App() {
type: "primary" type: "primary"
}, "Next Tip"), /*#__PURE__*/React.createElement(Button, { }, "Next Tip"), /*#__PURE__*/React.createElement(Button, {
className: "reset", className: "reset",
disabled: currentIndex === 0,
onClick: handleReset onClick: handleReset
}, "Reset"))); }, "Reset")));
} }

View File

@ -8,9 +8,13 @@ import BaseWidget from "widgets/BaseWidget";
import CustomComponent from "../component"; import CustomComponent from "../component";
import IconSVG from "../icon.svg"; import IconSVG from "../icon.svg";
import { RenderModes, WIDGET_TAGS } from "constants/WidgetConstants"; import { WIDGET_TAGS } from "constants/WidgetConstants";
import { ValidationTypes } from "constants/WidgetValidation"; import { ValidationTypes } from "constants/WidgetValidation";
import type { AppThemeProperties, SetterConfig } from "entities/AppTheming"; import type {
AppThemeProperties,
SetterConfig,
Stylesheet,
} from "entities/AppTheming";
import { DefaultAutocompleteDefinitions } from "widgets/WidgetUtils"; import { DefaultAutocompleteDefinitions } from "widgets/WidgetUtils";
import type { AutocompletionDefinitions } from "WidgetProvider/constants"; import type { AutocompletionDefinitions } from "WidgetProvider/constants";
import { FEATURE_FLAG } from "@appsmith/entities/FeatureFlag"; import { FEATURE_FLAG } from "@appsmith/entities/FeatureFlag";
@ -19,9 +23,14 @@ import { DEFAULT_MODEL } from "../constants";
import defaultApp from "./defaultApp"; import defaultApp from "./defaultApp";
import type { ExtraDef } from "utils/autocomplete/defCreatorUtils"; import type { ExtraDef } from "utils/autocomplete/defCreatorUtils";
import { generateTypeDef } from "utils/autocomplete/defCreatorUtils"; import { generateTypeDef } from "utils/autocomplete/defCreatorUtils";
import { CUSTOM_WIDGET_DOC_URL } from "pages/Editor/CustomWidgetBuilder/constants"; import {
CUSTOM_WIDGET_DEFAULT_MODEL_DOC_URL,
CUSTOM_WIDGET_DOC_URL,
} from "pages/Editor/CustomWidgetBuilder/constants";
import { Link } from "design-system"; import { Link } from "design-system";
import styled from "styled-components"; import styled from "styled-components";
import { ReduxActionTypes } from "@appsmith/constants/ReduxActionConstants";
import { Colors } from "constants/Colors";
const StyledLink = styled(Link)` const StyledLink = styled(Link)`
display: inline-block; display: inline-block;
@ -52,7 +61,7 @@ class CustomWidget extends BaseWidget<CustomWidgetProps, WidgetState> {
return { return {
widgetName: "Custom", widgetName: "Custom",
rows: 30, rows: 30,
columns: 20, columns: 23,
version: 1, version: 1,
onResetClick: "{{showAlert('Successfully reset!!', '');}}", onResetClick: "{{showAlert('Successfully reset!!', '');}}",
events: ["onResetClick"], events: ["onResetClick"],
@ -62,6 +71,9 @@ class CustomWidget extends BaseWidget<CustomWidgetProps, WidgetState> {
uncompiledSrcDoc: defaultApp.uncompiledSrcDoc, uncompiledSrcDoc: defaultApp.uncompiledSrcDoc,
theme: "{{appsmith.theme}}", theme: "{{appsmith.theme}}",
dynamicBindingPathList: [{ key: "theme" }], dynamicBindingPathList: [{ key: "theme" }],
borderColor: Colors.GREY_5,
borderWidth: "1",
backgroundColor: "#FFFFFF",
}; };
} }
@ -83,6 +95,13 @@ class CustomWidget extends BaseWidget<CustomWidgetProps, WidgetState> {
}; };
} }
static getStylesheetConfig(): Stylesheet {
return {
borderRadius: "{{appsmith.theme.borderRadius.appBorderRadius}}",
boxShadow: "{{appsmith.theme.boxShadow.appBoxShadow}}",
};
}
static getPropertyPaneContentConfig() { static getPropertyPaneContentConfig() {
return [ return [
{ {
@ -126,7 +145,7 @@ class CustomWidget extends BaseWidget<CustomWidgetProps, WidgetState> {
kind="secondary" kind="secondary"
rel="noopener noreferrer" rel="noopener noreferrer"
target="_blank" target="_blank"
to={CUSTOM_WIDGET_DOC_URL} to={CUSTOM_WIDGET_DEFAULT_MODEL_DOC_URL}
> >
Read more Read more
</StyledLink> </StyledLink>
@ -190,6 +209,7 @@ class CustomWidget extends BaseWidget<CustomWidgetProps, WidgetState> {
}, },
}, },
dependencies: ["events"], dependencies: ["events"],
helpText: "when the event is triggered from custom widget",
})); }));
}, },
children: [ children: [
@ -217,7 +237,71 @@ class CustomWidget extends BaseWidget<CustomWidgetProps, WidgetState> {
} }
static getPropertyPaneStyleConfig() { static getPropertyPaneStyleConfig() {
return []; return [
{
sectionName: "Color",
children: [
{
helpText: "Use a html color name, HEX, RGB or RGBA value",
placeholderText: "#FFFFFF / Gray / rgb(255, 99, 71)",
propertyName: "backgroundColor",
label: "Background color",
controlType: "COLOR_PICKER",
isJSConvertible: true,
isBindProperty: true,
isTriggerProperty: false,
validation: { type: ValidationTypes.TEXT },
},
{
helpText: "Use a html color name, HEX, RGB or RGBA value",
placeholderText: "#FFFFFF / Gray / rgb(255, 99, 71)",
propertyName: "borderColor",
label: "Border color",
controlType: "COLOR_PICKER",
isBindProperty: true,
isTriggerProperty: false,
validation: { type: ValidationTypes.TEXT },
},
],
},
{
sectionName: "Border and shadow",
children: [
{
helpText: "Enter value for border width",
propertyName: "borderWidth",
label: "Border width",
placeholderText: "Enter value in px",
controlType: "INPUT_TEXT",
isBindProperty: true,
isTriggerProperty: false,
validation: { type: ValidationTypes.NUMBER },
postUpdateAction: ReduxActionTypes.CHECK_CONTAINERS_FOR_AUTO_HEIGHT,
},
{
propertyName: "borderRadius",
label: "Border radius",
helpText: "Rounds the corners of the widgets's outer border edge",
controlType: "BORDER_RADIUS_OPTIONS",
isJSConvertible: true,
isBindProperty: true,
isTriggerProperty: false,
validation: { type: ValidationTypes.TEXT },
},
{
propertyName: "boxShadow",
label: "Box shadow",
helpText:
"Enables you to cast a drop shadow from the frame of the widget",
controlType: "BOX_SHADOW_OPTIONS",
isJSConvertible: true,
isBindProperty: true,
isTriggerProperty: false,
validation: { type: ValidationTypes.TEXT },
},
],
},
];
} }
static getDerivedPropertiesMap(): DerivedPropertiesMap { static getDerivedPropertiesMap(): DerivedPropertiesMap {
@ -270,17 +354,19 @@ class CustomWidget extends BaseWidget<CustomWidgetProps, WidgetState> {
getWidgetView() { getWidgetView() {
return ( return (
<CustomComponent <CustomComponent
backgroundColor={this.props.backgroundColor}
borderColor={this.props.borderColor}
borderRadius={this.props.borderRadius}
borderWidth={this.props.borderWidth}
boxShadow={this.props.boxShadow}
execute={this.execute} execute={this.execute}
height={this.props.componentHeight} height={this.props.componentHeight}
model={this.props.model || {}} model={this.props.model || {}}
needsOverlay={
this.props.renderMode === RenderModes.CANVAS &&
!this.props.isWidgetSelected
}
renderMode={this.getRenderMode()} renderMode={this.getRenderMode()}
srcDoc={this.props.srcDoc} srcDoc={this.props.srcDoc}
theme={this.props.theme} theme={this.props.theme}
update={this.update} update={this.update}
widgetId={this.props.widgetId}
width={this.props.componentWidth} width={this.props.componentWidth}
/> />
); );