Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
AClerbois committed Jan 22, 2025
1 parent 20f536c commit a7bc5aa
Show file tree
Hide file tree
Showing 13 changed files with 362 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<div style="display: flex;flex-direction: column; gap: 12px;">
<FluentCheckbox Shape="@CheckboxShape.Square" Label="Square checked" Checked="true" />
<FluentCheckbox Shape="@CheckboxShape.Circular" Label="Circular checked" Checked="true" />

<FluentCheckbox Shape="@CheckboxShape.Square" Label="Square unchecked" Checked="false" />
<FluentCheckbox Shape="@CheckboxShape.Circular" Label="Circular unchecked" Checked="false" />
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<div style="display: flex;flex-direction: column; gap: 12px;">
<FluentCheckbox Shape="@CheckboxShape.Square" Label="Square disabled checked" Disabled="true" Checked="true" />
<FluentCheckbox Shape="@CheckboxShape.Circular" Label="Circular disabled checked" Disabled="true" Checked="true" />

<FluentCheckbox Shape="@CheckboxShape.Square" Label="Square disabled unchecked" Disabled="true" Checked="false" />
<FluentCheckbox Shape="@CheckboxShape.Circular" Label="Circular disabled unchecked" Disabled="true" Checked="false" />
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<div>
<FluentCheckbox Indeterminate="true" /> <br />
<FluentCheckbox Indeterminate="true" Label="Indeterminate" />
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<div>
<FluentCheckbox Shape="@CheckboxShape.Square" Checked="true" />
<FluentCheckbox Shape="@CheckboxShape.Square" Checked="false" />
<FluentCheckbox Shape="@CheckboxShape.Square" Disabled="true" Checked="true" />
<FluentCheckbox Shape="@CheckboxShape.Square" Disabled="true" />
<FluentCheckbox Shape="@CheckboxShape.Square" Size="@CheckboxSize.Large" Checked="true" />
<FluentCheckbox Shape="@CheckboxShape.Square" Size="@CheckboxSize.Large" Checked="false" />
<FluentCheckbox Shape="@CheckboxShape.Square" Size="@CheckboxSize.Large" Disabled="true" Checked="true" />
<FluentCheckbox Shape="@CheckboxShape.Square" Size="@CheckboxSize.Large" Disabled="true" />

<br />

<FluentCheckbox Shape="@CheckboxShape.Circular" Checked="true" />
<FluentCheckbox Shape="@CheckboxShape.Circular" Checked="false" />
<FluentCheckbox Shape="@CheckboxShape.Circular" Disabled="true" Checked="true" />
<FluentCheckbox Shape="@CheckboxShape.Circular" Disabled="true" />
<FluentCheckbox Shape="@CheckboxShape.Circular" Size="@CheckboxSize.Large" Checked="true" />
<FluentCheckbox Shape="@CheckboxShape.Circular" Size="@CheckboxSize.Large" Checked="false" />
<FluentCheckbox Shape="@CheckboxShape.Circular" Size="@CheckboxSize.Large" Disabled="true" Checked="true" />
<FluentCheckbox Shape="@CheckboxShape.Circular" Size="@CheckboxSize.Large" Disabled="true" />
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<div>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
---
title: Checkbox
route: /Checkbox
---

# Checkbox

A **FluentCheckbox** component enables a user to select or deselect an option.
It's typically used to capture a boolean value.

## Appearance

The apparent style of a checkbox can be changed by setting the `Shape` property, but also by setting the `Size` property.

You can also add a label to the checkbox by setting the `Label` property.
The label will be automatically positioned next to the checkbox.

We recommend using a spacing of 24px between checkboxes and other components.

{{ CheckboxAppearances }}

### Size

The size of the checkbox can be adjusted using the `Size` property. The available sizes are:

- `Medium`: The default size.
- `Large`: A larger size for the checkbox.

{{ CheckboxSizes }}

### Indeterminate

The `FluentCheckbox` component supports an indeterminate state, which can be useful for scenarios where a checkbox represents a mixed or partial selection.
The indeterminate state is visually distinct from the checked and unchecked states.

To set the checkbox to the indeterminate state, use the `Indeterminate` property.

{{ CheckboxIndeterminate }}

## Three-State Checkbox

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.

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.

