Skip to content

Commit

Permalink
Merge pull request #132 from Blazored/v4
Browse files Browse the repository at this point in the history
v4
  • Loading branch information
chrissainty authored May 8, 2021
2 parents d39cf0d + 228a736 commit c18f2a0
Show file tree
Hide file tree
Showing 88 changed files with 3,588 additions and 1,836 deletions.
145 changes: 145 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
# top-most EditorConfig file
root = true

# Don't use tabs for indentation.
[*]
indent_style = space
# (Please don't specify an indent_size here; that has too many unintended consequences.)

# Code files
[*.{cs,csx,vb,vbx}]
indent_size = 4
insert_final_newline = true
charset = utf-8
end_of_line = crlf

# Xml project files
[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}]
indent_size = 2

# Xml config files
[*.{props,targets,ruleset,config,nuspec,resx,vsixmanifest,vsct}]
indent_size = 2

# JSON files
[*.json]
indent_size = 2

# Dotnet code style settings:
[*.{cs,vb}]
# Sort using and Import directives with System.* appearing first
dotnet_sort_system_directives_first = true

# Avoid "this." and "Me." if not necessary
dotnet_style_qualification_for_field = false : error
dotnet_style_qualification_for_property = false : error
dotnet_style_qualification_for_method = false : error
dotnet_style_qualification_for_event = false : error

# Use language keywords instead of framework type names for type references
dotnet_style_predefined_type_for_locals_parameters_members = true : error
dotnet_style_predefined_type_for_member_access = true : error

# Suggest more modern language features when available
dotnet_style_object_initializer = true : suggestion
dotnet_style_collection_initializer = true : suggestion
dotnet_style_coalesce_expression = true : error
dotnet_style_null_propagation = true : error
dotnet_style_explicit_tuple_names = true : error

# Naming rule: private static fields should use pascal casing
dotnet_naming_symbols.private_static_field.applicable_kinds = field
dotnet_naming_symbols.private_static_field.applicable_accessibilities = private
dotnet_naming_symbols.private_static_field.required_modifiers = static
dotnet_naming_style.private_static_field.capitalization = pascal_case
dotnet_naming_rule.private_static_field.severity = error
dotnet_naming_rule.private_static_field.symbols = private_static_field
dotnet_naming_rule.private_static_field.style = private_static_field

# Naming rule: private fields should use camel casing and use underscore as a prefix
dotnet_naming_symbols.private_field.applicable_kinds = field
dotnet_naming_symbols.private_field.applicable_accessibilities = private
dotnet_naming_style.private_field.capitalization = camel_case
dotnet_naming_style.private_field.required_prefix = _
dotnet_naming_rule.private_field.severity = error
dotnet_naming_rule.private_field.symbols = private_field
dotnet_naming_rule.private_field.style = private_field

# Naming rule: non-private fields should use pascal casing
dotnet_naming_symbols.non_private_field.applicable_kinds = field
dotnet_naming_symbols.non_private_field.applicable_accessibilities = public,internal,friend,protected,protected_internal,protected_friend
dotnet_naming_style.non_private_field.capitalization = pascal_case
dotnet_naming_rule.non_private_field.severity = error
dotnet_naming_rule.non_private_field.symbols = non_private_field
dotnet_naming_rule.non_private_field.style = non_private_field

# Naming rule: parameters should use camel casing
dotnet_naming_symbols.parameter.applicable_kinds = parameter
dotnet_naming_style.parameter.capitalization = camel_case
dotnet_naming_rule.parameter.severity = error
dotnet_naming_rule.parameter.symbols = parameter
dotnet_naming_rule.parameter.style = parameter

# Naming rule: interface types should use pascal casing with I as a prefix
dotnet_naming_symbols.interface_type.applicable_kinds = interface
dotnet_naming_style.interface_type.capitalization = pascal_case
dotnet_naming_style.interface_type.required_prefix = I
dotnet_naming_rule.interface_type.severity = error
dotnet_naming_rule.interface_type.symbols = interface_type
dotnet_naming_rule.interface_type.style = interface_type

# Naming rule: non-interface types should use pascal casing
dotnet_naming_symbols.non_interface_type.applicable_kinds = class,struct,enum,delegate
dotnet_naming_style.non_interface_type.capitalization = pascal_case
dotnet_naming_rule.non_interface_type.severity = error
dotnet_naming_rule.non_interface_type.symbols = non_interface_type
dotnet_naming_rule.non_interface_type.style = non_interface_type

