amazon-quicksight-embedding-sdk icon indicating copy to clipboard operation
amazon-quicksight-embedding-sdk copied to clipboard

adding action to embedded visual

Open tcths opened this issue 1 year ago • 3 comments

Hello,

I am encountering the following issue when I try to add an action.

Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'addActions')

I am loading the package with the <Script> tag in NextJS. I have tried two ways to add the action. Both ways I get the error.

Way 1: Initializing the onMessage property of the content options. (as in docs here).

  const onMessage = async (messageEvent: any) => {
    const { eventName, message } = messageEvent;
    switch (eventName) {
      case "CONTENT_LOADED":
        await embeddedVisual!.addActions([
          {
            CustomActionId: "console-log-action",
            Name: "log to console",
            Trigger: "DATA_POINT_CLICK",
            Status: "ENABLED",
            ActionOperations: [
              {
                CallbackOperation: {
                  EmbeddingMessage: {},
                },
              },
            ],
          },
        ]);
        break;
      case "CALLBACK_OPERATION_INVOKED":
        console.log("callback operation invoked");
        console.log(message);
        break;
    }
  };

Way 2: calling a method on the embedded visual.

    embeddedVisual.setActions([
      {
        Name: "test click action",
        CustomActionId: `test-click-action`,
        Status: "ENABLED",
        Trigger: "DATA_POINT_CLICK", // or 'DATA_POINT_MENU'
        ActionOperations: [
          {
            CallbackOperation: {
              EmbeddingMessage: {},
            },
          },
        ],
      },
    ]);
  };

I have also tried both of these with the package loaded through node_modules/package.json. [email protected].

tcths avatar Aug 28 '24 20:08 tcths

The message is indicating embeddedVisual is undefined. I would check to make sure you're defining it properly before your invoking addActions.

yradchen avatar Aug 29 '24 15:08 yradchen

Thanks. Confirmed that in at least one of the cases described above, embeddedVisual is defined before invoking addActions.

Here is my code, if you would like to take a look. Here I am loading the library via the NextJS <Script> tag.

When I invoke addAction directly on the embeddedVisual (as currently shown in the code), I get errors

Failed to execute 'postMessage' on 'DOMWindow': The target origin provided ('https://us-east-1.quicksight.aws.amazon.com') does not match the recipient window's origin ('http://localhost:3000').

and

Uncaught (in promise) SET_VISUAL_ACTIONS timed out

When I invoke addActions by setting a "CONTENT_LOADED" message handler on onMessage, I can see that embeddedMessage is not defined when the message is processed. Perhaps this issue is out of scope here.

"use client";

import "./Special.css";
import * as React from "react";
import { useEffect, useRef, useState } from "react";
import Script from 'next/script';
import {
 createEmbeddingContext,
 EmbeddingContext,
 VisualExperience,
} from "amazon-quicksight-embedding-sdk";

// https://aws.amazon.com/blogs/business-intelligence/level-up-your-react-app-with-amazon-quicksight-how-to-embed-your-dashboard-for-anonymous-access/

interface Props {
  url: string;
}

