microsoft / microsoft/AdaptiveCards

[Rendering] Add Missing Properties to Default Types

Open
#9,211 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Area-Renderers Bug
Dominant language
C#
Stars
2k
Forks
595
Avg merge
1d 19h
Merged PRs (30d)
1

Description

Target Platforms

NodeJS

SDK Version

3.0.5

Application Name

Custom UI

Problem Description

I have an application that renderes AdaptiveCards in my own UI.
I have been using some default adaptive card examples from the new designer: https://adaptivecards.microsoft.com/designer

A lot of them use the targetWidth property.
When rendering them using the Microsoft Adaptive Card JS SDK it looks like it doesn't support this property.

So I created my own custom Image Element that simply extends the default Image and overrides the existing default Image element according to the MS Docs

While my own renderer is now being called for images, the new property I added isn't being processed:

static readonly targetWidthProperty = new StringProperty(AdaptiveCards.Versions.v1_0, "targetWidth");

@AdaptiveCards.property(CustomElement.targetWidthProperty)
targetWidth?: string;

If I register my component under a different name (e.g. CustomImage) and rename the Images in the json config to CustomImage by new property is processed properly.

It seems like the schema of default elements is somehow cached. If I try to override the Image type the populateSchema() isn't called, if I use my own CustomImage it's called.
I've tried everything, using my own SerializationContext etc. but can't find a solution.

So how are we supposed to add missing features to the default components?

Screenshots

No response

Card JSON
{
    "type": "AdaptiveCard",
    "$schema": "https://adaptivecards.io/schemas/adaptive-card.json",
    "version": "1.5",
    "body": [
        {
            "type": "TextBlock",
            "text": "Time off request",
            "wrap": true,
            "size": "Large",
            "weight": "Bolder",
            "spacing": "None"
        },
        {
            "type": "Image",
            "url": "https://raw.githubusercontent.com/OfficeDev/Microsoft-Teams-Adaptive-Card-Samples/main/samples/time-off-request/assets/hero-image-default.png",
            "style": "RoundedCorners",
            "targetWidth": "Standard"
        },
        {
            "type": "Image",
            "targetWidth": "Wide",
            "url": "https://raw.githubusercontent.com/OfficeDev/Microsoft-Teams-Adaptive-Card-Samples/main/samples/time-off-request/assets/hero-wide.png",
            "style": "RoundedCorners"
        }
    ]
}
Sample Code Language

TypeScript

Sample Code
import * as AdaptiveCards from "adaptivecards";
import {Image, SerializableObjectSchema, StringProperty} from "adaptivecards";
import {PropertyBag} from "adaptivecards/src/serialization";

export default class AdaptiveCardApi {

    private readonly serializationContext: AdaptiveCards.SerializationContext;

    constructor() {
        // Create a custom registry for elements
        let elementRegistry = new AdaptiveCards.CardObjectRegistry<AdaptiveCards.CardElement>();

        // Populate it with the default set of elements
        AdaptiveCards.GlobalRegistry.populateWithDefaultElements(elementRegistry);

        // Parse a card payload using the custom registry
        this.serializationContext = new AdaptiveCards.SerializationContext();
        this.serializationContext.setElementRegistry(elementRegistry);

        const serializationContext = this.serializationContext;

        class CustomElement extends OriginalClass {
            //#region Schema
            static readonly targetWidthProperty = new StringProperty(AdaptiveCards.Versions.v1_0, "targetWidth");

            @AdaptiveCards.property(CustomElement.targetWidthProperty)
            targetWidth?: string;
            //#endregion

            protected populateSchema(schema: SerializableObjectSchema) {
                // Never logged as never being called
                console.log("populateSchema", this.getJsonTypeName(), 'schema', schema);
                super.populateSchema(schema);
            }

            protected internalRender(): HTMLElement | undefined {
                const renderedElement = super.internalRender();
                // Add our data attribute if targetWidth is present on the model.
                if (renderedElement && this.targetWidth) {
                    renderedElement.classList.add(`ac-${this.targetWidth}`);
                }

                return renderedElement;
            }
        }

        elementRegistry.register("Image", CustomElement);
    }

    public createCard(cardConfig: PropertyBag) {
        // Create an AdaptiveCard instance
        const adaptiveCard = new AdaptiveCards.AdaptiveCard();

        // Parse the card payload
        adaptiveCard.parse(cardConfig, this.serializationContext);

        // Render the card to an HTML element:
        return adaptiveCard.render();
    }
}

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the NodeJS SDK entry points named in the report: GlobalRegistry.populateWithDefaultElements, SerializationContext, the Image element, and populateSchema. Reproduce the card using the custom registry and compare the Image and CustomImage paths. Done means a default Image can be extended or overridden so targetWidth is processed while existing registry behavior remains intact.

Written by the indexing model from the issue text.

Assessment

Tech stack
node.js, typescript
Domain
frontend
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.