Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: add custom widget to anvil #37875

Closed
wants to merge 2 commits into from
Closed

Conversation

jsartisan
Copy link
Contributor

@jsartisan jsartisan commented Dec 2, 2024

Summary by CodeRabbit

Release Notes

  • New Features

    • Introduced a custom widget with enhanced console logging and error reporting.
    • Added configuration options for widget properties, including visibility and event handling.
    • Implemented a robust communication system between the widget and its parent window.
    • Added a CSS reset for consistent styling across elements.
  • Bug Fixes

    • Improved error handling for messaging and state updates within the widget.
  • Documentation

    • Enhanced documentation for widget configuration and usage, including tips for effective integration.
  • Tests

    • Expanded test coverage for widget functionality and event handling.
  • Chores

    • Updated widget mapping to include the new custom widget in the application.

@jsartisan jsartisan requested a review from a team as a code owner December 2, 2024 09:23
@jsartisan jsartisan requested review from jacquesikot and removed request for a team December 2, 2024 09:23
@jsartisan
Copy link
Contributor Author

/build-deploy-preview skip-tests=true

Copy link
Contributor

coderabbitai bot commented Dec 2, 2024

Walkthrough

The pull request introduces a new custom widget framework within the UI builder, comprising several JavaScript and TypeScript files. Key additions include a custom console implementation, configuration objects for widget properties, and a structured communication system between the widget and its parent window. The changes also enhance test coverage for the widget's functionality and introduce a CSS reset for consistent styling. Overall, the modifications aim to improve widget interaction, logging, and configuration management.

Changes

File Change Summary
app/client/src/modules/ui-builder/ui/wds/WDSCustomWidget/component/appsmithConsole.js New file implementing a custom console that intercepts console methods and sends messages to the parent window.
app/client/src/modules/ui-builder/ui/wds/WDSCustomWidget/component/constants.ts New constants for widget load events and a function to generate an application script schema.
app/client/src/modules/ui-builder/ui/wds/WDSCustomWidget/component/customWidgetScript.test.ts New test suite for customWidgetScript, covering message handling, CSS variable generation, and API function behavior.
app/client/src/modules/ui-builder/ui/wds/WDSCustomWidget/component/customWidgetscript.js New file for widget communication, defining event constants and a messaging channel to the parent window.
app/client/src/modules/ui-builder/ui/wds/WDSCustomWidget/component/index.tsx New CustomComponent integrating an iframe for rendering custom widgets, managing state and message events.
app/client/src/modules/ui-builder/ui/wds/WDSCustomWidget/component/reset.css New CSS reset stylesheet to standardize styling across HTML elements.
app/client/src/modules/ui-builder/ui/wds/WDSCustomWidget/component/styles.module.css New CSS class .iframe for styling the iframe.
app/client/src/modules/ui-builder/ui/wds/WDSCustomWidget/config/anvilConfig.ts New configuration object for widget sizing.
app/client/src/modules/ui-builder/ui/wds/WDSCustomWidget/config/autocompleteConfig.ts New function for generating autocomplete configurations.
app/client/src/modules/ui-builder/ui/wds/WDSCustomWidget/config/defaultsConfig.ts New configuration object defining properties for the custom widget.
app/client/src/modules/ui-builder/ui/wds/WDSCustomWidget/config/index.ts Export statements for various configuration settings related to the custom widget.
app/client/src/modules/ui-builder/ui/wds/WDSCustomWidget/config/metaConfig.ts New configuration object defining widget characteristics.
app/client/src/modules/ui-builder/ui/wds/WDSCustomWidget/config/propertyPaneConfig/contentConfig.tsx New configuration object for the property pane layout of the custom widget.
app/client/src/modules/ui-builder/ui/wds/WDSCustomWidget/config/propertyPaneConfig/index.ts Export statements for property pane style and content configurations.
app/client/src/modules/ui-builder/ui/wds/WDSCustomWidget/config/propertyPaneConfig/styleConfig.ts New configuration file for property pane styles.
app/client/src/modules/ui-builder/ui/wds/WDSCustomWidget/config/setterConfig.ts New constant defining visibility settings for the widget.
app/client/src/modules/ui-builder/ui/wds/WDSCustomWidget/constants.ts New constant DEFAULT_MODEL providing guidance on widget configuration.
app/client/src/modules/ui-builder/ui/wds/WDSCustomWidget/index.ts Export statement for WDSCustomWidget from the module.
app/client/src/modules/ui-builder/ui/wds/WDSCustomWidget/types.ts New interface CustomWidgetProps extending WidgetProps with an events property.
app/client/src/modules/ui-builder/ui/wds/WDSCustomWidget/widget/defaultApp.ts New file defining the structure of the custom widget application with uncompiled and compiled source documents.
app/client/src/modules/ui-builder/ui/wds/WDSCustomWidget/widget/index.tsx New class WDSCustomWidget extending BaseWidget, defining configuration management and event handling methods.
app/client/src/modules/ui-builder/ui/wds/constants.ts Updated WDS_V2_WIDGET_MAP to include WDS_CUSTOM_WIDGET.
app/client/src/widgets/index.ts New import for WDSCustomWidget, updating WDSWidgets and Widgets arrays.

