Skip to content

Commit

Permalink
Blazor integration for more Nimble components (#517)
Browse files Browse the repository at this point in the history
# Pull Request

## 🤨 Rationale

Partially addresses #508 

## 👩‍💻 Implementation

Newly supported controls:
- Switch
- Text Area (Multiline Text Field)
- Toggle Button, Toggle Icon Button
- Icons
    - A Razor component file is autogenerated for each icon, using a build script similar to the one from nimble-angular

Existing control updates:
- Checkbox
    - Add binding for Indeterminate property
    - Fix 2-way binding support (add JS file necessary to register the custom event used for value changes for controls that use a 'checked' property)

## 🧪 Testing

- Update [Blazor example app](https://60e89457a987cf003efc0a5b-zgartaedol.chromatic.com/blazor-client-app/wwwroot/) for newly supported components
- Manual testing in Blazor example app
- Add basic tests for newly supported components

## ✅ Checklist

- [x] I have updated the project documentation to reflect my changes or determined no changes are needed.
    - Minor updates to Blazor contributing doc
    - Component table will be updated in followup PR
  • Loading branch information
msmithNI authored Apr 27, 2022
1 parent 7bd2bfc commit 0a8039e
Show file tree
Hide file tree
Showing 29 changed files with 500 additions and 15 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "minor",
"comment": "Blazor integration for switch, text area, toggle button, icons. Fix 2-way binding for checkbox.",
"packageName": "@ni/nimble-blazor",
"email": "20709258+msmithNI@users.noreply.github.com",
"dependentChangeType": "patch"
}
8 changes: 6 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 5 additions & 2 deletions packages/nimble-blazor/.eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,17 @@ module.exports = {
overrides: [
{
files: [
'build/copyNimbleResources.js'
'build/**/*.js'
],
rules: {
// Okay to use dev dependencies in build scripts
'import/no-extraneous-dependencies': 'off',

// Okay to use console.log in build scripts
'no-console': 'off'
'no-console': 'off',

// Rollup config files use default exports
'import/no-default-export': 'off'
}
}
]
Expand Down
1 change: 1 addition & 0 deletions packages/nimble-blazor/.gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Folders
NimbleBlazor.Components/wwwroot/nimble-*/
NimbleBlazor.Components/Components/Icons/
artifacts/
bin/
obj/
Expand Down
29 changes: 28 additions & 1 deletion packages/nimble-blazor/CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
# Contributing to Nimble Blazor

## Getting Started (Windows)

For Nimble Blazor development on Windows, the suggested tools to install are:
- Visual Studio 2022 (Enterprise, if available): Choose the "ASP.NET and Web Development" Workload in the installer
- (Optional) Enable IIS (see "Enabling IIS", below)
- ASP.NET Core Runtime 6.0.x (6.0.3 or higher): Choose "Hosting Bundle" under ASP.NET Core Runtime, on the [.NET 6.0 Download Page](https://dotnet.microsoft.com/en-us/download/dotnet/6.0)

Initialize and build the Nimble monorepo (`npm install` + `npm run build` from the root `nimble` directory) before working with the solution in Visual Studio.

In Visual Studio, run either the `NimbleBlazor.Demo.Server` or `NimbleBlazor.Demo.Projects` to see the Blazor demo apps.

## Creating a Blazor wrapper for a Nimble element

In Nimble Blazor, the Nimble web components are wrapped as [Razor Components](https://docs.microsoft.com/en-us/aspnet/core/blazor/?view=aspnetcore-6.0#components) that consist of a `.razor` template file and a corresponding `.razor.cs` C# implementation file.
Expand Down Expand Up @@ -37,4 +48,20 @@ public partial class NimbleButton : ComponentBase

Testing the Nimble Blazor components is possible through the use of xUnit and bUnit. Each Nimble Blazor component should have a corresponding test file.

Each Nimble Blazor component should also be showcased in the `NimbleBlazor.Demo` example projects. Simple component examples can be added directly in the `ComponentsDemo.razor` file (in the `NimbleBlazor.Demo.Shared` project).
Each Nimble Blazor component should also be showcased in the `NimbleBlazor.Demo` example projects. Simple component examples can be added directly in the `ComponentsDemo.razor` file (in the `NimbleBlazor.Demo.Shared` project).

## Additional Tips

### Enabling IIS

Click Start, open "Turn Windows features on or off", and configure "Web Management Tools" and "World Wide Web Services" in the following way:
![IIS Feature Configuration](/packages/nimble-blazor/docs/WindowsFeatures-IIS.jpg)
### Running published output

The commandline build will create a published distribution of the Blazor client example app, which can also be tested via IIS:
- Open Internet Information Services (IIS) Manager
- In the left pane, right click "Sites" and click "Add Website..."
- Pick a site name
- Under "Physical Path", click [...] and browse to your `nimble-blazor\dist\blazor-client-app` directory
- Under "Binding", pick a port other than 80 (such as 8080), then click "OK"
- Open http://localhost:8080 (or whatever port you chose)
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,37 @@
<NimbleButton Appearance="Appearance.Block">Block Button</NimbleButton>
<NimbleButton Appearance="Appearance.Ghost">Ghost Button</NimbleButton>
</div>
<div class="sub-container">
<div class="container-label">Buttons - Toggle</div>
<NimbleToggleButton Appearance="Appearance.Outline">Outline Toggle Button</NimbleToggleButton>
<NimbleToggleButton Appearance="Appearance.Block">Block Toggle Button</NimbleToggleButton>
<NimbleToggleButton Appearance="Appearance.Ghost">Ghost Toggle Button</NimbleToggleButton>
<NimbleToggleButton ContentHidden="true"Appearance="Appearance.Outline">
<NimbleKeyIcon slot="start"></NimbleKeyIcon>
Icon Toggle Button
</NimbleToggleButton>
</div>
<div class="sub-container">
<div class="container-label">Checkbox</div>
<NimbleCheckbox>Checkbox label</NimbleCheckbox>
<NimbleCheckbox>Checkbox label</NimbleCheckbox>
<NimbleCheckbox>Checkbox label</NimbleCheckbox>
</div>
<div class="sub-container">
<div class="container-label">Icons (subset - see <a href="https://ni.github.io/nimble/storybook/?path=/docs/icons--icons">
Icons Storybook page</a> for complete set)</div>
<NimbleChartDiagramIcon></NimbleChartDiagramIcon>
<NimbleSquareCheckIcon class="pass"></NimbleSquareCheckIcon>
<NimbleXmarkCheckIcon class="warning"></NimbleXmarkCheckIcon>
<NimbleXmarkIcon class="fail"></NimbleXmarkIcon>
</div>
<div class="sub-container">
<div class="container-label">Menu</div>
<NimbleMenu>
<header>Header 1</header>
<NimbleMenuItem>
Item 1
<span slot="start">[+]</span>
<NimbleAddIcon slot="start"></NimbleAddIcon>
</NimbleMenuItem>
<NimbleMenuItem>Item 2</NimbleMenuItem>
<hr>
Expand All @@ -44,6 +62,19 @@
<NimbleListOption>Option 3</NimbleListOption>
</NimbleSelect>
</div>
<div class="sub-container">
<div class="container-label">Switch</div>
<NimbleSwitch>Switch</NimbleSwitch>
<NimbleSwitch Value="true">
Switch with checked/unchecked messages
<span slot="unchecked-message">Off</span>
<span slot="checked-message">On</span>
</NimbleSwitch>
</div>
<div class="sub-container">
<div class="container-label">Text Area</div>
<NimbleTextArea Placeholder="Text Area" Cols="50" Rows="5" TextAreaResize="TextAreaResize.Horizontal" Value="Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.">Text Area Label</NimbleTextArea>
</div>
<div class="sub-container">
<div class="container-label">Text Field</div>
<NimbleTextField Placeholder="Text Field" Value="Here is text!">Text Field Label</NimbleTextField>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
namespace NimbleBlazor.Demo.Shared.Pages
{
/// <summary>
/// The CustomApp Demo.
/// The components demo page
/// </summary>
public partial class CustomApp
public partial class ComponentsDemo
{
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
.container-label {
.root {
background-color: var(--ni-nimble-application-background-color);
}

.container-label {
font: var(--ni-nimble-group-header-font);
color: var(--ni-nimble-group-header-font-color);
padding-bottom: var(--ni-nimble-standard-padding);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,4 @@
</ErrorContent>
</ErrorBoundary>
</div>
</NimbleThemeProvider>>
</NimbleThemeProvider>
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
<nimble-checkbox disabled="@Disabled"
current-checked="@BindConverter.FormatValue(CurrentValue)"
@onnimblecheckedchange="(__value) => CurrentValue = __value.Checked"
indeterminate="@Indeterminate"
required="@Required"
readonly="@ReadOnly"
@attributes="AdditionalAttributes">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ public partial class NimbleCheckbox : NimbleInputBase<bool>
[Parameter]
public bool? Required { get; set; }

[Parameter]
public bool? Indeterminate { get; set; }

[Parameter]
public bool? ReadOnly { get; set; }

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
@namespace NimbleBlazor.Components
@inherits NimbleInputBase<bool>
<nimble-switch disabled="@Disabled"
current-checked="@BindConverter.FormatValue(CurrentValue)"
@onnimblecheckedchange="(__value) => CurrentValue = __value.Checked"
required="@Required"
readonly="@ReadOnly"
@attributes="AdditionalAttributes">
@ChildContent
</nimble-switch>
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using System.Diagnostics.CodeAnalysis;
using Microsoft.AspNetCore.Components;

namespace NimbleBlazor.Components;

public partial class NimbleSwitch : NimbleInputBase<bool>
{
[Parameter]
public bool? Disabled { get; set; }

[Parameter]
public bool? Required { get; set; }

[Parameter]
public bool? ReadOnly { get; set; }

[Parameter]
public RenderFragment? ChildContent { get; set; }

protected override bool TryParseValueFromString(string? value, [MaybeNullWhen(false)] out bool result, [NotNullWhen(false)] out string? validationErrorMessage) => throw new NotSupportedException($"This component does not parse string inputs. Bind to the '{nameof(CurrentValue)}' property, not '{nameof(CurrentValueAsString)}'.");
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
@namespace NimbleBlazor.Components
@inherits NimbleInputBase<string?>
<nimble-text-area autofocus="@AutoFocus"
appearance="@Appearance.ToAttributeValue()"
current-value="@BindConverter.FormatValue(CurrentValue)"
@onchange="@(EventCallback.Factory.CreateBinder<string?>(this, __value => CurrentValueAsString = __value, CurrentValueAsString))"
size="@Size"
minlength="@MinLength"
maxlength="@MaxLength"
resize="@TextAreaResize.ToAttributeValue()"
rows="@Rows"
cols="@Cols"
spellcheck="@Spellcheck"
readonly="@ReadOnly"
disabled="@Disabled"
required="@Required"
placeholder="@Placeholder"
@attributes="AdditionalAttributes">
@ChildContent
</nimble-text-area>
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
using System.Diagnostics.CodeAnalysis;
using Microsoft.AspNetCore.Components;

namespace NimbleBlazor.Components;

public partial class NimbleTextArea : NimbleInputBase<string?>
{
[Parameter]
public bool? Disabled { get; set; }

[Parameter]
public bool? ReadOnly { get; set; }

[Parameter]
public bool? Required { get; set; }

[Parameter]
public bool? AutoFocus { get; set; }

[Parameter]
public int? Size { get; set; }

[Parameter]
public Appearance? Appearance { get; set; }

[Parameter]
public TextAreaResize? TextAreaResize { get; set; }

[Parameter]
public string? Placeholder { get; set; }

[Parameter]
public int? MinLength { get; set; }

[Parameter]
public int? MaxLength { get; set; }

[Parameter]
public int? Rows { get; set; }

[Parameter]
public int? Cols { get; set; }

[Parameter]
public bool? Spellcheck { get; set; }

[Parameter]
public RenderFragment? ChildContent { get; set; }

protected override bool TryParseValueFromString(string? value, out string? result, [NotNullWhen(false)] out string? validationErrorMessage)
{
result = value;
validationErrorMessage = null;
return true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
@namespace NimbleBlazor.Components
@inherits NimbleInputBase<bool>
<nimble-toggle-button
current-checked="@BindConverter.FormatValue(CurrentValue)"
@onnimblecheckedchange="(__value) => CurrentValue = __value.Checked"
appearance="@Appearance.ToAttributeValue()"
disabled="@Disabled"
autofocus="@AutoFocus"
content-hidden="@ContentHidden"
@attributes="AdditionalAttributes">
@ChildContent
</nimble-toggle-button>
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using System.Diagnostics.CodeAnalysis;
using Microsoft.AspNetCore.Components;

namespace NimbleBlazor.Components;

public partial class NimbleToggleButton : NimbleInputBase<bool>
{
[Parameter]
public Appearance? Appearance { get; set; }

[Parameter]
public bool? Disabled { get; set; }

[Parameter]
public bool? ContentHidden { get; set; }

[Parameter]
public bool? AutoFocus { get; set; }

[Parameter]
public RenderFragment? ChildContent { get; set; }

protected override bool TryParseValueFromString(string? value, [MaybeNullWhen(false)] out bool result, [NotNullWhen(false)] out string? validationErrorMessage) => throw new NotSupportedException($"This component does not parse string inputs. Bind to the '{nameof(CurrentValue)}' property, not '{nameof(CurrentValueAsString)}'.");
}
14 changes: 14 additions & 0 deletions packages/nimble-blazor/NimbleBlazor.Components/NimbleIconBase.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using Microsoft.AspNetCore.Components;

namespace NimbleBlazor.Components;

/// <summary>
/// Base class for Nimble icons.
/// </summary>
public abstract class NimbleIconBase : ComponentBase
{
/// <summary>
/// Gets or sets a collection of additional attributes that will be applied to the created element.
/// </summary>
[Parameter(CaptureUnmatchedValues = true)] public IReadOnlyDictionary<string, object>? AdditionalAttributes { get; set; }
}
16 changes: 16 additions & 0 deletions packages/nimble-blazor/NimbleBlazor.Components/TextAreaResize.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
namespace NimbleBlazor.Components;

public enum TextAreaResize
{
None,
Both,
Horizontal,
Vertical
}

internal static class TextAreaResizeExtensions
{
private static readonly Dictionary<TextAreaResize, string> _textAreaResizeValues = AttributeHelpers.GetEnumNamesAsKebabCaseValues<TextAreaResize>();

public static string? ToAttributeValue(this TextAreaResize? value) => value == null ? null : _textAreaResizeValues[value.Value];
}
Loading

0 comments on commit 0a8039e

Please sign in to comment.