-
Notifications
You must be signed in to change notification settings - Fork 18
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Playground] Add Events components (#311)
* Implement EventItemComponent * Implement EventListComponent * Add EventList page * Fix flex align problem and replace nested div to FluentCard * Add CSS configurations on EventItemComponent * Adjust padding of EventListComponent * Add comment on EventList page * Rename EventList to Events * Integrate model, Add Id, Alignments * Add progress ring during loading in EventListComponent * Use parameter Id to FluentGrid's Id * Implement test methods for Event components * Show a message when there are no events user joined * Adjust EventItemComponent padding * Add tests for Events page in AppHost testing project * Revert launchSettings.json * Remove redundant references, Make properties nullable * Remove SetParametersAsync at EventItemComponent * Re-order methods by access modifiers priority * Revert launchSettings.json with the final new line * Add async to NavigateToEventDetails * Sort entities in code behind by common .NET coding convention * Rename events-list to event-list * Refactor no-events card with FluentCard * Replace event title element to FluentLabel * Implement event link with FluentNavLink * Relocate OnInitializedAsync * Remove progress ring * Make color follow Fluent UI neutral layer color * Add id attributes on sub-elements in event cards * Rename _events to events, Add getting up to 4 events prior to the showing loop * Remove redundant null initialization in EventListComponent * Integrate linq code to foreach * Integrate no-events item to EventItemComponent and adjust breakpoints * Remove breakpoint parameter, update grid's justification to center * Adjust breakpoint for desktop size * Fix FluentCard's class name * Add new tests dedicated to EventItemComponent * Update grid's justify to FlexStart * Remove EventListComponent Id * Add HasNoEvent parameter for EventItemComponent * Move grid's style code to a dedicated CSS file * Restore EventListComponent's Id parameter and change its parameter name value * Separate CSS code for EventItemComponent to its own CSS file * Refactor EventsPageTests for CSS isolation work * Prune redundant CSS configurations * Integrate more CSS properties * Add title in Events component page * Adjust margin properties to prevent hiding the focus square
- Loading branch information
Showing
7 changed files
with
343 additions
and
0 deletions.
There are no files selected for viewing
12 changes: 12 additions & 0 deletions
12
src/AzureOpenAIProxy.PlaygroundApp/Components/Pages/Events.razor
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
@page "/events" | ||
|
||
<PageTitle>Events</PageTitle> | ||
|
||
<h1>Your Events</h1> | ||
|
||
<p>This component demonstrates showing up to 4 events that user joined.</p> | ||
|
||
<EventListComponent Id="user-event-list" @rendermode="InteractiveServer" /> | ||
|
||
@code { | ||
} |
47 changes: 47 additions & 0 deletions
47
src/AzureOpenAIProxy.PlaygroundApp/Components/UI/EventItemComponent.razor
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
@if (HasNoEvent) | ||
{ | ||
<FluentGridItem Class="event-list-item" Id="@Id" xs="12" lg="12" xxl="12" Justify="JustifyContent.Center" Style="text-align: center;"> | ||
<div class="event-item-flex-wrapper"> | ||
<FluentCard Class="no-event" MinimalStyle="true"> | ||
<FluentLabel Typo="Typography.Header">You don’t have any events you have participated in now.</FluentLabel> | ||
</FluentCard> | ||
</div> | ||
</FluentGridItem> | ||
} | ||
else | ||
{ | ||
<FluentGridItem Class="event-list-item" Id="@Id" xs="12" lg="6" xxl="3" Justify="JustifyContent.Center" Style="text-align: center;"> | ||
<div class="event-item-flex-wrapper"> | ||
<FluentCard Class="event" MinimalStyle="true"> | ||
<FluentNavLink Class="event-details-link" Href="@($"/events/{Id}")" > | ||
<FluentLabel Class="event-title" Typo="Typography.PaneHeader" Style="margin-block: 0.5em;"> | ||
@Title | ||
</FluentLabel> | ||
</FluentNavLink> | ||
<div class="event-summary card border"> | ||
<div class="card-body"> | ||
<h5>@Summary</h5> | ||
</div> | ||
</div> | ||
</FluentCard> | ||
</div> | ||
</FluentGridItem> | ||
} | ||
|
||
|
||
|
||
|
||
|
||
@code { | ||
[Parameter] | ||
public string? Id { get; set; } | ||
|
||
[Parameter] | ||
public string? Title { get; set; } | ||
|
||
[Parameter] | ||
public string? Summary { get; set; } | ||
|
||
[Parameter] | ||
public bool HasNoEvent { get; set; } | ||
} |
37 changes: 37 additions & 0 deletions
37
src/AzureOpenAIProxy.PlaygroundApp/Components/UI/EventItemComponent.razor.css
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
::deep .event { | ||
display: flex; | ||
padding: 0em; | ||
flex-direction: column; | ||
align-items: normal; | ||
align-content: center; | ||
justify-content: center; | ||
} | ||
|
||
::deep .no-event { | ||
display: flex; | ||
padding: 0em; | ||
flex-direction: row; | ||
align-items: center; | ||
align-content: center; | ||
justify-content: center; | ||
background-color: transparent; | ||
box-shadow: none !important; | ||
border: hidden; | ||
} | ||
|
||
::deep .event-details-link { | ||
flex-grow: 0; | ||
align-self: center; | ||
margin-block: 0.3em; | ||
} | ||
|
||
div.event-summary.card.border { | ||
background-color: var(--neutral-layer-2); | ||
padding: 1em; | ||
padding-inline: 1.5em; | ||
flex-grow: 1; | ||
} | ||
|
||
div.event-item-flex-wrapper { | ||
flex-grow: 1; | ||
} |
78 changes: 78 additions & 0 deletions
78
src/AzureOpenAIProxy.PlaygroundApp/Components/UI/EventListComponent.razor
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
@using AzureOpenAIProxy.PlaygroundApp.Models; | ||
|
||
<!-- Source: https://khalidabuhakmeh.com/blazors-css-isolation-deep-issue-and-solution --> | ||
<div> | ||
<FluentGrid AdaptiveRendering="true" | ||
Id="@Id" | ||
Spacing="3" | ||
Justify="JustifyContent.FlexStart" | ||
Class="event-list"> | ||
@if (events == null || events.Any() == false) | ||
{ | ||
<EventItemComponent Id="no-event-item" HasNoEvent="true"></EventItemComponent> | ||
} | ||
else | ||
{ | ||
// Shows up to 4 events that the user currently joined. | ||
@foreach (var e in events.Take(4)) | ||
{ | ||
<EventItemComponent Id="@e.EventId.ToString()" | ||
Title="@e.Title" | ||
Summary="@e.Summary"> | ||
</EventItemComponent> | ||
} | ||
} | ||
</FluentGrid> | ||
</div> | ||
|
||
@code { | ||
private List<EventDetails>? events; | ||
|
||
[Parameter] | ||
public string? Id { get; set; } | ||
|
||
protected override async Task OnInitializedAsync() | ||
{ | ||
// TODO: Fetch events from the API server. | ||
events = await CreateEventDetailsAsync(); | ||
} | ||
|
||
private async Task<List<EventDetails>> CreateEventDetailsAsync() | ||
{ | ||
return await Task.FromResult(new List<EventDetails> | ||
{ | ||
new EventDetails | ||
{ | ||
EventId = Guid.NewGuid(), | ||
Title = "Event 1", | ||
Summary = "Summary 1", | ||
MaxTokenCap = 1000, | ||
DailyRequestCap = 100 | ||
}, | ||
new EventDetails | ||
{ | ||
EventId = Guid.NewGuid(), | ||
Title = "Event 2", | ||
Summary = "Et iusto clita ipsum et. Amet lorem est lorem takimata et aliquyam. Aliquyam invidunt dolor erat eu sed ut sadipscing justo sed justo amet magna ea lorem ipsum exerci. Erat diam tempor imperdiet lorem duis. Amet est sanctus tempor kasd erat odio diam accumsan stet. Voluptua aliquyam magna at no vulputate justo labore labore eos stet. Dolore ut ad sadipscing sit elitr ipsum commodo nam invidunt wisi labore vero feugait sanctus sea ad et sadipscing. Possim tempor nonummy erat et no erat lorem in dolore consequat eos feugiat justo vero. Ut eirmod et duis accusam dolore est sea duis dolor et duis illum. Esse ut aliquyam placerat enim amet et labore sadipscing sed stet duo eos at consequat autem accusam lorem invidunt. Sea clita rebum eum et no dolore et sit. Liber aliquyam duo eu. Feugiat sadipscing sed eos sanctus gubergren dolore amet. Erat liber nam ea aliquam ut autem dolores magna aliquyam illum vero vulputate ut accusam est rebum. Et takimata est dolore ut elitr gubergren sanctus ipsum magna magna at sed amet dolores amet. Rebum dolore sit ea et gubergren. Dolore aliquam ipsum in at est justo justo ipsum. Ipsum nisl sea lorem.", | ||
MaxTokenCap = 2000, | ||
DailyRequestCap = 200, | ||
}, | ||
new EventDetails | ||
{ | ||
EventId = Guid.NewGuid(), | ||
Title = "Event 3", | ||
Summary = "Lorem ipsum dolor sit amet stet ipsum invidunt amet invidunt magna vero delenit tempor invidunt no rebum eirmod. Duo labore eu no nonumy consequat lobortis consequat consetetur ipsum et ipsum ea eirmod esse. Eirmod rebum voluptua duo et autem eirmod vero amet dolores tincidunt lorem ipsum stet dolore sed aliquyam nonumy consetetur. Rebum no invidunt justo consetetur gubergren sea luptatum ut et amet ut aliquyam lorem ipsum. Nonummy et dolor placerat sit hendrerit invidunt. Et est dolore magna et suscipit duo aliquyam sed dolore ipsum erat nonummy eirmod. Nonummy consequat et et et accusam hendrerit et dolor et. Sanctus gubergren elitr sit takimata accusam lobortis quod sit nonumy nonumy diam clita clita. Ea takimata dolor molestie duo tempor invidunt amet nobis lorem accumsan duo rebum diam ipsum dolores erat ea. Amet nulla eirmod takimata no vel in et sea lobortis ut ullamcorper sadipscing delenit duo takimata ipsum eos consectetuer. Et et ea no duis eu labore quod ipsum feugiat esse lorem clita et nibh iriure diam magna. Sit duis tempor dolore sed et no magna et dolor labore clita erat sed dolores accusam molestie clita. Quis amet eum sit magna kasd eu invidunt nihil. Labore diam erat dignissim labore ipsum qui clita vel eos. Nisl praesent amet consequat ipsum justo quod tempor sed est aliquyam labore lorem accusam diam.", | ||
MaxTokenCap = 3000, | ||
DailyRequestCap = 300, | ||
}, | ||
new EventDetails | ||
{ | ||
EventId = Guid.NewGuid(), | ||
Title = "Event 4", | ||
Summary = "Summary 4", | ||
MaxTokenCap = 3000, | ||
DailyRequestCap = 300, | ||
} | ||
}); | ||
} | ||
} |
7 changes: 7 additions & 0 deletions
7
src/AzureOpenAIProxy.PlaygroundApp/Components/UI/EventListComponent.razor.css
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
::deep .event-list { | ||
background-color: var(--neutral-layer-4); | ||
padding-block: 2.0em; | ||
padding-inline: 1.5em; | ||
margin-top: 1rem; | ||
border-radius: 8px; | ||
} |
69 changes: 69 additions & 0 deletions
69
test/AzureOpenAIProxy.AppHost.Tests/PlaygroundApp/Pages/EventsPageTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
using System.Net; | ||
|
||
using AzureOpenAIProxy.AppHost.Tests.Fixtures; | ||
|
||
using FluentAssertions; | ||
|
||
namespace AzureOpenAIProxy.AppHost.Tests.PlaygroundApp.Pages; | ||
|
||
public class EventsPageTests(AspireAppHostFixture host) : IClassFixture<AspireAppHostFixture> | ||
{ | ||
[Fact] | ||
public async Task Given_Resource_When_Invoked_Endpoint_Then_It_Should_Return_OK() | ||
{ | ||
// Arrange | ||
using var httpClient = host.App!.CreateHttpClient("playgroundapp"); | ||
await host.ResourceNotificationService.WaitForResourceAsync("playgroundapp", KnownResourceStates.Running).WaitAsync(TimeSpan.FromSeconds(30)); | ||
|
||
// Act | ||
var response = await httpClient.GetAsync("/events"); | ||
|
||
// Assert | ||
response.StatusCode.Should().Be(HttpStatusCode.OK); | ||
} | ||
|
||
[Theory] | ||
[InlineData("_content/Microsoft.FluentUI.AspNetCore.Components/css/reboot.css")] | ||
public async Task Given_Resource_When_Invoked_Endpoint_Then_It_Should_Return_CSS_Elements(string expected) | ||
{ | ||
// Arrange | ||
using var httpClient = host.App!.CreateHttpClient("playgroundapp"); | ||
await host.ResourceNotificationService.WaitForResourceAsync("playgroundapp", KnownResourceStates.Running).WaitAsync(TimeSpan.FromSeconds(30)); | ||
|
||
// Act | ||
var html = await httpClient.GetStringAsync("/events"); | ||
|
||
// Assert | ||
html.Should().Contain(expected); | ||
} | ||
|
||
[Theory] | ||
[InlineData("_content/Microsoft.FluentUI.AspNetCore.Components/Microsoft.FluentUI.AspNetCore.Components.lib.module.js")] | ||
public async Task Given_Resource_When_Invoked_Endpoint_Then_It_Should_Return_JavaScript_Elements(string expected) | ||
{ | ||
// Arrange | ||
using var httpClient = host.App!.CreateHttpClient("playgroundapp"); | ||
await host.ResourceNotificationService.WaitForResourceAsync("playgroundapp", KnownResourceStates.Running).WaitAsync(TimeSpan.FromSeconds(30)); | ||
|
||
// Act | ||
var html = await httpClient.GetStringAsync("/events"); | ||
|
||
// Assert | ||
html.Should().Contain(expected); | ||
} | ||
|
||
[Theory] | ||
[InlineData("<div class=\"fluent-tooltip-provider\"></div>")] | ||
public async Task Given_Resource_When_Invoked_Endpoint_Then_It_Should_Return_HTML_Elements(string expected) | ||
{ | ||
// Arrange | ||
using var httpClient = host.App!.CreateHttpClient("playgroundapp"); | ||
await host.ResourceNotificationService.WaitForResourceAsync("playgroundapp", KnownResourceStates.Running).WaitAsync(TimeSpan.FromSeconds(30)); | ||
|
||
// Act | ||
var html = await httpClient.GetStringAsync("/events"); | ||
|
||
// Assert | ||
html.Should().Contain(expected); | ||
} | ||
} |
93 changes: 93 additions & 0 deletions
93
test/AzureOpenAIProxy.PlaygroundApp.Tests/Pages/EventsPageTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
using FluentAssertions; | ||
|
||
using Microsoft.Playwright; | ||
using Microsoft.Playwright.NUnit; | ||
|
||
namespace AzureOpenAIProxy.PlaygroundApp.Tests.Pages; | ||
|
||
[Parallelizable(ParallelScope.Self)] | ||
[TestFixture] | ||
[Property("Category", "Integration")] | ||
public class EventsPageTests : PageTest | ||
{ | ||
public override BrowserNewContextOptions ContextOptions() => new() | ||
{ | ||
IgnoreHTTPSErrors = true, | ||
}; | ||
|
||
[SetUp] | ||
public async Task Init() | ||
{ | ||
await Page.GotoAsync("https://localhost:5001/events"); | ||
await Page.WaitForLoadStateAsync(LoadState.NetworkIdle); | ||
} | ||
|
||
// Grid check | ||
[Test] | ||
public async Task Given_Events_Page_When_Navigated_Then_It_Should_Have_EventListComponent() | ||
{ | ||
// Act | ||
var eventListComponent = Page.Locator("div.event-list").First; | ||
|
||
// Assert | ||
await Expect(eventListComponent).ToBeVisibleAsync(); | ||
} | ||
|
||
[Test] | ||
public async Task Given_Events_When_Loaded_Then_It_Should_Have_Less_Than_Or_Equal_To_Four_EventItemComponents() | ||
{ | ||
// Arrange | ||
var eventList = Page.Locator("#user-event-list"); | ||
var listEvents = await eventList.Locator("div.event-list-item").AllAsync(); | ||
|
||
// Act | ||
var childrenCount = listEvents.Count; | ||
|
||
// Assert | ||
Assert.That(childrenCount, Is.GreaterThan(0)); | ||
Assert.That(childrenCount, Is.LessThanOrEqualTo(4)); | ||
} | ||
|
||
[Test] | ||
public async Task Given_Events_When_Loaded_Then_It_Should_Have_Header_And_Summary_In_The_Card() | ||
{ | ||
// Act | ||
var eventCards = await Page.Locator("div.fluent-card-minimal-style.event").AllAsync(); | ||
|
||
// Assert | ||
foreach (var card in eventCards) | ||
{ | ||
card.Should().NotBeNull(); | ||
// Check headers | ||
var header = card.Locator("div.fluent-nav-item.event-details-link").First; | ||
await Expect(header).ToBeVisibleAsync(); | ||
|
||
// Check summaries | ||
var summary = card.Locator("div.event-summary.card.border").First; | ||
await Expect(summary).ToBeVisibleAsync(); | ||
} | ||
} | ||
|
||
[Test] | ||
public async Task Given_Events_When_Loaded_Then_Their_Links_Are_Enabled_To_Click() | ||
{ | ||
// Act | ||
var eventCards = await Page.Locator("div.fluent-card-minimal-style.event").AllAsync(); | ||
|
||
// Assert | ||
foreach (var card in eventCards) | ||
{ | ||
// Getting a link element. | ||
var link = card.Locator("div.fluent-nav-item.event-details-link").First | ||
.Locator("a.fluent-nav-link").First; | ||
|
||
await Expect(link).ToBeEnabledAsync(); | ||
} | ||
} | ||
|
||
[TearDown] | ||
public async Task CleanUp() | ||
{ | ||
await Page.CloseAsync(); | ||
} | ||
} |