- `ThreeState`: Enables the three-state mode.
- `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.

{{ CheckboxThreeStates }}

## API FluentCheckbox

{{ API Type=FluentCheckbox }}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">

<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
Expand Down
43 changes: 43 additions & 0 deletions src/Core/Components/Checkbox/FluentCheckbox.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
@namespace Microsoft.FluentUI.AspNetCore.Components
@using Microsoft.FluentUI.AspNetCore.Components.Extensions
@inherits FluentInputBase<bool?>

<AddTag Name="fluent-field" TagWhen="@(() => HasLabel())" label-position="after">
@if (HasLabel())
{
<label Required="@Required" for="@Id" slot="label">
@Label
@LabelTemplate
</label>
}

<fluent-checkbox id="@Id"
@ref="@Element"
disabled="@Disabled"
autofocus="@Autofocus"
checked="@Checked"
indeterminate="@Indeterminate"
name="@Name"
required="@Required"
value="@Value"
shape="@Shape.ToAttributeValue()"
size="@Size.ToAttributeValue()"
@onchange="@ChangeHandlerAsync"
@attributes="AdditionalAttributes"
slot="input">

@if (StartTemplate != null)
{
<div slot="start">
@StartTemplate
</div>
}

@if (EndTemplate != null)
{
<div slot="end">
@EndTemplate
</div>
}
</fluent-checkbox>
</AddTag>
126 changes: 126 additions & 0 deletions src/Core/Components/Checkbox/FluentCheckbox.razor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
// ------------------------------------------------------------------------
// MIT License - Copyright (c) Microsoft Corporation. All rights reserved.
// ------------------------------------------------------------------------

using System.Diagnostics.CodeAnalysis;
using System.Reflection.Metadata;
using Microsoft.AspNetCore.Components;
using Microsoft.FluentUI.AspNetCore.Components.Utilities;
using Microsoft.JSInterop;

namespace Microsoft.FluentUI.AspNetCore.Components;

/// <summary>
/// The FluentCheckbox component is used to render a checkbox input...
/// Test WIP
/// </summary>
public partial class FluentCheckbox : FluentInputBase<bool?>, IFluentComponentElementBase
{
private const string JAVASCRIPT_FILE = FluentJSModule.JAVASCRIPT_ROOT + "Checkbox/FluentCheckbox.razor.js";

/// <inheritdoc />
[Parameter]
public ElementReference Element { get; set; }

/// <summary>
/// Gets or sets the checked state of the checkbox
/// </summary>
[Parameter]
public bool? Checked { get; set; }

/// <summary>
/// Gets or sets the indeterminate state of the checkbox
/// </summary>
[Parameter]
public bool? Indeterminate { get; set; }

/// <summary>
/// Gets or sets a value indicating whether the CheckBox will allow three check states rather than two.
/// </summary>
[Parameter]
public bool ThreeState { get; set; }

/// <summary>
/// Gets or sets a value indicating the order of the three states of the CheckBox.
/// False(by default), the order is Unchecked -> Checked -> Intermediate.
/// True: the order is Unchecked -> Intermediate -> Checked.
/// </summary>
[Parameter]
public bool ThreeStateChecked { get; set; }

/// <summary>
/// Gets or sets the shape of the checkbox
/// </summary>
[Parameter]
public CheckboxShape Shape { get; set; } = CheckboxShape.Square;

/// <summary>
/// Gets or sets the content to prefix the input component.
/// </summary>
[Parameter]
public virtual RenderFragment? StartTemplate { get; set; }

/// <summary>
/// Gets or sets the content to suffix the input component.
/// </summary>
[Parameter]
public virtual RenderFragment? EndTemplate { get; set; }

/// <summary>
/// Gets or sets the size of the checkbox. See <see cref="Components.CheckboxSize"/>
/// </summary>
[Parameter]
public CheckboxSize Size { get; set; } = CheckboxSize.Medium;

/// <inheritdoc />
protected override void OnParametersSet()
{
base.OnParametersSet();
if (HasLabel())
{
// When the id is not provided, generate a unique id. This allow to use the label for.
Id ??= $"checkbox-{Identifier.NewId()}";
}
}

/// <summary>
/// Parses a string to create the <see cref="Microsoft.AspNetCore.Components.Forms.InputBase{TValue}.Value"/>.
/// </summary>
/// <param name="value">The string value to be parsed.</param>
/// <param name="result">The result to inject into the Value.</param>
/// <param name="validationErrorMessage">If the value could not be parsed, provides a validation error message.</param>
/// <returns>True if the value could be parsed; otherwise false.</returns>
protected override bool TryParseValueFromString(string? value, [MaybeNullWhen(false)] out bool? result, [NotNullWhen(false)] out string? validationErrorMessage)
{
if (bool.TryParse(value, out var parsedValue))
{
result = parsedValue;
validationErrorMessage = null;
return true;
}

result = null;
validationErrorMessage = "The provided value is not a valid boolean.";
return false;
}
/// <inheritdoc />
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
if (Indeterminate.HasValue && Indeterminate == true)
{
await SetElementToIndeterminateAsync();
}
}
}

private async Task SetElementToIndeterminateAsync()
{
var jsModule = await JSModule.ImportJavaScriptModuleAsync(JAVASCRIPT_FILE);
await jsModule.InvokeVoidAsync("Microsoft.FluentUI.Blazor.TextInput.ObserveAttributeChanges", Element);
}

private bool HasLabel()
=> !string.IsNullOrEmpty(Label) || LabelTemplate is not null;
}
27 changes: 27 additions & 0 deletions src/Core/Components/Checkbox/FluentCheckbox.razor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
export namespace Microsoft.FluentUI.Blazor.Checkbox {

/**
* Observe the changes in the ‘value’ attribute to update the element's ‘value’ property.
* Wait for this PR to delete the code.
* https://github.com/microsoft/fluentui/pull/33144
*/
export function ObserveAttributeChanges(element: HTMLElement): void {
const observer = new MutationObserver((mutationsList) => {
for (let mutation of mutationsList) {
if (mutation.type === "attributes" && mutation.attributeName === "value") {
const newValue = element.getAttribute("value");
const field = element as any;
if (newValue !== field.value) {
field.value = newValue;
}
}
}
});

observer.observe(element, { attributes: true });
}

export function SetIndeterminate(element: any ): void {
element.indeterminate = true;
}
}
25 changes: 25 additions & 0 deletions src/Core/Enums/CheckboxShape.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// ------------------------------------------------------------------------
// MIT License - Copyright (c) Microsoft Corporation. All rights reserved.
// ------------------------------------------------------------------------

using System.ComponentModel;

namespace Microsoft.FluentUI.AspNetCore.Components;

/// <summary>
/// The visual appearance of the <see cref="FluentCheckbox" />.
/// </summary>
public enum CheckboxShape
{
/// <summary>
/// The default appearance. The border is square.
/// </summary>
[Description("square")]
Square,

/// <summary>
/// The appearance where the border is circular.
/// </summary>
[Description("circular")]
Circular,
}
25 changes: 25 additions & 0 deletions src/Core/Enums/CheckboxSize.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// ------------------------------------------------------------------------
// MIT License - Copyright (c) Microsoft Corporation. All rights reserved.
// ------------------------------------------------------------------------

using System.ComponentModel;

namespace Microsoft.FluentUI.AspNetCore.Components;

/// <summary>
/// Indicates the size of the <see cref="FluentCheckbox"/>.
/// </summary>
public enum CheckboxSize
{
/// <summary>
/// Medium size.
/// </summary>
[Description("medium")]
Medium,

/// <summary>
/// Large size.
/// </summary>
[Description("large")]
Large,
}
21 changes: 21 additions & 0 deletions src/Core/Events/CheckboxChangeEventArgs.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// ------------------------------------------------------------------------
// MIT License - Copyright (c) Microsoft Corporation. All rights reserved.
// ------------------------------------------------------------------------

namespace Microsoft.FluentUI.AspNetCore.Components;

/// <summary>
/// Event arguments for the Checkbox change event.
/// </summary>
public class CheckboxChangeEventArgs : EventArgs
{
/// <summary>
/// Gets or sets the checked state of the checkbox
/// </summary>
public bool? Checked { get; set; }

/// <summary>
/// Gets or sets the indeterminate state of the checkbox
/// </summary>
public bool? Indeterminate { get; set; }
}

0 comments on commit a7bc5aa

Please sign in to comment.