-
Notifications
You must be signed in to change notification settings - Fork 192
Usage of UI interface
UserInteraction
defines a unified user interaction interface for Both CLI toolkit and VS Code extension toolkit.
It is a utility interface that helps to show UI elements to collect input from human. It also provide some utility functions like open a link in external browser or show progress bar for some time consuming task.
Here we describes some basic usage sample for each of them and how it acts in both CLI and VS Code extension.
/**
* Definition of user interaction, which is platform independent
*/
export interface UserInteraction {
/**
* Shows a single selection list
* @param config single selection config
* @returns A promise that resolves to the single select result wrapper or FxError
* @throws FxError
*/
selectOption: (config: SingleSelectConfig) => Promise<Result<SingleSelectResult, FxError>>;
/**
* Shows a multiple selection list
* @param config multiple selection config
* @returns A promise that resolves to the multiple select result wrapper or FxError
* @throws FxError
*/
selectOptions: (config: MultiSelectConfig) => Promise<Result<MultiSelectResult, FxError>>;
/**
* Opens an input box to ask the user for input.
* @param config text input config
* @returns A promise that resolves to the text input result wrapper or FxError
* @throws FxError
*/
inputText: (config: InputTextConfig) => Promise<Result<InputTextResult, FxError>>;
/**
* Shows a file open dialog to the user which allows to select a single file
* @param config file selector config
* @returns A promise that resolves to the file selector result wrapper or FxError
* @throws FxError
*/
selectFile: (config: SelectFileConfig) => Promise<Result<SelectFileResult, FxError>>;
/**
* Shows a file open dialog to the user which allows to select multiple files
* @param config multiple files selector config
* @returns A promise that resolves to the multiple files selector result wrapper or FxError
* @throws FxError
*/
selectFiles: (config: SelectFilesConfig) => Promise<Result<SelectFilesResult, FxError>>;
/**
* Shows a file open dialog to the user which allows to select a folder
* @param config folder selector config
* @returns A promise that resolves to the folder selector result wrapper or FxError
* @throws FxError
*/
selectFolder: (config: SelectFolderConfig) => Promise<Result<SelectFolderResult, FxError>>;
/**
* Opens a link externally in the browser.
* @param link The uri that should be opened.
* @returns A promise indicating if open was successful.
*/
openUrl(link: string): Promise<Result<boolean, FxError>>;
/**
* Show an information/warnning/error message to users. Optionally provide an array of items which will be presented as clickable buttons.
* @param level message level
* @param message The message to show.
* @param items A set of items that will be rendered as actions in the message.
* @returns A promise that resolves to the selected item or `undefined` when being dismissed.
*/
showMessage(
level: "info" | "warn" | "error",
message: string,
modal: boolean,
...items: string[]
): Promise<Result<string | undefined, FxError>>;
/**
* Show an information/warnning/error message with different colors to users, which only works for CLI.
* @param level message level
* @param message The message with color to show. The color only works for CLI.
* @param items A set of items that will be rendered as actions in the message.
* @returns A promise that resolves to the selected item or `undefined` when being dismissed.
*/
showMessage(
level: "info" | "warn" | "error",
message: Array<{ content: string, color: Colors }>,
modal: boolean,
...items: string[]
): Promise<Result<string | undefined, FxError>>;
/**
* A function to run a task with progress bar. (CLI and VS Code has different UI experience for progress bar)
* @param task a runnable task with progress definition
* @param config task running confiuration
* @param args args for task run() API
* @returns A promise that resolves the wrapper of task running result or FxError
*/
runWithProgress<T>(task: RunnableTask<T>, config: TaskConfig, ...args: any): Promise<Result<T, FxError>>;
}
We have many different UI elements such as text input, selection, file selector. Most of them have some commonly defined config fields to specify.
The common UI config is defined by UIConfig
interface:
/**
* A base structure of user interaction (UI) configuration
*/
export interface UIConfig<T> {
/**
* name is the identifier of the UI
*/
name: string;
/**
* human readable meaningful display name of the UI
*/
title: string;
/**
* placeholder in the UI
*/
placeholder?: string;
/**
* prompt text providing some ask or explanation to the user
*/
prompt?: string;
/**
* `step` and `totalSteps` are used to describe the progress in question flow
* `step` is the sequence number of current question
*/
step?: number;
/**
* `totalStep` is the number of questions totally
*/
totalSteps?: number;
/**
* default input value
*/
default?: T;
/**
* A function that will be called to validate input and to give a hint to the user.
*
* @param input The current value of the input to be validated.
* @return A human-readable string which is presented as diagnostic message.
* Return `undefined` when 'value' is valid.
*/
validation?: (input: T) => string | undefined | Promise<string | undefined>;
}
name
, title
are basic required fields for a UI config. name
is the unique ID of the question, title is the display name of the question.
prompt
, placeholder
, default
provide user richer experience, which are supported in VS Code extension.
step
and totalSteps
are used to describe the progress in question flow. Each UI element can have a go-back
and continue
button to control the progress of the question flow:
The left arrow is go back
button if the UI element has step
greater than one.
The tick is an ok
button to continue to next question in the question flow.
validation
is a validation function to validate the user input. Both VS Code extension and CLI will provide validating mechanism and show warning message if validation fails:
In VS Code extension, validation warning look like this:
In CLI, it looks like this:
The following UI operations are basic UI types which aims to collect user inputs/answers: inputText
,selectOption
, selectOptions
,selectFile
,selectFiles
,selectFolder
. So the return values of these APIs are wrappered in an InputResult
interface:
export interface InputResult<T> {
type: "success" | "skip" | "back";
result?: T;
}
In question flow, in addition to the real value that user provide, the operation type is also provided. We define 5 types of operation types:
-
success
: the returned answer value is successfully collected when user click predefined ok button/key, user will continue to answer the next question if available -
skip
: the answer value is automatically selected whenskipSingleOption
is true for single/multiple selection list, user will continue to answer the next question if available -
back
: the returned answer is undefined because user click the go-back button/key and will go back to re-answer the previous question in the question flow -
cancel: Cancel operation is encoded into
UserCancelError
inResult<>
interface, which means user want to cancel the input. -
other error: some other error happens in progress of collecting user input.
The first three cases are encoded in InputResult<>
structure, while the last two cases are returned as FxError
in Result<>
object.
Text inputs are common ways to collect user input, which is an input text box for user to input a text string. You can call the following API to show a text box and collect input from human:
const res:Result<InputTextResult, FxError> = await ui.inputText({
name: "questionId",
title: "title",
prompt: 'prompt',
placeholder: 'My Placeholder'
});
For VS Code extension, you can define richer experience with prompt
and placeholder
. Text input looks like this:
For CLI, text input is just a command-line query like this:
Selections are frequently used when a list of options will pop up for user to select. There are two types of selections: single selection (selectOption
) and multiple selection (selectOptions
).
By calling or
APIs, you need to specify the option list with type string[]
or OptionItem[]
.
const res:Result<SingleSelectResult, FxError> = await ui.selectOption({
name: "questionId",
title: "title",
options: ["option1","option2"]
});
string[]
option type is straitforward while OptionItem[]
provides more sophisticated ways to display the option item.
Here is a sample of UI in VS Code extension:
Here is the CLI counterparts:
File pickers are use when the system need user to select file(s) from local file system. The answer to the file picker is a string path or an array of paths.
selectFile
, selectFiles
and selectFolder
are three APIs related to file picker. The difference is selectFile
is used for single file selection, selectFiles
is used for multiple files selection, and selectFolder
is used for folder selection.
File picker shows richer UI in VS Code extension than what in CLI. In VS Code, the file picker looks like this: a dialog will pop up for user to select file(s):
In CLI, the file picker is simply a text input box:
There are two cases when message dialogs are needed:
- The system shows some message and wait for user input.
- The system shows some informative message and don't need to wait for user input.
showMessage
API is provided to show such message dialog.
For the first case, you need to set parameter modal
as true to show a modal dialog which will not disappear until user inputs.
For modal dialog, you need to wait until user input, so you need to await until the returned promise is resolved:
const res:Result<string | undefined, FxError> = await ui.showMessasge("info", "message content", true, "item1", "item2");
The returned result string is the item that user clicked, which is defined by the items
parameter of showMessage
method.
For VS Code extension, when user click the cancel button or the cross button on the right corner, the returned result is undefined
.
Notice that the cancel button exists by default and there is no need to passing cancel
in the items
parameter.
For the second case, you need to set modal
as false, which will disappear if it loses focus.
For non-modal dialog, normally, you don't need the user input. So we suggest you don't need to add wait the promise to be resolved:
ui.showMessasge("info", "message content", false, "item1", "item2");
Here is the UI of showing message using modal dialog in VS Code extension:
Here is the UI of showing message using non-modal dialog in VS Code extension:
Build Custom Engine Copilots
- Build a basic AI chatbot for Teams
- Build an AI agent chatbot for Teams
- Expand AI bot's knowledge with your content
Scenario-based Tutorials
- Send notifications to Teams
- Respond to chat commands in Teams
- Respond to card actions in Teams
- Embed a dashboard canvas in Teams
Extend your app across Microsoft 365
- Teams tabs in Microsoft 365 and Outlook
- Teams message extension for Outlook
- Add Outlook Add-in to a Teams app
App settings and Microsoft Entra Apps
- Manage Application settings with Teams Toolkit
- Manage Microsoft Entra Application Registration with Teams Toolkit
- Use an existing Microsoft Entra app
- Use a multi-tenant Microsoft Entra app
Configure multiple capabilities
- How to configure Tab capability within your Teams app
- How to configure Bot capability within your Teams app
- How to configure Message Extension capability within your Teams app
Add Authentication to your app
- How to add single sign on in Teams Toolkit for Visual Studio Code
- How to enable Single Sign-on in Teams Toolkit for Visual Studio
Connect to cloud resources
- How to integrate Azure Functions with your Teams app
- How to integrate Azure API Management
- Integrate with Azure SQL Database
- Integrate with Azure Key Vault
Deploy apps to production