Skip to content

Commit a7bc5aa

Browse files
committed
WIP
Update documentation WIP WIP with Denis on phone Example Test Example Add new checkbox features and update documentation - Added new checkboxes in `CheckboxAppearances.razor` for square and circular unchecked states. - Removed required validation section from `CheckboxRequired.razor`. - Updated `FluentCheckbox.md` with info on sizes and three-state checkboxes; removed inline text rendering example. - Added `ThreeState` and `ThreeStateChecked` parameters to `FluentCheckbox.razor.cs` for three-state support. - Made `Size` property in `FluentCheckbox.razor.cs` non-nullable. - Minor formatting changes in `CheckboxShape.cs`. - Added placeholder `TODO` div in `CheckboxIndeterminate.razor`. - Added examples of different checkbox sizes and states in `CheckboxSizes.razor`. - Added placeholder div in `CheckboxThreeStates.razor`. Add indeterminate state support to FluentCheckbox Enhanced the FluentCheckbox component to support an indeterminate state: - Updated CheckboxIndeterminate.razor to include FluentCheckbox components. - Documented the Indeterminate property in FluentCheckbox.md. - Removed AddTag element from FluentCheckbox.razor. - Added constant for JS file path and implemented OnAfterRenderAsync in FluentCheckbox.razor.cs. - Added SetElementToIndeterminateAsync method for JS interop. - Introduced FluentCheckbox.razor.ts with functions for observing attribute changes and setting indeterminate state.
1 parent 20f536c commit a7bc5aa

File tree

13 files changed

+362
-1
lines changed

13 files changed

