How To Create SPFx Adaptive Card Extension(SPFx ACEs)

In the previous article, we discussed about built-in cards for the Microsoft Viva Connections Dashboard.aspx page. In this article, we are going to build a custom Adaptive Card Extension in detail. Hope this article will help you to learn something new.

Pre-requisite

Before starting with SPFx ACE, we will understand some basics of Adaptive cards. 

What are Adaptive Cards? 

Adaptive cards are simple JSON objects which will render the UI in consistent and direct way on all the devices as it is portable and automatically styled. 

Adaptive card structure  

build a custom Adaptive Card Extension

  • AdaptiveCard: The root object consist of Adaptivecard itself 
  • Version: version of Adaptive cards JSON… it recommended to use 1.3 from Microsoft. 
  • Body: consists of several elements and actions which are optional. 
    • Adaptive Elements --> TextBlock, RichTextBlock, Image, Media, ActionSet 
    • Container Elements --> Container, ColumnSet,…..etc 
    • Input Elements --> Input.Text, Input.Date, Input.Time 
  • Actions: these are buttons which performs different actions, it can be Action.OpenUrl, Action.ShowCard and Action.Submit 

Example

{
    "type": "AdaptiveCard",
    "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
    "version": "1.3",
    "body": [{
        "type": "Container",
        "items": [{
            "type": "TextBlock",
            "text": "Welcome to SPFx ACE ${version}",
            "wrap": true
        }],
        "selectAction": {
            "type": "Action.Submit"
        }
    }]
}

From above JSON schema, there is body element, under that there can be optional container element with Adaptive element of type “TextBlock”. There is an action of type submit as a trigger. JSON contains ${version} which is dynamic value. Now we have some basic idea of how Adaptive cards JSON looks like, we will start with SPFx ACEs(Adaptive Card Extension).

Run below command to create SPFx ACEs project (Please follow the Microsoft Link for more details and SPFx setup development environment)

yo @microsoft/sharepoint

create SPFx Adaptive Card Extension

Which type of client-side component to create?

Select Adaptive Card Extension, it gives you 3 types of template or card type.

Note
If Adaptive Card Extension option is not available, check SPFx version and node version Link.

Basic Card Template

create SPFx Adaptive Card Extension

Returns BaseBasicCardView template for the card with Title on the card. 

Image Card Template 

create SPFx Adaptive Card Extension

Returns BaseImageCardView for the card with Title of the card and Image.    

Primary Text Template 

create SPFx Adaptive Card Extension

Returns BasePrimaryTextCardView for the card with Title and Description of the card. 

You can choose different template based on your requirement. In this article, we are going with Image Card view template.

create SPFx Adaptive Card Extension

Above screenshot show SPFx ACEs project structure. In the SPFx ACEs solution  

create SPFx Adaptive Card Extension

renderCard() function added by default in the solution. Without a registered card Id SPFx ACEs solution renders default card view with Basic template card view. 

create SPFx Adaptive Card Extension

create SPFx Adaptive Card Extension

In the QuickView.ts file

create SPFx Adaptive Card Extension

create SPFx Adaptive Card Extension

Now with some basic understanding of SPFx ACEs solution and Adaptive Cards, we will fetch SharePoint list items by following below steps.

Step 1

Create an interface for List items in SolutionName_AdaptiveCardExtension.ts file.

export interface IListItems {
    issueTitle: string;
    issueDesc: string;
    priority: string;
    status: string;
    date: Date;
    id: number;
}
export interface IIssueTrackerAdaptiveCardExtensionState {
    issueList: IListItems[];
    index:number;
}

Step 2

In onInit() method initialize the pnp context,(make sure you have imported pnp js library)

import {
    setup as pnpSetup
} from "@pnp/common";
pnpSetup({
    spfxContext: this.context
});

Step 3

Create SharePoint list of your choice. For the purpose of this article, I have created a List with OOB template available in SharePoint "Issue tracker" with mock data.

create SPFx Adaptive Card Extension

 Let's add below code to fetch items from list in onInit function and create a function with the name GetListItems() which will fetch SharePoint List items.

//function to get SharePoint list items
this.GetListItems().then((issueListResponse) => {
    let issueList: IListItems[];
    issueList= [];
    issueListResponse.map((item) => {
        issueList.push({
            issueTitle: item.Title,
            issueDesc: item.Description,
            priority: item.Priority,
            status: item.Status,
            date: item.DateReported,
            id: item.ID
        });
    });
    this.setState({
        issueList
    });
});
protected async GetListItems(): Promise < any > {
    var items = await sp.web.lists.getByTitle("Issue tracker").items.get();
    if (items.length > 0) {
        return items;
    }
    return null;
}

Step 4

Go to card view, under card view data function replace with below code.

public get data(): IImageCardParameters {
    var primaryText;
    if(this.state.issueList.length > 0){
      primaryText=this.state.issueList.length+" items exists."
    }
    else{
      primaryText="No items exists."
    }
    return {
      primaryText: primaryText,
      imageUrl: require('../assets/MicrosoftLogo.png')
    };
  }

The above code will show the number of items from SharePoint list. Now you can add the card in workbench which will fetch you total count from SharePoint list.

Step 5

Now go to QuickView.ts file, replace the interface and QuickView data function with following code changes.

export interface IQuickViewData {
  issueList:IListItems[]
}
 public get data(): IQuickViewData {
    return {
     issueList:this.state.issueList
    };
  }

Step 6

Go to QuickViewTemplate.json file and replace the code with below JSON.

{
    "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
    "type": "AdaptiveCard",
    "version": "1.3",
    "body": [
        {
            "type": "Container",
            "$data": "${issueList}",
            "selectAction": {
                "type": "Action.Submit",
                "data": {
                    "id": "selectAction",
                    "newIndex": "${$index}"
                }
            },
            "separator": true,
            "items": [
                {
                    "type": "Container",
                    "items": [
                        {
                            "type": "ColumnSet",
                            "columns": [
                                {
                                    "type": "Column",
                                    "width": "stretch",
                                    "items": [
                                        {
                                            "type": "Image",
                                            "style": "Person",
                                            "url": "https://pbs.twimg.com/profile_images/3647943215/d7f12830b3c17a5a9e4afcc370e3a37e_400x400.jpeg",
                                            "size": "Small"
                                        }
                                    ]
                                },
                                {
                                    "type": "Column",
                                    "width": "auto",
                                    "items": [
                                        {
                                            "type": "TextBlock",
                                            "text": "**Reported Date** : ${date}",
                                            "wrap": true,
                                            "spacing": "None",
                                            "horizontalAlignment": "Left"
                                        }
                                    ]
                                }
                            ]
                        }
                    ]
                },
                {
                    "type": "ColumnSet",
                    "columns": [
                        {
                            "type": "Column",
                            "width": "stretch",
                            "items": [
                                {
                                    "type": "TextBlock",
                                    "text": "**Title** : ${issueTitle}",
                                    "color": "Dark",
                                    "size": "Medium",
                                    "wrap": true,
                                    "maxLines": 2,
                                    "spacing": "None"
                                }
                            ]
                        },
                        {
                            "type": "Column",
                            "width": "auto",
                            "items": [
                                {
                                    "type": "TextBlock",
                                    "text": "**Priority** : ${priority}",
                                    "wrap": true,
                                    "horizontalAlignment": "Right",
                                    "size": "Medium"
                                }
                            ]
                        }
                    ]
                }
            ]
        }
    ]
}

After successful build, you can see the changes on the QuickView.

Step 7

In SPFx ACEs there is an option to navigate between multiple QuickViews by push or replace or pop operation, which will help to navigate between QuickViews. Now create new QuickView with the name "ListItemQuickView.ts" and add below code.

import { ISPFxAdaptiveCard, BaseAdaptiveCardView, IActionArguments } from '@microsoft/sp-adaptive-card-extension-base';
import { IIssueTrackerAdaptiveCardExtensionProps, IIssueTrackerAdaptiveCardExtensionState } from '../IssueTrackerAdaptiveCardExtension';
export interface IListItemView {
    issueTitle: string;
    issueDesc:string;
    priority:string;
    status:string;
    date:Date;     
}
export class ListItemQuickView extends BaseAdaptiveCardView<
  IIssueTrackerAdaptiveCardExtensionProps,
  IIssueTrackerAdaptiveCardExtensionState,
  IListItemView
> {
  public get data(): IListItemView {
      const {issueTitle,issueDesc,priority,status,date}=this.state.issueList[this.state.index];
    return {
    issueTitle,
    issueDesc,priority,status,date
    };
  }
  public get template(): ISPFxAdaptiveCard {
    return require('./template/ListItemViewTemplate.json');
  }
}

Step 8

Register ListItemQuickView in SolutionName_AdaptiveCardExtension.ts file, add below code.

Creating a const with unique name,

export const LIST_ITEM_QUICK_VIEW_REGISTRY_ID:string='ListItem_QUICK_VIEW';

register the newly created const,

    this.quickViewNavigator.register(LIST_ITEM_QUICK_VIEW_REGISTRY_ID,()=> new ListItemQuickView());

Step 9

Now create JSON file with the name ListItemViewTemplate.json and add below JSON code...

Below Adaptive Card JSON code will show only the selected item from the previous screen with a detailed view of it.

{
    "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
    "type": "AdaptiveCard",
    "version": "1.3",
    "body": [{
        "type": "Container",
        "items": [{
            "type": "Container",
            "items": [{
                "type": "ColumnSet",
                "columns": [{
                    "type": "Column",
                    "items": [{
                        "type": "Image",
                        "style": "Person",
                        "url": "https://pbs.twimg.com/profile_images/3647943215/d7f12830b3c17a5a9e4afcc370e3a37e_400x400.jpeg",
                        "size": "Small"
                    }]
                }, {
                    "type": "Column",
                    "width": "auto",
                    "items": [{
                        "type": "TextBlock",
                        "text": "**Reported Date** : ${date}",
                        "wrap": true,
                        "spacing": "None",
                        "horizontalAlignment": "Left"
                    }]
                }]
            }]
        }, {
            "type": "TextBlock",
            "text": "**Title** : ${issueTitle}",
            "color": "Dark",
            "size": "Medium",
            "wrap": true,
            "maxLines": 2,
            "spacing": "None"
        }, {
            "type": "TextBlock",
            "text": "**Description** :${issueDesc}",
            "color": "Dark",
            "wrap": true,
            "size": "Medium",
            "spacing": "None"
        }, {
            "type": "ColumnSet",
            "columns": [{
                "type": "Column",
                "width": "stretch",
                "items": [{
                    "type": "TextBlock",
                    "text": "**Status** :${status}",
                    "color": "Dark",
                    "wrap": true,
                    "size": "Medium",
                    "maxLines": 1,
                    "spacing": "None"
                }]
            }, {
                "type": "Column",
                "width": "stretch",
                "items": [{
                    "type": "TextBlock",
                    "text": "**Priority** :${priority}",
                    "color": "Dark",
                    "wrap": true,
                    "size": "Medium",
                    "maxLines": 1,
                    "spacing": "None"
                }]
            }]
        }]
    }]
}

On registering LisItemQuickView , navigate back to QuickView.ts file add below code, which will handle navigation.

public onAction(action: IActionArguments): void {
  if (action.type === 'Submit') {
    const { id, newIndex } = action.data;
    if (id === 'selectAction') { 
      this.quickViewNavigator.push(LIST_ITEM_QUICK_VIEW_REGISTRY_ID, true);      
      this.setState({ index: newIndex});
    }
  }
}

once build is successfull, click on the Quick View button on card which load all items from SharePoint list. On selection of item it will take you to detailed screen view.

In the final result you will be able to navigate between the quick view with SharePoint data. 

Summary

Hope you have learned something new on Adaptive Cards and how to use Adaptive Cards in SPFx ACEs solution. 


Similar Articles