Possibly related PRs

Suggested labels

Enhancement, Task, Anvil Pod, Widgets & Accelerators Pod, ok-to-test

Suggested reviewers

  • KelvinOm
  • sagar-qa007

"In the land of code where widgets play,
A custom console brightens the way.
With events and styles, they dance and twirl,
Enhancing the app, giving it a whirl.
So here's to the changes, both big and small,
In the world of widgets, we're having a ball!" 🎉


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

github-actions bot commented Dec 2, 2024

Deploying Your Preview: https://github.com/appsmithorg/appsmith/actions/runs/12116317297.
Workflow: On demand build Docker image and deploy preview.
skip-tests: true.
env: ``.
PR: 37875.
recreate: .

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 15

🧹 Outside diff range and nitpick comments (19)
app/client/src/modules/ui-builder/ui/wds/WDSCustomWidget/component/index.tsx (1)

111-112: Simplify code with optional chaining

Use optional chaining to make property access safer and more concise.

Apply these diffs:

At lines 111-112:

- props.onConsole &&
-   props.onConsole(message.data.type, message.data.args);
+ props.onConsole?.(message.data.type, message.data.args);

At line 124:

- if (iframe.current && iframe.current.contentWindow && isIframeReady) {
+ if (iframe.current?.contentWindow && isIframeReady) {

At line 136:

- if (iframe.current && iframe.current.contentWindow && isIframeReady) {
+ if (iframe.current?.contentWindow && isIframeReady) {

At line 151:

- if (iframe.current && iframe.current.contentWindow && isIframeReady) {
+ if (iframe.current?.contentWindow && isIframeReady) {

Also applies to: 124-124, 136-136, 151-151

🧰 Tools
🪛 Biome (1.9.4)

[error] 111-112: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

app/client/src/modules/ui-builder/ui/wds/WDSCustomWidget/component/customWidgetscript.js (1)

95-106: Remove unnecessary try-catch block

The try-catch block that only rethrows the error is redundant and can be removed.

Apply this diff:

- try {
    postMessageQueue.push({
      type,
      data,
    });

    scheduleMicrotaskToflushPostMessageQueue();
- } catch (e) {
-   throw e;
- }
🧰 Tools
🪛 Biome (1.9.4)

[error] 104-104: The catch clause that only rethrows the original error is useless.

An unnecessary catch clause can be confusing.
Unsafe fix: Remove the try/catch clause.

(lint/complexity/noUselessCatch)

app/client/src/modules/ui-builder/ui/wds/WDSCustomWidget/config/setterConfig.ts (1)

1-8: Add TypeScript type definitions for configuration object.

The configuration object would benefit from explicit TypeScript interfaces to ensure type safety and better IDE support.

+interface SetterConfig {
+  path: string;
+  type: string;
+}
+
+interface WidgetSetterConfig {
+  __setters: {
+    [key: string]: SetterConfig;
+  };
+}
+
-export const setterConfig = {
+export const setterConfig: WidgetSetterConfig = {
   __setters: {
     setVisibility: {
       path: "isVisible",
       type: "boolean",
     },
   },
 };
app/client/src/modules/ui-builder/ui/wds/WDSCustomWidget/config/anvilConfig.ts (1)

1-9: Extract magic values into constants and add type definitions.

The configuration would be more maintainable with explicit types and constants for breakpoints and sizing values.

+const BREAKPOINTS = {
+  MOBILE: "180px",
+} as const;
+
+const SIZING = {
+  FULL: "100%",
+  SIZING_30: "sizing-30",
+} as const;
+
+interface WidgetSizeConfig {
+  base: string;
+  [breakpoint: string]: string;
+}
+
+interface AnvilConfig {
+  isLargeWidget: boolean;
+  widgetSize: {
+    minWidth: WidgetSizeConfig;
+  };
+}
+
-export const anvilConfig = {
+export const anvilConfig: AnvilConfig = {
   isLargeWidget: true,
   widgetSize: {
     minWidth: {
-      base: "100%",
-      "180px": "sizing-30",
+      base: SIZING.FULL,
+      [BREAKPOINTS.MOBILE]: SIZING.SIZING_30,
     },
   },
 };
app/client/src/modules/ui-builder/ui/wds/WDSCustomWidget/config/metaConfig.ts (2)

14-14: Consider expanding search tags for better discoverability.

The search tags array contains only "external". Consider adding more relevant terms to improve widget discoverability.

-  searchTags: ["external"],
+  searchTags: ["external", "custom", "widget", "component"],

7-17: Add TypeScript interface for meta configuration.

Define a clear contract for the meta configuration structure using TypeScript interfaces.

+interface WidgetMetaConfig {
+  name: string;
+  iconSVG: string;
+  thumbnailSVG: string;
+  needsMeta: boolean;
+  isCanvas: boolean;
+  tags: string[];
+  searchTags: string[];
+  isSearchWildcard: boolean;
+  hideCard: boolean;
+}
+
-export const metaConfig = {
+export const metaConfig: WidgetMetaConfig = {
   name: "Custom",
   iconSVG: IconSVG,
   thumbnailSVG: ThumbnailSVG,
   needsMeta: true,
   isCanvas: false,
   tags: [WIDGET_TAGS.DISPLAY],
   searchTags: ["external"],
   isSearchWildcard: true,
   hideCard: isAirgapped(),
 };
app/client/src/modules/ui-builder/ui/wds/WDSCustomWidget/config/autocompleteConfig.ts (1)

7-13: Consider adding JSDoc documentation for the function.

While the function implementation is clean and follows good practices, adding documentation would help explain the purpose and expected behavior of the configuration.

+/**
+ * Generates autocomplete configuration for custom widgets
+ * @param widget - The custom widget properties
+ * @param extraDefsToDefine - Optional additional type definitions
+ * @returns Configuration object with visibility and model definitions
+ */
 export const autocompleteConfig = (
   widget: CustomWidgetProps,
   extraDefsToDefine?: ExtraDef,
 ) => ({
app/client/src/modules/ui-builder/ui/wds/WDSCustomWidget/constants.ts (1)

2-9: Consider using a typed constant instead of a string literal.

While the JSON string works, using a typed constant would provide better type safety and IDE support.

-export const DEFAULT_MODEL = `{
-  "tips": [
-    "Pass data to this widget in the default model field",
-    "Access data in the javascript file using the appsmith.model variable",
-    "Create events in the widget and trigger them in the javascript file using appsmith.triggerEvent('eventName')",
-    "Access data in CSS as var(--appsmith-model-{property-name})"
-  ]
-}`;
+interface DefaultModel {
+  tips: string[];
+}
+
+export const DEFAULT_MODEL: DefaultModel = {
+  tips: [
+    "Pass data to this widget in the default model field",
+    "Access data in the javascript file using the appsmith.model variable",
+    "Create events in the widget and trigger them in the javascript file using appsmith.triggerEvent('eventName')",
+    "Access data in CSS as var(--appsmith-model-{property-name})"
+  ]
+};
app/client/src/modules/ui-builder/ui/wds/WDSCustomWidget/config/defaultsConfig.ts (1)

9-10: Consider making dimensions configurable

Hardcoded values for rows and columns might not be suitable for all use cases. Consider making these configurable or responsive.

app/client/src/modules/ui-builder/ui/wds/WDSCustomWidget/component/appsmithConsole.js (1)

32-34: Enhance error event handling

The error event handler could be more comprehensive by including additional error details.

 window.addEventListener("error", (event) => {
-  postMessage("error", [event.message]);
+  postMessage("error", [{
+    message: event.message,
+    filename: event.filename,
+    lineno: event.lineno,
+    colno: event.colno
+  }]);
 });
app/client/src/modules/ui-builder/ui/wds/WDSCustomWidget/config/propertyPaneConfig/styleConfig.ts (1)

3-24: LGTM with enhancement suggestion

The style configuration is well-structured with proper validation and documentation. Consider adding more enterprise-grade styling options like:

  • Custom border radius
  • Shadow depth
  • Background opacity
app/client/src/modules/ui-builder/ui/wds/WDSCustomWidget/widget/defaultApp.ts (1)

8-51: Optimize CSS structure

Consider consolidating CSS rules to reduce duplication between uncompiled and compiled versions. Use a CSS preprocessor or CSS-in-JS solution for better maintainability.

Also applies to: 100-148

app/client/src/modules/ui-builder/ui/wds/WDSCustomWidget/component/customWidgetScript.test.ts (4)

61-102: Improve async test reliability

Replace setTimeout with proper Jest async utilities for more reliable tests.

-    return new Promise((resolve) => {
-      setTimeout(() => {
+    return act(async () => {
         expect(window.parent.postMessage).toHaveBeenCalledWith(
           // ...
         );
-        resolve(true);
-      });
-    });
+    });

105-124: Enhance CSS variable test coverage

Add test cases for:

  • Empty source object
  • Nested properties
  • Invalid values

130-152: Extract common test setup

Move the repeated event setup logic into a helper function to reduce duplication.

function setupMessageEventHandling() {
  const events = new Map();
  window.addEventListener = (type: string, handler: Function) => {
    events.set(type, handler);
  };
  window.triggerEvent = (type: string, event: unknown) => {
    events.get(type)(event);
  };
  return events;
}

384-386: Remove TODO comment and fix type casting

Replace the TODO comment with proper type definition for postMessage.

(window.parent.postMessage as jest.Mock).mockClear();
app/client/src/modules/ui-builder/ui/wds/WDSCustomWidget/config/propertyPaneConfig/contentConfig.tsx (3)

33-33: Type 'widget' as 'CustomWidgetProps' in 'dynamicDependencies'

The parameter 'widget' in 'dynamicDependencies' relies on properties from 'CustomWidgetProps' like 'events'. Update its type for accurate typing.


97-98: Use 'CustomWidgetProps' instead of 'WidgetProps'

In 'generateDynamicProperties', 'widgetProps' should be typed as 'CustomWidgetProps' since it accesses 'events' specific to custom widgets.


35-46: Consider extracting inline styles to styled components

Move the inline styles in 'helperText' to styled components for consistency.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 2b64e65 and a62717f.

⛔ Files ignored due to path filters (2)
  • app/client/src/modules/ui-builder/ui/wds/WDSCustomWidget/icon.svg is excluded by !**/*.svg
  • app/client/src/modules/ui-builder/ui/wds/WDSCustomWidget/thumbnail.svg is excluded by !**/*.svg
📒 Files selected for processing (23)
  • app/client/src/modules/ui-builder/ui/wds/WDSCustomWidget/component/appsmithConsole.js (1 hunks)
  • app/client/src/modules/ui-builder/ui/wds/WDSCustomWidget/component/constants.ts (1 hunks)
  • app/client/src/modules/ui-builder/ui/wds/WDSCustomWidget/component/customWidgetScript.test.ts (1 hunks)
  • app/client/src/modules/ui-builder/ui/wds/WDSCustomWidget/component/customWidgetscript.js (1 hunks)
  • app/client/src/modules/ui-builder/ui/wds/WDSCustomWidget/component/index.tsx (1 hunks)
  • app/client/src/modules/ui-builder/ui/wds/WDSCustomWidget/component/reset.css (1 hunks)
  • app/client/src/modules/ui-builder/ui/wds/WDSCustomWidget/component/styles.module.css (1 hunks)
  • app/client/src/modules/ui-builder/ui/wds/WDSCustomWidget/config/anvilConfig.ts (1 hunks)
  • app/client/src/modules/ui-builder/ui/wds/WDSCustomWidget/config/autocompleteConfig.ts (1 hunks)
  • app/client/src/modules/ui-builder/ui/wds/WDSCustomWidget/config/defaultsConfig.ts (1 hunks)
  • app/client/src/modules/ui-builder/ui/wds/WDSCustomWidget/config/index.ts (1 hunks)
  • app/client/src/modules/ui-builder/ui/wds/WDSCustomWidget/config/metaConfig.ts (1 hunks)
  • app/client/src/modules/ui-builder/ui/wds/WDSCustomWidget/config/propertyPaneConfig/contentConfig.tsx (1 hunks)
  • app/client/src/modules/ui-builder/ui/wds/WDSCustomWidget/config/propertyPaneConfig/index.ts (1 hunks)
  • app/client/src/modules/ui-builder/ui/wds/WDSCustomWidget/config/propertyPaneConfig/styleConfig.ts (1 hunks)
  • app/client/src/modules/ui-builder/ui/wds/WDSCustomWidget/config/setterConfig.ts (1 hunks)
  • app/client/src/modules/ui-builder/ui/wds/WDSCustomWidget/constants.ts (1 hunks)
  • app/client/src/modules/ui-builder/ui/wds/WDSCustomWidget/index.ts (1 hunks)
  • app/client/src/modules/ui-builder/ui/wds/WDSCustomWidget/types.ts (1 hunks)
  • app/client/src/modules/ui-builder/ui/wds/WDSCustomWidget/widget/defaultApp.ts (1 hunks)
  • app/client/src/modules/ui-builder/ui/wds/WDSCustomWidget/widget/index.tsx (1 hunks)
  • app/client/src/modules/ui-builder/ui/wds/constants.ts (1 hunks)
  • app/client/src/widgets/index.ts (2 hunks)
✅ Files skipped from review due to trivial changes (5)
  • app/client/src/modules/ui-builder/ui/wds/WDSCustomWidget/component/styles.module.css
  • app/client/src/modules/ui-builder/ui/wds/WDSCustomWidget/index.ts
  • app/client/src/modules/ui-builder/ui/wds/WDSCustomWidget/config/propertyPaneConfig/index.ts
  • app/client/src/modules/ui-builder/ui/wds/WDSCustomWidget/component/reset.css
  • app/client/src/modules/ui-builder/ui/wds/WDSCustomWidget/config/index.ts
🧰 Additional context used
📓 Learnings (2)
app/client/src/modules/ui-builder/ui/wds/WDSCustomWidget/config/anvilConfig.ts (1)
Learnt from: riodeuno
PR: appsmithorg/appsmith#30351
File: app/client/src/widgets/wds/WDSModalWidget/config/anvilConfig.ts:1-6
Timestamp: 2024-11-12T08:11:43.278Z
Learning: The `anvilConfig` is optional for widgets that do not participate in the main container's layout flow, such as the modal widget in the current context.
app/client/src/modules/ui-builder/ui/wds/WDSCustomWidget/config/propertyPaneConfig/styleConfig.ts (1)
Learnt from: riodeuno
PR: appsmithorg/appsmith#30351
File: app/client/src/widgets/wds/WDSModalWidget/config/propertyPaneConfig/styleConfig.ts:1-3
Timestamp: 2024-11-12T08:11:25.417Z
Learning: The `WDSModalWidget` currently has no style configurations, and they will be added in the future if needed.
🪛 Biome (1.9.4)
app/client/src/modules/ui-builder/ui/wds/WDSCustomWidget/component/appsmithConsole.js

[error] 23-23: Unsafe usage of 'return'.

'return' in 'finally' overwrites the control flow statements inside 'try' and 'catch'.

(lint/correctness/noUnsafeFinally)

app/client/src/modules/ui-builder/ui/wds/WDSCustomWidget/component/index.tsx

[error] 102-102: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Unsafe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 111-112: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 124-124: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 136-136: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 151-151: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

app/client/src/modules/ui-builder/ui/wds/WDSCustomWidget/component/customWidgetscript.js

[error] 104-104: The catch clause that only rethrows the original error is useless.

An unnecessary catch clause can be confusing.
Unsafe fix: Remove the try/catch clause.

(lint/complexity/noUselessCatch)


[error] 23-23: Use Array.isArray() instead of instanceof Array.

instanceof Array returns false for array-like objects and arrays from other execution contexts.
Unsafe fix: Use Array.isArray() instead.

(lint/suspicious/useIsArray)

app/client/src/modules/ui-builder/ui/wds/WDSCustomWidget/widget/index.tsx

[error] 64-64: Do not access Object.prototype method 'hasOwnProperty' from target object.

It's recommended using Object.hasOwn() instead of using Object.hasOwnProperty().
See MDN web docs for more details.

(lint/suspicious/noPrototypeBuiltins)

🔇 Additional comments (5)
app/client/src/modules/ui-builder/ui/wds/WDSCustomWidget/types.ts (1)

1-5: Interface CustomWidgetProps looks good

The CustomWidgetProps interface is well-defined and properly extends WidgetProps.

app/client/src/modules/ui-builder/ui/wds/WDSCustomWidget/config/autocompleteConfig.ts (1)

1-5: LGTM! Clean imports with proper type definitions.

The imports are well-organized and properly typed.

app/client/src/modules/ui-builder/ui/wds/WDSCustomWidget/component/constants.ts (1)

1-5: LGTM! Well-defined event constants.

The load events are clearly defined and follow a logical progression.

app/client/src/modules/ui-builder/ui/wds/constants.ts (1)

64-64: LGTM! Widget constant follows naming convention

The addition maintains consistency with existing widget mapping pattern.

app/client/src/widgets/index.ts (1)

90-90: LGTM! Widget registration follows established pattern

The import and registration of WDSCustomWidget maintain consistency with existing widget organization.

Also applies to: 189-189

Comment on lines +55 to +57
// TODO: Fix this the next time the file is edited
// eslint-disable-next-line @typescript-eslint/no-explicit-any
static getMetaPropertiesMap(): Record<string, any> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Address the TODO and avoid using any type

There's a TODO indicating this code needs fixing. Also, replace any with a more specific type for better type safety.

Apply this diff:

- // TODO: Fix this the next time the file is edited
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- static getMetaPropertiesMap(): Record<string, any> {
+ static getMetaPropertiesMap(): Record<string, unknown> {

Would you like assistance in addressing this TODO and specifying the appropriate type?

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// TODO: Fix this the next time the file is edited
// eslint-disable-next-line @typescript-eslint/no-explicit-any
static getMetaPropertiesMap(): Record<string, any> {
static getMetaPropertiesMap(): Record<string, unknown> {

}

execute = (eventName: string, contextObj: Record<string, unknown>) => {
if (this.props.hasOwnProperty(eventName)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Use safer property check method

Accessing hasOwnProperty directly can lead to issues if the property is overridden. Use Object.prototype.hasOwnProperty.call(this.props, eventName) instead.

Apply this diff:

- if (this.props.hasOwnProperty(eventName)) {
+ if (Object.prototype.hasOwnProperty.call(this.props, eventName)) {

Committable suggestion skipped: line range outside the PR's diff.

🧰 Tools
🪛 Biome (1.9.4)

[error] 64-64: Do not access Object.prototype method 'hasOwnProperty' from target object.

It's recommended using Object.hasOwn() instead of using Object.hasOwnProperty().
See MDN web docs for more details.

(lint/suspicious/noPrototypeBuiltins)

Comment on lines +102 to +109
const height = message.data.height;

if (props.renderMode !== "BUILDER" && height) {
iframe.current?.style.setProperty("height", `${height}px`);
setHeight(height);
}

break;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Wrap switch case code block to prevent variable scope leakage

Declare const height within a block to ensure it doesn't affect other cases.

Apply this diff:

case EVENTS.CUSTOM_WIDGET_UPDATE_HEIGHT:
+  {
    const height = message.data.height;

    if (props.renderMode !== "BUILDER" && height) {
      iframe.current?.style.setProperty("height", `${height}px`);
      setHeight(height);
    }

    break;
+  }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const height = message.data.height;
if (props.renderMode !== "BUILDER" && height) {
iframe.current?.style.setProperty("height", `${height}px`);
setHeight(height);
}
break;
{
const height = message.data.height;
if (props.renderMode !== "BUILDER" && height) {
iframe.current?.style.setProperty("height", `${height}px`);
setHeight(height);
}
break;
}
🧰 Tools
🪛 Biome (1.9.4)

[error] 102-102: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Unsafe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)

function onMessage(type, fn) {
let eventHandlerList = onMessageMap.get(type);

if (eventHandlerList && eventHandlerList instanceof Array) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Use Array.isArray() for array checking

Replace instanceof Array with Array.isArray() for accurate array detection.

Apply this diff:

- if (eventHandlerList && eventHandlerList instanceof Array) {
+ if (eventHandlerList && Array.isArray(eventHandlerList)) {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (eventHandlerList && eventHandlerList instanceof Array) {
if (eventHandlerList && Array.isArray(eventHandlerList)) {
🧰 Tools
🪛 Biome (1.9.4)

[error] 23-23: Use Array.isArray() instead of instanceof Array.

instanceof Array returns false for array-like objects and arrays from other execution contexts.
Unsafe fix: Use Array.isArray() instead.

(lint/suspicious/useIsArray)

Comment on lines +15 to +20
theme: {
primaryColor: "",
backgroundColor: "",
borderRadius: "",
boxShadow: "",
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add type definitions for theme properties.

Theme properties should have proper type definitions for better type safety.

+interface ThemeConfig {
+  primaryColor: string;
+  backgroundColor: string;
+  borderRadius: string;
+  boxShadow: string;
+}
+
   theme: {
     primaryColor: "",
     backgroundColor: "",
     borderRadius: "",
     boxShadow: "",
   },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
theme: {
primaryColor: "",
backgroundColor: "",
borderRadius: "",
boxShadow: "",
},
interface ThemeConfig {
primaryColor: string;
backgroundColor: string;
borderRadius: string;
boxShadow: string;
}
theme: {
primaryColor: "",
backgroundColor: "",
borderRadius: "",
boxShadow: "",
},

Comment on lines +53 to +56
js: `import React from 'https://cdn.jsdelivr.net/npm/react@18.2.0/+esm'
import reactDom from 'https://cdn.jsdelivr.net/npm/react-dom@18.2.0/+esm'
import { Button, Card } from 'https://cdn.jsdelivr.net/npm/antd@5.11.1/+esm'
import Markdown from 'https://cdn.jsdelivr.net/npm/react-markdown@9.0.1/+esm';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Consider security implications of CDN dependencies

External CDN dependencies without integrity checks could pose security risks. Consider:

  1. Hosting dependencies locally
  2. Adding integrity checks
  3. Implementing fallback mechanisms

Also applies to: 149-152

Comment on lines +61 to +63
const handleNext = () => {
setCurrentIndex((prevIndex) => (prevIndex + 1) % appsmith.model.tips.length);
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add validation for appsmith.model.tips

Add null checks and validation for appsmith.model.tips to prevent runtime errors.

 const handleNext = () => {
+  if (!Array.isArray(appsmith.model.tips) || !appsmith.model.tips.length) return;
   setCurrentIndex((prevIndex) => (prevIndex + 1) % appsmith.model.tips.length);
 };

Also applies to: 156-158

Comment on lines +14 to +20
// TODO: Fix this the next time the file is edited
// eslint-disable-next-line @typescript-eslint/no-explicit-any
appsmith: any;
// TODO: Fix this the next time the file is edited
// eslint-disable-next-line @typescript-eslint/no-explicit-any
triggerEvent: any;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Replace any types with proper interfaces

Instead of using any and TODO comments, define proper interfaces for appsmith and triggerEvent.

interface AppsmithAPI {
  model: Record<string, unknown>;
  ui: { width: number; height: number };
  theme: { color: string };
  mode: string;
  onReady: (handler: () => void) => void;
  // Add other methods...
}

type TriggerEventFn = (type: string, event: MessageEvent) => void;

Comment on lines +1 to +412
window.triggerEvent("message", {
source: window.parent,
data: {
type: EVENTS.CUSTOM_WIDGET_THEME_UPDATE,
theme: {
color: "#000",
},
},
});

expect(window.appsmith.theme).toEqual({
color: "#000",
});

expect(handler).toHaveBeenCalledWith(
{
color: "#000",
},
{
color: "#fff",
},
);

handler.mockClear();
unlisten();

window.triggerEvent("message", {
source: window.parent,
data: {
type: EVENTS.CUSTOM_WIDGET_THEME_UPDATE,
theme: {
color: "#f0f",
},
},
});

expect(window.appsmith.theme).toEqual({
color: "#f0f",
});

expect(handler).not.toHaveBeenCalled();
});

it("should check API functions - updateModel", async () => {
window.appsmith.updateModel({
test: 4,
});

return new Promise((resolve) => {
setTimeout(() => {
expect(window.parent.postMessage).toHaveBeenCalledWith(
{
type: EVENTS.CUSTOM_WIDGET_UPDATE_MODEL,
data: {
test: 4,
},
key: expect.any(Number),
},
"*",
);

expect(window.appsmith.model).toEqual({
test: 4,
});

resolve(true);
});
});
});

it("should check API functions - triggerEvent", async () => {
// TODO: Fix this the next time the file is edited
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(window.parent.postMessage as any).mockClear();

window.appsmith.triggerEvent("test", {
test: 5,
});

return new Promise((resolve) => {
setTimeout(() => {
expect(window.parent.postMessage).toHaveBeenCalledWith(
{
type: EVENTS.CUSTOM_WIDGET_TRIGGER_EVENT,
data: {
eventName: "test",
contextObj: {
test: 5,
},
},
key: expect.any(Number),
},
"*",
);

resolve(true);
});
});
});
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codebase verification

Test suite needs error handling coverage

The current test suite lacks error scenarios testing. Add test cases for:

  • Invalid message formats
  • Parent window communication failures
  • Event handler error scenarios
  • Model/UI update failures
🔗 Analysis chain

Verify test coverage metrics

Ensure adequate test coverage for edge cases and error scenarios.


Let me check the error handling coverage more specifically.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check test coverage for the custom widget implementation

# Find all related test files
fd -e ts -e js . | grep -i "customwidget.*test"

# Search for error handling tests
rg -l "throw|catch|error" --type ts --type js | grep -i "customwidget"

Length of output: 1564


Script:

#!/bin/bash
# Check error handling in the implementation and test files

# Search for error handling in the implementation
ast-grep --pattern 'catch ($_) { $$$ }' app/client/src/modules/ui-builder/ui/wds/WDSCustomWidget/component/customWidgetscript.js

# Search for error test cases
rg "it\(.*error.*|test\(.*error.*|describe\(.*error.*" app/client/src/modules/ui-builder/ui/wds/WDSCustomWidget/component/customWidgetScript.test.ts

# Check message validation tests
rg "invalid|malformed|wrong" app/client/src/modules/ui-builder/ui/wds/WDSCustomWidget/component/customWidgetScript.test.ts

Length of output: 406

Comment on lines +149 to +151
onAdd: (widget: CustomWidgetProps, event: string) => {
const events = widget.events;

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Initialize 'events' to prevent potential errors

Ensure 'events' is initialized to an empty array to avoid issues when it's undefined.

Apply this diff:

onAdd: (widget: CustomWidgetProps, event: string) => {
-  const events = widget.events;
+  const events = widget.events || [];

  return {
    events: [...events, event],
  };
},

Committable suggestion skipped: line range outside the PR's diff.

@jsartisan
Copy link
Contributor Author

/build-deploy-preview skip-tests=true

Copy link

github-actions bot commented Dec 2, 2024

Deploying Your Preview: https://github.com/appsmithorg/appsmith/actions/runs/12116480051.
Workflow: On demand build Docker image and deploy preview.
skip-tests: true.
env: ``.
PR: 37875.
recreate: .

@jsartisan jsartisan closed this Dec 2, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant