Skip to content

Commit f0c3153

Browse files
committed
feat: prevent navigation if blogpost is dirty
1 parent f925d6d commit f0c3153

File tree

10 files changed

+122
-17
lines changed

10 files changed

+122
-17
lines changed

.DS_Store

8 KB
Binary file not shown.

src/.DS_Store

6 KB
Binary file not shown.

src/LinkDotNet.Blog.Web/.DS_Store

6 KB
Binary file not shown.

src/LinkDotNet.Blog.Web/Features/Admin/BlogPostEditor/Components/CreateNewBlogPost.razor

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@
7575
</div>
7676
<FeatureInfoDialog @ref="FeatureDialog"></FeatureInfoDialog>
7777

78+
<NavigationLock ConfirmExternalNavigation="@model.IsDirty" OnBeforeInternalNavigation="PreventNavigationWhenDirty"></NavigationLock>
7879
@code {
7980
[Parameter]
8081
public BlogPost BlogPost { get; set; }
@@ -120,4 +121,10 @@
120121
{
121122
model.Content = content;
122123
}
124+
125+
private void PreventNavigationWhenDirty(LocationChangingContext context)
126+
{
127+
if (model.IsDirty)
128+
context.PreventNavigation();
129+
}
123130
}

src/LinkDotNet.Blog.Web/Features/Admin/BlogPostEditor/Components/CreateNewModel.cs

Lines changed: 90 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,57 +7,130 @@ namespace LinkDotNet.Blog.Web.Features.Admin.BlogPostEditor.Components;
77

88
public class CreateNewModel
99
{
10-
public string Id { get; set; }
10+
private DateTime originalUpdatedDate;
11+
private string id;
12+
private string title;
13+
private string shortDescription;
14+
private string content;
15+
private string previewImageUrl;
16+
private bool isPublished = true;
17+
private bool shouldUpdateDate;
18+
private string tags;
19+
private string previewImageUrlFallback;
1120

1221
[Required]
13-
public string Title { get; set; }
22+
public string Title
23+
{
24+
get => title;
25+
set
26+
{
27+
title = value;
28+
IsDirty = true;
29+
}
30+
}
1431

1532
[Required]
16-
public string ShortDescription { get; set; }
33+
public string ShortDescription
34+
{
35+
get => shortDescription;
36+
set
37+
{
38+
shortDescription = value;
39+
IsDirty = true;
40+
}
41+
}
1742

1843
[Required]
19-
public string Content { get; set; }
44+
public string Content
45+
{
46+
get => content;
47+
set
48+
{
49+
content = value;
50+
IsDirty = true;
51+
}
52+
}
2053

2154
[Required]
22-
public string PreviewImageUrl { get; set; }
55+
public string PreviewImageUrl
56+
{
57+
get => previewImageUrl;
58+
set
59+
{
60+
previewImageUrl = value;
61+
IsDirty = true;
62+
}
63+
}
2364

2465
[Required]
25-
public bool IsPublished { get; set; } = true;
66+
public bool IsPublished
67+
{
68+
get => isPublished;
69+
set
70+
{
71+
isPublished = value;
72+
IsDirty = true;
73+
}
74+
}
2675

2776
[Required]
28-
public bool ShouldUpdateDate { get; set; } = false;
77+
public bool ShouldUpdateDate
78+
{
79+
get => shouldUpdateDate;
80+
set
81+
{
82+
shouldUpdateDate = value;
83+
IsDirty = true;
84+
}
85+
}
2986

30-
public string Tags { get; set; }
87+
public string Tags
88+
{
89+
get => tags;
90+
set
91+
{
92+
tags = value;
93+
IsDirty = true;
94+
}
95+
}
3196

32-
public DateTime OriginalUpdatedDate { get; set; }
97+
public string PreviewImageUrlFallback
98+
{
99+
get => previewImageUrlFallback;
100+
set
101+
{
102+
previewImageUrlFallback = value;
103+
IsDirty = true;
104+
}
105+
}
33106

34-
public string PreviewImageUrlFallback { get; set; }
107+
public bool IsDirty { get; private set; }
35108

36109
public static CreateNewModel FromBlogPost(BlogPost blogPost)
37110
{
38111
return new CreateNewModel
39112
{
40-
Id = blogPost.Id,
113+
id = blogPost.Id,
41114
Content = blogPost.Content,
42115
Tags = blogPost.Tags != null ? string.Join(",", blogPost.Tags.Select(t => t.Content)) : null,
43116
Title = blogPost.Title,
44117
ShortDescription = blogPost.ShortDescription,
45118
IsPublished = blogPost.IsPublished,
46119
PreviewImageUrl = blogPost.PreviewImageUrl,
47-
OriginalUpdatedDate = blogPost.UpdatedDate,
120+
originalUpdatedDate = blogPost.UpdatedDate,
48121
PreviewImageUrlFallback = blogPost.PreviewImageUrlFallback,
49122
};
50123
}
51124

52125
public BlogPost ToBlogPost()
53126
{
54-
var tags = string.IsNullOrWhiteSpace(Tags) ? ArraySegment<string>.Empty : Tags.Split(",");
55-
DateTime? updatedDate = ShouldUpdateDate || OriginalUpdatedDate == default
127+
var tagList = string.IsNullOrWhiteSpace(Tags) ? ArraySegment<string>.Empty : Tags.Split(",");
128+
DateTime? updatedDate = ShouldUpdateDate || originalUpdatedDate == default
56129
? null
57-
: OriginalUpdatedDate;
130+
: originalUpdatedDate;
58131

59-
var blogPost = BlogPost.Create(Title, ShortDescription, Content, PreviewImageUrl, IsPublished, updatedDate, tags, PreviewImageUrlFallback);
60-
blogPost.Id = Id;
132+
var blogPost = BlogPost.Create(Title, ShortDescription, Content, PreviewImageUrl, IsPublished, updatedDate, tagList, PreviewImageUrlFallback);
133+
blogPost.Id = id;
61134
return blogPost;
62135
}
63136
}
6 KB
Binary file not shown.
Binary file not shown.
Binary file not shown.

