Skip to content

Commit e28e4ff

Browse files
authored
[dev-v5] Add Toast (#4584)
1 parent 6fae06c commit e28e4ff

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+3405
-807
lines changed

examples/Demo/FluentUI.Demo.Client/Documentation/Components/Toast/DebugPages/DebugToast.razor

Lines changed: 0 additions & 32 deletions
This file was deleted.

examples/Demo/FluentUI.Demo.Client/Documentation/Components/Toast/DebugPages/DebugToastContent.razor

Lines changed: 0 additions & 34 deletions
This file was deleted.
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
@inject IToastService ToastService
2+
3+
<FluentButton OnClick="@OpenToastAsync">
4+
Make toast
5+
</FluentButton>
6+
7+
@code {
8+
int clickCount = 0;
9+
10+
private async Task OpenToastAsync()
11+
{
12+
var result = await ToastService.ShowToastAsync(options =>
13+
{
14+
options.Intent = ToastIntent.Success;
15+
options.Title = $"Toast title {++clickCount}";
16+
options.Body = "This toast has a custom dismiss action.";
17+
options.IsDismissable = true;
18+
options.DismissAction = "Undo";
19+
options.DismissActionCallback = () =>
20+
{
21+
Console.WriteLine("Undo action executed.");
22+
return Task.CompletedTask;
23+
};
24+
options.OnStatusChange = (e) =>
25+
{
26+
Console.WriteLine($"Status changed: {e.Id} - {e.Status}");
27+
};
28+
29+
});
30+
Console.WriteLine($"Toast result: {result}");
31+
}
32+
}
33+
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
@inject IToastService ToastService
2+
3+
<FluentButton OnClick="@OpenToastAsync">
4+
Make toast
5+
</FluentButton>
6+
7+
@code {
8+
int clickCount = 0;
9+
10+
private async Task OpenToastAsync()
11+
{
12+
var result = await ToastService.ShowToastAsync(options =>
13+
{
14+
options.Title = $"Toast title {++clickCount}";
15+
options.Body = "Toasts are used to show brief messages to the user.";
16+
options.Subtitle = "subtitle";
17+
options.QuickAction1 = "Action";
18+
options.QuickAction1Callback = () =>
19+
{
20+
Console.WriteLine("Action 1 executed.");
21+
return Task.CompletedTask;
22+
};
23+
options.QuickAction2 = "Action";
24+
options.QuickAction2Callback = () =>
25+
{
26+
Console.WriteLine("Action 2 executed.");
27+
return Task.CompletedTask;
28+
};
29+
options.IsDismissable = true;
30+
options.OnStatusChange = (e) =>
31+
{
32+
Console.WriteLine($"Status changed: {e.Id} - {e.Status}");
33+
};
34+
35+
});
36+
Console.WriteLine($"Toast result: {result}");
37+
}
38+
}
39+
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
@inject IToastService ToastService
2+
3+
<FluentButton @ref="openToastButton" OnClick="@OpenToastAsync">
4+
Make toast
5+
</FluentButton>
6+
7+
@code {
8+
FluentButton openToastButton = default!;
9+
10+
private static RenderFragment BuildProgressContent(int value) =>
11+
@<div>
12+
<FluentProgressBar Min="0" Max="100" Value="value" Width="100%" />
13+
@($"{value}% complete")
14+
</div>;
15+
16+
private async Task OpenToastAsync()
17+
{
18+
openToastButton.SetDisabled(true);
19+
20+
var instance = await ToastService.ShowToastInstanceAsync(options =>
21+
{
22+
options.Type = ToastType.DeterminateProgress;
23+
options.Icon = new Icons.Regular.Size20.ArrowDownload();
24+
options.Title = "Downloading file";
25+
options.BodyContent = BuildProgressContent(0);
26+
});
27+
28+
for (int i = 0; i <= 100; i += 10)
29+
{
30+
await Task.Delay(500); // Simulate work being done
31+
await instance.UpdateAsync(options =>
32+
{
33+
options.BodyContent = BuildProgressContent(i);
34+
});
35+
}
36+
37+
openToastButton.SetDisabled(false);
38+
}
39+
}
40+
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
@inject IToastService ToastService
2+
3+
<FluentStack HorizontalGap="16">
4+
<FluentButton @ref="openToastButton" OnClick="@OpenToastAsync">
5+
Make toast
6+
</FluentButton>
7+
<FluentButton OnClick="@FinishProcessAsync">
8+
Finish process
9+
</FluentButton>
10+
</FluentStack>
11+
12+
@code {
13+
int clickCount = 0;
14+
FluentButton openToastButton = default!;
15+
16+
private async Task OpenToastAsync()
17+
{
18+
// Disable the button to prevent multiple toasts from being opened.
19+
// In a real app, you would likely want to track the toast ID and only disable if that specific toast is open.
20+
openToastButton.SetDisabled(true);
21+
var result = await ToastService.ShowToastAsync(options =>
22+
{
23+
options.Id = "indeterminate-toast";
24+
options.Timeout = 0;
25+
options.Type = ToastType.IndeterminateProgress;
26+
options.Intent = ToastIntent.Success;
27+
options.Title = $"Toast title {++clickCount}";
28+
options.Body = "No idea when this will be finished...";
29+
});
30+
Console.WriteLine($"Toast result: {result}");
31+
}
32+
33+
private async Task FinishProcessAsync()
34+
{
35+
// In a real app, you would likely keep track of the toast ID and update that specific toast.
36+
await ToastService.DismissAsync("indeterminate-toast");
37+
// Enable the button again so a new toast can be opened.
38+
openToastButton.SetDisabled(false);
39+
}
40+
}
41+
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
@inject IToastService ToastService
2+
3+
<FluentButton OnClick="@OpenToastAsync">
4+
Make toast
5+
</FluentButton>
6+
7+
@code {
8+
int clickCount = 0;
9+
10+
private async Task OpenToastAsync()
11+
{
12+
var result = await ToastService.ShowToastAsync(options =>
13+
{
14+
options.Intent = ToastIntent.Info;
15+
options.Title = $"Toast title {++clickCount}";
16+
options.Body = "Toasts are used to show brief messages to the user.";
17+
options.Subtitle = "subtitle";
18+
options.QuickAction1 = "Action";
19+
options.QuickAction1Callback = () =>
20+
{
21+
Console.WriteLine("Action 1 executed.");
22+
return Task.CompletedTask;
23+
};
24+
options.IsDismissable = true;
25+
options.DismissAction = "Close";
26+
options.OnStatusChange = (e) =>
27+
{
28+
Console.WriteLine($"Status changed: {e.Id} - {e.Status}");
29+
};
30+
options.Inverted = true;
31+
});
32+
Console.WriteLine($"Toast result: {result}");
33+
}
34+
}
35+
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
---
2+
title: Toast
3+
route: /Toast
4+
category: 20|Components
5+
icon: FoodToast
6+
---
7+
8+
# Toast
9+
10+
A toast communicates the status of an action someone is trying to take or that something happened elsewhere in the app. Toasts are temporary surfaces.
11+
Use them for information that's useful and relevant, but not critical.
12+
13+
The library provides a `FluentToast` component that can be used to display these notifications. To display a toast, you **must** use the `ToastService`. You use
14+
the `ToastOptions` class to configure the toast's content and behavior.
15+
16+
## Types
17+
18+
Toasts generally fall into three categories: confirmation, progress, and communication. The toast component has slots that can be turned on and off to best
19+
help people achieve their goals. The ideal configuration and usage of each toast type is described below:
20+
21+
### Confirmation toast
22+
23+
Confirmation toasts are shown to someone as a direct result of their action. A confirmation toast’s state can be success, error, warning,
24+
informational, or progress.
25+
26+
### Progress toast
27+
28+
Progress toasts inform someone about the status of an operation they initiated.
29+
30+
### Communication toast
31+
32+
Communication toasts inform someone of messages from the system or another person’s actions. These messages can include mentions, event reminders, replies,
33+
and system updates.
34+
They include a call to action directly linking to a solution or the content that they reference. They can be either temporary or persistent. They’re
35+
dismissible only if there is another surface, like a notification center, where the customer can find this content again later.
36+
37+
## Behavior
38+
39+
### Dismissal
40+
41+
Toasts can have timed, conditional, or express dismissals, dependent on their use case.
42+
43+
#### Timed dismissal
44+
45+
If there is no action to take, toast will time out after seven seconds. Timed dismissal is best when there is no further action to take, like for a successful
46+
confirmation toast.
47+
48+
People who navigate via mouse can pause the timer by hovering over the toast. However, toasts that don’t include actions won’t receive keyboard focus for
49+
people who navigate primarily by keyboard.
50+
51+
#### Conditional dismissal
52+
53+
Use conditional dismissal for toasts that should persist until a condition is met, like a progress toast that dismisses once a task is complete.
54+
55+
Don’t use toasts for necessary actions. If you need the encourage people to take an action before moving forward, try a more forceful surface like a message
56+
bar or a dialog.
57+
58+
#### Express dismissal
59+
60+
Include the Close button to allow people to expressly dismiss toasts only if they can find that information again elsewhere, like in a notification center.
61+
62+
>[!Note] We do not have a way yet to facilitate showing toast messages on other surfaces like a notification center, so use the express dismissal option with
63+
caution.
64+
65+
### Determinate and indeterminate progress
66+
67+
Progress toasts can be either determinate or indeterminate, depending on the needs of your app and the capabilities of the technology you’re building on.
68+
69+
When the completion time can be predicted, show a determinate progress bar and percentage of completion. Determinate progress bars offer a reliable user
70+
experience since they communicate status and assure people things are still working.
71+
72+
If the completion time is unknown or its accuracy is unreliable, show an indeterminate spinner icon instead.
73+
74+
Although a specific type of toast needs to be specified through the `ToastOptions`, the library does not prevent you from showing both a spinner icon and a
75+
progress bar in the same toast, but we recommend strongly against doing this.
76+
77+
## Accessibility
78+
79+
By using the `Intent` property (from `ToastOptions`) semantic styles, icons and aria-live regions and roles used in the toast are automatically applied.
80+
81+
All feedback states except info have an “assertive” aria-live and interrupt any other announcement a screen reader is making. Too many interruptions can disrupt someone’s flow,
82+
so don’t overload people with too many assertive toasts.
83+
84+
## Examples
85+
86+
### Default
87+
88+
This example shows a toast with the default configuration, which includes a title and a message. It also has the default intent of `Info`, which applies the corresponding icon.
89+
It shows 2 action links in the footer, which is the maximum number of what is possible for a toast.
90+
91+
{{ FluentToastDefault }}
92+
93+
### Custom dismissal
94+
95+
This example shows a toast with a custom dismissal configuration. It uses an action link (with a custom callback) instead of the standard dismiss icon to dismiss the toast.
96+
97+
{{ FluentToastCustomDismiss }}
98+
99+
### Inverted toast
100+
101+
You can use the `Inverted` property to show a toast with an inverted color scheme. This allows for showing a dark toast on a light background, or a light toast on a dark background.
102+
103+
>[!Note] When setting `IsDismissable` to `true`, without setting a custom `DismissAction`, a toast will render a default dismiss button using the `FluentButton` component.
104+
As a `FluentButton` has no notion of an `Inverted` property, you need to set an explicit `DismissAction` so a inverted aware link is rendered instead of the default button.
105+
106+
{{ FluentToastInverted }}
107+
108+
### Indeterminate progress
109+
110+
This example shows a toast with an indeterminate progress configuration. Timeout has been set to zero, so the toast will never close by itself. Use the 'Finish process' button to dismiss the toast.
111+
112+
{{ FluentToastIndeterminateProgress }}
113+
114+
### Determinate progress
115+
116+
This example shows how a toast can be updated during a longer running process (with a predictable duration).
117+
118+
{{ FluentToastDeterminateProgress }}
119+
120+
## API ToastService
121+
122+
{{ API Type=ToastService }}
123+
124+
## API FluentToast
125+
126+
{{ API Type=FluentToast }}
127+
128+
## API ToastOptions
129+
130+
{{ API Type=ToastOptions Properties=All }}
131+
132+
## API FluentToastProvider
133+
134+
{{ API Type=FluentToastProvider }}

0 commit comments

Comments
 (0)