+362
-1
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<div style="display: flex;flex-direction: column; gap: 12px;">
2+
<FluentCheckbox Shape="@CheckboxShape.Square" Label="Square checked" Checked="true" />
3+
<FluentCheckbox Shape="@CheckboxShape.Circular" Label="Circular checked" Checked="true" />
4+
5+
<FluentCheckbox Shape="@CheckboxShape.Square" Label="Square unchecked" Checked="false" />
6+
<FluentCheckbox Shape="@CheckboxShape.Circular" Label="Circular unchecked" Checked="false" />
7+
</div>
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<div style="display: flex;flex-direction: column; gap: 12px;">
2+
<FluentCheckbox Shape="@CheckboxShape.Square" Label="Square disabled checked" Disabled="true" Checked="true" />
3+
<FluentCheckbox Shape="@CheckboxShape.Circular" Label="Circular disabled checked" Disabled="true" Checked="true" />
4+
5+
<FluentCheckbox Shape="@CheckboxShape.Square" Label="Square disabled unchecked" Disabled="true" Checked="false" />
6+
<FluentCheckbox Shape="@CheckboxShape.Circular" Label="Circular disabled unchecked" Disabled="true" Checked="false" />
7+
</div>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<div>
2+
<FluentCheckbox Indeterminate="true" /> <br />
3+
<FluentCheckbox Indeterminate="true" Label="Indeterminate" />
4+
</div>
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<div>
2+
<FluentCheckbox Shape="@CheckboxShape.Square" Checked="true" />
3+
<FluentCheckbox Shape="@CheckboxShape.Square" Checked="false" />
4+
<FluentCheckbox Shape="@CheckboxShape.Square" Disabled="true" Checked="true" />
5+
<FluentCheckbox Shape="@CheckboxShape.Square" Disabled="true" />
6+
<FluentCheckbox Shape="@CheckboxShape.Square" Size="@CheckboxSize.Large" Checked="true" />
7+
<FluentCheckbox Shape="@CheckboxShape.Square" Size="@CheckboxSize.Large" Checked="false" />
8+
<FluentCheckbox Shape="@CheckboxShape.Square" Size="@CheckboxSize.Large" Disabled="true" Checked="true" />
9+
<FluentCheckbox Shape="@CheckboxShape.Square" Size="@CheckboxSize.Large" Disabled="true" />
10+
11+
<br />
12+
13+
<FluentCheckbox Shape="@CheckboxShape.Circular" Checked="true" />
14+
<FluentCheckbox Shape="@CheckboxShape.Circular" Checked="false" />
15+
<FluentCheckbox Shape="@CheckboxShape.Circular" Disabled="true" Checked="true" />
16+
<FluentCheckbox Shape="@CheckboxShape.Circular" Disabled="true" />
17+
<FluentCheckbox Shape="@CheckboxShape.Circular" Size="@CheckboxSize.Large" Checked="true" />
18+
<FluentCheckbox Shape="@CheckboxShape.Circular" Size="@CheckboxSize.Large" Checked="false" />
19+
<FluentCheckbox Shape="@CheckboxShape.Circular" Size="@CheckboxSize.Large" Disabled="true" Checked="true" />
20+
<FluentCheckbox Shape="@CheckboxShape.Circular" Size="@CheckboxSize.Large" Disabled="true" />
21+
</div>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
<div>
2+
</div>
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
---
2+
title: Checkbox
3+
route: /Checkbox
4+
---
5+
6+
# Checkbox
7+
8+
A **FluentCheckbox** component enables a user to select or deselect an option.
9+
It's typically used to capture a boolean value.
10+
11+
## Appearance
12+
13+
The apparent style of a checkbox can be changed by setting the `Shape` property, but also by setting the `Size` property.
14+
15+
You can also add a label to the checkbox by setting the `Label` property.
16+
The label will be automatically positioned next to the checkbox.
17+
18+
We recommend using a spacing of 24px between checkboxes and other components.
19+
20+
{{ CheckboxAppearances }}
21+
22+
### Size
23+
24+
The size of the checkbox can be adjusted using the `Size` property. The available sizes are:
25+
26+
- `Medium`: The default size.
27+
- `Large`: A larger size for the checkbox.
28+
29+
{{ CheckboxSizes }}
30+
31+
### Indeterminate
32+
33+
The `FluentCheckbox` component supports an indeterminate state, which can be useful for scenarios where a checkbox represents a mixed or partial selection.
34+
The indeterminate state is visually distinct from the checked and unchecked states.
35+
36+
To set the checkbox to the indeterminate state, use the `Indeterminate` property.
37+
38+
{{ CheckboxIndeterminate }}
39+
40+
## Three-State Checkbox
41+
42+
The `FluentCheckbox` component supports a three-state mode, which allows the checkbox to have an additional indeterminate state. This can be useful for scenarios where a checkbox represents a mixed or partial selection.
43+
44+
To enable the three-state mode, set the `ThreeState` property to `true`. You can also control the order of the states using the `ThreeStateOrderUncheckToIntermediate` property.
45+
46+
- `ThreeState`: Enables the three-state mode.
47+
- `ThreeStateOrderUncheckToIntermediate`: Controls the order of the states. If set to `true`, the order will be Unchecked -> Intermediate -> Checked. If set to `false` (default), the order will be Unchecked -> Checked -> Intermediate.
48+
49+
{{ CheckboxThreeStates }}
50+
51+
## API FluentCheckbox
52+
53+
{{ API Type=FluentCheckbox }}

examples/Demo/FluentUI.Demo.Client/FluentUI.Demo.Client.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
1+
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
22