tests/.DS_Store

6 KB
Binary file not shown.

tests/LinkDotNet.Blog.UnitTests/Web/Features/Admin/BlogPostEditor/Components/CreateNewBlogPostTests.cs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
using System;
22
using System.Linq;
3+
using Bunit;
34
using LinkDotNet.Blog.Domain;
45
using LinkDotNet.Blog.TestUtilities;
56
using LinkDotNet.Blog.Web.Features.Admin.BlogPostEditor.Components;
7+
using Microsoft.AspNetCore.Components.Routing;
8+
using Microsoft.Extensions.DependencyInjection;
69

710
namespace LinkDotNet.Blog.UnitTests.Web.Features.Admin.BlogPostEditor.Components;
811

@@ -165,4 +168,26 @@ public void ShouldAcceptInputWithoutLosingFocusOrEnter()
165168
blogPost.Tags.Should().HaveCount(3);
166169
blogPost.Tags.Select(t => t.Content).Should().Contain(new[] { "Tag1", "Tag2", "Tag3" });
167170
}
171+
172+
[Fact(Skip = "Need bUnit > 1.9.8")]
173+
public void ShouldStopExternalNavigationWhenDirty()
174+
{
175+
var cut = RenderComponent<CreateNewBlogPost>();
176+
177+
cut.Find("#title").Change("Hey");
178+
179+
cut.FindComponent<NavigationLock>().Instance.ConfirmExternalNavigation.Should().BeTrue();
180+
}
181+
182+
[Fact(Skip = "Need bUnit > 1.9.8")]
183+
public void ShouldStopInternalNavigationWhenDirty()
184+
{
185+
var cut = RenderComponent<CreateNewBlogPost>();
186+
cut.Find("#tags").Change("Hey");
187+
var fakeNavigationManager = Services.GetRequiredService<FakeNavigationManager>();
188+
189+
fakeNavigationManager.NavigateTo("/internal");
190+
191+
fakeNavigationManager.History.Count.Should().Be(1);
192+
}
168193
}

0 commit comments

Comments
 (0)