Is there any we can connect with task module from html parsed adaptive card?

Parag Jadhav 1 Reputation point
2023-08-21T14:50:37.87+00:00
I'm using an HTML-rendered version of the adaptive card using these 2 packages in MS Teams.

https://www.npmjs.com/package/adaptivecards (version 2.11.1)
https://www.npmjs.com/package/adaptivecards-react (version 1.1.1)
React 16

Previously I was using the Adaptive Cards as is and posting them in the MS Teams group chat or channels with the help of Teams Apps. Any button clicks events used to get captured in my task module microservice.

Now when I'm parsing and rendering the same adaptive card to an HTML format, how do I achieve the same results when an HTML button is clicked?

This is how I'm parsing the adaptive card to HTML

Card.tsx

import { AdaptiveCardUsingHostConfigContext } from "adaptivecards-react";
import React, { useState, useEffect } from "react";
import * as AC from "adaptivecards";
import { SubmitAction } from "./SubmitAction";
import { ExecuteAction } from "./ExecuteAction";

AC.GlobalRegistry.actions.register(SubmitAction.JsonTypeName, SubmitAction);
AC.GlobalRegistry.actions.register(ExecuteAction.JsonTypeName, ExecuteAction);

const Card = (props: { card: any }) => {
  const { card } = props;
  const [isLoading, setIsLoading] = useState(false);
  const [cardPayload, setCardPayload] = useState(card);

  useEffect(() => {
    // console.log("Card: ", JSON.stringify(cardPayload));
  }, [cardPayload]);

  const onActionSubmit = (e: any) => {
    console.log("SUBMIT");
    console.log("inputs", e.data, e.id);
  };

  const onExecuteAction = (e: any) => {
    const actionType = e.getJsonTypeName();
    const { verb } = e;

    console.log(`executing action for ${verb}`);

    let response;
    if (actionType === "Action.Execute") {
      setIsLoading(true);
      response = cardPayload;
      response.body = [
        ...cardPayload.body,
        {
          text: "This is a fresh dynamic detail!",
          wrap: true,
          type: "TextBlock",
        },
      ];
      // }

      setCardPayload(response);
      setIsLoading(false);
    }
  };

  return (
    <div className="Card">
      <AdaptiveCardUsingHostConfigContext
        payload={cardPayload}
        onActionSubmit={onActionSubmit}
        onExecuteAction={onExecuteAction}
      />
    </div>
  );
};

export default Card;
ExecuteAction.tsx
import React from "react";
import * as AC from "adaptivecards";
import Button from "../components/Button";
import { reactDomRender } from "./shared";

export class ExecuteAction extends AC.ExecuteAction {
  private internalRenderedElement: any;

  static readonly titleProperty = new AC.StringProperty(
    AC.Versions.v1_0,
    "title",
    true
  );

  getTitle(): string | undefined {
    return this.getValue(ExecuteAction.titleProperty);
  }

  get renderedElement(): HTMLElement | undefined {
    return this.internalRenderedElement;
  }

  render() {
    const element = reactDomRender(this.renderElement());
    this.internalRenderedElement = element;
  }

  private renderElement = (): JSX.Element => {
    return <Button label={this.getTitle()} onClick={() => this.execute()} />;
  };
}
SubmitAction.tsx
import React from "react";
import * as AC from "adaptivecards";
import Button from "../components/Button";
import { reactDomRender } from "./shared";

export class SubmitAction extends AC.SubmitAction {
  private internalRenderedElement: any;

  static readonly textProperty = new AC.StringProperty(
    AC.Versions.v1_3,
    "text",
    true
  );

  static readonly titleProperty = new AC.StringProperty(
    AC.Versions.v1_3,
    "title",
    true
  );

  static readonly dataProperty = new AC.PropertyDefinition(
    AC.Versions.v1_3,
    "data"
  );

  getTitle(): string | undefined {
    return this.getValue(SubmitAction.titleProperty);
  }

  getText(): string | undefined {
    return this.getValue(SubmitAction.textProperty);
  }

  getData(): string | undefined {
    return this.getValue(SubmitAction.dataProperty);
  }

  getInputs(): any {
    return this.getValue(SubmitAction.associatedInputsProperty);
  }

  getInputValues(): any {
    return this.parent?.getAllInputs().map((input) => {
      return { id: input.id, value: input.value };
    });
  }

  get renderedElement(): HTMLElement | undefined {
    return this.internalRenderedElement;
  }

  render() {
    const element = reactDomRender(this.renderElement());
    this.internalRenderedElement = element;
  }

  execute() {
    console.log("-->", this.getData());
    const inputs = this.validateInputs();
    console.log(inputs);
    if (inputs.length === 0) {
      console.log("inputs valid");
      const inputValues = this.getInputValues();
      console.log("input values", inputValues);
    } else {
      for (const input of inputs) {
        console.log("input id", input.id);
        console.log("isValid", input.isValid());
        this.render();
      }
    }
  }

  onExecuteAction(action: any) {
    console.log("onExecuteAction");
    action();
  }

  private renderElement = (): JSX.Element => {
    return <Button label={this.title} onClick={() => this.execute()} />;
  };
}
shared.tsx

import React from "react";
import * as ReactDOM from "react-dom";