# Naming rule: methods, properties and events should use pascal casing
dotnet_naming_symbols.member.applicable_kinds = method,property,event
dotnet_naming_style.member.capitalization = pascal_case
dotnet_naming_rule.member.severity = error
dotnet_naming_rule.member.symbols = member
dotnet_naming_rule.member.style = member

# CSharp code style settings:

# IDE0060: Remove unused parameter
dotnet_code_quality_unused_parameters = non_public:suggestion

[*.cs]
# Prefer "var" everywhere
csharp_style_var_for_built_in_types = true : suggestion
csharp_style_var_when_type_is_apparent = true : error
csharp_style_var_elsewhere = true : suggestion

# Prefer method-like constructs to have a block body
csharp_style_expression_bodied_methods = false : none
csharp_style_expression_bodied_constructors = false : error
csharp_style_expression_bodied_operators = false : none

# Prefer property-like constructs to have an expression-body
csharp_style_expression_bodied_properties = true : suggestion
csharp_style_expression_bodied_indexers = true : suggestion
csharp_style_expression_bodied_accessors = true : suggestion

# Suggest more modern language features when available
csharp_style_pattern_matching_over_is_with_cast_check = true : suggestion
csharp_style_pattern_matching_over_as_with_null_check = true : suggestion
csharp_style_inlined_variable_declaration = true : suggestion
csharp_style_throw_expression = true : suggestion
csharp_style_conditional_delegate_call = true : suggestion

# Newline settings
csharp_new_line_before_open_brace = all
csharp_new_line_before_else = true
csharp_new_line_before_catch = true
csharp_new_line_before_finally = true
csharp_new_line_before_members_in_object_initializers = true
csharp_new_line_before_members_in_anonymous_types = true

# Spacing
csharp_space_after_cast = false
csharp_space_after_keywords_in_control_flow_statements = true
csharp_space_between_method_call_parameter_list_parentheses = false
csharp_space_between_method_declaration_parameter_list_parentheses = false
2 changes: 1 addition & 1 deletion .github/workflows/ci-main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ on:
types: [ published ]

env:
NETCORE_VERSION: '3.1.301'
NETCORE_VERSION: '5.0.202'
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
DOTNET_CLI_TELEMETRY_OPTOUT: true
PROJECT_NAME: LocalStorage
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ci-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ on:
branches: [ main ]

env:
NETCORE_VERSION: '3.1.301'
NETCORE_VERSION: '5.0.202'
PROJECT_NAME: Blazored.LocalStorage

