Skip to content

Commit 5bbd5eb

Browse files
committed
Prevent PrivilegeText from rendering masked value when there is no content
1 parent d499fd5 commit 5bbd5eb

File tree

2 files changed

+110
-1
lines changed

2 files changed

+110
-1
lines changed

src/Privileged.Components/PrivilegeText.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,19 @@ public class PrivilegeText : ComponentBase
254254
/// </remarks>
255255
protected bool? IsAllowed { get; set; }
256256

257+
/// <summary>
258+
/// Gets or sets whether the component has content to display.
259+
/// </summary>
260+
/// <value>
261+
/// <c>true</c> if either <see cref="ChildContent"/> is not null or <see cref="Text"/> is not null/empty/whitespace;
262+
/// <c>false</c> otherwise.
263+
/// </value>
264+
/// <remarks>
265+
/// This property is calculated in <see cref="OnParametersSet"/> and is used to determine
266+
/// whether to render the toggle button and masked content.
267+
/// </remarks>
268+
protected bool HasContent { get; set; }
269+
257270
/// <summary>
258271
/// Gets or sets whether the content is currently shown (unmasked) by user interaction.
259272
/// </summary>
@@ -293,6 +306,8 @@ protected override void OnParametersSet()
293306

294307
IsAllowed = string.IsNullOrWhiteSpace(Subject)
295308
|| PrivilegeContext?.Allowed(Action, Subject, Qualifier) == true;
309+
310+
HasContent = ChildContent != null || !string.IsNullOrWhiteSpace(Text);
296311
}
297312

298313
/// <summary>
@@ -318,6 +333,9 @@ protected override void OnParametersSet()
318333
/// </remarks>
319334
protected override void BuildRenderTree(RenderTreeBuilder builder)
320335
{
336+
if (!HasContent)
337+
return;
338+
321339
// Container
322340
builder.OpenElement(0, "div");
323341
builder.AddAttribute(1, "style", ContainerStyle);

test/Privileged.Components.Tests/PrivilegeTextTests.cs

Lines changed: 92 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ public void AuthorizedWithText_ShowsMaskedText()
1818

1919
var button = cut.Find("button");
2020
Assert.NotNull(button);
21-
21+
2222
cut.Find("div div").MarkupMatches("<div>••••••</div>");
2323
}
2424

@@ -340,4 +340,95 @@ public void DefaultAction_IsRead()
340340
var button = cut.Find("button");
341341
Assert.NotNull(button);
342342
}
343+
344+
[Fact]
345+
public void NoContent_NullTextAndChildContent_RendersNothing()
346+
{
347+
var context = new PrivilegeBuilder()
348+
.Allow("read", "Secret")
349+
.Build();
350+
351+
var cut = Render<PrivilegeText>(parameters => parameters
352+
.AddCascadingValue(context)
353+
.Add(p => p.Action, "read")
354+
.Add(p => p.Subject, "Secret")
355+
.Add(p => p.Text, (string?)null)
356+
);
357+
358+
Assert.Throws<Bunit.ElementNotFoundException>(() => cut.Find("button"));
359+
Assert.Throws<Bunit.ElementNotFoundException>(() => cut.Find("div"));
360+
}
361+
362+
[Fact]
363+
public void NoContent_EmptyText_RendersNothing()
364+
{
365+
var context = new PrivilegeBuilder()
366+
.Allow("read", "Secret")
367+
.Build();
368+
369+
var cut = Render<PrivilegeText>(parameters => parameters
370+
.AddCascadingValue(context)
371+
.Add(p => p.Action, "read")
372+
.Add(p => p.Subject, "Secret")
373+
.Add(p => p.Text, "")
374+
);
375+
376+
Assert.Throws<Bunit.ElementNotFoundException>(() => cut.Find("button"));
377+
Assert.Throws<Bunit.ElementNotFoundException>(() => cut.Find("div"));
378+
}
379+
380+
[Fact]
381+
public void NoContent_WhitespaceText_RendersNothing()
382+
{
383+
var context = new PrivilegeBuilder()
384+
.Allow("read", "Secret")
385+
.Build();
386+
387+
var cut = Render<PrivilegeText>(parameters => parameters
388+
.AddCascadingValue(context)
389+
.Add(p => p.Action, "read")
390+
.Add(p => p.Subject, "Secret")
391+
.Add(p => p.Text, " ")
392+
);
393+
394+
Assert.Throws<Bunit.ElementNotFoundException>(() => cut.Find("button"));
395+
Assert.Throws<Bunit.ElementNotFoundException>(() => cut.Find("div"));
396+
}
397+
398+
[Fact]
399+
public void NoContent_UnauthorizedWithNoContent_RendersNothing()
400+
{
401+
var context = new PrivilegeBuilder()
402+
.Forbid("read", "Secret")
403+
.Build();
404+
405+
var cut = Render<PrivilegeText>(parameters => parameters
406+
.AddCascadingValue(context)
407+
.Add(p => p.Action, "read")
408+
.Add(p => p.Subject, "Secret")
409+
.Add(p => p.Text, "")
410+
);
411+
412+
Assert.Throws<Bunit.ElementNotFoundException>(() => cut.Find("button"));
413+
Assert.Throws<Bunit.ElementNotFoundException>(() => cut.Find("div"));
414+
}
415+
416+
[Fact]
417+
public void NoContent_WithMaskedContent_StillRendersNothing()
418+
{
419+
var context = new PrivilegeBuilder()
420+
.Allow("read", "Secret")
421+
.Build();
422+
423+
var cut = Render<PrivilegeText>(parameters => parameters
424+
.AddCascadingValue(context)
425+
.Add(p => p.Action, "read")
426+
.Add(p => p.Subject, "Secret")
427+
.Add(p => p.Text, "")
428+
.Add(p => p.MaskedContent, "<em>Hidden</em>")
429+
);
430+
431+
Assert.Throws<Bunit.ElementNotFoundException>(() => cut.Find("button"));
432+
Assert.Throws<Bunit.ElementNotFoundException>(() => cut.Find("div"));
433+
}
343434
}

0 commit comments

Comments
 (0)