export const reactDomRender = (
  reactElement: React.ReactElement
): HTMLElement | undefined => {
  const div = document.createElement("div");
  ReactDOM.render(reactElement, div);
  return div;
};
RenderCard.tsx
/* eslint-disable react/prop-types */
import React from "react";
import Card from "./components/Card";
import { Loader } from "@fluentui/react-northstar";

const RenderCard = (props) => {
  const { card } = props;

  if (card) {
    return (
      <div className="App">
        <div>
          <Card card={card} />
        </div>
      </div>
    );
  } else {
    return <Loader label="Please wait..." size="largest" />;
  }
};

export default RenderCard;
hostConfig.tsx
const hostConfig = () => {
  return {
    spacing: {
      small: 3,
      default: 8,
      medium: 20,
      large: 30,
      extraLarge: 40,
      padding: 10,
    },
    separator: {
      lineThickness: 1,
      lineColor: "#EEEEEE",
    },
    supportsInteractivity: true,
    fontTypes: {
      default: {
        fontFamily:
          "'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif",
        fontSizes: {
          small: 12,
          default: 14,
          medium: 17,
          large: 21,
          extraLarge: 26,
        },
        fontWeights: {
          lighter: 200,
          default: 400,
          bolder: 600,
        },
      },
      monospace: {
        fontFamily: "'Courier New', Courier, monospace",
        fontSizes: {
          small: 12,
          default: 14,
          medium: 17,
          large: 21,
          extraLarge: 26,
        },
        fontWeights: {
          lighter: 200,
          default: 400,
          bolder: 600,
        },
      },
    },
    containerStyles: {
      default: {
        backgroundColor: "#FFFFFF",
        foregroundColors: {
          default: {
            default: "#000000",
            subtle: "#767676",
          },
          accent: {
            default: "#0063B1",
            subtle: "#0063B1",
          },
          attention: {
            default: "#FF0000",
            subtle: "#DDFF0000",
          },
          good: {
            default: "#54a254",
            subtle: "#DD54a254",
          },
          warning: {
            default: "#c3ab23",
            subtle: "#DDc3ab23",
          },
        },
      },
      emphasis: {
        backgroundColor: "#F0F0F0",
        foregroundColors: {
          default: {
            default: "#000000",
            subtle: "#767676",
          },
          accent: {
            default: "#2E89FC",
            subtle: "#882E89FC",
          },
          attention: {
            default: "#FF0000",
            subtle: "#DDFF0000",
          },
          good: {
            default: "#54a254",
            subtle: "#DD54a254",
          },
          warning: {
            default: "#c3ab23",
            subtle: "#DDc3ab23",
          },
        },
      },
      accent: {
        foregroundColors: {
          default: {
            default: "#333333",
            subtle: "#EE333333",
          },
          dark: {
            default: "#000000",
            subtle: "#66000000",
          },
          light: {
            default: "#FFFFFF",
            subtle: "#33000000",
          },
          accent: {
            default: "pink",
            subtle: "#882E89FC",
          },
          attention: {
            default: "#cc3300",
            subtle: "#DDcc3300",
          },
          good: {
            default: "#54a254",
            subtle: "#DD54a254",
          },
          warning: {
            default: "#e69500",
            subtle: "#DDe69500",
          },
        },
      },
      good: {
        backgroundColor: "#CCFFCC",
        foregroundColors: {
          default: {
            default: "#333333",
            subtle: "#EE333333",
          },
          dark: {
            default: "#000000",
            subtle: "#66000000",
          },
          light: {
            default: "#FFFFFF",
            subtle: "#33000000",
          },
          accent: {
            default: "#2E89FC",
            subtle: "#882E89FC",
          },
          attention: {
            default: "#cc3300",
            subtle: "#DDcc3300",
          },
          good: {
            default: "#54a254",
            subtle: "#DD54a254",
          },
          warning: {
            default: "#e69500",
            subtle: "#DDe69500",
          },
        },
      },
    },
    imageSizes: {
      small: 40,
      medium: 80,
      large: 160,
    },
    actions: {
      maxActions: 6,
      spacing: "default",
      buttonSpacing: 10,
      showCard: {
        actionMode: "inline",
        inlineTopMargin: 8,
      },
      actionsOrientation: "vertical",
      actionAlignment: "stretch",
      iconSize: 50,
    },
    adaptiveCard: {
      allowCustomStyle: true,
    },
    imageSet: {
      imageSize: "medium",
      maxImageHeight: 100,
    },
    factSet: {
      title: {
        color: "default",
        size: "default",
        isSubtle: false,
        weight: "bolder",
        wrap: true,
        maxWidth: 150,
      },
      value: {
        color: "default",
        size: "default",
        isSubtle: false,
        weight: "default",
        wrap: true,
      },
      spacing: 8,
    },
  };
};

export default hostConfig;
And this is the gist of the code from the component where I'm using the RenderCard.tsx component

import { ProvidesHostConfigContext } from "adaptivecards-react";
import hostConfig from "./hostConfig";
import RenderCard from "./rendercard";

<ProvidesHostConfigContext hostConfig={hostConfig}>
    <RenderCard card={card} />
</ProvidesHostConfigContext>
Any help or nudge in the right direction will be very helpful.

Thank you
Azure AI Bot Service
Azure AI Bot Service
An Azure service that provides an integrated environment for bot development.
918 questions
Microsoft Teams Development
Microsoft Teams Development
Microsoft Teams: A Microsoft customizable chat-based workspace.Development: The process of researching, productizing, and refining new or existing technologies.
3,803 questions
{count} votes

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.