33
<PropertyGroup>
44
<TargetFramework>net9.0</TargetFramework>
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
@namespace Microsoft.FluentUI.AspNetCore.Components
2+
@using Microsoft.FluentUI.AspNetCore.Components.Extensions
3+
@inherits FluentInputBase<bool?>
4+
5+
<AddTag Name="fluent-field" TagWhen="@(() => HasLabel())" label-position="after">
6+
@if (HasLabel())
7+
{
8+
<label Required="@Required" for="@Id" slot="label">
9+
@Label
10+
@LabelTemplate
11+
</label>
12+
}
13+
14+
<fluent-checkbox id="@Id"
15+
@ref="@Element"
16+
disabled="@Disabled"
17+
autofocus="@Autofocus"
18+
checked="@Checked"
19+
indeterminate="@Indeterminate"
20+
name="@Name"
21+
required="@Required"
22+
value="@Value"
23+
shape="@Shape.ToAttributeValue()"
24+
size="@Size.ToAttributeValue()"
25+
@onchange="@ChangeHandlerAsync"
26+
@attributes="AdditionalAttributes"
27+
slot="input">
28+
29+
@if (StartTemplate != null)
30+
{
31+
<div slot="start">
32+
@StartTemplate
33+
</div>
34+
}
35+
36+
@if (EndTemplate != null)
37+
{
38+
<div slot="end">
39+
@EndTemplate
40+
</div>
41+
}
42+
</fluent-checkbox>
43+
</AddTag>
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
// ------------------------------------------------------------------------
2+
// MIT License - Copyright (c) Microsoft Corporation. All rights reserved.
3+
// ------------------------------------------------------------------------
4+
5+
using System.Diagnostics.CodeAnalysis;
6+
using System.Reflection.Metadata;
7+
using Microsoft.AspNetCore.Components;
8+
using Microsoft.FluentUI.AspNetCore.Components.Utilities;
9+
using Microsoft.JSInterop;
10+
11+
namespace Microsoft.FluentUI.AspNetCore.Components;
12+
13+
/// <summary>
14+
/// The FluentCheckbox component is used to render a checkbox input...
15+
/// Test WIP
16+
/// </summary>
17+
public partial class FluentCheckbox : FluentInputBase<bool?>, IFluentComponentElementBase
18+
{
19+
private const string JAVASCRIPT_FILE = FluentJSModule.JAVASCRIPT_ROOT + "Checkbox/FluentCheckbox.razor.js";
20+
21+
/// <inheritdoc />
22+
[Parameter]
23+
public ElementReference Element { get; set; }
24+
25+
/// <summary>
26+
/// Gets or sets the checked state of the checkbox
27+
/// </summary>
28+
[Parameter]
29+
public bool? Checked { get; set; }
30+
31+
/// <summary>
32+
/// Gets or sets the indeterminate state of the checkbox
33+
/// </summary>
34+
[Parameter]
35+
public bool? Indeterminate { get; set; }
36+
37+
/// <summary>
38+
/// Gets or sets a value indicating whether the CheckBox will allow three check states rather than two.
39+
/// </summary>
40+
[Parameter]
41+
public bool ThreeState { get; set; }
42+
43+
/// <summary>
44+
/// Gets or sets a value indicating the order of the three states of the CheckBox.
45+
/// False(by default), the order is Unchecked -> Checked -> Intermediate.
46+
/// True: the order is Unchecked -> Intermediate -> Checked.
47+
/// </summary>
48+
[Parameter]
49+
public bool ThreeStateChecked { get; set; }
50+
51+
/// <summary>
52+
/// Gets or sets the shape of the checkbox
53+
/// </summary>
54+
[Parameter]
55+
public CheckboxShape Shape { get; set; } = CheckboxShape.Square;
56+
57+
/// <summary>
58+
/// Gets or sets the content to prefix the input component.
59+
/// </summary>
60+
[Parameter]
61+
public virtual RenderFragment? StartTemplate { get; set; }
62+
63+
/// <summary>
64+
/// Gets or sets the content to suffix the input component.
65+
/// </summary>
66+
[Parameter]
67+
public virtual RenderFragment? EndTemplate { get; set; }
68+
69+
/// <summary>
70+
/// Gets or sets the size of the checkbox. See <see cref="Components.CheckboxSize"/>
71+
/// </summary>
72+
[Parameter]
73+
public CheckboxSize Size { get; set; } = CheckboxSize.Medium;
74+
75+
/// <inheritdoc />
76+
protected override void OnParametersSet()
77+
{
78+
base.OnParametersSet();
79+
if (HasLabel())
80+
{
81+
// When the id is not provided, generate a unique id. This allow to use the label for.
82+
Id ??= $"checkbox-{Identifier.NewId()}";
83+
}
84+
}
85+
86+
/// <summary>
87+
/// Parses a string to create the <see cref="Microsoft.AspNetCore.Components.Forms.InputBase{TValue}.Value"/>.
88+
/// </summary>
89+
/// <param name="value">The string value to be parsed.</param>
90+
/// <param name="result">The result to inject into the Value.</param>
91+
/// <param name="validationErrorMessage">If the value could not be parsed, provides a validation error message.</param>
92+
/// <returns>True if the value could be parsed; otherwise false.</returns>
93+
protected override bool TryParseValueFromString(string? value, [MaybeNullWhen(false)] out bool? result, [NotNullWhen(false)] out string? validationErrorMessage)
94+
{
95+
if (bool.TryParse(value, out var parsedValue))
96+
{
97+
result = parsedValue;
98+
validationErrorMessage = null;
99+
return true;
100+
}
101+
102+
result = null;
103+
validationErrorMessage = "The provided value is not a valid boolean.";
104+
return false;
105+
}
106+
/// <inheritdoc />
107+
protected override async Task OnAfterRenderAsync(bool firstRender)
108+
{
109+
if (firstRender)
110+
{
111+
if (Indeterminate.HasValue && Indeterminate == true)
112+
{
113+
await SetElementToIndeterminateAsync();
114+
}
115+
}
116+
}
117+
118+
private async Task SetElementToIndeterminateAsync()
119+
{
120+
var jsModule = await JSModule.ImportJavaScriptModuleAsync(JAVASCRIPT_FILE);
121+
await jsModule.InvokeVoidAsync("Microsoft.FluentUI.Blazor.TextInput.ObserveAttributeChanges", Element);
122+
}
123+
124+
private bool HasLabel()
125+
=> !string.IsNullOrEmpty(Label) || LabelTemplate is not null;
126+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
export namespace Microsoft.FluentUI.Blazor.Checkbox {
2+
3+
/**
4+
* Observe the changes in the ‘value’ attribute to update the element's ‘value’ property.
5+
* Wait for this PR to delete the code.
6+
* https://github.com/microsoft/fluentui/pull/33144
7+
*/
8+
export function ObserveAttributeChanges(element: HTMLElement): void {
9+
const observer = new MutationObserver((mutationsList) => {
10+
for (let mutation of mutationsList) {
11+
if (mutation.type === "attributes" && mutation.attributeName === "value") {
12+
const newValue = element.getAttribute("value");
13+
const field = element as any;
14+
if (newValue !== field.value) {
15+
field.value = newValue;
16+
}
17+
}
18+
}
19+
});
20+
21+
observer.observe(element, { attributes: true });
22+
}
23+
24+
export function SetIndeterminate(element: any ): void {
25+
element.indeterminate = true;
26+
}
27+
}

src/Core/Enums/CheckboxShape.cs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// ------------------------------------------------------------------------
2+
// MIT License - Copyright (c) Microsoft Corporation. All rights reserved.
3+
// ------------------------------------------------------------------------
4+
5+
using System.ComponentModel;
6+
7+
namespace Microsoft.FluentUI.AspNetCore.Components;
8+
9+
/// <summary>
10+
/// The visual appearance of the <see cref="FluentCheckbox" />.
11+
/// </summary>
12+
public enum CheckboxShape
13+
{
14+
/// <summary>
15+
/// The default appearance. The border is square.
16+
/// </summary>
17+
[Description("square")]
18+
Square,
19+
20+
/// <summary>
21+
/// The appearance where the border is circular.
22+
/// </summary>
23+
[Description("circular")]
24+
Circular,
25+
}

src/Core/Enums/CheckboxSize.cs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// ------------------------------------------------------------------------
2+
// MIT License - Copyright (c) Microsoft Corporation. All rights reserved.
3+
// ------------------------------------------------------------------------
4+
5+
using System.ComponentModel;
6+
7+
namespace Microsoft.FluentUI.AspNetCore.Components;
8+
9+
/// <summary>
10+
/// Indicates the size of the <see cref="FluentCheckbox"/>.
11+
/// </summary>
12+
public enum CheckboxSize
13+
{
14+
/// <summary>
15+
/// Medium size.
16+
/// </summary>
17+
[Description("medium")]
18+
Medium,
19+
20+
/// <summary>
21+
/// Large size.
22+
/// </summary>
23+
[Description("large")]
24+
Large,
25+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// ------------------------------------------------------------------------
2+
// MIT License - Copyright (c) Microsoft Corporation. All rights reserved.
3+
// ------------------------------------------------------------------------
4+
5+
namespace Microsoft.FluentUI.AspNetCore.Components;
6+
7+
/// <summary>
8+
/// Event arguments for the Checkbox change event.
9+
/// </summary>
10+
public class CheckboxChangeEventArgs : EventArgs
11+
{
12+
/// <summary>
13+
/// Gets or sets the checked state of the checkbox
14+
/// </summary>
15+
public bool? Checked { get; set; }
16+
17+
/// <summary>
18+
/// Gets or sets the indeterminate state of the checkbox
19+
/// </summary>
20+
public bool? Indeterminate { get; set; }
21+
}

0 commit comments

Comments
 (0)