export const Embed: React.FC<Props> = ({ url }) => {
  console.log(url);
  const dashboardRef = useRef(null);
  const [dashboardId, setDashboardId] = useState(
    "some dashboard id",
  );
  const [filter, setFilter] = useState("");
  const [embeddedVisual, setEmbeddedVisual] = useState<VisualExperience>();
  const [dashboardUrl, setDashboardUrl] = useState("");
  const [embeddingContext, setEmbeddingContext] = useState<EmbeddingContext>();

  useEffect(() => {
    console.log("now it is callednmnm");
    const fetchUrl = async () => {
      console.log("here is where the fetch would happen");
      if (url) {
        //   console.log(response.EmbedUrl);
        setDashboardUrl(url);
      } else {
        setDashboardUrl("none"); // ToDo: this is just to make an error go away. It doesn't actually make any pragmatic sense the way it is written.
      }
    };
    console.log("fetching url");
    fetchUrl().catch(console.error);
  }, []);

  const createContext = async () => {
    const context = await createEmbeddingContext();
    setEmbeddingContext(context);
  };

  useEffect(() => {
    if (dashboardUrl) {
      createContext();
    }
  }, [dashboardUrl]);

  useEffect(() => {
    if (embeddingContext) {
      embed();
    }
  }, [embeddingContext]);



  // const onMessage = async (messageEvent: any) => {
  //   const { eventName, message } = messageEvent;
  //   switch (eventName) {
  //     case "CONTENT_LOADED":
  //       console.log(embeddedVisual);
  //       await embeddedVisual!.addActions([
  //         {
  //           CustomActionId: "console-log-action",
  //           Name: "log to console",
  //           Trigger: "DATA_POINT_CLICK",
  //           Status: "ENABLED",
  //           ActionOperations: [
  //             {
  //               CallbackOperation: {
  //                 EmbeddingMessage: {},
  //               },
  //             },
  //           ],
  //         },
  //       ]);
  //       console.log("ok");
  //       break;
  //     case "CALLBACK_OPERATION_INVOKED":
  //       console.log("callback operation invoked");
  //       console.log(message);
  //   }
  // };

  useEffect(() => {
    if (embeddedVisual) {
    console.log('running effect for embedded visual')
    console.log(embeddedVisual)
    embeddedVisual.setActions([
      {
        Name: "test click action",
        CustomActionId: `test-click-action`,
        Status: "ENABLED",
        Trigger: "DATA_POINT_CLICK", // or 'DATA_POINT_MENU'
        ActionOperations: [
          {
            CallbackOperation: {
              EmbeddingMessage: {},
            },
          },
        ],
      },
    ]);
  }}, [embeddedVisual]);

  const embed = async () => {
    const frame_options = {
      url: dashboardUrl,
      container: "#mytest",
      height: "300px",
      width: "900px",
    };

    const content_options = {
      // onMessage: onMessage,
    };

    const newEmbeddedVisual = await embeddingContext!.embedVisual(
      frame_options,
      content_options,
    );

    // console.log("embedded");

    console.log(newEmbeddedVisual)
    await setEmbeddedVisual(newEmbeddedVisual);
    
    console.log(newEmbeddedVisual)
    // console.log(embeddedVisual)
  };

  useEffect(() => {
    console.log("filter changed");
    embeddedVisual?.setParameters([
      {
        Name: "OpportunityStage",
        Values: [filter],
      },
    ]);
  }, [filter]);

  const changeFilter = async (e: any) => {
    console.log("received click");
    const target_value = e.target.value;
    console.log(`target value is ${target_value}`);
    console.log(await embeddedVisual!.getFilterGroups())
    //console.log(embeddedVisual?.getParameters());
    setFilter(target_value);
  };

  return (
    <>
        <Script src="https://unpkg.com/[email protected]/dist/quicksight-embedding-js-sdk.min.js"></Script>
        <p>Welcome to the QuickSight visual embedding sample page</p>
        <p>Here is a visual</p>
        <select id="dashboard" value={filter} onChange={changeFilter}>
          <option value="">No filter</option>
          <option value="Contracting">Contracting</option>
          <option value="Lead">Lead</option>
        </select>
        <div id="mytest"></div>
    </>
  );
};

tcths avatar Aug 30 '24 09:08 tcths

It seems that this is really a question about React and Embedding SDK integration. I'm not sure if it's in scope here to ensure that the approach documented in https://community.amazonquicksight.com/t/build-a-seamless-interaction-between-your-application-and-embedded-amazon-quicksight-dashboards-and-visuals-using-embedded-callback-actions/17791 is applicable within a React app.

That's what we are trying to do, though. It seems to me that the approach mentioned in my first message, the first one, should work somehow, either by tweaking what we are doing in our client code or possibly by tweaking the SDK. We're working on this in consultation, but reopening here just in case we need to be part of the discussion.

tcths avatar Sep 12 '24 13:09 tcths