jobs:
Expand Down
20 changes: 19 additions & 1 deletion Blazored.LocalStorage.sln
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,22 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{F20B
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BlazorWebAssembly", "samples\BlazorWebAssembly\BlazorWebAssembly.csproj", "{D6953CE0-6AD5-434C-99AA-7FF1E26724D4}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BlazorServerSide", "samples\BlazorServerSide\BlazorServerSide.csproj", "{767D657A-5410-4714-9477-99929D3FF709}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BlazorServer", "samples\BlazorServer\BlazorServer.csproj", "{767D657A-5410-4714-9477-99929D3FF709}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{9170D7A9-70CE-48E3-88A3-F11D2983103E}"
ProjectSection(SolutionItems) = preProject
.github\workflows\ci-main.yml = .github\workflows\ci-main.yml
.github\workflows\ci-pr.yml = .github\workflows\ci-pr.yml
README.md = README.md
.github\workflows\release-drafter.yml = .github\workflows\release-drafter.yml
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{B82A5126-EE01-4B4D-B642-F954B6E4E695}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Blazored.LocalStorage.Tests", "tests\Blazored.LocalStorage.Tests\Blazored.LocalStorage.Tests.csproj", "{5A6A013E-325D-4A5F-B2FA-659B4FD2BDBC}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "bUnitExample", "samples\bUnitExample\bUnitExample.csproj", "{10F6FB83-3135-4B73-B0EB-D008A31AE8FA}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -78,6 +83,18 @@ Global
{5A6A013E-325D-4A5F-B2FA-659B4FD2BDBC}.Release|x64.Build.0 = Release|Any CPU
{5A6A013E-325D-4A5F-B2FA-659B4FD2BDBC}.Release|x86.ActiveCfg = Release|Any CPU
{5A6A013E-325D-4A5F-B2FA-659B4FD2BDBC}.Release|x86.Build.0 = Release|Any CPU
{10F6FB83-3135-4B73-B0EB-D008A31AE8FA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{10F6FB83-3135-4B73-B0EB-D008A31AE8FA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{10F6FB83-3135-4B73-B0EB-D008A31AE8FA}.Debug|x64.ActiveCfg = Debug|Any CPU
{10F6FB83-3135-4B73-B0EB-D008A31AE8FA}.Debug|x64.Build.0 = Debug|Any CPU
{10F6FB83-3135-4B73-B0EB-D008A31AE8FA}.Debug|x86.ActiveCfg = Debug|Any CPU
{10F6FB83-3135-4B73-B0EB-D008A31AE8FA}.Debug|x86.Build.0 = Debug|Any CPU
{10F6FB83-3135-4B73-B0EB-D008A31AE8FA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{10F6FB83-3135-4B73-B0EB-D008A31AE8FA}.Release|Any CPU.Build.0 = Release|Any CPU
{10F6FB83-3135-4B73-B0EB-D008A31AE8FA}.Release|x64.ActiveCfg = Release|Any CPU
{10F6FB83-3135-4B73-B0EB-D008A31AE8FA}.Release|x64.Build.0 = Release|Any CPU
{10F6FB83-3135-4B73-B0EB-D008A31AE8FA}.Release|x86.ActiveCfg = Release|Any CPU
{10F6FB83-3135-4B73-B0EB-D008A31AE8FA}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -86,6 +103,7 @@ Global
{D6953CE0-6AD5-434C-99AA-7FF1E26724D4} = {F20B2AAB-FB71-4A1C-82FE-F1750B922BF6}
{767D657A-5410-4714-9477-99929D3FF709} = {F20B2AAB-FB71-4A1C-82FE-F1750B922BF6}
{5A6A013E-325D-4A5F-B2FA-659B4FD2BDBC} = {B82A5126-EE01-4B4D-B642-F954B6E4E695}
{10F6FB83-3135-4B73-B0EB-D008A31AE8FA} = {F20B2AAB-FB71-4A1C-82FE-F1750B922BF6}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {2B91883F-A85A-49EB-A9C1-ABD567A9375E}
Expand Down
9 changes: 9 additions & 0 deletions Directory.Build.props
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<Project>

<PropertyGroup Label="Package Versions">
<DotNet3Version>3.1.14</DotNet3Version>
<DotNet5Version>5.0.5</DotNet5Version>
</PropertyGroup>

</Project>
130 changes: 91 additions & 39 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,43 @@
[![Nuget version](https://img.shields.io/nuget/v/blazored.localstorage.svg?logo=nuget)](https://www.nuget.org/packages/Blazored.LocalStorage/)
[![Nuget downloads](https://img.shields.io/nuget/dt/Blazored.LocalStorage?logo=nuget)](https://www.nuget.org/packages/Blazored.LocalStorage/)
![Build & Test Main](https://github.com/Blazored/LocalStorage/workflows/Build%20&%20Test%20Main/badge.svg)

# Blazored LocalStorage
A library to provide access to local storage in Blazor applications
Blazored LocalStorage is a library that provides access to the browsers local storage APIs for Blazor applications. An additional benefit of using this library is that it will handle serializing and deserializing values when saving or retrieving them.

## Breaking Change (v3 > v4): JsonSerializerOptions
From v4 onwards we use the default the `JsonSerializerOptions` for `System.Text.Json` instead of using custom ones. This will cause values saved to local storage with v3 to break things.
To retain the old settings use the following configuration when adding Blazored LocalStorage to the DI container:

```csharp
builder.Services.AddBlazoredLocalStorage(config =>
config.JsonSerializerOptions.DictionaryKeyPolicy = JsonNamingPolicy.CamelCase;
config.JsonSerializerOptions.IgnoreNullValues = true;
config.JsonSerializerOptions.IgnoreReadOnlyProperties = true;
config.JsonSerializerOptions.PropertyNameCaseInsensitive = true;
config.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
config.JsonSerializerOptions.ReadCommentHandling = JsonCommentHandling.Skip;
config.JsonSerializerOptions.WriteIndented = false;
);
```

![Build & Test Main](https://github.com/Blazored/LocalStorage/workflows/Build%20&%20Test%20Main/badge.svg)
## Installing

[![Nuget](https://img.shields.io/nuget/v/blazored.localstorage.svg)](https://www.nuget.org/packages/Blazored.LocalStorage/)
To install the package add the following line to you csproj file replacing x.x.x with the latest version number (found at the top of this file):

### Installing
```
<PackageReference Include="Blazored.LocalStorage" Version="x.x.x" />
```

You can install from NuGet using the following command:
You can also install via the .NET CLI with the following command:

`Install-Package Blazored.LocalStorage`
```
dotnet add package Blazored.LocalStorage
```

Or via the Visual Studio package manager.
If you're using Visual Studio you can also install via the built in NuGet package manager.

### Setup
## Setup

You will need to register the local storage services with the service collection in your _Startup.cs_ file in Blazor Server.

Expand All @@ -38,34 +62,7 @@ public static async Task Main(string[] args)
}
```

### Configuration

The local storage provides options that can be modified by you at registration in your _Startup.cs_ file in Blazor Server.


```c#
public void ConfigureServices(IServiceCollection services)
{
services.AddBlazoredLocalStorage(config =>
config.JsonSerializerOptions.WriteIndented = true);
}
```
Or in your _Program.cs_ file in Blazor WebAssembly.

```c#
public static async Task Main(string[] args)
{
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("app");

builder.Services.AddBlazoredLocalStorage(config =>
config.JsonSerializerOptions.WriteIndented = true);

await builder.Build().RunAsync();
}
```

### Usage (Blazor WebAssembly)
## Usage (Blazor WebAssembly)
To use Blazored.LocalStorage in Blazor WebAssembly, inject the `ILocalStorageService` per the example below.

```c#
Expand Down Expand Up @@ -98,7 +95,7 @@ With Blazor WebAssembly you also have the option of a synchronous API, if your u
}
```

### Usage (Blazor Server)
## Usage (Blazor Server)

**NOTE:** Due to pre-rendering in Blazor Server you can't perform any JS interop until the `OnAfterRender` lifecycle method.

Expand Down Expand Up @@ -138,6 +135,61 @@ The APIs available are:
- Key()
- ContainKey()

**Note:** Blazored.LocalStorage methods will handle the serialisation and de-serialisation of the data for you, the exception is the `GetItemAsString[Async]` method.
**Note:** Blazored.LocalStorage methods will handle the serialisation and de-serialisation of the data for you, the exception is the `GetItemAsString[Async]` method which will return the raw string value from local storage.

## Configuring JSON Serializer Options
You can configure the options for the default serializer (System.Text.Json) when calling the `AddBlazoredLocalStorage` method to register services.

```c#
public static async Task Main(string[] args)
{
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("app");

builder.Services.AddBlazoredLocalStorage(config =>
config.JsonSerializerOptions.WriteIndented = true
);

await builder.Build().RunAsync();
}
```

## Using a custom JSON serializer
By default, the library uses `System.Text.Json`. If you prefer to use a different JSON library for serialization--or if you want to add some custom logic when serializing or deserializing--you can provide your own serializer which implements the `Blazored.LocalStorage.Serialization.IJsonSerializer` interface.

To register your own serializer in place of the default one, you can do the following:

```csharp
builder.Services.AddBlazoredLocalStorage();
builder.Services.Replace(ServiceDescriptor.Scoped<IJsonSerializer, MySerializer>());
```

You can find an example of this in the Blazor Server sample project. The standard serializer has been replaced with a new serializer which uses NewtonsoftJson.

## Testing with bUnit
This library provides test extensions for use with the [bUnit testing library](https://bunit.dev/). Using these test extensions will provide an in memory implementation which mimics local storage allowing more realistic testing of your components.

If you want to handle serialising and de-serialising yourself, serialise the data to a string and save using the `SetItem[Async]` method, as normal -- This method will not attempt to serialise a string value. You can then read out the data using the `GetItemAsString[Async]` method and de-serialise it yourself.
Below is an example test which uses these extensions. You can find an example project which shows this code in action in the samples folder.

```c#
public class IndexPageTests : TestContext
{
[Fact]
public async Task SavesNameToLocalStorage()
{
// Arrange
const string inputName = "John Smith";
var localStorage = this.AddBlazoredLocalStorage();
var cut = RenderComponent<BlazorWebAssembly.Pages.Index>();

// Act
cut.Find("#Name").Change(inputName);
cut.Find("#NameButton").Click();

// Assert
var name = await localStorage.GetItemAsync<string>("name");

Assert.Equal(inputName, name);
}
}
```
Loading

0 comments on commit c18f2a0

Please sign in to comment.